Files
paliad/internal/handlers/fristenrechner.go
m 460736ad1e refactor(t-paliad-092): rename Go module path patholo → paliad
F-6 from t-paliad-074 architecture audit. The Gitea repo was renamed
m/patholo → mAi/paliad → m/paliad, but go.mod still declared
`mgit.msbls.de/m/patholo` and every internal import echoed the
pre-rebrand name.

Sweep:
- go.mod: module path → mgit.msbls.de/m/paliad
- All *.go files: imports rewritten via sed
- README.md, docs/design-kanzlai-integration.md: mAi/paliad → m/paliad
- Frontend issue-reference comments (mAi/paliad#N → m/paliad#N) in
  i18n.ts, theme.ts, sidebar.ts, app.ts, Sidebar.tsx, PWAHead.tsx,
  global.css

Verified: go build/vet/test ./... clean, bun run build clean,
no remaining mgit.msbls.de/m/patholo or mAi/paliad references
outside docs that intentionally describe the rename history.
2026-04-30 16:46:31 +02:00

126 lines
4.7 KiB
Go

package handlers
import (
"encoding/json"
"errors"
"net/http"
"mgit.msbls.de/m/paliad/internal/services"
)
// Fristenrechner page handler: serves the static HTML. No DB dependency.
func handleFristenrechnerPage(w http.ResponseWriter, r *http.Request) {
http.ServeFile(w, r, "dist/fristenrechner.html")
}
// POST /api/tools/fristenrechner — calculate the UI timeline for a proceeding.
//
// Phase C: routes through FristenrechnerService which pulls rules from
// paliad.deadline_rules. When DATABASE_URL is unset, returns 503; the page
// itself still renders because it's static HTML.
func handleFristenrechnerAPI(w http.ResponseWriter, r *http.Request) {
if dbSvc == nil || dbSvc.fristenrechner == nil {
writeJSON(w, http.StatusServiceUnavailable, map[string]string{
"error": "Fristenrechner ist vorübergehend nicht verfügbar (keine Datenbank).",
})
return
}
var req struct {
ProceedingType string `json:"proceedingType"`
TriggerDate string `json:"triggerDate"`
PriorityDate string `json:"priorityDate,omitempty"`
Flags []string `json:"flags,omitempty"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "Ungültige Anfrage"})
return
}
if req.ProceedingType == "" || req.TriggerDate == "" {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "proceedingType und triggerDate sind erforderlich"})
return
}
resp, err := dbSvc.fristenrechner.Calculate(r.Context(), req.ProceedingType, req.TriggerDate, services.CalcOptions{
PriorityDateStr: req.PriorityDate,
Flags: req.Flags,
})
if err != nil {
if errors.Is(err, services.ErrUnknownProceedingType) {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "unbekannter Verfahrenstyp: " + req.ProceedingType})
return
}
writeJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
return
}
writeJSON(w, http.StatusOK, resp)
}
// GET /api/tools/proceeding-types — metadata list for the wizard buttons.
// Returns 503 with an empty array when DATABASE_URL is unset so the page
// still renders (buttons are server-rendered from tsx and don't depend on
// this endpoint for existence, only for dynamic list updates).
func handleProceedingTypes(w http.ResponseWriter, r *http.Request) {
if dbSvc == nil || dbSvc.fristenrechner == nil {
writeJSON(w, http.StatusServiceUnavailable, map[string]string{
"error": "Verfahrenstypen vorübergehend nicht verfügbar (keine Datenbank).",
})
return
}
types, err := dbSvc.fristenrechner.ListFristenrechnerTypes(r.Context())
if err != nil {
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "konnte Verfahrenstypen nicht laden"})
return
}
writeJSON(w, http.StatusOK, types)
}
// GET /api/tools/trigger-events — list active UPC trigger events for the
// "Was kommt nach…" mode picker. Sorted alphabetically by name.
func handleTriggerEventsList(w http.ResponseWriter, r *http.Request) {
if dbSvc == nil || dbSvc.eventDeadline == nil {
writeJSON(w, http.StatusServiceUnavailable, map[string]string{
"error": "Trigger-Ereignisse vorübergehend nicht verfügbar (keine Datenbank).",
})
return
}
events, err := dbSvc.eventDeadline.ListTriggerEvents(r.Context())
if err != nil {
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "konnte Trigger-Ereignisse nicht laden"})
return
}
writeJSON(w, http.StatusOK, events)
}
// POST /api/tools/event-deadlines — compute all deadlines flowing from a
// trigger event + date. Body: {"triggerEventId": <int>, "triggerDate": "YYYY-MM-DD"}.
func handleEventDeadlinesCalculate(w http.ResponseWriter, r *http.Request) {
if dbSvc == nil || dbSvc.eventDeadline == nil {
writeJSON(w, http.StatusServiceUnavailable, map[string]string{
"error": "Fristenrechner ist vorübergehend nicht verfügbar (keine Datenbank).",
})
return
}
var req struct {
TriggerEventID int64 `json:"triggerEventId"`
TriggerDate string `json:"triggerDate"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "Ungültige Anfrage"})
return
}
if req.TriggerEventID <= 0 || req.TriggerDate == "" {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "triggerEventId und triggerDate sind erforderlich"})
return
}
resp, err := dbSvc.eventDeadline.Calculate(r.Context(), req.TriggerEventID, req.TriggerDate)
if err != nil {
if errors.Is(err, services.ErrUnknownTriggerEvent) {
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "unbekanntes Trigger-Ereignis"})
return
}
writeJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
return
}
writeJSON(w, http.StatusOK, resp)
}