- Converted several async components to synchronous functions, including `Layout`, `AddOrdersPage`, `ProfilePage`, `SlotPage`, and `ServicePage`, enhancing rendering efficiency. - Removed unnecessary prefetching logic and hydration boundaries, simplifying component structure and improving maintainability. - Updated the `TelegramProvider` to return null during the initial mount instead of a loading message, streamlining the loading state handling. - Enhanced loading state management in order-related components by adding loading spinners and data not found alerts, improving user experience during data fetching.
118 lines
3.7 KiB
TypeScript
118 lines
3.7 KiB
TypeScript
'use client';
|
||
|
||
import { NumberField, TextareaField, TextField, TimeField } from '@/components/shared/data-fields';
|
||
import { useServiceMutation, useServiceQuery } from '@/hooks/api/services';
|
||
import { Button } from '@repo/ui/components/ui/button';
|
||
import { Card } from '@repo/ui/components/ui/card';
|
||
import { convertTimeString } from '@repo/utils/datetime-format';
|
||
import { useState } from 'react';
|
||
|
||
type Props = {
|
||
serviceId: string;
|
||
};
|
||
|
||
export function ServiceDataCard({ serviceId }: Readonly<Props>) {
|
||
const { data: { service } = {}, isLoading } = useServiceQuery({ documentId: serviceId });
|
||
const { cancelChanges, hasChanges, isPending, resetTrigger, saveChanges, updateField } =
|
||
useServiceEdit(serviceId);
|
||
|
||
if (isLoading) {
|
||
return (
|
||
<Card className="p-4">
|
||
<div className="flex animate-pulse flex-col gap-4">
|
||
<div className="h-4 w-16 rounded bg-muted" />
|
||
<div className="h-10 w-full rounded bg-muted" />
|
||
<div className="h-4 w-16 rounded bg-muted" />
|
||
<div className="h-10 w-full rounded bg-muted" />
|
||
<div className="h-4 w-16 rounded bg-muted" />
|
||
<div className="h-10 w-full rounded bg-muted" />
|
||
<div className="h-4 w-16 rounded bg-muted" />
|
||
<div className="h-28 w-full rounded bg-muted" />
|
||
</div>
|
||
</Card>
|
||
);
|
||
}
|
||
|
||
if (!service) return null;
|
||
|
||
return (
|
||
<Card className="p-4">
|
||
<div className="flex flex-col gap-4">
|
||
<TextField
|
||
id="name"
|
||
key={`name-${resetTrigger}`}
|
||
label="Название"
|
||
onChange={(name) => updateField('name', name)}
|
||
value={service?.name ?? ''}
|
||
/>
|
||
<TimeField
|
||
id="duration"
|
||
key={`duration-${resetTrigger}`}
|
||
label="Длительность"
|
||
onChange={(time) => updateField('duration', convertTimeString(time).db())}
|
||
value={service?.duration ?? ''}
|
||
/>
|
||
<NumberField
|
||
id="price"
|
||
key={`price-${resetTrigger}`}
|
||
label="Цена (₽)"
|
||
min={0}
|
||
onChange={(price) => updateField('price', price)}
|
||
value={service?.price ?? null}
|
||
/>
|
||
<TextareaField
|
||
id="description"
|
||
key={`description-${resetTrigger}`}
|
||
label="Описание"
|
||
onChange={(description) => updateField('description', description)}
|
||
rows={4}
|
||
value={service?.description ?? ''}
|
||
/>
|
||
{hasChanges && (
|
||
<div className="flex justify-end gap-2">
|
||
<Button disabled={isPending} onClick={cancelChanges} variant="outline">
|
||
Отмена
|
||
</Button>
|
||
<Button disabled={isPending} onClick={saveChanges}>
|
||
{isPending ? 'Сохранение...' : 'Сохранить'}
|
||
</Button>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</Card>
|
||
);
|
||
}
|
||
|
||
function useServiceEdit(serviceId: string) {
|
||
const { isPending, mutate } = useServiceMutation({ documentId: serviceId });
|
||
const [pendingChanges, setPendingChanges] = useState<Record<string, unknown>>({});
|
||
const [resetTrigger, setResetTrigger] = useState(0);
|
||
|
||
const updateField = (field: string, value: unknown) => {
|
||
setPendingChanges((previous) => ({ ...previous, [field]: value }));
|
||
};
|
||
|
||
const saveChanges = () => {
|
||
if (Object.keys(pendingChanges).length === 0) return;
|
||
|
||
mutate({ data: pendingChanges });
|
||
setPendingChanges({});
|
||
};
|
||
|
||
const cancelChanges = () => {
|
||
setPendingChanges({});
|
||
setResetTrigger((previous) => previous + 1);
|
||
};
|
||
|
||
const hasChanges = Object.keys(pendingChanges).length > 0;
|
||
|
||
return {
|
||
cancelChanges,
|
||
hasChanges,
|
||
isPending,
|
||
resetTrigger,
|
||
saveChanges,
|
||
updateField,
|
||
};
|
||
}
|