trello_fake/components/project-form/project-form-sheet.tsx

300 lines
8.5 KiB
TypeScript
Raw Permalink Normal View History

"use client";
import { useState, useEffect } from "react";
import { Loader2 } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import {
Sheet,
SheetContent,
SheetDescription,
SheetFooter,
SheetHeader,
SheetTitle,
} from "@/components/ui/sheet";
import { Switch } from "@/components/ui/switch";
import {
Project,
ProjectStatus,
ProjectFormData,
STATUS_CONFIG,
projectToFormData,
formDataToCreateProject,
formDataToUpdateProject,
} from "@/types/project";
import { createProject, updateProject } from "@/lib/projectsService";
interface ProjectFormSheetProps {
open: boolean;
onOpenChange: (open: boolean) => void;
mode: "create" | "edit";
project?: Project | null;
onSuccess?: (project: Project) => void;
}
const defaultFormData: ProjectFormData = {
name: "",
description: "",
status: "0",
progress: 0,
budget: 0,
startDate: "",
endDate: "",
isPublic: true,
};
export function ProjectFormSheet({
open,
onOpenChange,
mode,
project,
onSuccess,
}: ProjectFormSheetProps) {
const [formData, setFormData] = useState<ProjectFormData>(defaultFormData);
const [isSubmitting, setIsSubmitting] = useState(false);
const [error, setError] = useState<string | null>(null);
// Initialize form data when project changes (for edit mode)
useEffect(() => {
if (mode === "edit" && project) {
setFormData(projectToFormData(project));
} else {
setFormData(defaultFormData);
}
setError(null);
}, [mode, project, open]);
const handleInputChange = (
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
) => {
const { name, value, type } = e.target;
setFormData((prev) => ({
...prev,
[name]: type === "number" ? parseFloat(value) || 0 : value,
}));
};
const handleSelectChange = (name: string, value: string) => {
setFormData((prev) => ({
...prev,
[name]: value,
}));
};
const handleSwitchChange = (name: string, checked: boolean) => {
setFormData((prev) => ({
...prev,
[name]: checked,
}));
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setIsSubmitting(true);
setError(null);
try {
let result: Project;
if (mode === "create") {
const createData = formDataToCreateProject(formData);
result = await createProject(createData);
} else {
if (!project) {
throw new Error("No hay proyecto para editar");
}
const updateData = formDataToUpdateProject(formData);
result = await updateProject(project.id, updateData);
}
onSuccess?.(result);
onOpenChange(false);
} catch (err) {
console.error("Error submitting project:", err);
setError(
err instanceof Error ? err.message : "Error al guardar el proyecto"
);
} finally {
setIsSubmitting(false);
}
};
const isFormValid = formData.name.trim().length > 0;
return (
<Sheet open={open} onOpenChange={onOpenChange}>
<SheetContent className="sm:max-w-lg overflow-y-auto">
<SheetHeader>
<SheetTitle>
{mode === "create" ? "Nuevo Proyecto" : "Editar Proyecto"}
</SheetTitle>
<SheetDescription>
{mode === "create"
? "Crea un nuevo proyecto para gestionar tus tareas."
: "Modifica los detalles del proyecto."}
</SheetDescription>
</SheetHeader>
<form onSubmit={handleSubmit} className="mt-6 space-y-6">
{/* Nombre */}
<div className="space-y-2">
<Label htmlFor="name">
Nombre <span className="text-red-500">*</span>
</Label>
<Input
id="name"
name="name"
value={formData.name}
onChange={handleInputChange}
placeholder="Nombre del proyecto"
required
/>
</div>
{/* Descripción */}
<div className="space-y-2">
<Label htmlFor="description">Descripción</Label>
<Textarea
id="description"
name="description"
value={formData.description}
onChange={handleInputChange}
placeholder="Descripción del proyecto..."
rows={3}
/>
</div>
{/* Estado (solo en modo edición) */}
{mode === "edit" && (
<div className="space-y-2">
<Label htmlFor="status">Estado</Label>
<Select
value={formData.status}
onValueChange={(value) => handleSelectChange("status", value)}
>
<SelectTrigger>
<SelectValue placeholder="Seleccionar estado" />
</SelectTrigger>
<SelectContent>
{(Object.keys(STATUS_CONFIG) as ProjectStatus[]).map(
(status) => (
<SelectItem key={status} value={status}>
{STATUS_CONFIG[status].label}
</SelectItem>
)
)}
</SelectContent>
</Select>
</div>
)}
{/* Progreso */}
<div className="space-y-2">
<Label htmlFor="progress">Progreso (%)</Label>
<Input
id="progress"
name="progress"
type="number"
min="0"
max="100"
value={formData.progress}
onChange={handleInputChange}
placeholder="0"
/>
</div>
{/* Presupuesto */}
<div className="space-y-2">
<Label htmlFor="budget">Presupuesto (€)</Label>
<Input
id="budget"
name="budget"
type="number"
min="0"
step="0.01"
value={formData.budget}
onChange={handleInputChange}
placeholder="0.00"
/>
</div>
{/* Fechas - Grid de 2 columnas */}
<div className="grid grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="startDate">Fecha inicio</Label>
<Input
id="startDate"
name="startDate"
type="date"
value={formData.startDate}
onChange={handleInputChange}
/>
</div>
<div className="space-y-2">
<Label htmlFor="endDate">Fecha fin</Label>
<Input
id="endDate"
name="endDate"
type="date"
value={formData.endDate}
onChange={handleInputChange}
/>
</div>
</div>
{/* Proyecto público */}
<div className="flex items-center justify-between">
<div className="space-y-0.5">
<Label htmlFor="isPublic">Proyecto público</Label>
<p className="text-sm text-muted-foreground">
Los proyectos públicos son visibles para todos los usuarios
</p>
</div>
<Switch
id="isPublic"
checked={formData.isPublic}
onCheckedChange={(checked) => handleSwitchChange("isPublic", checked)}
/>
</div>
{/* Error message */}
{error && (
<div className="p-3 text-sm text-red-600 bg-red-50 dark:bg-red-950/50 dark:text-red-400 rounded-md">
{error}
</div>
)}
{/* Submit button */}
<SheetFooter className="pt-4">
<Button
type="button"
variant="outline"
onClick={() => onOpenChange(false)}
disabled={isSubmitting}
>
Cancelar
</Button>
<Button type="submit" disabled={!isFormValid || isSubmitting}>
{isSubmitting && (
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
)}
{mode === "create" ? "Crear proyecto" : "Guardar cambios"}
</Button>
</SheetFooter>
</form>
</SheetContent>
</Sheet>
);
}