ui phase 9

This commit is contained in:
hamid
2026-07-19 15:14:44 +03:30
parent 1ef4feb911
commit b638e25a0e
47 changed files with 2628 additions and 666 deletions
@@ -1,5 +1,5 @@
'use client';
import { FunctionComponent, useState } from 'react';
import { FunctionComponent, useEffect, useState } from 'react';
import { useTranslations } from 'next-intl';
import FormControlLabel from '@mui/material/FormControlLabel';
import Stack from '@mui/material/Stack';
@@ -28,6 +28,8 @@ export interface AddressFormProps {
submitting?: boolean;
onSubmit: (input: CreateAddressInput) => void;
onCancel?: () => void;
/** Reports whether any field differs from `initial` — drives a host `FormDialogShell`'s discard-confirm. */
onDirtyChange?: (dirty: boolean) => void;
}
// Only prefill the region when we have the province — a city id without its province can't drive
@@ -50,21 +52,41 @@ function initialPin(initial?: AddressFormInitial): LatLng | null {
* `CreateAddressInput` carrying the picked coordinates. Reused for create and edit.
* @component AddressForm
*/
const AddressForm: FunctionComponent<AddressFormProps> = ({ initial, submitting = false, onSubmit, onCancel }) => {
const AddressForm: FunctionComponent<AddressFormProps> = ({ initial, submitting = false, onSubmit, onCancel, onDirtyChange }) => {
const t = useTranslations('address');
const tc = useTranslations('common');
const [title, setTitle] = useState(initial?.title ?? '');
const initialTitle = initial?.title ?? '';
const initialAddressLine = initial?.addressLine ?? '';
const initialIsPrimary = initial?.isPrimary ?? false;
const [title, setTitle] = useState(initialTitle);
const [region, setRegion] = useState<CascadingRegionValue>(() => initialRegion(initial));
const [addressLine, setAddressLine] = useState(initial?.addressLine ?? '');
const [addressLine, setAddressLine] = useState(initialAddressLine);
const [pin, setPin] = useState<LatLng | null>(() => initialPin(initial));
const [isPrimary, setIsPrimary] = useState(initial?.isPrimary ?? false);
const [isPrimary, setIsPrimary] = useState(initialIsPrimary);
const [titleError, setTitleError] = useState(false);
const [cityError, setCityError] = useState(false);
const [lineError, setLineError] = useState(false);
const [pinError, setPinError] = useState(false);
useEffect(() => {
if (!onDirtyChange) return;
const initialPinValue = initialPin(initial);
const dirty =
title !== initialTitle ||
addressLine !== initialAddressLine ||
isPrimary !== initialIsPrimary ||
region.provinceId !== (initial?.provinceId ?? null) ||
region.cityId !== (initial?.cityId ?? null) ||
region.districtId !== (initial?.districtId ?? null) ||
pin?.latitude !== initialPinValue?.latitude ||
pin?.longitude !== initialPinValue?.longitude;
onDirtyChange(dirty);
// eslint-disable-next-line react-hooks/exhaustive-deps -- `initial` is a stable prefill snapshot (the caller re-keys the form on edit-target change), not reactive state to track.
}, [title, addressLine, isPrimary, region, pin]);
const handleSubmit = () => {
const titleInvalid = title.trim().length === 0;
const cityInvalid = region.cityId == null;