refactor: modularize road management modules

This commit is contained in:
2026-06-17 13:31:25 +05:30
parent 9bb740e446
commit 182d4ac293
41 changed files with 2386 additions and 1894 deletions

View File

@@ -0,0 +1,151 @@
'use client';
import { zodResolver } from '@hookform/resolvers/zod';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { useCallback } from 'react';
import { useForm, useWatch } from 'react-hook-form';
import { toast } from 'sonner';
import { z } from 'zod';
import { packageService } from '@/services/api';
import type { Package, PackageCreate, PackageUpdate } from '@/types';
import { packageKeys } from '../queries/packageKeys';
const optionalNumber = z.string().refine(
(value) => {
if (!value.trim()) {
return true;
}
return Number.isFinite(Number(value));
},
{ message: 'Enter a valid number' },
);
const packageFormSchema = z.object({
id: z.string().optional(),
project_id: z.string().trim().min(1, 'Project is required'),
name: z.string().trim().min(1, 'Package name is required'),
region: z.string().optional(),
chainage_start_km: optionalNumber,
chainage_end_km: optionalNumber,
});
export type PackageFormValues = z.infer<typeof packageFormSchema>;
const defaultValues: PackageFormValues = {
project_id: '',
name: '',
region: '',
chainage_start_km: '',
chainage_end_km: '',
};
function trimOptional(value?: string) {
return value?.trim() || null;
}
function toNumberOrZero(value: string) {
return value.trim() ? Number(value) : 0;
}
function toPackagePayload(values: PackageFormValues): PackageCreate | PackageUpdate {
return {
project_id: values.project_id,
name: values.name.trim(),
region: trimOptional(values.region),
chainage_start_km: toNumberOrZero(values.chainage_start_km),
chainage_end_km: toNumberOrZero(values.chainage_end_km),
};
}
export function usePackageForm({ onSaved }: { onSaved: () => void }) {
const queryClient = useQueryClient();
const {
register,
handleSubmit,
reset,
setValue,
control,
formState: { errors, isSubmitting, touchedFields },
} = useForm<PackageFormValues>({
defaultValues,
mode: 'onTouched',
reValidateMode: 'onChange',
resolver: zodResolver(packageFormSchema),
});
const packageId = useWatch({ control, name: 'id' });
const projectId = useWatch({ control, name: 'project_id' }) || '';
const packageName = useWatch({ control, name: 'name' }) || '';
const canSubmit = projectId.trim().length > 0 && packageName.trim().length > 0;
const saveMutation = useMutation({
mutationFn: (values: PackageFormValues) => {
const payload = toPackagePayload(values);
return values.id
? packageService.updatePackage(values.id, payload)
: packageService.createPackage(payload as PackageCreate);
},
onSuccess: (_data, values) => {
toast.success(values.id ? 'Package updated' : 'Package created');
reset(defaultValues);
onSaved();
queryClient.invalidateQueries({ queryKey: packageKeys.all });
},
onError: (_error, values) => {
toast.error(values.id ? 'Failed to update package' : 'Failed to create package');
},
});
const onSubmit = handleSubmit(
(values) => saveMutation.mutate(values),
(formErrors) => {
if (formErrors.project_id?.message) {
toast.error(formErrors.project_id.message);
return;
}
if (formErrors.name?.message) {
toast.error(formErrors.name.message);
}
},
);
const openCreate = useCallback(() => {
reset(defaultValues);
}, [reset]);
const openEdit = useCallback(
(pkg: Package) => {
reset({
id: pkg.id,
project_id: pkg.project_id,
name: pkg.name || '',
region: pkg.region || '',
chainage_start_km: pkg.chainage_start_km?.toString() || '',
chainage_end_km: pkg.chainage_end_km?.toString() || '',
});
},
[reset],
);
const setProjectId = useCallback(
(value: string) => setValue('project_id', value, { shouldDirty: true, shouldValidate: true }),
[setValue],
);
return {
register,
onSubmit,
openCreate,
openEdit,
resetForm: () => reset(defaultValues),
packageId,
projectId,
setProjectId,
errors,
touchedFields,
canSubmit,
isSaving: isSubmitting || saveMutation.isPending,
};
}