This commit is contained in:
2025-02-08 23:07:07 +08:00
parent 3fcddf2d20
commit ce4e90e18d
5 changed files with 214 additions and 30 deletions

View File

@@ -15,6 +15,7 @@
"@tauri-apps/api": "^2.2.0",
"@tauri-apps/plugin-os": "^2.2.0",
"@tauri-apps/plugin-shell": "^2.2.0",
"axios": "^1.7.9",
"framer-motion": "^11.18.2",
"lodash": "^4.17.21",
"react": "^18.3.1",

View File

@@ -23,6 +23,9 @@ importers:
'@tauri-apps/plugin-shell':
specifier: ^2.2.0
version: 2.2.0
axios:
specifier: ^1.7.9
version: 1.7.9
framer-motion:
specifier: ^11.18.2
version: 11.18.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -1717,6 +1720,9 @@ packages:
arg@5.0.2:
resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
asynckit@0.4.0:
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
autoprefixer@10.4.20:
resolution: {integrity: sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==}
engines: {node: ^10 || ^12 || >=14}
@@ -1724,6 +1730,9 @@ packages:
peerDependencies:
postcss: ^8.1.0
axios@1.7.9:
resolution: {integrity: sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==}
balanced-match@1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
@@ -1779,6 +1788,10 @@ packages:
resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==}
engines: {node: '>=12.5.0'}
combined-stream@1.0.8:
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
engines: {node: '>= 0.8'}
commander@4.1.1:
resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
engines: {node: '>= 6'}
@@ -1817,6 +1830,10 @@ packages:
resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
engines: {node: '>=0.10.0'}
delayed-stream@1.0.0:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'}
didyoumean@1.2.2:
resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
@@ -1859,10 +1876,23 @@ packages:
resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==}
hasBin: true
follow-redirects@1.15.9:
resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==}
engines: {node: '>=4.0'}
peerDependencies:
debug: '*'
peerDependenciesMeta:
debug:
optional: true
foreground-child@3.3.0:
resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==}
engines: {node: '>=14'}
form-data@4.0.1:
resolution: {integrity: sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==}
engines: {node: '>= 6'}
fraction.js@4.3.7:
resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
@@ -1999,6 +2029,14 @@ packages:
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
engines: {node: '>=8.6'}
mime-db@1.52.0:
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
engines: {node: '>= 0.6'}
mime-types@2.1.35:
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
engines: {node: '>= 0.6'}
minimatch@9.0.5:
resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
engines: {node: '>=16 || 14 >=14.17'}
@@ -2113,6 +2151,9 @@ packages:
resolution: {integrity: sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==}
engines: {node: ^10 || ^12 || >=14}
proxy-from-env@1.1.0:
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
@@ -3687,7 +3728,7 @@ snapshots:
'@react-aria/calendar@3.6.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@internationalized/date': 3.6.0
'@internationalized/date': 3.7.0
'@react-aria/i18n': 3.12.4(react@18.3.1)
'@react-aria/interactions': 3.22.5(react@18.3.1)
'@react-aria/live-announcer': 3.4.1
@@ -3739,7 +3780,7 @@ snapshots:
'@react-aria/datepicker@3.12.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@internationalized/date': 3.6.0
'@internationalized/date': 3.7.0
'@internationalized/number': 3.6.0
'@internationalized/string': 3.2.5
'@react-aria/focus': 3.19.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -4241,7 +4282,7 @@ snapshots:
'@react-stately/calendar@3.6.0(react@18.3.1)':
dependencies:
'@internationalized/date': 3.6.0
'@internationalized/date': 3.7.0
'@react-stately/utils': 3.10.5(react@18.3.1)
'@react-types/calendar': 3.5.0(react@18.3.1)
'@react-types/shared': 3.26.0(react@18.3.1)
@@ -4284,7 +4325,7 @@ snapshots:
'@react-stately/datepicker@3.11.0(react@18.3.1)':
dependencies:
'@internationalized/date': 3.6.0
'@internationalized/date': 3.7.0
'@internationalized/string': 3.2.5
'@react-stately/form': 3.1.1(react@18.3.1)
'@react-stately/overlays': 3.6.13(react@18.3.1)
@@ -4499,7 +4540,7 @@ snapshots:
'@react-types/calendar@3.5.0(react@18.3.1)':
dependencies:
'@internationalized/date': 3.6.0
'@internationalized/date': 3.7.0
'@react-types/shared': 3.26.0(react@18.3.1)
react: 18.3.1
@@ -4526,7 +4567,7 @@ snapshots:
'@react-types/datepicker@3.9.0(react@18.3.1)':
dependencies:
'@internationalized/date': 3.6.0
'@internationalized/date': 3.7.0
'@react-types/calendar': 3.6.0(react@18.3.1)
'@react-types/overlays': 3.8.12(react@18.3.1)
'@react-types/shared': 3.27.0(react@18.3.1)
@@ -4858,6 +4899,8 @@ snapshots:
arg@5.0.2: {}
asynckit@0.4.0: {}
autoprefixer@10.4.20(postcss@8.5.1):
dependencies:
browserslist: 4.24.4
@@ -4868,6 +4911,14 @@ snapshots:
postcss: 8.5.1
postcss-value-parser: 4.2.0
axios@1.7.9:
dependencies:
follow-redirects: 1.15.9
form-data: 4.0.1
proxy-from-env: 1.1.0
transitivePeerDependencies:
- debug
balanced-match@1.0.2: {}
binary-extensions@2.3.0: {}
@@ -4925,6 +4976,10 @@ snapshots:
color-convert: 2.0.1
color-string: 1.9.1
combined-stream@1.0.8:
dependencies:
delayed-stream: 1.0.0
commander@4.1.1: {}
compute-scroll-into-view@3.1.1: {}
@@ -4949,6 +5004,8 @@ snapshots:
deepmerge@4.3.1: {}
delayed-stream@1.0.0: {}
didyoumean@1.2.2: {}
dlv@1.1.3: {}
@@ -5007,11 +5064,19 @@ snapshots:
flat@5.0.2: {}
follow-redirects@1.15.9: {}
foreground-child@3.3.0:
dependencies:
cross-spawn: 7.0.6
signal-exit: 4.1.0
form-data@4.0.1:
dependencies:
asynckit: 0.4.0
combined-stream: 1.0.8
mime-types: 2.1.35
fraction.js@4.3.7: {}
framer-motion@11.18.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
@@ -5124,6 +5189,12 @@ snapshots:
braces: 3.0.3
picomatch: 2.3.1
mime-db@1.52.0: {}
mime-types@2.1.35:
dependencies:
mime-db: 1.52.0
minimatch@9.0.5:
dependencies:
brace-expansion: 2.0.1
@@ -5212,6 +5283,8 @@ snapshots:
picocolors: 1.1.1
source-map-js: 1.2.1
proxy-from-env@1.1.0: {}
queue-microtask@1.2.3: {}
react-dom@18.3.1(react@18.3.1):

View File

@@ -1,13 +1,8 @@
import {
Input,
Button,
Link,
Select,
SelectItem,
DatePicker,
} from "@heroui/react";
import { Input, Button, Link, Select, SelectItem } from "@heroui/react";
import { IconMail, IconLock } from "@tabler/icons-react";
import { useState } from "react";
import http from "../http";
import { useNavigate } from "react-router-dom";
export default function SignupView({
onLogin,
@@ -17,6 +12,44 @@ export default function SignupView({
email: string;
}) {
const [emailValue, setEmailValue] = useState(email);
const [firstName, setFirstName] = useState("");
const [lastName, setLastName] = useState("");
const [nric, setNric] = useState("");
const [gender, setGender] = useState("0");
const dateOfBirthInput = document.getElementById("dateOfBirthInput");
const [password, setPassword] = useState("");
const [confirmPassword, setConfirmPassword] = useState("");
const navigate = useNavigate();
const handleSubmit = async () => {
const user = {
id: "",
firstName,
lastName,
gender: Number(gender),
nationalRegistrationIdentityCardNumber: nric,
email: emailValue,
password,
dateOfBirth: new Date((dateOfBirthInput as any).value),
resumeName: "",
whoAmI: "",
};
try {
const response = await http.post("/User/register", user);
if (response.status != 200) {
throw new Error("Failed to sign up");
}
// Handle successful signup (e.g., show a message, redirect, etc.)
console.log("User signed up successfully");
} catch (error) {
console.error("Error during signup:", error);
}
};
return (
<div className="flex flex-col gap-6">
<div className="flex flex-col">
@@ -25,8 +58,16 @@ export default function SignupView({
</div>
<div className="flex flex-col gap-2">
<div className="flex flex-row gap-2">
<Input label="First name" />
<Input label="Last name" />
<Input
label="First name"
value={firstName}
onValueChange={setFirstName}
/>
<Input
label="Last name"
value={lastName}
onValueChange={setLastName}
/>
</div>
<Input
endContent={<IconMail />}
@@ -35,34 +76,46 @@ export default function SignupView({
value={emailValue}
onValueChange={setEmailValue}
/>
<Input label="NRIC" />
<Input label="NRIC" value={nric} onValueChange={setNric} />
<div className="flex flex-row gap-2">
<Select label="Gender">
<SelectItem>Male</SelectItem>
<SelectItem>Female</SelectItem>
<Select
label="Gender"
selectedKeys={[gender]}
onChange={(e) => {
setGender(e.target.value);
}}
>
<SelectItem key={"0"}>Male</SelectItem>
<SelectItem key={"1"}>Female</SelectItem>
</Select>
<DatePicker label="Date of birth" />
<input
type="date"
className="rounded-xl px-4 transition-colors dark:bg-neutral-800 dark:hover:bg-neutral-700"
id="dateOfBirthInput"
/>
</div>
<Input endContent={<IconLock />} label="Password" type="password" />
<Input
endContent={<IconLock />}
label="Password"
type="password"
value={password}
onValueChange={setPassword}
/>
<Input
endContent={<IconLock />}
label="Confirm password"
type="password"
value={confirmPassword}
onValueChange={setConfirmPassword}
/>
</div>
<div className="flex flex-col gap-4 w-full">
<Button color="primary" className="w-full">
<Button color="primary" className="w-full" onPress={handleSubmit}>
Sign up
</Button>
<div className="flex flex-row gap-2 w-full justify-center *:my-auto">
<p className="text-sm">Already have an account?</p>
<Link
color="primary"
onPress={() => {
onLogin();
}}
className="text-sm"
>
<Link color="primary" onPress={onLogin} className="text-sm">
Login
</Link>
</div>

View File

@@ -0,0 +1,44 @@
import axios from "axios";
const http = axios.create({
baseURL: "http://localhost:5117",
});
// Add a request interceptor
http.interceptors.request.use(
function (config) {
// Do something before request is sent
let accessToken = localStorage.getItem("accessToken");
if (accessToken) {
config.headers["Authorization"] = `Bearer ${accessToken}`;
}
if (config.data && config.data.user) {
delete config.data.user;
}
return config;
},
function (error) {
// Do something with request error
return Promise.reject(error);
}
);
// Add a response interceptor
http.interceptors.response.use(
function (response) {
// Any status code that lie within the range of 2xx cause this function to trigger
// Do something with response data
return response;
},
function (error) {
// Any status codes that falls outside the range of 2xx cause this function to trigger
// Do something with response error
if (error.response.status === 401 || error.response.status === 403) {
localStorage.clear();
window.location.assign("/error");
}
return Promise.reject(error);
}
);
export default http;

View File

@@ -12,6 +12,17 @@ var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddDbContext<DataContext>();
// Add CORS services
builder.Services.AddCors(options =>
{
options.AddDefaultPolicy(corsBuilder =>
{
corsBuilder.WithOrigins("http://localhost:1420")
.AllowAnyHeader()
.AllowAnyMethod();
});
});
// Authentication
var secret = builder.Configuration.GetValue<string>("Authentication:Secret");
if (string.IsNullOrEmpty(secret))
@@ -72,6 +83,8 @@ if (app.Environment.IsDevelopment())
app.UseSwaggerUI();
}
app.UseCors();
app.UseHttpsRedirection();
app.UseAuthorization();