feat: auto-assign strategy (round_robin/least_loaded/first_free); urgent tasks always use first_free
This commit is contained in:
@@ -566,6 +566,8 @@ export interface HkSettings {
|
||||
inspection_after_clean: boolean
|
||||
require_completion_photo?: boolean
|
||||
emergency_close_days?: number
|
||||
auto_assign?: boolean
|
||||
auto_assign_strategy?: string
|
||||
}
|
||||
|
||||
export interface HotelPayload {
|
||||
|
||||
@@ -113,6 +113,7 @@ export function HousekeepingPage() {
|
||||
const ss = s as unknown as {
|
||||
checkoutAuto: boolean; checkoutPriority: string
|
||||
inspectionAfterClean: boolean; requireCompletionPhoto: boolean; emergencyCloseDays: number
|
||||
autoAssign: boolean; autoAssignStrategy: string
|
||||
}
|
||||
setPlans(prev => ({
|
||||
...prev,
|
||||
@@ -121,6 +122,8 @@ export function HousekeepingPage() {
|
||||
inspectionAfterClean: ss.inspectionAfterClean,
|
||||
requireCompletionPhoto: ss.requireCompletionPhoto ?? false,
|
||||
emergencyCloseDays: ss.emergencyCloseDays ?? 1,
|
||||
autoAssign: ss.autoAssign ?? false,
|
||||
autoAssignStrategy: (ss.autoAssignStrategy ?? 'least_loaded') as 'round_robin' | 'least_loaded' | 'first_free',
|
||||
}))
|
||||
}
|
||||
}).catch(console.error)
|
||||
@@ -143,8 +146,8 @@ export function HousekeepingPage() {
|
||||
checkoutInspection: true, dailyEnabled: true, dailyIntervalDays: 1,
|
||||
dailyStartFromDay: 1, onDemandEnabled: true, onDemandPriority: 'medium' as 'high' | 'medium' | 'low',
|
||||
deepCleanEnabled: false, deepCleanEveryDays: 7,
|
||||
inspectionAfterClean: true, autoAssign: false, requireCompletionPhoto: false,
|
||||
emergencyCloseDays: 1,
|
||||
inspectionAfterClean: true, autoAssign: false, autoAssignStrategy: 'least_loaded' as 'round_robin' | 'least_loaded' | 'first_free',
|
||||
requireCompletionPhoto: false, emergencyCloseDays: 1,
|
||||
})
|
||||
|
||||
const setP = <K extends keyof typeof plans>(k: K, v: typeof plans[K]) =>
|
||||
@@ -160,6 +163,8 @@ export function HousekeepingPage() {
|
||||
inspection_after_clean: plans.inspectionAfterClean,
|
||||
require_completion_photo: plans.requireCompletionPhoto,
|
||||
emergency_close_days: plans.emergencyCloseDays,
|
||||
auto_assign: plans.autoAssign,
|
||||
auto_assign_strategy: plans.autoAssignStrategy,
|
||||
}
|
||||
await api.housekeeping.saveSettings(slug, settings)
|
||||
setPlanSaved(true)
|
||||
@@ -516,14 +521,41 @@ export function HousekeepingPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="card p-4">
|
||||
<div className="card p-4 space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="font-semibold text-slate-900 dark:text-slate-100">Автоназначение горничной</p>
|
||||
<p className="text-xs text-slate-500 dark:text-slate-400 mt-0.5">Автоматически назначать ответственную горничную по расписанию смен</p>
|
||||
<p className="text-xs text-slate-500 dark:text-slate-400 mt-0.5">Автоматически назначать горничную при создании задачи</p>
|
||||
</div>
|
||||
<Toggle on={plans.autoAssign} onChange={() => setP('autoAssign', !plans.autoAssign)} />
|
||||
</div>
|
||||
{plans.autoAssign && (
|
||||
<div className="border-t border-slate-100 dark:border-slate-700 pt-3 space-y-2">
|
||||
<p className="text-xs font-semibold text-slate-700 dark:text-slate-300">Стратегия распределения (обычные задачи)</p>
|
||||
<p className="text-[11px] text-slate-400 dark:text-slate-500">Срочные задачи всегда назначаются первой свободной горничной</p>
|
||||
<div className="space-y-1.5">
|
||||
{([
|
||||
{ value: 'round_robin', label: 'По кругу', desc: 'Кому сегодня назначено меньше задач' },
|
||||
{ value: 'least_loaded', label: 'Наименее загруженная', desc: 'У кого сейчас меньше активных задач' },
|
||||
{ value: 'first_free', label: 'Первая свободная', desc: 'Кто раньше завершила последнюю задачу' },
|
||||
] as const).map(opt => (
|
||||
<button
|
||||
key={opt.value}
|
||||
onClick={() => setP('autoAssignStrategy', opt.value)}
|
||||
className={cn(
|
||||
'w-full text-left px-3 py-2 rounded-lg border text-xs transition-colors',
|
||||
plans.autoAssignStrategy === opt.value
|
||||
? 'bg-blue-50 dark:bg-blue-900/20 border-blue-300 dark:border-blue-700 text-blue-700 dark:text-blue-300'
|
||||
: 'bg-white dark:bg-slate-700 border-slate-200 dark:border-slate-600 text-slate-600 dark:text-slate-300 hover:border-blue-200',
|
||||
)}
|
||||
>
|
||||
<span className="font-medium">{opt.label}</span>
|
||||
<span className="text-slate-400 dark:text-slate-500 ml-2">{opt.desc}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="card p-4">
|
||||
|
||||
Reference in New Issue
Block a user