feat: add shared validation for chainage and coordinates

This commit is contained in:
2026-06-23 18:32:38 +05:30
parent 066374bebc
commit b282fdf56d
7 changed files with 219 additions and 95 deletions

View File

@@ -182,16 +182,16 @@ export function SegmentDialog({
<div className="grid gap-4 md:grid-cols-2">
<FormField
id="segment-start-km"
label="Start (km)"
label="Start Chainage"
required
error={getError('chainage_start_km')}
>
<Input
id="segment-start-km"
type="number"
step="any"
step="1"
min="0"
placeholder="0.0"
placeholder="Enter start chainage"
aria-invalid={!!getError('chainage_start_km')}
{...register('chainage_start_km')}
/>
@@ -199,16 +199,16 @@ export function SegmentDialog({
<FormField
id="segment-end-km"
label="End (km)"
label="End Chainage"
required
error={getError('chainage_end_km')}
>
<Input
id="segment-end-km"
type="number"
step="any"
step="1"
min="0"
placeholder="1.0"
placeholder="Enter end chainage"
aria-invalid={!!getError('chainage_end_km')}
{...register('chainage_end_km')}
/>
@@ -312,7 +312,9 @@ export function SegmentDialog({
Cancel
</Button>
<Button type="submit" disabled={isSaving || !canSubmit}>
{isSaving ? <Loader2 className="mr-2 size-4 animate-spin" /> : null}
{isSaving ? (
<Loader2 className="mr-2 size-4 animate-spin" />
) : null}
{segmentId ? 'Update Segment' : 'Create Segment'}
</Button>
</DialogFooter>

View File

@@ -7,49 +7,32 @@ import { useForm, useWatch } from 'react-hook-form';
import { toast } from 'sonner';
import { z } from 'zod';
import {
requiredIntegerNumber,
requiredLatitude,
requiredLongitude,
validateChainageRange,
} from '@/lib/validation/numberField';
import { chainageService } from '@/services/api';
import type { Chainage, ChainageCreate, ChainageUpdate } from '@/types';
import { segmentKeys } from '../queries/segmentKeys';
const requiredNumber = (message: string) =>
z
.string()
.trim()
.min(1, message)
.refine((value) => Number.isFinite(Number(value)), {
message: 'Enter a valid number',
});
const latitude = requiredNumber('Latitude is required').refine(
(value) => {
const numberValue = Number(value);
return numberValue >= -90 && numberValue <= 90;
},
{ message: 'Latitude must be between -90 and 90' },
);
const longitude = requiredNumber('Longitude is required').refine(
(value) => {
const numberValue = Number(value);
return numberValue >= -180 && numberValue <= 180;
},
{ message: 'Longitude must be between -180 and 180' },
);
const segmentFormSchema = z.object({
id: z.string().optional(),
project_id: z.string().trim().min(1, 'Project is required'),
package_id: z.string().trim().min(1, 'Package is required'),
segment_name: z.string().trim().min(1, 'Segment name is required'),
chainage_start_km: requiredNumber('Start km is required'),
chainage_end_km: requiredNumber('End km is required'),
start_lat: latitude,
start_lng: longitude,
end_lat: latitude,
end_lng: longitude,
direction: z.enum(['UP', 'DOWN']),
});
const segmentFormSchema = z
.object({
id: z.string().optional(),
project_id: z.string().trim().min(1, 'Project is required'),
package_id: z.string().trim().min(1, 'Package is required'),
segment_name: z.string().trim().min(1, 'Segment name is required'),
chainage_start_km: requiredIntegerNumber('Start km is required'),
chainage_end_km: requiredIntegerNumber('End km is required'),
start_lat: requiredLatitude,
start_lng: requiredLongitude,
end_lat: requiredLatitude,
end_lng: requiredLongitude,
direction: z.enum(['UP', 'DOWN']),
})
.superRefine(validateChainageRange);
export type SegmentFormValues = z.infer<typeof segmentFormSchema>;