Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions frontend/src/components/ui/Card/KVCard.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div className={"py-4 px-6 bg-[#ebefff] rounded-lg h-full"}>
{icon && <Icon className={"size-6 text-blue-500"} icon={icon}/>}
<p className={"text-sm mt-2 mb-1"}>{myKey}</p>
<p className={"font-semibold"}>{value}</p>
</div>
)
}
68 changes: 68 additions & 0 deletions frontend/src/components/ui/TabsBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { Link, type LinkProps } from "react-router";
import { type ReactNode } from "react";

type Tab<T> = {
label: string;
value: T;
to?: LinkProps["to"];
}

interface TabsBarProps<T extends string> {
variant?: "underline" | "segmented";
items: Tab<T>[];
current: T;
onChange?: (value: T) => void;
children?: ReactNode;
}


export const TabsBar = <T extends string>({
items,
onChange,
current,
children,
variant = "segmented"
}: TabsBarProps<T>) => {

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 (
<div data-slot={"root"} className={`p-1 relative w-fit ${tabsClass}`}>
<nav className={"flex"}>
{items.map(item => {
return item.to ?
<Link
key={item.value}
to={item.to}
onClick={() => onChange?.(item.value)}
className={`block cursor-pointer ${tabItemClass} ${current === item.value ? `${tabItemActiveClass}` : ''}`}>
{item.label}
</Link>
: <div
key={item.value}
onClick={() => onChange?.(item.value)}
className={`cursor-pointer ${tabItemClass} ${current === item.value ? `${tabItemActiveClass}` : ''}`}>
{item.label}
</div>
})}
</nav>

{children}
</div>
)
}
68 changes: 28 additions & 40 deletions frontend/src/features/auth/pages/Login.tsx
Original file line number Diff line number Diff line change
@@ -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<Record<string, string>>({})
import { Link } from "react-router";

async function onFormLoginSubmit(event: SubmitEvent<HTMLFormElement>) {
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<string, string> = {};
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 (
<div className={"flex min-h-dvh justify-center items-center"}>
<form
onSubmit={onFormLoginSubmit}
onSubmit={onFormSubmit}
className={"max-w-100 bg-neutral-50/30 w-full border border-neutral-100 min-h-60 flex flex-col gap-6 justify-start items-center p-6"}>
<div>
<div className={"max-w-34 w-full mx-auto"}>
<div className={"w-full"}>
<Link to={"/"} className={"max-w-28 block w-full mx-auto mb-4"}>
<HwLogo/>
</div>
</Link>

<div className={"px-1 mt-4"}><p className={"text-gray-500 text-center"}>Rejoignez notre communauté
de plus de 2000 employeurs afin
de trouver le job qui vous correspond</p>
</div>
<div className={"text-left"}><h1 className={"text-xl font-semibold"}>Se connecter</h1></div>
</div>

<div className={"flex flex-col gap-4 w-full"}>
Expand All @@ -78,7 +62,11 @@ const Login = () => {

</div>

<HButton disabled={isSubmitting} type={"submit"}>Se connecter</HButton>

<div className={"w-full flex flex-col gap-6"}>
<p className={"text-gray-500 text-sm"}>Vous n'avez pas de compte ? Inscrivez vous <Link to={"/auth/register"} className={"underline underline-offset-2"}>ici</Link></p>
<HButton disabled={isSubmitting} type={"submit"}>Se connecter</HButton>
</div>
</form>
</div>
)
Expand Down
86 changes: 86 additions & 0 deletions frontend/src/features/auth/pages/Register.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import FormField from "@ui/Form/FormField.tsx";
import FormLabel from "@ui/Form/FormLabel.tsx";
import FormError from "@ui/Form/FormError.tsx";

import HwLogo from "@components/Icon.tsx";
import HInput from "@ui/HInput.tsx";
import HButton from "@ui/HButton.tsx";

import { Link } from "react-router";

import { useUncontrolledForm } from "@utils/hooks/useUncontrolledForm.ts";
import { registerSchema } from "@utils/schema.ts";
import { useAuth } from "@features/auth/provider/AuthProvider.tsx";
import { FetchError } from "ofetch";

const Register = () => {

const { register } = useAuth();

const {errors, onFormSubmit, isSubmitting} = useUncontrolledForm({
schema: registerSchema,
onSubmit: async (values) => {
await register(values);
},
onError: (error, {setFieldError}) => {
if (error instanceof FetchError) {
if(!error.status) setFieldError("email", "Une erreur est survenue veuillez réessayée");
}
}
})

return (
<div className={"flex justify-center items-center min-h-dvh"}>
<form
onSubmit={onFormSubmit}
className={"max-w-100 bg-neutral-50/30 w-full border border-neutral-100 min-h-60 flex flex-col gap-6 justify-start items-center p-6"}
>
<div>
<Link to={"/"} className={"block max-w-16 w-full mx-auto"}>
<HwLogo/>
</Link>

<h1 className={"text-xl font-semibold"}>
S'inscrire
</h1>

<div>
<p className={"text-gray-500 text-left text-sm"}>
Rejoignez notre communauté
de plus de 2000 employeurs afin
de trouver le job qui vous correspond
</p>
</div>
</div>

<div className={"flex flex-col gap-4 w-full"}>
<FormField className={"flex flex-col gap-2"} required name={"email"} error={errors?.email}>
<FormLabel>Adresse email</FormLabel>
<HInput autoComplete={"email"} type={"email"} placeholder={"ex. corentin.nelhomme@gmail.com"}/>
<FormError/>
</FormField>

<FormField className={"flex flex-col gap-2"} required name={"password"} error={errors?.password}>
<FormLabel>Mot de passe</FormLabel>
<HInput type={"password"} placeholder={"ex. password123"}/>
<FormError/>
</FormField>

<FormField className={"flex flex-col gap-2"} required name={"confirmPassword"} error={errors?.confirmPassword}>
<FormLabel>Mot de passe de confirmation</FormLabel>
<HInput type={"password"} placeholder={"ex. password123"}/>
<FormError/>
</FormField>

<div className={"w-full flex flex-col gap-6"}>
<p className={"text-gray-500 text-sm"}>Vous avez dêja un compte ? Connectez vous <Link
to={"/auth/login"} className={"underline underline-offset-2"}>ici</Link></p>
<HButton disabled={isSubmitting} className={"w-full"} type={"submit"}>S'inscrire</HButton>
</div>
</div>
</form>
</div>
)
}

export default Register
Loading
Loading