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:
Alex Polo
2025-10-31 21:05:14 +01:00
parent 7edcbbb1bf
commit 02cef8d619
2 changed files with 35 additions and 47 deletions
+1
View File
@@ -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
+34 -47
View File
@@ -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">