Delete the four orphan files behind /deadlines/calendar +
/appointments/calendar:
- frontend/src/{deadlines,appointments}-calendar.tsx
- frontend/src/client/{deadlines,appointments}-calendar.ts
The standalone pages were unreachable from the UI since t-paliad-110
(Sidebar/BottomNav point at /events?type=…); their only role was as
bookmark targets.
Handlers in internal/handlers/{deadlines,appointments}_pages.go now
301-redirect to /events?type=…&view=calendar so bookmarks still
work. Route registrations in handlers.go remain unchanged — the
gate + redirect pair gives us the same URL surface with one canonical
renderer.
build.ts: drop the renderDeadlinesCalendar / renderAppointmentsCalendar
imports + entry-point bundle paths + dist HTML writes.
frontend/src/client/paliadin-context.ts: drop the two route-key
matches for the standalone URLs (the client never sees those
pathnames any more — 301 fires server-side).
Dead CSS pruned in frontend/src/styles/global.css (~180 lines):
- .frist-calendar, .frist-cal-{controls,month-label,grid,cell,…}
block (lines 7464-7613 pre-refactor)
- @media (max-width: 700px) { .frist-cal-cell { min-height: 64px; } }
- .termin-cal-legend{,-item}
- .frist-cal-popup-time
- .frist-cal-dot.events-cal-dot-appointment
All verified by grep across frontend/ + internal/ to have no
non-calendar consumers before deletion.
Dead i18n keys removed (DE + EN + i18n-keys.ts union type):
- deadlines.kalender.{title,heading,subtitle,list,today,empty}
- appointments.kalender.{title,heading,subtitle,list,empty}
- deadlines.list.calendar, appointments.list.calendar (button labels
on the deleted standalone routes)
- events.calendar.empty (replaced by cal.day.no_entries inside
mountCalendar's day view)
Per head decisions §11 Q1 + Q8 (drop standalone pages as 301s; drop
dead i18n now).
Tests: go build ./... clean; go test ./internal/... 9 packages pass;
cd frontend && bun run build clean (2535 i18n keys); bun test
frontend/src/client/{calendar,views}/ all 73/73 pass.
68 lines
2.9 KiB
Go
68 lines
2.9 KiB
Go
package handlers
|
|
|
|
import "net/http"
|
|
|
|
// Server-rendered page endpoints for the Phase F Appointments UI.
|
|
// HTML is generated at build time by frontend/build.ts; the per-page
|
|
// client TS bundles call /api/appointments* to populate the DOM and read
|
|
// id/project_id from window.location.
|
|
|
|
// handleAppointmentsListRedirect 301-redirects the legacy /appointments
|
|
// list URL to the canonical /events?type=appointment (t-paliad-115).
|
|
// Detail page /appointments/{id} stays type-specific. Drop this redirect
|
|
// once we're confident no caches / bookmarks / external links still hit
|
|
// the old URL.
|
|
func handleAppointmentsListRedirect(w http.ResponseWriter, r *http.Request) {
|
|
http.Redirect(w, r, "/events?type=appointment", http.StatusMovedPermanently)
|
|
}
|
|
|
|
func handleAppointmentsNewPage(w http.ResponseWriter, r *http.Request) {
|
|
http.ServeFile(w, r, "dist/appointments-new.html")
|
|
}
|
|
|
|
func handleAppointmentsDetailPage(w http.ResponseWriter, r *http.Request) {
|
|
http.ServeFile(w, r, "dist/appointments-detail.html")
|
|
}
|
|
|
|
// handleAppointmentsCalendarPage 301-redirects the legacy standalone
|
|
// calendar route to the canonical /events Kalender tab (t-paliad-224 /
|
|
// m/paliad#55). Counterpart of handleDeadlinesCalendarPage — same
|
|
// reasoning: the standalone page was orphaned in navigation since
|
|
// t-paliad-110, the canonical calendar lives inside /events.
|
|
func handleAppointmentsCalendarPage(w http.ResponseWriter, r *http.Request) {
|
|
http.Redirect(w, r, "/events?type=appointment&view=calendar", http.StatusMovedPermanently)
|
|
}
|
|
|
|
// handleSettingsPage serves the unified settings page with tabs for
|
|
// Profil / Benachrichtigungen / CalDAV. The active tab is picked
|
|
// client-side from ?tab=<name> so switching tabs doesn't round-trip.
|
|
func handleSettingsPage(w http.ResponseWriter, r *http.Request) {
|
|
http.ServeFile(w, r, "dist/settings.html")
|
|
}
|
|
|
|
// settingsTabAliases maps every supported /settings/<slug> deep-link to its
|
|
// canonical ?tab=<name> value the client TS understands. Both the German tab
|
|
// IDs (profil/benachrichtigungen) and intuitive English aliases
|
|
// (profile/notifications) are accepted so bookmarks, smoke tests, and
|
|
// manually-typed URLs all land on the right tab.
|
|
var settingsTabAliases = map[string]string{
|
|
"profil": "profil",
|
|
"profile": "profil",
|
|
"benachrichtigungen": "benachrichtigungen",
|
|
"notifications": "benachrichtigungen",
|
|
"caldav": "caldav",
|
|
}
|
|
|
|
// handleSettingsTabRedirect turns /settings/<slug> into /settings?tab=<canonical>
|
|
// as 301 Moved Permanently. Unknown slugs fall back to the bare /settings page
|
|
// (the client picks the default tab) so a typo doesn't 404.
|
|
func handleSettingsTabRedirect(w http.ResponseWriter, r *http.Request) {
|
|
slug := r.PathValue("tab")
|
|
canonical, ok := settingsTabAliases[slug]
|
|
if !ok {
|
|
http.Redirect(w, r, "/settings", http.StatusMovedPermanently)
|
|
return
|
|
}
|
|
http.Redirect(w, r, "/settings?tab="+canonical, http.StatusMovedPermanently)
|
|
}
|