feat: add deadline management frontend (Phase 1G)

- Fristen page with list view (sortable, filterable by status/case)
- Calendar view with month navigation and deadline dots
- Deadline calculator page (proceeding type + trigger date = timeline)
- Traffic light urgency: red (overdue), amber (this week), green (OK)
- Backend: GET /api/deadlines (all tenant deadlines), GET /api/proceeding-types
- API client: added patch() method
- Types: DeadlineRule, ProceedingType, CalculatedDeadline, RuleTreeNode
This commit is contained in:
m
2026-03-25 13:53:12 +01:00
parent 0fac764211
commit 1fa7d90050
11 changed files with 790 additions and 0 deletions

View File

@@ -39,6 +39,17 @@ func (h *DeadlineRuleHandlers) List(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusOK, rules)
}
// ListProceedingTypes handles GET /api/proceeding-types
func (h *DeadlineRuleHandlers) ListProceedingTypes(w http.ResponseWriter, r *http.Request) {
types, err := h.rules.ListProceedingTypes()
if err != nil {
writeError(w, http.StatusInternalServerError, "failed to list proceeding types")
return
}
writeJSON(w, http.StatusOK, types)
}
// GetRuleTree handles GET /api/deadline-rules/{type}
// {type} is the proceeding type code (e.g., "INF", "REV")
func (h *DeadlineRuleHandlers) GetRuleTree(w http.ResponseWriter, r *http.Request) {

View File

@@ -20,6 +20,23 @@ func NewDeadlineHandlers(ds *services.DeadlineService, db *sqlx.DB) *DeadlineHan
return &DeadlineHandlers{deadlines: ds, db: db}
}
// ListAll handles GET /api/deadlines
func (h *DeadlineHandlers) ListAll(w http.ResponseWriter, r *http.Request) {
tenantID, err := resolveTenant(r, h.db)
if err != nil {
handleTenantError(w, err)
return
}
deadlines, err := h.deadlines.ListAll(tenantID)
if err != nil {
writeError(w, http.StatusInternalServerError, "failed to list deadlines")
return
}
writeJSON(w, http.StatusOK, deadlines)
}
// ListForCase handles GET /api/cases/{caseID}/deadlines
func (h *DeadlineHandlers) ListForCase(w http.ResponseWriter, r *http.Request) {
tenantID, err := resolveTenant(r, h.db)

View File

@@ -81,6 +81,7 @@ func New(db *sqlx.DB, authMW *auth.Middleware, cfg *config.Config) http.Handler
scoped.HandleFunc("DELETE /api/parties/{partyId}", partyH.Delete)
// Deadlines
scoped.HandleFunc("GET /api/deadlines", deadlineH.ListAll)
scoped.HandleFunc("GET /api/cases/{caseID}/deadlines", deadlineH.ListForCase)
scoped.HandleFunc("POST /api/cases/{caseID}/deadlines", deadlineH.Create)
scoped.HandleFunc("PUT /api/deadlines/{deadlineID}", deadlineH.Update)
@@ -90,6 +91,7 @@ func New(db *sqlx.DB, authMW *auth.Middleware, cfg *config.Config) http.Handler
// Deadline rules (reference data)
scoped.HandleFunc("GET /api/deadline-rules", ruleH.List)
scoped.HandleFunc("GET /api/deadline-rules/{type}", ruleH.GetRuleTree)
scoped.HandleFunc("GET /api/proceeding-types", ruleH.ListProceedingTypes)
// Deadline calculator
scoped.HandleFunc("POST /api/deadlines/calculate", calcH.Calculate)

View File

@@ -21,6 +21,23 @@ func NewDeadlineService(db *sqlx.DB) *DeadlineService {
return &DeadlineService{db: db}
}
// ListAll returns all deadlines for a tenant, ordered by due_date
func (s *DeadlineService) ListAll(tenantID uuid.UUID) ([]models.Deadline, error) {
query := `SELECT id, tenant_id, case_id, title, description, due_date, original_due_date,
warning_date, source, rule_id, status, completed_at,
caldav_uid, caldav_etag, notes, created_at, updated_at
FROM deadlines
WHERE tenant_id = $1
ORDER BY due_date ASC`
var deadlines []models.Deadline
err := s.db.Select(&deadlines, query, tenantID)
if err != nil {
return nil, fmt.Errorf("listing all deadlines: %w", err)
}
return deadlines, nil
}
// ListForCase returns all deadlines for a case, scoped to tenant
func (s *DeadlineService) ListForCase(tenantID, caseID uuid.UUID) ([]models.Deadline, error) {
query := `SELECT id, tenant_id, case_id, title, description, due_date, original_due_date,