Skip to content

Commit 295873c

Browse files
authored
Merge branch 'devhub-ai:main' into Danny-Update-Profile-page
2 parents f179d81 + cb84077 commit 295873c

27 files changed

+2828
-656
lines changed

client/.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ pnpm-debug.log*
88
lerna-debug.log*
99

1010
node_modules
11-
dist
1211
dist-ssr
1312
*.local
1413

client/dist/assets/index-CLZc1W7c.js

Lines changed: 256 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/dist/assets/index-ndDbH7-g.css

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/dist/index.html

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<link rel="icon" type="image" href="/logo.png" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7+
<title>DevHub</title>
8+
<script type="module" crossorigin src="/assets/index-CLZc1W7c.js"></script>
9+
<link rel="stylesheet" crossorigin href="/assets/index-ndDbH7-g.css">
10+
</head>
11+
<body>
12+
<div id="root"></div>
13+
</body>
14+
</html>

client/dist/logo.png

22.2 KB
Loading

client/package-lock.json

Lines changed: 362 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
"@radix-ui/react-toast": "^1.2.1",
2323
"@reduxjs/toolkit": "^2.2.7",
2424
"@tabler/icons-react": "^3.11.0",
25+
"@tsparticles/engine": "^3.5.0",
26+
"@tsparticles/react": "^3.0.0",
27+
"@tsparticles/slim": "^3.5.0",
2528
"axios": "^1.7.4",
2629
"class-variance-authority": "^0.7.0",
2730
"clsx": "^2.1.1",

client/src/components/Auth/user-auth-form-login.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"use client"
1+
"use client";
22

33
import React, { useState } from 'react';
44
import axios from 'axios';
@@ -30,8 +30,11 @@ export function UserAuthForm({ className, onLoginSuccess, ...props }: UserAuthFo
3030
try {
3131
const response = await axios.post(`${backendUrl}/login`, { username, password }, { withCredentials: true });
3232
if (response.status === 200) {
33+
34+
localStorage.setItem('devhub_username', username);
35+
3336
if (onLoginSuccess) {
34-
onLoginSuccess(username); // Pass the username to the callback
37+
onLoginSuccess(username);
3538
}
3639
navigate('/home');
3740
}
@@ -103,4 +106,4 @@ export function UserAuthForm({ className, onLoginSuccess, ...props }: UserAuthFo
103106
</form>
104107
</div>
105108
);
106-
}
109+
}

client/src/components/Auth/user-auth-form-signup.tsx

Lines changed: 100 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,71 @@
1-
"use client"
1+
"use client";
22

3-
import React, { useState, useEffect } from 'react';
4-
import axios from 'axios';
5-
import { useNavigate } from 'react-router-dom';
6-
import { toast } from "sonner";
3+
import React, { useState, useEffect } from "react";
4+
import axios from "axios";
5+
import { useNavigate } from "react-router-dom";
6+
import { toast } from "sonner";
77

88
import { cn } from "@/lib/utils";
99
import { Icons } from "@/components/ui/icons";
1010
import { Button } from "@/components/ui/button";
1111
import { Input } from "@/components/ui/input";
1212
import { Label } from "@/components/ui/label";
1313

14-
interface UserAuthFormProps extends React.HTMLAttributes<HTMLDivElement> { }
14+
interface UserAuthFormProps extends React.HTMLAttributes<HTMLDivElement> {}
15+
interface PasswordRules {
16+
minLength: boolean;
17+
containsUpper: boolean;
18+
containsLower: boolean;
19+
containsNumber: boolean;
20+
containsSpecial: boolean;
21+
noWhitespace: boolean;
22+
}
1523

16-
const backendUrl = import.meta.env.VITE_BACKEND_URL || 'http://localhost:5000';
24+
const backendUrl = import.meta.env.VITE_BACKEND_URL || "http://localhost:5000";
1725

1826
export function UserAuthForm({ className, ...props }: UserAuthFormProps) {
1927
const [isLoading, setIsLoading] = useState<boolean>(false);
20-
const [username, setUsername] = useState<string>('');
21-
const [isUsernameAvailable, setIsUsernameAvailable] = useState<boolean | null>(null);
22-
const [email, setEmail] = useState<string>('');
23-
const [password, setPassword] = useState<string>('');
28+
const [username, setUsername] = useState<string>("");
29+
const [isUsernameAvailable, setIsUsernameAvailable] = useState<
30+
boolean | null
31+
>(null);
32+
const [email, setEmail] = useState<string>("");
33+
const [password, setPassword] = useState<string>("");
34+
const [usernameError, setUsernameError] = useState<string>("");
35+
const [passwordRules, setPasswordRules] = useState<PasswordRules | null>(
36+
null
37+
);
38+
const [isPasswordValid, setIsPasswordValid] = useState<boolean>(false);
2439
const navigate = useNavigate(); // Hook for navigation
2540

2641
useEffect(() => {
2742
const checkUsernameAvailability = async () => {
2843
if (username) {
2944
try {
45+
if (username.match(/^[a-zA-Z0-9]+$/) === null) {
46+
setUsernameError("Username must be alphanumeric");
47+
setIsUsernameAvailable(null);
48+
return;
49+
} else if (username.length <= 7) {
50+
setUsernameError("Username must be greater than 7 characters");
51+
setIsUsernameAvailable(null);
52+
return;
53+
} else if (username.length >= 12) {
54+
setUsernameError("Username must be less than 12 characters");
55+
setIsUsernameAvailable(null);
56+
return;
57+
}
58+
setUsernameError("");
3059
const response = await axios.get(`${backendUrl}/check_username`, {
31-
params: { username }
60+
params: { username },
3261
});
3362
setIsUsernameAvailable(response.data.available);
3463
} catch (error) {
35-
console.error('Error checking username availability:', error);
64+
console.error("Error checking username availability:", error);
3665
}
3766
} else {
3867
setIsUsernameAvailable(null);
68+
setUsernameError("");
3969
}
4070
};
4171

@@ -46,20 +76,55 @@ export function UserAuthForm({ className, ...props }: UserAuthFormProps) {
4676
return () => clearTimeout(delayDebounceFn);
4777
}, [username]);
4878

79+
const validatePassword = (password: string): PasswordRules => {
80+
return {
81+
minLength: password.length >= 6,
82+
containsUpper: /[A-Z]/.test(password),
83+
containsLower: /[a-z]/.test(password),
84+
containsNumber: /\d/.test(password),
85+
containsSpecial: /[!@#$%^&*(),.?":{}|<>]/.test(password),
86+
noWhitespace: !/\s/.test(password),
87+
};
88+
};
89+
90+
const handlePasswordChange = (event: React.ChangeEvent<HTMLInputElement>) => {
91+
const newPassword = event.target.value;
92+
setPassword(newPassword);
93+
const passwordValidityConditions = validatePassword(newPassword);
94+
setPasswordRules(passwordValidityConditions);
95+
const isNewPasswordValid = Object.values(passwordValidityConditions).every(
96+
(rulePassed) => rulePassed === true
97+
);
98+
setIsPasswordValid(isNewPasswordValid);
99+
};
100+
101+
const passwordRuleMessages: Record<keyof PasswordRules, string> = {
102+
minLength: "Should be a minimum of 6 characters.",
103+
containsUpper: "Contains at least one uppercase letter.",
104+
containsLower: "Contains at least one lowercase letter.",
105+
containsNumber: "Contains at least one number.",
106+
containsSpecial: "Contains at least one special symbol.",
107+
noWhitespace: "Should not contain spaces.",
108+
};
109+
49110
async function onSubmit(event: React.SyntheticEvent) {
50111
event.preventDefault();
51112
setIsLoading(true);
52113

114+
if (usernameError || !isPasswordValid) {
115+
return;
116+
}
117+
53118
try {
54119
await axios.post(`${backendUrl}/signup`, { username, email, password });
55120
toast.success("Signup successful", {
56121
description: "You can now log in with your new account.",
57122
action: {
58123
label: "Login",
59-
onClick: () => navigate('/login'),
124+
onClick: () => navigate("/login"),
60125
},
61126
});
62-
navigate('/login'); // Redirect to login page on successful signup
127+
navigate("/login"); // Redirect to login page on successful signup
63128
} catch (err: any) {
64129
if (err.response && err.response.data && err.response.data.message) {
65130
toast.error(err.response.data.message, {
@@ -78,7 +143,7 @@ export function UserAuthForm({ className, ...props }: UserAuthFormProps) {
78143
},
79144
});
80145
}
81-
console.error('Error during signup:', err);
146+
console.error("Error during signup:", err);
82147
} finally {
83148
setIsLoading(false);
84149
}
@@ -110,6 +175,7 @@ export function UserAuthForm({ className, ...props }: UserAuthFormProps) {
110175
{isUsernameAvailable === true && (
111176
<p className="text-green-500">Username is available</p>
112177
)}
178+
{usernameError && <p className="text-red-500">{usernameError}</p>}
113179
</div>
114180
<div className="grid gap-1">
115181
<Label className="sr-only" htmlFor="email">
@@ -139,11 +205,27 @@ export function UserAuthForm({ className, ...props }: UserAuthFormProps) {
139205
autoComplete="current-password"
140206
disabled={isLoading}
141207
value={password}
142-
onChange={(e) => setPassword(e.target.value)}
208+
onChange={handlePasswordChange}
143209
required
144210
/>
211+
{passwordRules &&
212+
Object.entries(passwordRules).map(([rule, passed]) => (
213+
<p
214+
key={rule}
215+
className={passed ? "text-green-500" : "text-red-500"}
216+
>
217+
{passwordRuleMessages[rule as keyof PasswordRules]}
218+
</p>
219+
))}
145220
</div>
146-
<Button disabled={isLoading || isUsernameAvailable === false}>
221+
<Button
222+
disabled={
223+
isLoading ||
224+
isUsernameAvailable === false ||
225+
usernameError !== "" ||
226+
isPasswordValid === false
227+
}
228+
>
147229
{isLoading && (
148230
<Icons.spinner className="mr-2 h-4 w-4 animate-spin" />
149231
)}

client/src/components/FAQ/Faq.tsx

Lines changed: 0 additions & 42 deletions
This file was deleted.

0 commit comments

Comments
 (0)