Simplify availability logic: all days open by default
Breaking change: Weekly schedule is now reference-only - Remove nonWorking modifier and gray styling - All calendar days show as green (available) by default - Only manually blocked dates show as red - Remove confirmation prompts about work schedule - Update UI text to clarify: 'all days are open unless manually blocked' - Weekly schedule section now explains it's reference information only This aligns with the actual business logic: Christian sets his normal work hours for 2-3 weeks at a time, and the calendar should be open for bookings on all days unless he manually blocks specific dates (vacation, sick days, etc.)
This commit is contained in:
@@ -15,3 +15,4 @@
|
||||
2025-10-31T20:05:46: > Ready on http://localhost:3000
|
||||
2025-10-31T20:42:18: > Ready on http://localhost:3000
|
||||
2025-10-31T20:54:03: > Ready on http://localhost:3000
|
||||
2025-10-31T21:04:52: > Ready on http://localhost:3000
|
||||
|
||||
@@ -150,18 +150,12 @@ function AvailabilityAdminContent() {
|
||||
const iso = isoFromDate(d)
|
||||
|
||||
const found = blocked.find(b => b.block_date === iso)
|
||||
const weeklyForDay = weekly.find(w => w.weekday === d.getDay())
|
||||
// If this date falls on a working day, confirm user intention to block full day
|
||||
if (!found && weeklyForDay) {
|
||||
const proceed = confirm(`Der er allerede et arbejdsskema for denne ugedag (${['Søndag','Mandag','Tirsdag','Onsdag','Torsdag','Fredag','Lørdag'][weeklyForDay.weekday]} ${weeklyForDay.start_time}–${weeklyForDay.end_time}).\n\nVil du blokere hele dagen alligevel?`)
|
||||
if (!proceed) return
|
||||
}
|
||||
if (found) {
|
||||
// optimistic UI: remove locally first
|
||||
setBlocked(prev => prev.filter(b => b.block_date !== iso))
|
||||
try {
|
||||
await fetch('/api/availability/blocked', { method: 'DELETE', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ id: found.id }) })
|
||||
addToast('Blokering fjernet', 'success')
|
||||
addToast('Blokering fjernet — dag er nu åben', 'success')
|
||||
} catch (err) {
|
||||
// revert on failure
|
||||
fetchAll()
|
||||
@@ -173,7 +167,7 @@ function AvailabilityAdminContent() {
|
||||
setBlocked(prev => [...prev, { id: `tmp-${iso}`, block_date: iso }])
|
||||
try {
|
||||
await fetch('/api/availability/blocked', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({ block_date: iso, note: '' }) })
|
||||
addToast('Dato blokeret', 'success')
|
||||
addToast('Dag blokeret', 'success')
|
||||
// refresh to pick up real id
|
||||
fetchAll()
|
||||
return
|
||||
@@ -269,8 +263,11 @@ function AvailabilityAdminContent() {
|
||||
<div className="space-y-6">
|
||||
{/* Main calendar section */}
|
||||
<section className="bg-white rounded-lg p-6">
|
||||
<h2 className="text-xl font-semibold mb-2">Kalender — klik for at skifte tilgængelighed</h2>
|
||||
<p className="text-sm text-gray-600 mb-4">Klik på en dato for at blokere/åbne den.</p>
|
||||
<h2 className="text-xl font-semibold mb-2">Kalender — klik for at blokere dage</h2>
|
||||
<p className="text-sm text-gray-600 mb-4">
|
||||
Alle dage er som standard <strong>åbne for bookinger</strong>. Klik på en dato for at blokere den (fx ferie, sygdom).
|
||||
Ugentligt arbejdsskema vises kun som reference.
|
||||
</p>
|
||||
|
||||
<div className="flex flex-wrap gap-3 mb-4">
|
||||
<label className="inline-flex items-center gap-2 cursor-pointer">
|
||||
@@ -334,11 +331,9 @@ function AvailabilityAdminContent() {
|
||||
locale={da}
|
||||
modifiers={{
|
||||
blocked: blocked.map(b => new Date(b.block_date)),
|
||||
nonWorking: (day: Date) => !weekly.some(w => w.weekday === day.getDay())
|
||||
}}
|
||||
modifiersClassNames={{
|
||||
blocked: 'rdp-day_blocked',
|
||||
nonWorking: 'rdp-day_nonWorking',
|
||||
}}
|
||||
disabled={showOnlyBlocked ? ((day: Date) => !blocked.some(b => sameDay(new Date(b.block_date), day))) : undefined}
|
||||
/>
|
||||
@@ -367,16 +362,6 @@ function AvailabilityAdminContent() {
|
||||
if (e && e.shiftKey && lastClickedDate) {
|
||||
setSaving(true)
|
||||
const range = datesBetween(lastClickedDate, d)
|
||||
// confirm if any date in range falls on a working weekly schedule
|
||||
const hasWorking = range.some(rd => weekly.some(w => w.weekday === rd.getDay()))
|
||||
if (hasWorking) {
|
||||
const proceed = confirm('Nogle dage i det valgte interval har et arbejdsskema. Vil du blokere alle dagene alligevel?')
|
||||
if (!proceed) {
|
||||
setLastClickedDate(d)
|
||||
setSaving(false)
|
||||
return
|
||||
}
|
||||
}
|
||||
// optimistic add
|
||||
const isos = range.map(r => isoFromDate(r))
|
||||
setBlocked(prev => [...prev, ...isos.map(i => ({ id: `tmp-${i}`, block_date: i }))])
|
||||
@@ -400,45 +385,47 @@ function AvailabilityAdminContent() {
|
||||
locale={da}
|
||||
modifiers={{
|
||||
blocked: blocked.map(b => new Date(b.block_date)),
|
||||
nonWorking: (day: Date) => !weekly.some(w => w.weekday === day.getDay()),
|
||||
pending: pendingDates,
|
||||
}}
|
||||
modifiersClassNames={{
|
||||
blocked: 'rdp-day_blocked',
|
||||
pending: 'rdp-day_pending',
|
||||
nonWorking: 'rdp-day_nonWorking',
|
||||
}}
|
||||
disabled={showOnlyBlocked ? ((day: Date) => !blocked.some(b => sameDay(new Date(b.block_date), day))) : undefined}
|
||||
/>
|
||||
)}
|
||||
<div className="mt-3 text-sm flex items-center gap-4">
|
||||
<span className="inline-flex items-center gap-2">
|
||||
<span className="w-3 h-3 bg-green-400 rounded-sm inline-block" />
|
||||
<span>Tilgængelig</span>
|
||||
</span>
|
||||
<span className="inline-flex items-center gap-2">
|
||||
<span className="w-3 h-3 bg-red-500 rounded-sm inline-block" />
|
||||
<span>Blokeret</span>
|
||||
</span>
|
||||
<span className="inline-flex items-center gap-2">
|
||||
<span className="w-3 h-3 bg-amber-500 rounded-sm inline-block" />
|
||||
<span>Valgt</span>
|
||||
</span>
|
||||
<span className="inline-flex items-center gap-2">
|
||||
<span className="w-3 h-3 bg-blue-400 rounded-sm inline-block" />
|
||||
<span>Venter (Ctrl+klik)</span>
|
||||
</span>
|
||||
<div className="mt-4 flex flex-wrap gap-4 text-sm">
|
||||
<span className="inline-flex items-center gap-2">
|
||||
<span className="w-4 h-4 bg-green-400 rounded border border-green-500" />
|
||||
<span>Åben (tilgængelig)</span>
|
||||
</span>
|
||||
<span className="inline-flex items-center gap-2">
|
||||
<span className="w-4 h-4 bg-red-500 rounded border border-red-600" />
|
||||
<span>Blokeret</span>
|
||||
</span>
|
||||
<span className="inline-flex items-center gap-2">
|
||||
<span className="w-4 h-4 bg-amber-500 rounded border border-amber-600" />
|
||||
<span>Valgt (multi-select)</span>
|
||||
</span>
|
||||
<span className="inline-flex items-center gap-2">
|
||||
<span className="w-4 h-4 bg-blue-400 rounded border border-blue-500" />
|
||||
<span>Venter (Ctrl+klik)</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{!multiSelectMode && (
|
||||
<div className="mt-3 p-3 bg-blue-50 rounded text-sm text-gray-700">
|
||||
<p><strong>Tips:</strong> Hold <kbd className="px-1.5 py-0.5 bg-white border rounded text-xs font-mono">Shift</kbd> og klik for interval · Hold <kbd className="px-1.5 py-0.5 bg-white border rounded text-xs font-mono">Ctrl/⌘</kbd> og klik for flere enkelte dage</p>
|
||||
</div>
|
||||
{!multiSelectMode && (
|
||||
<div className="mt-2 text-xs text-gray-600">
|
||||
<p>Tips: Hold Shift og klik for at vælge interval. Hold Ctrl/⌘ og klik for at vælge flere enkeltdage.</p>
|
||||
</div>
|
||||
)}
|
||||
)}
|
||||
</div>
|
||||
|
||||
</section>
|
||||
<section className="bg-white rounded-lg p-6">
|
||||
<h2 className="text-xl font-semibold mb-4">Ugentligt arbejdsskema</h2>
|
||||
<h2 className="text-xl font-semibold mb-2">Ugentligt arbejdsskema</h2>
|
||||
<p className="text-sm text-gray-600 mb-4">
|
||||
Angiv dine normale arbejdstider. Dette bruges kun som reference — alle dage er åbne medmindre du manuelt blokerer dem.
|
||||
</p>
|
||||
<form onSubmit={addWeekly} className="grid gap-3 mb-4">
|
||||
<label>Ugedag
|
||||
<select value={weekday} onChange={e => setWeekday(parseInt(e.target.value))} className="ml-2">
|
||||
|
||||
Reference in New Issue
Block a user