Files
wamarket/components/needs/need-form.tsx
T
softgrey af81124d48 Initial commit: WaMarket MVP
Marketplace inversée (Next.js 16 App Router + Supabase Cloud) : auth,
schéma + RLS, publication de besoins avec upload d'images, feed avec
filtres, offres, acceptation via RPC, messagerie temps réel,
dashboards, et Dockerfile pour déploiement Coolify.
2026-07-30 14:33:17 +00:00

110 lines
3.6 KiB
TypeScript

"use client";
import { useActionState } from "react";
import { createNeed } from "@/lib/actions/needs";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
import { Label } from "@/components/ui/label";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { ImageUploader } from "@/components/shared/image-uploader";
type Category = { id: string; name: string; slug: string };
export function NeedForm({ categories }: { categories: Category[] }) {
const [state, action, pending] = useActionState(createNeed, undefined);
return (
<form action={action} className="flex flex-col gap-5">
<div className="flex flex-col gap-2">
<Label htmlFor="title">Titre</Label>
<Input
id="title"
name="title"
placeholder="Ex: Besoin d'un plombier pour une fuite"
/>
{state?.errors?.title && (
<p className="text-sm text-destructive">{state.errors.title[0]}</p>
)}
</div>
<div className="flex flex-col gap-2">
<Label htmlFor="description">Description</Label>
<Textarea
id="description"
name="description"
rows={5}
placeholder="Décris ton besoin en détail : contexte, urgence, contraintes..."
/>
{state?.errors?.description && (
<p className="text-sm text-destructive">
{state.errors.description[0]}
</p>
)}
</div>
<div className="flex flex-col gap-2">
<Label htmlFor="categoryId">Catégorie</Label>
<Select name="categoryId">
<SelectTrigger id="categoryId" className="w-full">
<SelectValue placeholder="Choisis une catégorie" />
</SelectTrigger>
<SelectContent>
{categories.map((category) => (
<SelectItem key={category.id} value={category.id}>
{category.name}
</SelectItem>
))}
</SelectContent>
</Select>
{state?.errors?.categoryId && (
<p className="text-sm text-destructive">
{state.errors.categoryId[0]}
</p>
)}
</div>
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
<div className="flex flex-col gap-2">
<Label htmlFor="city">Ville</Label>
<Input id="city" name="city" placeholder="Paris" />
{state?.errors?.city && (
<p className="text-sm text-destructive">{state.errors.city[0]}</p>
)}
</div>
<div className="flex flex-col gap-2">
<Label htmlFor="region">Région (optionnel)</Label>
<Input id="region" name="region" placeholder="Île-de-France" />
</div>
</div>
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
<div className="flex flex-col gap-2">
<Label htmlFor="budgetMin">Budget min (optionnel, )</Label>
<Input id="budgetMin" name="budgetMin" type="number" min="0" />
</div>
<div className="flex flex-col gap-2">
<Label htmlFor="budgetMax">Budget max (optionnel, )</Label>
<Input id="budgetMax" name="budgetMax" type="number" min="0" />
</div>
</div>
<ImageUploader name="images" maxFiles={5} />
{state?.message && (
<p className="text-sm text-destructive">{state.message}</p>
)}
<Button type="submit" disabled={pending} className="mt-2 w-fit">
{pending ? "Publication..." : "Publier le besoin"}
</Button>
</form>
);
}