Merge remote-tracking branch 'origin/dev/guerby' into dev/cedric

This commit is contained in:
cdricms
2025-01-22 17:40:31 +01:00
6 changed files with 474 additions and 701 deletions

View File

@@ -0,0 +1,172 @@
'use client'
import { z } from 'zod'
import { zodResolver } from '@hookform/resolvers/zod'
import { useForm } from 'react-hook-form'
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@/components/ui/form'
import { Button } from '@/components/ui/button'
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from '@/components/ui/card'
import { Input } from '@/components/ui/input'
const formSchema = z.object({
firstname: z
.string()
.min(2, { message: 'Prénom trop court' }),
lastname: z
.string()
.min(2, { message: 'Nom trop court' }),
phone: z
.string()
.min(10, { message: 'Un numéro de téléphone à 10 chiffres' }),
email: z.string().email({ message: 'Invalid email address' }),
message: z
.string()
.min(10, { message: 'Message must be at least 10 characters long' }),
})
export default function ContactFormPreview() {
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
firstname: '',
lastname: '',
phone: '',
email: '',
},
})
async function onSubmit(values: z.infer<typeof formSchema>) {
try {
console.log(values);
await fetch("/api/users/new", {
method: "POST",
body: JSON.stringify(values),
});
} catch (error) {
console.error("Form submission error", error);
}
console.log("subimted")
}
return (
<div className="flex min-h-[60vh] h-full w-full items-center justify-center px-4">
<Card className="mx-auto max-w-md">
<CardHeader>
<CardTitle className="text-2xl">Contact Us</CardTitle>
<CardDescription>
Please fill out the form below and we will get back to you shortly.
</CardDescription>
</CardHeader>
<CardContent>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8">
<div className="grid gap-4">
{/* Firstname Field */}
<FormField
control={form.control}
name="firstname"
render={({ field }) => (
<FormItem className="grid gap-2">
<FormLabel htmlFor="name">Prénom</FormLabel>
<FormControl>
<Input
id="firstname"
placeholder="John Doe"
type="text"
autoComplete="firstname"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<div className="grid gap-4">
{/* Firstname Field */}
<FormField
control={form.control}
name="lastname"
render={({ field }) => (
<FormItem className="grid gap-2">
<FormLabel htmlFor="lastname">Nom</FormLabel>
<FormControl>
<Input
id="lastname"
placeholder="John Doe"
type="text"
autoComplete="lastname"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{/* Email Field */}
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem className="grid gap-2">
<FormLabel htmlFor="email">Email</FormLabel>
<FormControl>
<Input
id="email"
placeholder="johndoe@mail.com"
type="email"
autoComplete="email"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{/* Message Field */}
<FormField
control={form.control}
name="phone"
render={({ field }) => (
<FormItem className="grid gap-2">
<FormLabel htmlFor="phone">Phone number</FormLabel>
<FormControl>
<Input
id="phone"
placeholder="0648265441"
type="number"
autoComplete="phone"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit" className="w-full">
Add member
</Button>
</div>
</form>
</Form>
</CardContent>
</Card>
</div>
)
}

View File

@@ -23,21 +23,26 @@ export default async function About() {
);
return (
<>
<div className="px-24 py-28 lg:py-32">
<div className="flex flex-row gap-4 justify-center w-full pb-24">
<div className="flex flex-col gap-4 w-3/5 justify-center">
<Card className="pt-10 pb-5 max-h-fit">
<div className="px-4 py-8 lg:px-24 lg:py-32">
<div className="
flex flex-col lg:flex-row gap-4
justify-center w-full pb-24">
<div className="
flex flex-col gap-4 w-full
lg:w-3/5 justify-center">
<Card className="py-5 sm:mx-10 max-h-fit">
<CardHeader className="text-center p-2">
<CardTitle className="m-5">
Entraîneur depuis 60 ans
</CardTitle>
<span className="font-bold text-5xl">
<span className="font-bold text-4xl
sm:text-4xl">
Robert Louis Jean Jacke
</span>
</CardHeader>
<CardContent className="px-28 py-14">
<div className="flex gap-4 flex-col justify-center">
<h2 className="text-pretty text-xl font-semibold md:mb-0.5 md:text-4xl lg:mb-1 lg:max-w-3xl lg:text-3xl">
<CardContent className="px-8 sm:px-10 py-14">
<div className="flex flex-col gap-4 justify-center">
<h2 className="text-pretty text-center text-xl font-semibold md:mb-0.5 lg:mb-1 lg:max-w-3xl sm:text-3xl">
Lorem ipsum, dolor sit amet
</h2>
<p className="blog-paragraph text-muted-foreground">
@@ -48,7 +53,7 @@ export default async function About() {
praesentium ea placeat ad, neque eveniet
adipisci?
</p>
<h2 className="text-pretty text-xl font-semibold md:mb-0.5 md:text-4xl lg:mb-1 lg:max-w-3xl lg:text-3xl">
<h2 className="text-pretty text-center text-xl font-semibold md:mb-0.5 lg:mb-1 lg:max-w-3xl sm:text-3xl">
Lorem ipsum, dolor sit amet
</h2>
<p className="blog-paragraph text-muted-foreground">
@@ -62,28 +67,44 @@ export default async function About() {
</div>
</CardContent>
</Card>
<div className="max-w-full text-center" >
<h2 className="text-pretty text-xl font-semibold md:mb-0.5 md:text-4xl lg:mb-1 lg:max-w-3xl lg:text-3xl">
<div className="max-w-full my-8 text-center lg:hidden" >
<h2 className="text-pretty text-xl font-semibold mb-4 sm:mb-8 sm:text-3xl lg:mb-1 lg:max-w-3xl">
Mes associés
</h2>
<div className="relative items-center justify-center flex flex-row gap-14 w-full">
<Avatar className="w-20 h-20 m-4">
<AvatarImage className="rounded-full z-10" src="https://github.com/shadcn.png" alt="@shadcn" />
<div className="absolute border rounded-xl py-0.5 px-5 w-fit opacity-100 top-20 left-[calc(50%+11em)] translate-x-[-50%] bg-white z-50">
Marc Zuckenberg
<div className="relative flex flex-col lg:flex-row items-center justify-center overflow-y-visible gap-6 w-full">
<Avatar className="flex flex-row gap-9 w-20 h-20 m-1">
<AvatarImage className="rounded-full z-50 ld:md:z-10" src="https://github.com/shadcn.png" alt="@shadcn" />
<div className="
md:absolute border self-center rounded-xl font-bold
text-center
py-4 px-8 lg:md:px-5 w-fit opacity-100 lg:md:top-20
md:left-[calc(50%-11em)] translate-x-[-50%] lg:left-[calc(50% -4em)]
sm:translate-x-[100%] lg:translate-x-[50%] z-40 lg:md:z-50">
Richard Vagneur
</div>
<AvatarFallback>CN</AvatarFallback>
</Avatar>
<Avatar className="w-20 h-20 m-4">
<AvatarImage className="rounded-full z-10" src="https://github.com/shadcn.png" alt="@shadcn" />
<div className="absolute border rounded-xl py-0.5 px-5 w-fit opacity-100 top-20 left-[calc(50%)] translate-x-[-50%] bg-white z-50">
<Avatar className="flex flex-row translate-x-[-8em] gap-9 w-20 h-20 m-1">
<AvatarImage className="rounded-full z-50 ld:md:z-10" src="https://github.com/shadcn.png" alt="@shadcn" />
<div className="
md:absolute border rounded-xl font-bold text-center
py-4 px-8 lg:md:px-5 w-fit opacity-100 lg:md:top-20
md:left-[calc(50%)] translate-x-[-50%] z-40 lg:md:z-50
sm:translate-x-[-50%] lg:translate-x-[-50%] self-center">
Robert Lewis
</div>
<AvatarFallback>CN</AvatarFallback>
</Avatar>
<Avatar className="w-20 h-20 m-4">
<AvatarImage className="rounded-full z-10" src="https://github.com/shadcn.png" alt="@shadcn" />
<div className="absolute border rounded-xl py-0.5 px-5 w-fit opacity-100 top-20 left-[calc(50%-11em)] translate-x-[-50%] bg-white z-50">
<Avatar className="flex flex-row gap-9 w-20 h-20 m-1">
<AvatarImage className="rounded-full z-50 ld:md:z-10" src="https://github.com/shadcn.png" alt="@shadcn" />
<div className="
md:absolute border rounded-xl font-bold text-center
py-4 px-8 lg:md:px-5 w-fit opacity-100 lg:md:top-20
md:left-[calc(50%-11em)] translate-x-[-50%] z-40
lg:md:z-50 sm:translate-x-[125%] sm:self-center
lg:translate-x-[-100%]">
Marria Caré
</div>
<AvatarFallback>CN</AvatarFallback>
@@ -91,9 +112,9 @@ export default async function About() {
</div>
</div>
</div>
<div className="container w-2/5 h-fit border rounded">
<div className="w-full lg:w-2/5 sm:self-center sm:w-3/5 h-fit border rounded">
<img
className=""
className="w-full"
src="https://shadcnblocks.com/images/block/placeholder-dark-1.svg"
alt="president profile image"
/>
@@ -111,7 +132,7 @@ export default async function About() {
equipement (gants, casque) pris en compte. Prévoir une tenue sportive adaptée.
</p>
</div>
<div className="mt-12 flex flex-row justify-center gap-6 lg:items-center">
<div className="mt-12 flex flex-col sm:flex-row px-12 justify-center gap-6 lg:items-center">
<Card className="border-primary">
<CardHeader className="text-center pb-2">
<Badge className="uppercase w-max self-center mb-3">

View File

@@ -0,0 +1,178 @@
"use client"
import * as React from "react"
import * as LabelPrimitive from "@radix-ui/react-label"
import { Slot } from "@radix-ui/react-slot"
import {
Controller,
ControllerProps,
FieldPath,
FieldValues,
FormProvider,
useFormContext,
} from "react-hook-form"
import { cn } from "@/lib/utils"
import { Label } from "@/components/ui/label"
const Form = FormProvider
type FormFieldContextValue<
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
> = {
name: TName
}
const FormFieldContext = React.createContext<FormFieldContextValue>(
{} as FormFieldContextValue
)
const FormField = <
TFieldValues extends FieldValues = FieldValues,
TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>
>({
...props
}: ControllerProps<TFieldValues, TName>) => {
return (
<FormFieldContext.Provider value={{ name: props.name }}>
<Controller {...props} />
</FormFieldContext.Provider>
)
}
const useFormField = () => {
const fieldContext = React.useContext(FormFieldContext)
const itemContext = React.useContext(FormItemContext)
const { getFieldState, formState } = useFormContext()
const fieldState = getFieldState(fieldContext.name, formState)
if (!fieldContext) {
throw new Error("useFormField should be used within <FormField>")
}
const { id } = itemContext
return {
id,
name: fieldContext.name,
formItemId: `${id}-form-item`,
formDescriptionId: `${id}-form-item-description`,
formMessageId: `${id}-form-item-message`,
...fieldState,
}
}
type FormItemContextValue = {
id: string
}
const FormItemContext = React.createContext<FormItemContextValue>(
{} as FormItemContextValue
)
const FormItem = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => {
const id = React.useId()
return (
<FormItemContext.Provider value={{ id }}>
<div ref={ref} className={cn("space-y-2", className)} {...props} />
</FormItemContext.Provider>
)
})
FormItem.displayName = "FormItem"
const FormLabel = React.forwardRef<
React.ElementRef<typeof LabelPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root>
>(({ className, ...props }, ref) => {
const { error, formItemId } = useFormField()
return (
<Label
ref={ref}
className={cn(error && "text-destructive", className)}
htmlFor={formItemId}
{...props}
/>
)
})
FormLabel.displayName = "FormLabel"
const FormControl = React.forwardRef<
React.ElementRef<typeof Slot>,
React.ComponentPropsWithoutRef<typeof Slot>
>(({ ...props }, ref) => {
const { error, formItemId, formDescriptionId, formMessageId } = useFormField()
return (
<Slot
ref={ref}
id={formItemId}
aria-describedby={
!error
? `${formDescriptionId}`
: `${formDescriptionId} ${formMessageId}`
}
aria-invalid={!!error}
{...props}
/>
)
})
FormControl.displayName = "FormControl"
const FormDescription = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, ...props }, ref) => {
const { formDescriptionId } = useFormField()
return (
<p
ref={ref}
id={formDescriptionId}
className={cn("text-[0.8rem] text-muted-foreground", className)}
{...props}
/>
)
})
FormDescription.displayName = "FormDescription"
const FormMessage = React.forwardRef<
HTMLParagraphElement,
React.HTMLAttributes<HTMLParagraphElement>
>(({ className, children, ...props }, ref) => {
const { error, formMessageId } = useFormField()
const body = error ? String(error?.message) : children
if (!body) {
return null
}
return (
<p
ref={ref}
id={formMessageId}
className={cn("text-[0.8rem] font-medium text-destructive", className)}
{...props}
>
{body}
</p>
)
})
FormMessage.displayName = "FormMessage"
export {
useFormField,
Form,
FormItem,
FormLabel,
FormControl,
FormDescription,
FormMessage,
FormField,
}

710
frontend/deno.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -8,6 +8,7 @@
"name": "latosa-frontend",
"version": "0.1.0",
"dependencies": {
"@hookform/resolvers": "^3.10.0",
"@radix-ui/react-accordion": "^1.2.2",
"@radix-ui/react-avatar": "^1.1.2",
"@radix-ui/react-collapsible": "^1.1.2",
@@ -35,10 +36,12 @@
"next-themes": "^0.4.4",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-hook-form": "^7.54.2",
"react-icons": "^5.4.0",
"swr": "^2.3.0",
"tailwind-merge": "^2.6.0",
"tailwindcss-animate": "^1.0.7"
"tailwindcss-animate": "^1.0.7",
"zod": "^3.24.1"
},
"devDependencies": {
"@eslint/eslintrc": "^3",
@@ -242,6 +245,15 @@
"integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==",
"license": "MIT"
},
"node_modules/@hookform/resolvers": {
"version": "3.10.0",
"resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.10.0.tgz",
"integrity": "sha512-79Dv+3mDF7i+2ajj7SkypSKHhl1cbln1OGavqrsF7p6mbUv11xpqpacPsGDCTRvCSjEEIez2ef1NveSVL3b0Ag==",
"license": "MIT",
"peerDependencies": {
"react-hook-form": "^7.0.0"
}
},
"node_modules/@humanfs/core": {
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
@@ -5793,6 +5805,22 @@
"react": "^19.0.0"
}
},
"node_modules/react-hook-form": {
"version": "7.54.2",
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.54.2.tgz",
"integrity": "sha512-eHpAUgUjWbZocoQYUHposymRb4ZP6d0uwUnooL2uOybA9/3tPUvoAKqEWK1WaSiTxxOfTpffNZP7QwlnM3/gEg==",
"license": "MIT",
"engines": {
"node": ">=18.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/react-hook-form"
},
"peerDependencies": {
"react": "^16.8.0 || ^17 || ^18 || ^19"
}
},
"node_modules/react-icons": {
"version": "5.4.0",
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.4.0.tgz",
@@ -7226,6 +7254,15 @@
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/zod": {
"version": "3.24.1",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz",
"integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
}
}
}

View File

@@ -9,6 +9,7 @@
"lint": "next lint"
},
"dependencies": {
"@hookform/resolvers": "^3.10.0",
"@radix-ui/react-accordion": "^1.2.2",
"@radix-ui/react-avatar": "^1.1.2",
"@radix-ui/react-collapsible": "^1.1.2",
@@ -36,10 +37,12 @@
"next-themes": "^0.4.4",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-hook-form": "^7.54.2",
"react-icons": "^5.4.0",
"swr": "^2.3.0",
"tailwind-merge": "^2.6.0",
"tailwindcss-animate": "^1.0.7"
"tailwindcss-animate": "^1.0.7",
"zod": "^3.24.1"
},
"devDependencies": {
"@eslint/eslintrc": "^3",