feat(aufgaben): Datenmodell und Regeln für Aufgaben und Projekte
Aufgabe mit Typ, Status, Priorität, Aufwand, Fälligkeit und Herkunft; Projekt als eigene Klammer darum. Zwei Entwurfsentscheidungen: - Die Inbox ist kein eigener Zustand, sondern schlicht eine Aufgabe ohne Bereich. Einsortieren heißt: einen Bereich setzen. Ein zusätzliches Feld könnte mit der Zuordnung auseinanderlaufen. - Projekt ist ein eigenes Modell, kein Aufgabentyp. Ein Projekt hat selbst weder Aufwand noch Fälligkeit, sondern leitet beides aus dem ab, was drinsteckt. ARBEITSPAKET und MEILENSTEIN sind Typen der Aufgaben *innerhalb* eines Projekts. @@unique([quelle, quelleRef]) ist die Grundlage für die Integrationen ab M6: sie upserten darüber, statt zu doppeln. Von Hand erfasste Aufgaben tragen quelleRef = null; PostgreSQL sieht NULL-Werte als verschieden an, sie behindern sich also nicht. Verworfene Aufgaben zählen bewusst gar nicht in den Fortschritt eines Projekts — sonst sähe es fertig aus, weil die Hälfte gestrichen wurde. Fälligkeit wird in Kalendertagen in Europe/Berlin gerechnet, nicht in 24-Stunden-Schritten: was heute um 23:00 fällig ist, ist heute fällig. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
704c490368
commit
63c3c15a3c
@@ -0,0 +1,96 @@
|
||||
import type { AufgabenStatus, AufgabenTyp, Quelle } from '@/generated/prisma/enums'
|
||||
import {
|
||||
faelligkeitEinordnen,
|
||||
faelligkeitText,
|
||||
istUeberfaellig,
|
||||
type Faelligkeit,
|
||||
} from '@/lib/aufgabe'
|
||||
import { alsDatum, alsTagesschluessel } from '@/lib/zeit'
|
||||
|
||||
/**
|
||||
* Bereitet eine Aufgabe für die Anzeige auf.
|
||||
*
|
||||
* Reine Funktion mit dem Zeitpunkt als Parameter: Formatierung und
|
||||
* Fälligkeitsrechnung passieren serverseitig in Europe/Berlin, damit im
|
||||
* Browser nichts nachgerechnet wird und alle Listen gleich aussehen.
|
||||
*/
|
||||
|
||||
export type AufgabeRoh = {
|
||||
id: string
|
||||
titel: string
|
||||
notiz: string | null
|
||||
typ: AufgabenTyp
|
||||
status: AufgabenStatus
|
||||
prioritaet: number
|
||||
aufwandMinuten: number | null
|
||||
faelligAm: Date | null
|
||||
quelle: Quelle
|
||||
quelleUrl: string | null
|
||||
bereich: { id: string; name: string; farbe: string } | null
|
||||
projekt: { id: string; name: string } | null
|
||||
}
|
||||
|
||||
export type AufgabeAnzeige = {
|
||||
id: string
|
||||
titel: string
|
||||
notiz: string | null
|
||||
typ: AufgabenTyp
|
||||
status: AufgabenStatus
|
||||
prioritaet: number
|
||||
aufwandMinuten: number | null
|
||||
/** ISO-Datum für das Formularfeld, sonst leer. */
|
||||
faelligWert: string
|
||||
/** "heute fällig", "seit 3 Tagen überfällig", "ohne Termin". */
|
||||
faelligText: string
|
||||
/** Ausgeschriebenes Datum für den Titelhinweis, sonst null. */
|
||||
faelligDatum: string | null
|
||||
faelligkeit: Faelligkeit['art']
|
||||
ueberfaellig: boolean
|
||||
quelle: Quelle
|
||||
quelleUrl: string | null
|
||||
bereich: { id: string; name: string; farbe: string } | null
|
||||
projekt: { id: string; name: string } | null
|
||||
}
|
||||
|
||||
/**
|
||||
* ISO-Datum in Europe/Berlin, passend für ein <input type="date">.
|
||||
* Der Wert des Feldes ist ein reines Datum ohne Zeitzone, also der Berliner
|
||||
* Kalendertag — dieselbe Rechnung wie überall sonst im Planer.
|
||||
*/
|
||||
function alsDatumswert(zeitpunkt: Date | null): string {
|
||||
return zeitpunkt ? alsTagesschluessel(zeitpunkt) : ''
|
||||
}
|
||||
|
||||
export function fuerAnzeige(aufgabe: AufgabeRoh, jetzt: Date): AufgabeAnzeige {
|
||||
const faelligkeit = faelligkeitEinordnen(aufgabe.faelligAm, jetzt)
|
||||
|
||||
return {
|
||||
id: aufgabe.id,
|
||||
titel: aufgabe.titel,
|
||||
notiz: aufgabe.notiz,
|
||||
typ: aufgabe.typ,
|
||||
status: aufgabe.status,
|
||||
prioritaet: aufgabe.prioritaet,
|
||||
aufwandMinuten: aufgabe.aufwandMinuten,
|
||||
faelligWert: alsDatumswert(aufgabe.faelligAm),
|
||||
faelligText: faelligkeitText(faelligkeit),
|
||||
faelligDatum: aufgabe.faelligAm ? alsDatum(aufgabe.faelligAm) : null,
|
||||
faelligkeit: faelligkeit.art,
|
||||
ueberfaellig: istUeberfaellig(aufgabe.faelligAm, aufgabe.status, jetzt),
|
||||
quelle: aufgabe.quelle,
|
||||
quelleUrl: aufgabe.quelleUrl,
|
||||
bereich: aufgabe.bereich,
|
||||
projekt: aufgabe.projekt,
|
||||
}
|
||||
}
|
||||
|
||||
/** Wie dringend die Fälligkeit aussieht — steuert die Farbe des Kennzeichens. */
|
||||
export function faelligkeitFarbe(
|
||||
anzeige: AufgabeAnzeige,
|
||||
): 'neutral' | 'warnung' | 'gefahr' {
|
||||
if (anzeige.ueberfaellig) return 'gefahr'
|
||||
if (anzeige.faelligkeit === 'heute' || anzeige.faelligkeit === 'morgen') {
|
||||
return 'warnung'
|
||||
}
|
||||
return 'neutral'
|
||||
}
|
||||
Reference in New Issue
Block a user