104 lines
3.0 KiB
TypeScript
104 lines
3.0 KiB
TypeScript
|
|
'use client';
|
||
|
|
|
||
|
|
import { useEffect, useState } from 'react';
|
||
|
|
import { WorkingHoursEditor } from '@/components/staff/WorkingHoursEditor';
|
||
|
|
import {
|
||
|
|
blocksFromEditorDays,
|
||
|
|
editorDaysFromBlocks,
|
||
|
|
emptyWorkingHoursEditorDays,
|
||
|
|
validateEditorDays,
|
||
|
|
type WorkingHoursEditorDay,
|
||
|
|
} from '@/components/staff/workingHours';
|
||
|
|
|
||
|
|
interface StaffWorkingHoursStepProps {
|
||
|
|
days: WorkingHoursEditorDay[];
|
||
|
|
autoRepeatWeekly: boolean;
|
||
|
|
onDaysChange: (days: WorkingHoursEditorDay[]) => void;
|
||
|
|
onAutoRepeatWeeklyChange: (value: boolean) => void;
|
||
|
|
onValidationChange?: (error: string | null) => void;
|
||
|
|
disabled?: boolean;
|
||
|
|
}
|
||
|
|
|
||
|
|
export function StaffWorkingHoursStep({
|
||
|
|
days,
|
||
|
|
autoRepeatWeekly,
|
||
|
|
onDaysChange,
|
||
|
|
onAutoRepeatWeeklyChange,
|
||
|
|
onValidationChange,
|
||
|
|
disabled,
|
||
|
|
}: StaffWorkingHoursStepProps) {
|
||
|
|
useEffect(() => {
|
||
|
|
onValidationChange?.(validateEditorDays(days));
|
||
|
|
}, [days, onValidationChange]);
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className="space-y-3">
|
||
|
|
<div className="rounded-[var(--radius-md)] border border-primary/25 bg-primary/5 px-3 py-3">
|
||
|
|
<p className="text-sm text-text-primary font-medium">Working hours recommended</p>
|
||
|
|
<p className="text-sm text-text-secondary mt-1">
|
||
|
|
Staff with treatment edit access appear as provider columns in Appointments. Set their
|
||
|
|
weekly hours so the schedule grid shows the right bookable times.
|
||
|
|
</p>
|
||
|
|
</div>
|
||
|
|
<WorkingHoursEditor
|
||
|
|
days={days}
|
||
|
|
autoRepeatWeekly={autoRepeatWeekly}
|
||
|
|
onDaysChange={onDaysChange}
|
||
|
|
onAutoRepeatWeeklyChange={onAutoRepeatWeeklyChange}
|
||
|
|
disabled={disabled}
|
||
|
|
/>
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
export function createDefaultWorkingHoursState() {
|
||
|
|
return {
|
||
|
|
days: emptyWorkingHoursEditorDays(),
|
||
|
|
autoRepeatWeekly: true,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
export function workingHoursPayloadFromState(state: {
|
||
|
|
days: WorkingHoursEditorDay[];
|
||
|
|
autoRepeatWeekly: boolean;
|
||
|
|
}) {
|
||
|
|
return {
|
||
|
|
autoRepeatWeekly: state.autoRepeatWeekly,
|
||
|
|
blocks: blocksFromEditorDays(state.days),
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
export function workingHoursStateFromApi(data: {
|
||
|
|
autoRepeatWeekly: boolean;
|
||
|
|
blocks: { dayOfWeek: number; startMinute: number; endMinute: number; sortOrder?: number }[];
|
||
|
|
}) {
|
||
|
|
const hasBlocks = data.blocks.length > 0;
|
||
|
|
return {
|
||
|
|
days: hasBlocks ? editorDaysFromBlocks(data.blocks) : emptyWorkingHoursEditorDays(),
|
||
|
|
autoRepeatWeekly: data.autoRepeatWeekly,
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
export function useWorkingHoursForm(initial?: {
|
||
|
|
days: WorkingHoursEditorDay[];
|
||
|
|
autoRepeatWeekly: boolean;
|
||
|
|
}) {
|
||
|
|
const [days, setDays] = useState(initial?.days ?? emptyWorkingHoursEditorDays());
|
||
|
|
const [autoRepeatWeekly, setAutoRepeatWeekly] = useState(initial?.autoRepeatWeekly ?? true);
|
||
|
|
const [validationError, setValidationError] = useState<string | null>(null);
|
||
|
|
|
||
|
|
return {
|
||
|
|
days,
|
||
|
|
setDays,
|
||
|
|
autoRepeatWeekly,
|
||
|
|
setAutoRepeatWeekly,
|
||
|
|
validationError,
|
||
|
|
setValidationError,
|
||
|
|
reset(next?: { days: WorkingHoursEditorDay[]; autoRepeatWeekly: boolean }) {
|
||
|
|
setDays(next?.days ?? emptyWorkingHoursEditorDays());
|
||
|
|
setAutoRepeatWeekly(next?.autoRepeatWeekly ?? true);
|
||
|
|
setValidationError(null);
|
||
|
|
},
|
||
|
|
};
|
||
|
|
}
|