Skip to content

Commit

Permalink
Merge branch 'dev' of https://github.com/AkekoChan/flami-app into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
Maxencee committed Mar 12, 2024
2 parents 0be9597 + 821e69c commit 0d736b9
Show file tree
Hide file tree
Showing 12 changed files with 88 additions and 43 deletions.
4 changes: 2 additions & 2 deletions client/src/components/auth/otp/OtpForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const OtpForm = () => {
email: auth.user?.email,
};

APIHandler<GenericResponse>("/auth/send-otp", false, "post", body).then(
APIHandler<GenericResponse>("/auth/send-otp", false, "POST", body).then(
(res) => {
toast.success(res.data.message, {
style: {
Expand All @@ -42,7 +42,7 @@ const OtpForm = () => {
otp: values.otp,
};

APIHandler<AuthResponse>("/auth/verify-otp", false, "post", body).then(
APIHandler<AuthResponse>("/auth/verify-otp", false, "POST", body).then(
(res) => {
toast.success(res.data.message, {
style: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const ForgetPasswordForm = () => {
APIHandler<GenericResponse>(
"/auth/forget-password",
false,
"post",
"POST",
body
).then((res) => {
toast.success(res.data.message, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const ResetPasswordForm = () => {
APIHandler<ResetPasswordResponse>(
`/auth/reset-password/${token}`,
false,
"post",
"POST",
resetPasswordBody
).then((res) => {
toast.success(res.data.message, {
Expand Down
54 changes: 36 additions & 18 deletions client/src/components/profile/UpdateForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,42 +6,41 @@ import { Button } from "../ui";
import { useAuth } from "../../hooks/useAuth";
import { User } from "../../interfaces/user.interface";
import { APIHandler } from "../../utils/api/api-handler";
import { UpdateAccountBody } from "../../interfaces/api-body/update-account-body";
import { AuthResponse } from "../../interfaces/api-response/auth-reponse";
import toast from "react-hot-toast";
import { useNavigate } from "react-router";

const validationSchema = Yup.object().shape({
name: Yup.string().required("Le nom est obligatoire.").trim(),
email: Yup.string()
.email("Le format de l'e-mail est incorrect.")
.required("L'e-mail est obligatoire.")
.trim(),
name: Yup.string().trim(),
email: Yup.string().email("Le format de l'e-mail est incorrect.").trim(),
password: Yup.string()
.required("Le mot de passe est obligatoire.")
.matches(
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/,
"Le mot de passe doit contenir au moins huit caractères, une lettre majuscule, une lettre minuscule, un chiffre et un caractère special."
)
.trim(),
confirmPassword: Yup.string()
.oneOf([Yup.ref("password")], "Le mot de passe ne correspond pas.")
.required("La confirmation du mot de passe est obligatoire.")
.trim(),
});
const UpdateForm = () => {
const { token } = useAuth();
const { token, setToken } = useAuth();
const [user, setUser] = useState<User>();
const [showPassword, setShowPassword] = useState<boolean>(false);
const [showConfirmPassword, setShowConfirmPassword] =
useState<boolean>(false);

const navigate = useNavigate();

const getAccount = useCallback(() => {
APIHandler<User>("/my/profile", false, "get", undefined, token).then(
APIHandler<User>("/my/profile", false, "GET", undefined, token).then(
(res) => {
setUser(res.data);
}
);
}, [token]);

console.log(user);

useEffect(() => {
getAccount();
}, [getAccount]);
Expand All @@ -57,11 +56,34 @@ const UpdateForm = () => {
}}
validationSchema={validationSchema}
onSubmit={(values, actions) => {
console.log(values);
const body: UpdateAccountBody = {
email: values.email,
name: values.name,
password: values.password,
};
console.log(token, body);
APIHandler<AuthResponse>(
"/my/account",
false,
"PATCH",
body,
token
).then((res) => {
setToken(res.data.token);
localStorage.setItem("token", res.data.token);
toast.success(res.data.message, {
style: {
background: "#3D3D3D",
color: "#FAFAFA",
borderRadius: "12px",
},
});
navigate("/profile");
});
actions.setSubmitting(false);
}}
>
{({ errors, touched, isValid, dirty }) => (
{({ errors, touched }) => (
<Form className="flex flex-col gap-8">
<div className="flex flex-col gap-6">
<div className="flex flex-col gap-4">
Expand Down Expand Up @@ -229,11 +251,7 @@ const UpdateForm = () => {
</div>
</div>
<div className="flex flex-col gap-4">
<Button
variant={"primary"}
type="submit"
disabled={!(isValid && dirty)}
>
<Button variant={"primary"} type="submit">
Sauvegarder
</Button>
</div>
Expand Down
4 changes: 3 additions & 1 deletion client/src/components/topbar/TopBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ import { useNavigate } from "react-router";
const TopBar = ({
title,
hasReturn,
prevPage,
}: {
title: string;
hasReturn: boolean;
prevPage: string;
}) => {
const navigate = useNavigate();
const handlePrevPage = () => {
navigate(-1);
navigate(prevPage);
};
return (
<div className="flex items-center gap-4">
Expand Down
2 changes: 2 additions & 0 deletions client/src/components/ui/link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ const linksVariants = {
secondary:
"bg-alabaster-950 text-alabaster-50 rounded-xl shadow-secondary border-3 border-alabaster-400 active:shadow-none active:translate-y-1 hover:brightness-90",
tertiary: "text-mandy-500 hover:text-mandy-600",
disabled:
"rounded-xl bg-alabaster-600 text-alabaster-300 shadow-none hover:brightness-100 active:translate-y-0 pointer-events-none",
};

const link = cva(baseLink, {
Expand Down
10 changes: 5 additions & 5 deletions client/src/context/authContextProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const AuthContextProvider = ({

const signin = useCallback(
(body: SigninBody) => {
APIHandler<AuthResponse>("/auth/signin", false, "post", body)
APIHandler<AuthResponse>("/auth/signin", false, "POST", body)
.then((res) => {
setToken(res.data.token);
localStorage.setItem("token", res.data.token);
Expand Down Expand Up @@ -61,10 +61,10 @@ export const AuthContextProvider = ({
};

const signup = (body: SignupBody) => {
APIHandler<AuthResponse>("/auth/signup", false, "post", body)
APIHandler<AuthResponse>("/auth/signup", false, "POST", body)
.then(() => {
navigate("/otp");
APIHandler<GenericResponse>("/auth/send-otp", false, "post", body).then(
APIHandler<GenericResponse>("/auth/send-otp", false, "POST", body).then(
(res) => {
setUser({
name: body.name,
Expand Down Expand Up @@ -93,7 +93,7 @@ export const AuthContextProvider = ({
APIHandler<RefreshTokenResponse>(
"/auth/token",
false,
"get",
"GET",
undefined,
token
)
Expand All @@ -114,7 +114,7 @@ export const AuthContextProvider = ({
}, 5 * 60 * 1000);

return () => clearInterval(interval);
}, [refreshToken]);
});

const authContextValue: AuthContextType = {
signin,
Expand Down
5 changes: 5 additions & 0 deletions client/src/interfaces/api-body/update-account-body.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface UpdateAccountBody {
name?: string;
email?: string;
password?: string;
}
4 changes: 2 additions & 2 deletions client/src/pages/profile/AccountPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import TopBar from "../../components/topbar/TopBar";

const AccountPage = () => {
return (
<div>
<TopBar title="Mon compte" hasReturn={true} />
<div className="flex flex-col gap-8">
<TopBar title="Mon compte" hasReturn={true} prevPage="/profile" />
<UpdateForm />
</div>
);
Expand Down
21 changes: 16 additions & 5 deletions client/src/pages/profile/ProfilePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const ProfilePage = () => {
const [user, setUser] = useState<User>();

const getUser = useCallback(() => {
APIHandler<User>("/my/profile", false, "get", undefined, token).then(
APIHandler<User>("/my/profile", false, "GET", undefined, token).then(
(res) => {
setUser(res.data);
}
Expand All @@ -22,10 +22,11 @@ const ProfilePage = () => {
useEffect(() => {
getUser();
}, [getUser]);
console.log(user?.badges);

return (
<div className="flex flex-col gap-8">
<TopBar title="Mon profil" hasReturn={false} />
<TopBar title="Mon profil" hasReturn={false} prevPage="" />
<div className="flex flex-col gap-6">
{user && (
<div className="flex flex-col gap-2">
Expand All @@ -46,10 +47,20 @@ const ProfilePage = () => {
<div className="flex flex-col gap-6">
<h3 className="text-2xl font-bold">Mes badges</h3>
<div className="flex justify-between">
{
user && user.badges && user.badges.map(badge => <BadgeDisplay badge={badge}></BadgeDisplay>)
}
{user && user.badges?.length !== 0 ? (
user.badges?.map((badge) => (
<BadgeDisplay badge={badge}></BadgeDisplay>
))
) : (
<p>Tu n'as pas de badges !</p>
)}
</div>
<LinkComponent
to={"/badges"}
variant={`${user?.badges?.length === 0 ? "disabled" : "primary"}`}
>
Voir tous mes badges
</LinkComponent>
</div>
</div>
);
Expand Down
6 changes: 3 additions & 3 deletions client/src/utils/api/api-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,21 @@ import { ApiResponse } from "../../interfaces/api-response/api-response";
import { ErrorResponse } from "../../interfaces/api-response/error-response";
import toast from "react-hot-toast";

type HTTPMethod = "get" | "post" | "patch";
type HTTPMethod = "GET" | "POST" | "PATCH";

const apiURLFlami = "http://localhost:3001/api";
const apiURLMap = "https://maksance.alwaysdata.net/api-jo";

export const APIHandler = <T>(
endpoint: string,
isMap: boolean = false,
method: HTTPMethod = "get",
method: HTTPMethod = "GET",
body: unknown = undefined,
token: string | null = null
): Promise<ApiResponse<T>> => {
const headers = new Headers();
const url = isMap ? apiURLMap : apiURLFlami;
if (method === "post") {
if (method === "POST" || method === "PATCH") {
headers.append("Content-Type", "application/json");
}
if (token) {
Expand Down
17 changes: 12 additions & 5 deletions server/controllers/user.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,26 @@ import { readFile } from "fs/promises";
const userController = {
getProfile: async (req, res) => {
let userdata = res.locals.user;
let content = await readFile('./data/badges.json', { encoding: "utf8" });
let content = await readFile("./data/badges.json", { encoding: "utf8" });
let json = JSON.parse(content);
return res.status(200).json({
data: {
name: userdata.name,
email: userdata.email,
badges: userdata.badges.slice(Math.max(0, userdata.badges.length - 3)).map(id => json[id] ?? json[0]),
badges: userdata.badges
.slice(Math.max(0, userdata.badges.length - 3))
.map((id) => json[id] ?? json[0]),
created_at: new Date(userdata.date).toDateString(),
},
});
},
getBadges: async (req, res) => {
let userdata = res.locals.user;
let content = await readFile('./data/badges.json', { encoding: "utf8" });
let content = await readFile("./data/badges.json", { encoding: "utf8" });
let json = JSON.parse(content);
return res.status(200).json({
data: {
badges: userdata.badges.map(id => json[id] ?? json[0]),
badges: userdata.badges.map((id) => json[id] ?? json[0]),
},
});
},
Expand All @@ -33,7 +35,12 @@ const userController = {

let patch = {};

if (password && String(password).match(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/))
if (
password &&
String(password).match(
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/
)
)
patch.password = bcrypt.hashSync(password, bcrypt.genSaltSync(11));
if (name) patch.name = name;
if (email) patch.email = email;
Expand Down

0 comments on commit 0d736b9

Please sign in to comment.