Files
sistema_base/apps/web/feactures/training/components/form.tsx

1527 lines
56 KiB
TypeScript

'use client';
import { zodResolver } from '@hookform/resolvers/zod';
import { Button } from '@repo/shadcn/button';
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@repo/shadcn/form';
import { Input } from '@repo/shadcn/input';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@repo/shadcn/select';
import { Textarea } from '@repo/shadcn/textarea';
import { useForm, useWatch } from 'react-hook-form';
import {
ACTIVIDAD_CENTRAL_MAP,
ACTIVIDAD_PRINCIPAL_MAP,
ACTIVIDAD_PRODUCTIVA_MAP,
SECTOR_ECONOMICO_OPTIONS,
SECTOR_PRODUCTIVO_MAP,
} from '../constants/osp-data';
import { useCreateTraining, useUpdateTraining } from '../hooks/use-training';
import { TrainingSchema, trainingSchema } from '../schemas/training';
import { EquipmentList } from './equipment-list';
import { ProductActivityList } from './product-activity-list';
import { ProductionList } from './production-list';
import {
useMunicipalityQuery,
useParishQuery,
useStateQuery,
} from '@/feactures/location/hooks/use-query-location';
import {
Card,
CardContent,
CardHeader,
CardTitle,
} from '@repo/shadcn/components/ui/card';
import React from 'react';
import { SelectSearchable } from '@repo/shadcn/components/ui/select-searchable';
const OSP_TYPES = ['EPSIC', 'EPSDC', 'UPF', 'OTROS', 'COOPERATIVA'];
const STATUS_OPTIONS = ['ACTIVA', 'INACTIVA'];
const CIVIL_STATE_OPTIONS = ['Soltero', 'Casado'];
interface CreateTrainingFormProps {
onSuccess?: () => void;
onCancel?: () => void;
defaultValues?: Partial<TrainingSchema>;
}
export function CreateTrainingForm({
onSuccess,
onCancel,
defaultValues,
}: CreateTrainingFormProps) {
const { mutate: createTraining, isPending: isCreating } = useCreateTraining();
const { mutate: updateTraining, isPending: isUpdating } = useUpdateTraining();
const isSaving = isCreating || isUpdating;
const [state, setState] = React.useState(0);
const [municipality, setMunicipality] = React.useState(0);
const [disabledMunicipality, setDisabledMunicipality] = React.useState(true);
const [disabledParish, setDisabledParish] = React.useState(true);
const [coorState, setcoorState] = React.useState(0);
const [coorMunicipality, setcoorMunicipality] = React.useState(0);
const [disabledCoorMunicipality, setDisabledCoorMunicipality] =
React.useState(true);
const [disabledCoorParish, setDisabledCoorParish] = React.useState(true);
const [selectedFiles, setSelectedFiles] = React.useState<File[]>([]);
const { data: dataState } = useStateQuery();
const { data: dataMunicipality } = useMunicipalityQuery(state);
const { data: dataParish } = useParishQuery(municipality);
const formatToLocalISO = (dateStr?: string | Date) => {
const date = dateStr ? new Date(dateStr) : new Date();
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
return `${year}-${month}-${day}T${hours}:${minutes}`;
};
const form = useForm<TrainingSchema>({
resolver: zodResolver(trainingSchema),
defaultValues: {
firstname: defaultValues?.firstname || '',
lastname: defaultValues?.lastname || '',
coorState: defaultValues?.coorState || undefined,
coorMunicipality: defaultValues?.coorMunicipality || undefined,
coorParish: defaultValues?.coorParish || undefined,
coorPhone: defaultValues?.coorPhone || '',
visitDate: formatToLocalISO(defaultValues?.visitDate),
productiveActivity: defaultValues?.productiveActivity || '',
ecoSector: defaultValues?.ecoSector || undefined,
productiveSector: defaultValues?.productiveSector || undefined,
centralProductiveActivity: defaultValues?.centralProductiveActivity || '',
mainProductiveActivity: defaultValues?.mainProductiveActivity || '',
photo1: defaultValues?.photo1 || '',
photo2: defaultValues?.photo2 || '',
photo3: defaultValues?.photo3 || '',
siturCodeCommune: defaultValues?.siturCodeCommune || '',
communeName: defaultValues?.communeName || '',
communeRif: defaultValues?.communeRif || '',
communeSpokespersonName: defaultValues?.communeSpokespersonName || '',
communeSpokespersonCedula: defaultValues?.communeSpokespersonCedula || '',
communeSpokespersonRif: defaultValues?.communeSpokespersonRif || '',
communeSpokespersonPhone: defaultValues?.communeSpokespersonPhone || '',
communeEmail: defaultValues?.communeEmail || '',
communalCouncil: defaultValues?.communalCouncil || '',
siturCodeCommunalCouncil: defaultValues?.siturCodeCommunalCouncil || '',
communalCouncilRif: defaultValues?.communalCouncilRif || '',
communalCouncilSpokespersonName:
defaultValues?.communalCouncilSpokespersonName || '',
communalCouncilSpokespersonCedula:
defaultValues?.communalCouncilSpokespersonCedula || '',
communalCouncilSpokespersonRif:
defaultValues?.communalCouncilSpokespersonRif || '',
communalCouncilSpokespersonPhone:
defaultValues?.communalCouncilSpokespersonPhone || '',
communalCouncilEmail: defaultValues?.communalCouncilEmail || '',
ospGoogleMapsLink: defaultValues?.ospGoogleMapsLink || '',
ospName: defaultValues?.ospName || '',
ospAddress: defaultValues?.ospAddress || '',
ospRif: defaultValues?.ospRif || '',
ospType: defaultValues?.ospType || '',
currentStatus: defaultValues?.currentStatus || 'ACTIVA',
companyConstitutionYear:
defaultValues?.companyConstitutionYear || new Date().getFullYear(),
ospResponsibleFullname: defaultValues?.ospResponsibleFullname || '',
ospResponsibleCedula: defaultValues?.ospResponsibleCedula || '',
ospResponsibleRif: defaultValues?.ospResponsibleRif || '',
ospResponsiblePhone: defaultValues?.ospResponsiblePhone || '',
civilState: defaultValues?.civilState || '',
familyBurden: defaultValues?.familyBurden || 0,
numberOfChildren: defaultValues?.numberOfChildren || 0,
generalObservations: defaultValues?.generalObservations || '',
ospResponsibleEmail: defaultValues?.ospResponsibleEmail || '',
paralysisReason: defaultValues?.paralysisReason || '',
infrastructureMt2: defaultValues?.infrastructureMt2 || '',
hasTransport: defaultValues?.hasTransport || false,
structureType: defaultValues?.structureType || '',
isOpenSpace: defaultValues?.isOpenSpace || false,
equipmentList: defaultValues?.equipmentList || [],
productionList: defaultValues?.productionList || [],
productList: defaultValues?.productList || [],
state: defaultValues?.state || undefined,
municipality: defaultValues?.municipality || undefined,
parish: defaultValues?.parish || undefined,
},
mode: 'onChange',
});
// Cascading Select Logic
const ecoSector = useWatch({ control: form.control, name: 'ecoSector' });
const productiveSector = useWatch({
control: form.control,
name: 'productiveSector',
});
const centralProductiveActivity = useWatch({
control: form.control,
name: 'centralProductiveActivity',
});
const mainProductiveActivity = useWatch({
control: form.control,
name: 'mainProductiveActivity',
});
const productiveSectorOptions = ecoSector
? SECTOR_PRODUCTIVO_MAP[ecoSector] || []
: [];
const centralProductiveActivityOptions = productiveSector
? ACTIVIDAD_CENTRAL_MAP[productiveSector] || []
: [];
const mainProductiveActivityOptions = centralProductiveActivity
? ACTIVIDAD_PRINCIPAL_MAP[centralProductiveActivity] || []
: [];
const productiveActivityOptions = mainProductiveActivity
? ACTIVIDAD_PRODUCTIVA_MAP[mainProductiveActivity] || []
: [];
// Reset dependent fields when parent changes
React.useEffect(() => {
// This effect handles shifts in options, but react-hook-form values persist
// You might want to clear values if they are no longer valid,
// but for now we rely on the user making a valid selection.
}, [
ecoSector,
productiveSector,
centralProductiveActivity,
mainProductiveActivity,
]);
const { data: dataCoorState } = useStateQuery();
const { data: dataCoorMunicipality } = useMunicipalityQuery(coorState);
const { data: dataCoorParish } = useParishQuery(coorMunicipality);
const coorStateOptions = dataCoorState?.data || [
{ id: 0, name: 'Sin estados' },
];
const coorMunicipalityOptions =
Array.isArray(dataCoorMunicipality?.data) &&
dataCoorMunicipality.data.length > 0
? dataCoorMunicipality.data
: [{ id: 0, stateId: 0, name: 'Sin Municipios' }];
const coorParishOptions =
Array.isArray(dataCoorParish?.data) && dataCoorParish?.data.length > 0
? dataCoorParish.data
: [{ id: 0, stateId: 0, name: 'Sin Parroquias' }];
const stateOptions = dataState?.data || [{ id: 0, name: 'Sin estados' }];
const municipalityOptions =
Array.isArray(dataMunicipality?.data) && dataMunicipality.data.length > 0
? dataMunicipality.data
: [{ id: 0, stateId: 0, name: 'Sin Municipios' }];
const parishOptions =
Array.isArray(dataParish?.data) && dataParish.data.length > 0
? dataParish.data
: [{ id: 0, stateId: 0, name: 'Sin Parroquias' }];
// No local state needed for existing photos, we use form values
React.useEffect(() => {
if (defaultValues) {
if (defaultValues.state) {
setState(Number(defaultValues.state));
setDisabledMunicipality(false);
}
if (defaultValues.municipality) {
setMunicipality(Number(defaultValues.municipality));
setDisabledParish(false);
}
if (defaultValues.coorState) {
setcoorState(Number(defaultValues.coorState));
setDisabledCoorMunicipality(false);
}
if (defaultValues.coorMunicipality) {
setcoorMunicipality(Number(defaultValues.coorMunicipality));
setDisabledCoorParish(false);
}
}
}, [defaultValues]);
const onSubmit = async (formData: TrainingSchema) => {
const data = new FormData();
// 1. Definimos las claves que NO queremos enviar en el bucle general
// 'files' se procesa aparte.
// 'photo1/2/3' son strings (urls viejas) que no queremos reenviar como texto.
const excludedKeys = ['files', 'photo1', 'photo2', 'photo3', 'coorState', 'coorMunicipality', 'coorParish'];
Object.entries(formData).forEach(([key, value]) => {
// 2. Condición actualizada: Si la key está en la lista de excluidos, la saltamos
if (excludedKeys.includes(key) || value === undefined || value === null) {
return;
}
// 3. Lógica de conversión (Igual que tenías)
if (
Array.isArray(value) ||
(typeof value === 'object' && !(value instanceof Date))
) {
data.append(key, JSON.stringify(value));
} else {
data.append(key, value.toString());
}
});
// 2. Mapeo manual y conversión a numérico
// if (formData.state) {
// data.append('state', Number(formData.state).toString());
// }
// if (formData.municipality) {
// data.append('municipality', Number(formData.municipality).toString());
// }
// if (formData.parish) {
// data.append('parish', Number(formData.parish).toString());
// }
if (defaultValues?.id) {
data.append('id', defaultValues.id.toString());
}
// 4. Aquí se agregan las NUEVAS fotos (binary) si el usuario seleccionó alguna
selectedFiles.forEach((file) => {
data.append('files', file);
console.log(file);
});
const mutation = defaultValues?.id ? updateTraining : createTraining;
mutation(data as any, {
onSuccess: () => {
form.reset();
setSelectedFiles([]);
onSuccess?.();
},
onError: (e) => {
console.error(e);
form.setError('root', {
type: 'manual',
message: 'Error al guardar el registro',
});
},
});
};
return (
<>
<div className="flex items-start justify-between mb-2">
<div className="ml-2">
<h2 className="text-3xl font-bold tracking-tight">
Formulario de Registro de Organizaciones Socioproductivas
</h2>
<p className="text-sm text-muted-foreground">
Complete el Formulario para guardar la organización
</p>
</div>
</div>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6 mt-4">
{form.formState.errors.root && (
<div className="text-destructive text-sm font-medium p-3 bg-destructive/10 rounded-md">
{form.formState.errors.root.message}
</div>
)}
{/* 1. Datos de la visita */}
<Card>
<CardHeader>
<CardTitle>1. Datos de la visita</CardTitle>
</CardHeader>
<CardContent className="grid grid-cols-1 md:grid-cols-2 gap-4">
<FormField
control={form.control}
name="firstname"
render={({ field }) => (
<FormItem>
<FormLabel>Nombre del Coordinador Estadal</FormLabel>
<FormControl>
<Input {...field} placeholder="Ej. Juan" />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="lastname"
render={({ field }) => (
<FormItem>
<FormLabel>Apellido del Coordinador Estadal</FormLabel>
<FormControl>
<Input {...field} placeholder="Ej. Pérez" />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="coorPhone"
render={({ field }) => (
<FormItem>
<FormLabel>Teléfono</FormLabel>
<FormControl>
<Input
type="number"
{...field}
placeholder="Ej. 04121234567"
value={field.value ?? ''}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="visitDate"
render={({ field }) => (
<FormItem>
<FormLabel>Fecha y Hora de la visita</FormLabel>
<FormControl>
<Input
type="datetime-local"
{...field}
value={field.value || ''}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="state"
render={({ field }) => (
<FormItem>
<FormLabel>Estado</FormLabel>
<SelectSearchable
options={stateOptions.map((item) => ({
value: item.id.toString(),
label: item.name,
}))}
onValueChange={(value) => {
field.onChange(Number(value));
setState(Number(value));
setDisabledMunicipality(false);
setDisabledParish(true);
}}
placeholder="Selecciona un estado"
defaultValue={field.value?.toString()}
/>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="municipality"
render={({ field }) => (
<FormItem>
<FormLabel>Municipio</FormLabel>
<SelectSearchable
options={municipalityOptions.map((item) => ({
value: item.id.toString(),
label: item.name,
}))}
onValueChange={(value) => {
field.onChange(Number(value));
setMunicipality(Number(value));
setDisabledParish(false);
}}
placeholder="Selecciona un municipio"
disabled={disabledMunicipality}
defaultValue={field.value?.toString()}
/>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="parish"
render={({ field }) => (
<FormItem>
<FormLabel>Parroquia</FormLabel>
<SelectSearchable
options={parishOptions.map((item) => ({
value: item.id.toString(),
label: item.name,
}))}
onValueChange={(value) => field.onChange(Number(value))}
placeholder="Selecciona una parroquia"
disabled={disabledParish}
defaultValue={field.value?.toString()}
/>
<FormMessage />
</FormItem>
)}
/>
</CardContent>
</Card>
{/* 2. Datos de la OSP */}
<Card>
<CardHeader>
<CardTitle>
2. Datos de la Organización Socioproductiva (OSP)
</CardTitle>
</CardHeader>
{/* CAMBIO 1: lg:grid-cols-2 para evitar columnas estrechas en tablets */}
<CardContent className="grid grid-cols-1 lg:grid-cols-2 gap-6 items-start">
<FormField
control={form.control}
name="ospType"
render={({ field }) => (
/* CAMBIO 2: flex flex-col y space-y-3 para forzar separación vertical interna */
<FormItem className="w-full flex flex-col space-y-3">
<FormLabel className="whitespace-normal leading-tight font-semibold">
Tipo de Organización
</FormLabel>
<Select
onValueChange={field.onChange}
defaultValue={field.value}
>
<FormControl>
<SelectTrigger className="w-full">
<SelectValue placeholder="Seleccione tipo" />
</SelectTrigger>
</FormControl>
<SelectContent>
{OSP_TYPES.map((type) => (
<SelectItem key={type} value={type}>
{type}
</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="ecoSector"
render={({ field }) => (
<FormItem className="w-full flex flex-col space-y-3">
<FormLabel className="whitespace-normal leading-tight font-semibold">
Sector Económico
</FormLabel>
<Select
onValueChange={(val) => {
field.onChange(val);
form.setValue('productiveSector', '');
form.setValue('centralProductiveActivity', '');
form.setValue('mainProductiveActivity', '');
form.setValue('productiveActivity', '');
}}
defaultValue={field.value}
>
<FormControl>
<SelectTrigger className="w-full">
<SelectValue placeholder="Seleccione sector económico" />
</SelectTrigger>
</FormControl>
<SelectContent>
{SECTOR_ECONOMICO_OPTIONS.map((type) => (
<SelectItem key={type} value={type}>
{type}
</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="productiveSector"
render={({ field }) => (
<FormItem className="w-full flex flex-col space-y-3">
<FormLabel className="whitespace-normal leading-tight font-semibold">
Sector Productivo
</FormLabel>
<Select
onValueChange={(val) => {
field.onChange(val);
form.setValue('centralProductiveActivity', '');
form.setValue('mainProductiveActivity', '');
form.setValue('productiveActivity', '');
}}
defaultValue={field.value}
disabled={!ecoSector}
>
<FormControl>
<SelectTrigger className="w-full">
<SelectValue placeholder="Seleccione sector productivo" />
</SelectTrigger>
</FormControl>
<SelectContent>
{productiveSectorOptions.map((type) => (
<SelectItem key={type} value={type}>
{type}
</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="centralProductiveActivity"
render={({ field }) => (
<FormItem className="w-full flex flex-col space-y-3">
<FormLabel className="whitespace-normal leading-tight font-semibold">
Actividad Central Productiva
</FormLabel>
<Select
onValueChange={(val) => {
field.onChange(val);
form.setValue('mainProductiveActivity', '');
form.setValue('productiveActivity', '');
}}
defaultValue={field.value}
disabled={!productiveSector}
>
<FormControl>
<SelectTrigger className="w-full">
<SelectValue placeholder="Seleccione actividad central" />
</SelectTrigger>
</FormControl>
<SelectContent>
{centralProductiveActivityOptions.map((type) => (
<SelectItem key={type} value={type}>
{type}
</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="mainProductiveActivity"
render={({ field }) => (
<FormItem className="w-full flex flex-col space-y-3">
<FormLabel className="whitespace-normal leading-tight font-semibold">
Actividad Productiva Principal
</FormLabel>
<Select
onValueChange={(val) => {
field.onChange(val);
form.setValue('productiveActivity', '');
}}
defaultValue={field.value}
disabled={!centralProductiveActivity}
>
<FormControl>
<SelectTrigger className="w-full">
<SelectValue placeholder="Seleccione actividad principal" />
</SelectTrigger>
</FormControl>
<SelectContent>
{mainProductiveActivityOptions.map((type) => (
<SelectItem key={type} value={type}>
{type}
</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="productiveActivity"
render={({ field }) => (
<FormItem className="w-full flex flex-col space-y-3">
<FormLabel className="whitespace-normal leading-tight font-semibold">
Actividad Productiva
</FormLabel>
<Select
onValueChange={field.onChange}
defaultValue={field.value}
disabled={!mainProductiveActivity}
>
<FormControl>
<SelectTrigger className="w-full">
<SelectValue placeholder="Seleccione actividad productiva" />
</SelectTrigger>
</FormControl>
<SelectContent>
{productiveActivityOptions.map((type) => (
<SelectItem key={type} value={type}>
{type}
</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="ospRif"
render={({ field }) => (
<FormItem className="w-full flex flex-col space-y-3">
<FormLabel className="whitespace-normal leading-tight font-semibold">
RIF de la organización (opcional)
</FormLabel>
<FormControl>
<Input {...field} placeholder="J-12345678-9" />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="ospName"
render={({ field }) => (
<FormItem className="w-full flex flex-col space-y-3">
<FormLabel className="whitespace-normal leading-tight font-semibold">
Nombre de la organización
</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="companyConstitutionYear"
render={({ field }) => (
<FormItem className="w-full flex flex-col space-y-3">
<FormLabel className="whitespace-normal leading-tight font-semibold">
Año de constitución
</FormLabel>
<FormControl>
<Input type="number" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="currentStatus"
render={({ field }) => (
<FormItem className="w-full flex flex-col space-y-3">
<FormLabel className="whitespace-normal leading-tight font-semibold">
Estatus
</FormLabel>
<Select
onValueChange={field.onChange}
defaultValue={field.value}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Seleccione estatus" />
</SelectTrigger>
</FormControl>
<SelectContent>
{STATUS_OPTIONS.map((status) => (
<SelectItem key={status} value={status}>
{status}
</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="infrastructureMt2"
render={({ field }) => (
<FormItem className="w-full flex flex-col space-y-3">
<FormLabel className="whitespace-normal leading-tight font-semibold">
infraestrutura (MT2)
</FormLabel>
<FormControl>
<Input {...field} placeholder="e.g. 500" />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="hasTransport"
render={({ field }) => (
<FormItem className="w-full flex flex-col space-y-3">
<FormLabel className="whitespace-normal leading-tight font-semibold">
¿Posee Transporte?
</FormLabel>
<Select
onValueChange={(val) => field.onChange(val === 'true')}
defaultValue={field.value ? 'true' : 'false'}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Seleccione" />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="true">Si</SelectItem>
<SelectItem value="false">No</SelectItem>
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="structureType"
render={({ field }) => (
<FormItem className="w-full flex flex-col space-y-3">
<FormLabel className="whitespace-normal leading-tight font-semibold">
Tipo Estructura
</FormLabel>
<Select
onValueChange={field.onChange}
defaultValue={field.value}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Seleccione tipo" />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="CASA">CASA</SelectItem>
<SelectItem value="GALPON">GALPON</SelectItem>
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="isOpenSpace"
render={({ field }) => (
<FormItem className="w-full flex flex-col space-y-3">
<FormLabel className="whitespace-normal leading-tight font-semibold">
¿Es un Espacio Abierto?
</FormLabel>
<Select
onValueChange={(val) => field.onChange(val === 'true')}
defaultValue={field.value ? 'true' : 'false'}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Seleccione" />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="true">Si</SelectItem>
<SelectItem value="false">No</SelectItem>
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="paralysisReason"
render={({ field }) => (
/* CAMBIO: Se mantiene col-span-2 para que ocupe todo el ancho si hay espacio */
<FormItem className="col-span-1 lg:col-span-2 flex flex-col space-y-3">
<FormLabel className="whitespace-normal leading-tight font-semibold">
Razones de paralización (opcional)
</FormLabel>
<FormControl>
<Textarea {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</CardContent>
</Card>
{/* New Dynamic Sections */}
<Card>
<CardContent className="pt-6">
<EquipmentList />
</CardContent>
</Card>
<Card>
<CardContent className="pt-6">
<ProductionList />
</CardContent>
</Card>
<Card>
<CardContent className="pt-6">
<ProductActivityList />
</CardContent>
</Card>
{/* 3. Detalles de la ubicación */}
<Card>
<CardHeader>
<CardTitle>3. Detalles de la ubicación</CardTitle>
</CardHeader>
<CardContent className="grid grid-cols-1 lg:grid-cols-2 gap-6 items-start">
<FormField
control={form.control}
name="ospAddress"
render={({ field }) => (
<FormItem className="col-span-1 lg:col-span-2 flex flex-col space-y-2">
<FormLabel>
Dirección de la Organización Socio Productivo
</FormLabel>
<FormControl>
<Textarea {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="ospGoogleMapsLink"
render={({ field }) => (
<FormItem className="col-span-1 lg:col-span-2 flex flex-col space-y-2">
<FormLabel>Dirección Link Google Maps</FormLabel>
<FormControl>
<Input
{...field}
placeholder="https://maps.google.com/..."
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<div className="col-span-1 lg:col-span-2 border-b pb-2 mt-4 hidden md:inline">
<h4 className="font-semibold mb-2">Datos de la Comuna</h4>
</div>
<FormField
control={form.control}
name="communeName"
render={({ field }) => (
<FormItem className="w-full flex flex-col space-y-2">
<FormLabel className="font-semibold">
Nombre de la Comuna
</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="siturCodeCommune"
render={({ field }) => (
<FormItem className="w-full flex flex-col space-y-2">
<FormLabel className="font-semibold">
Código SITUR de la Comuna
</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="communeRif"
render={({ field }) => (
<FormItem className="w-full flex flex-col space-y-2">
<FormLabel className="font-semibold">
Rif de la Comuna
</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="communeSpokespersonName"
render={({ field }) => (
<FormItem className="w-full flex flex-col space-y-2">
<FormLabel className="font-semibold">
Nombre del Vocero o Vocera
</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="communeSpokespersonCedula"
render={({ field }) => (
<FormItem className="w-full flex flex-col space-y-2">
<FormLabel className="font-semibold">
Cédula de Identidad del Vocero
</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="communeSpokespersonRif"
render={({ field }) => (
<FormItem className="w-full flex flex-col space-y-2">
<FormLabel className="font-semibold">
RIF del Vocero
</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="communeSpokespersonPhone"
render={({ field }) => (
<FormItem className="w-full flex flex-col space-y-2">
<FormLabel className="font-semibold">
Número de Teléfono del Vocero
</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="communeEmail"
render={({ field }) => (
<FormItem className="w-full flex flex-col space-y-2">
<FormLabel className="font-semibold">
Correo Electrónico de la Comuna
</FormLabel>
<FormControl>
<Input type="email" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<div className="col-span-2 hidden md:inline">
<h4 className="font-semibold mb-2">
Datos del Consejo Comunal
</h4>
</div>
<FormField
control={form.control}
name="communalCouncil"
render={({ field }) => (
<FormItem className="w-full flex flex-col space-y-2">
<FormLabel className="font-semibold">
Nombre del Consejo Comunal
</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="siturCodeCommunalCouncil"
render={({ field }) => (
<FormItem className="w-full flex flex-col space-y-2">
<FormLabel className="font-semibold">
Código SITUR del Consejo Comunal
</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="communalCouncilRif"
render={({ field }) => (
<FormItem className="w-full flex flex-col space-y-2">
<FormLabel className="font-semibold">
Rif del Consejo Comunal
</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="communalCouncilSpokespersonName"
render={({ field }) => (
<FormItem className="w-full flex flex-col space-y-2">
<FormLabel className="font-semibold">
Nombre del Vocero o Vocera
</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="communalCouncilSpokespersonCedula"
render={({ field }) => (
<FormItem className="w-full flex flex-col space-y-2">
<FormLabel className="font-semibold">
Cédula de Identidad del Vocero
</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="communalCouncilSpokespersonRif"
render={({ field }) => (
<FormItem className="w-full flex flex-col space-y-2">
<FormLabel className="font-semibold">
RIF del Vocero
</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="communalCouncilSpokespersonPhone"
render={({ field }) => (
<FormItem className="w-full flex flex-col space-y-2">
<FormLabel className="font-semibold">
Número de Teléfono del Vocero
</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="communalCouncilEmail"
render={({ field }) => (
<FormItem className="w-full flex flex-col space-y-2">
<FormLabel className="font-semibold">
Correo Electrónico del Consejo Comunal
</FormLabel>
<FormControl>
<Input type="email" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</CardContent>
</Card>
{/* 4. Datos del Responsable OSP */}
<Card>
<CardHeader>
<CardTitle>4. Datos del Responsable OSP</CardTitle>
</CardHeader>
<CardContent className="grid grid-cols-1 md:grid-cols-2 gap-4">
<FormField
control={form.control}
name="ospResponsibleCedula"
render={({ field }) => (
<FormItem>
<FormLabel>Cédula</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="ospResponsibleFullname"
render={({ field }) => (
<FormItem>
<FormLabel>Nombre y apellido</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="ospResponsibleRif"
render={({ field }) => (
<FormItem>
<FormLabel>RIF</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="civilState"
render={({ field }) => (
<FormItem>
<FormLabel>Estado Civil</FormLabel>
<Select
onValueChange={field.onChange}
defaultValue={field.value}
>
<FormControl>
<SelectTrigger className="w-full">
<SelectValue placeholder="Seleccione estado civil" />
</SelectTrigger>
</FormControl>
<SelectContent>
{CIVIL_STATE_OPTIONS.map((state) => (
<SelectItem key={state} value={state}>
{state}
</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="ospResponsiblePhone"
render={({ field }) => (
<FormItem>
<FormLabel>Teléfono</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="ospResponsibleEmail"
render={({ field }) => (
<FormItem>
<FormLabel>Correo Electrónico</FormLabel>
<FormControl>
<Input type="email" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="familyBurden"
render={({ field }) => (
<FormItem>
<FormLabel>Carga Familiar</FormLabel>
<FormControl>
<Input type="number" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="numberOfChildren"
render={({ field }) => (
<FormItem>
<FormLabel>Número de Hijos</FormLabel>
<FormControl>
<Input type="number" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</CardContent>
</Card>
{/* 5. Datos adicionales */}
<Card>
<CardHeader>
<CardTitle>5. Datos adicionales</CardTitle>
</CardHeader>
<CardContent>
<FormField
control={form.control}
name="generalObservations"
render={({ field }) => (
<FormItem>
<FormLabel>Observaciones Generales</FormLabel>
<FormControl>
<Textarea {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</CardContent>
</Card>
{/* 6. Registro fotográfico */}
<Card>
<CardHeader>
<CardTitle>6. Registro fotográfico</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-4">
<div className="space-y-2">
{['photo1', 'photo2', 'photo3'].some((field) =>
form.watch(field as any),
) && <FormLabel>Imágenes actuales</FormLabel>}
<div className="grid grid-cols-3 gap-2">
{(['photo1', 'photo2', 'photo3'] as const).map(
(field, idx) => {
const photoUrl = form.watch(field);
if (!photoUrl) return null;
return (
<div
key={field}
className="relative aspect-square rounded-md overflow-hidden bg-muted group"
>
<img
src={`${process.env.NEXT_PUBLIC_API_URL}${photoUrl}`}
alt={`Existing ${idx + 1}`}
className="object-cover w-full h-full"
/>
<button
type="button"
onClick={() => {
form.setValue(field, '');
}}
className="absolute top-1 right-1 bg-destructive text-destructive-foreground rounded-full p-1 opacity-0 group-hover:opacity-100 transition-opacity"
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<line x1="18" y1="6" x2="6" y2="18"></line>
<line x1="6" y1="6" x2="18" y2="18"></line>
</svg>
</button>
</div>
);
},
)}
</div>
</div>
<div className="flex flex-col gap-2">
<FormLabel>
Subir nuevas imágenes (máximo 3 en total)
</FormLabel>
<Input
type="file"
multiple
accept="image/*"
onChange={(e) => {
const files = Array.from(e.target.files || []);
const existingCount = [
form.watch('photo1'),
form.watch('photo2'),
form.watch('photo3'),
].filter(Boolean).length;
if (files.length + existingCount > 3) {
alert('Máximo 3 imágenes en total');
e.target.value = '';
return;
}
setSelectedFiles(files);
}}
/>
</div>
{selectedFiles.length > 0 && (
<div className="space-y-2">
<FormLabel>Nuevas imágenes seleccionadas</FormLabel>
<div className="grid grid-cols-3 gap-2 mt-2">
{selectedFiles.map((file, idx) => (
<div
key={idx}
className="relative aspect-square rounded-md overflow-hidden bg-muted"
>
<img
src={URL.createObjectURL(file)}
alt={`Preview ${idx + 1}`}
className="object-cover w-full h-full"
/>
</div>
))}
</div>
</div>
)}
{selectedFiles.length === 0 &&
!['photo1', 'photo2', 'photo3'].some((f) =>
form.watch(f as any),
) && (
<p className="text-sm text-muted-foreground">
Debe subir al menos una imagen o mantener una existente.
</p>
)}
</div>
</CardContent>
</Card>
<div className="flex justify-end gap-4 mt-8">
<Button
variant="outline"
type="button"
onClick={onCancel}
className="w-32"
>
Cancelar
</Button>
<Button
type="submit"
disabled={
isSaving ||
(selectedFiles.length === 0 &&
!['photo1', 'photo2', 'photo3'].some((f) =>
form.watch(f as any),
))
}
className="w-32"
>
{isSaving
? 'Guardando...'
: defaultValues?.id
? 'Actualizar'
: 'Guardar'}
</Button>
</div>
</form>
</Form>
</>
);
}