diff --git a/frontend/src/components/ui/Card/KVCard.tsx b/frontend/src/components/ui/Card/KVCard.tsx
index e69de29..e1bffcc 100644
--- a/frontend/src/components/ui/Card/KVCard.tsx
+++ b/frontend/src/components/ui/Card/KVCard.tsx
@@ -0,0 +1,17 @@
+import { Icon } from "@iconify/react";
+
+interface KVCardProps {
+ icon?: string;
+ myKey: string;
+ value: string;
+}
+
+export const KVCard = ({icon, myKey, value}: KVCardProps) => {
+ return (
+
+ {icon &&
}
+ {myKey}
+ {value}
+
+ )
+}
\ No newline at end of file
diff --git a/frontend/src/components/ui/TabsBar.tsx b/frontend/src/components/ui/TabsBar.tsx
index e69de29..d5503a4 100644
--- a/frontend/src/components/ui/TabsBar.tsx
+++ b/frontend/src/components/ui/TabsBar.tsx
@@ -0,0 +1,68 @@
+import { Link, type LinkProps } from "react-router";
+import { type ReactNode } from "react";
+
+type Tab = {
+ label: string;
+ value: T;
+ to?: LinkProps["to"];
+}
+
+interface TabsBarProps {
+ variant?: "underline" | "segmented";
+ items: Tab[];
+ current: T;
+ onChange?: (value: T) => void;
+ children?: ReactNode;
+}
+
+
+export const TabsBar = ({
+ items,
+ onChange,
+ current,
+ children,
+ variant = "segmented"
+ }: TabsBarProps) => {
+
+ let tabsClass = "";
+ let tabItemClass = "";
+ let tabItemActiveClass = "";
+
+ switch (variant) {
+ case 'segmented':
+ tabsClass = "bg-[#e5e5e5] rounded-md";
+ tabItemClass = "rounded-lg px-4 py-2 md:py-3 md:px-6"
+ tabItemActiveClass = "bg-white";
+ break;
+ case 'underline':
+ tabsClass = "pt-3 bg-white";
+ tabItemClass = "pb-3 mr-3.5 text-sm text-gray-500"
+ tabItemActiveClass = "border-b-2 border-black text-black!"
+ break;
+ }
+
+ return (
+
+
+
+ {children}
+
+ )
+}
\ No newline at end of file
diff --git a/frontend/src/features/auth/pages/Login.tsx b/frontend/src/features/auth/pages/Login.tsx
index 77e1bc9..c934a90 100644
--- a/frontend/src/features/auth/pages/Login.tsx
+++ b/frontend/src/features/auth/pages/Login.tsx
@@ -1,62 +1,46 @@
import HwLogo from "@components/Icon.tsx";
import HButton from "@ui/HButton.tsx";
import HInput from "@ui/HInput.tsx";
+
import FormField from "@ui/Form/FormField.tsx";
import FormLabel from "@ui/Form/FormLabel.tsx";
-import { type SubmitEvent, useState } from "react";
import FormError from "@ui/Form/FormError.tsx";
-import { loginSchema } from "@utils/schema.ts";
-import { useAuth } from "@features/auth/provider/AuthProvider.tsx";
-const Login = () => {
+import { useAuth } from "@features/auth/provider/AuthProvider.tsx";
+import { loginSchema } from "@utils/schema.ts";
- const { login } = useAuth();
- const [isSubmitting, setIsSubmitting] = useState(false);
- const [errors, setErrorState] = useState>({})
+import { Link } from "react-router";
- async function onFormLoginSubmit(event: SubmitEvent) {
- event.preventDefault();
- const formData = new FormData(event.currentTarget)
- const result = loginSchema.safeParse(Object.fromEntries(formData))
+import { useUncontrolledForm } from "@utils/hooks/useUncontrolledForm.ts";
+import { FetchError } from "ofetch";
- if (!result.success) {
- const errors: Record = {};
- result.error.issues.map((issue) => {
- const field = issue.path[0];
- if (typeof field === "string") {
- errors[field] = issue.message;
- }
- })
- setErrorState(errors);
- return;
- }
+const Login = () => {
- setErrorState({});
+ const { login } = useAuth();
- try {
- setIsSubmitting(true)
- await login(result.data);
- } catch (e) {
- console.log(e)
- } finally {
- setIsSubmitting(false);
+ const {errors, onFormSubmit, isSubmitting} = useUncontrolledForm({
+ schema: loginSchema,
+ onSubmit: async (values) => {
+ await login(values);
+ },
+ onError: (error, {setFieldError}) => {
+ if (error instanceof FetchError) {
+ if(!error.status) setFieldError("email", "Une erreur est survenue veuillez réessayée");
+ }
}
- }
+ })
return (