chore: Projektgerüst und Werkzeugkette einrichten
Next 16 mit App Router und standalone-Ausgabe, React 19, TypeScript strict, Tailwind 4, Vitest und Playwright. Alle Versionen exakt gepinnt. Abweichungen vom ursprünglich notierten Stand, jeweils begründet: - Prisma 7.10.0 statt 6, weil 6 überholt ist und 8 nur als RC vorliegt. - TypeScript 6.0.3 statt 7, weil typescript-eslint 8 nur "<6.1.0" trägt. - ESLint 9.39.5 statt 10, weil das in eslint-config-next gebündelte eslint-plugin-react noch context.getFilename() benutzt. - npm-overrides für SimpleWebAuthn und nodemailer, weil next-auth dort ältere optionale Peers deklariert. Die betroffenen next-auth-Provider werden nicht benutzt: Passkeys laufen direkt über SimpleWebAuthn 14. "npm run pruefen" bündelt Prettier, ESLint, tsc, Vitest und ShellCheck. ShellCheck läuft über das Docker-Abbild, wenn es lokal fehlt. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
8220adc449
commit
de1f923767
Executable
+175
@@ -0,0 +1,175 @@
|
||||
#!/usr/bin/env bash
|
||||
# Zeiterfassung für den Planer.
|
||||
# Schreibt nach devlog/zeiten.csv: datum;start;ende;minuten;meilenstein;art;notiz
|
||||
set -euo pipefail
|
||||
|
||||
WURZEL="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
CSV="$WURZEL/devlog/zeiten.csv"
|
||||
KOPF='datum;start;ende;minuten;meilenstein;art;notiz'
|
||||
OFFEN="$WURZEL/devlog/.zeit-offen"
|
||||
|
||||
hilfe() {
|
||||
cat <<'ENDE'
|
||||
Verwendung: zeit.sh <befehl> [argumente]
|
||||
|
||||
start <Meilenstein> [Notiz] Eintrag öffnen (Art: claude)
|
||||
stop [Notiz] Offenen Eintrag schließen
|
||||
nachtrag <Meilenstein> <Minuten> <Art> <Notiz>
|
||||
Eigene Zeit nachtragen
|
||||
(Art: review, test, einrichtung)
|
||||
summe [Meilenstein] Summen je Meilenstein und Art
|
||||
status Zeigt, ob ein Eintrag offen ist
|
||||
hilfe Diese Übersicht
|
||||
ENDE
|
||||
}
|
||||
|
||||
fehler() {
|
||||
printf 'Fehler: %s\n' "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Semikolons und Zeilenumbrüche würden die CSV zerlegen.
|
||||
saeubern() {
|
||||
printf '%s' "$1" | tr '\n\r;' ' ' | sed 's/ */ /g; s/^ //; s/ $//'
|
||||
}
|
||||
|
||||
csv_anlegen() {
|
||||
mkdir -p "$(dirname "$CSV")"
|
||||
if [ ! -f "$CSV" ]; then
|
||||
printf '%s\n' "$KOPF" > "$CSV"
|
||||
fi
|
||||
}
|
||||
|
||||
# Minuten seit Mitternacht, damit auch ohne GNU date gerechnet werden kann.
|
||||
zu_minuten() {
|
||||
local uhr="$1"
|
||||
local h="${uhr%%:*}"
|
||||
local m="${uhr##*:}"
|
||||
printf '%s' "$((10#$h * 60 + 10#$m))"
|
||||
}
|
||||
|
||||
befehl_start() {
|
||||
local meilenstein="${1:-}"
|
||||
[ -n "$meilenstein" ] || fehler 'Meilenstein fehlt. Beispiel: zeit.sh start M1 "Gerüst"'
|
||||
shift
|
||||
local notiz
|
||||
notiz="$(saeubern "${*:-}")"
|
||||
|
||||
if [ -f "$OFFEN" ]; then
|
||||
local alt
|
||||
alt="$(cat "$OFFEN")"
|
||||
fehler "Es läuft bereits ein Eintrag ($alt). Erst 'zeit.sh stop'."
|
||||
fi
|
||||
|
||||
csv_anlegen
|
||||
mkdir -p "$(dirname "$OFFEN")"
|
||||
printf '%s;%s;%s;%s\n' "$(date '+%Y-%m-%d')" "$(date '+%H:%M')" \
|
||||
"$(saeubern "$meilenstein")" "$notiz" > "$OFFEN"
|
||||
printf 'Gestartet: %s um %s Uhr%s\n' "$meilenstein" "$(date '+%H:%M')" \
|
||||
"${notiz:+ – $notiz}"
|
||||
}
|
||||
|
||||
befehl_stop() {
|
||||
[ -f "$OFFEN" ] || fehler "Kein offener Eintrag. Erst 'zeit.sh start <Meilenstein>'."
|
||||
|
||||
local zeile datum start meilenstein notiz_start
|
||||
zeile="$(cat "$OFFEN")"
|
||||
IFS=';' read -r datum start meilenstein notiz_start <<< "$zeile"
|
||||
|
||||
local ende notiz
|
||||
ende="$(date '+%H:%M')"
|
||||
notiz="$(saeubern "${*:-}")"
|
||||
[ -n "$notiz" ] || notiz="$notiz_start"
|
||||
|
||||
local von bis minuten
|
||||
von="$(zu_minuten "$start")"
|
||||
bis="$(zu_minuten "$ende")"
|
||||
minuten=$((bis - von))
|
||||
# Über Mitternacht hinweg.
|
||||
[ "$minuten" -ge 0 ] || minuten=$((minuten + 1440))
|
||||
|
||||
csv_anlegen
|
||||
printf '%s;%s;%s;%s;%s;claude;%s\n' \
|
||||
"$datum" "$start" "$ende" "$minuten" "$meilenstein" "$notiz" >> "$CSV"
|
||||
rm -f "$OFFEN"
|
||||
printf 'Gestoppt: %s, %s Minuten (%s–%s)\n' "$meilenstein" "$minuten" "$start" "$ende"
|
||||
}
|
||||
|
||||
befehl_nachtrag() {
|
||||
local meilenstein="${1:-}" minuten="${2:-}" art="${3:-}"
|
||||
[ -n "$meilenstein" ] || fehler 'Meilenstein fehlt.'
|
||||
[ -n "$minuten" ] || fehler 'Minuten fehlen.'
|
||||
[ -n "$art" ] || fehler 'Art fehlt (review, test, einrichtung).'
|
||||
case "$minuten" in
|
||||
''|*[!0-9]*) fehler "Minuten müssen eine ganze Zahl sein, nicht '$minuten'." ;;
|
||||
esac
|
||||
shift 3
|
||||
local notiz
|
||||
notiz="$(saeubern "${*:-}")"
|
||||
[ -n "$notiz" ] || fehler 'Notiz fehlt.'
|
||||
|
||||
csv_anlegen
|
||||
printf '%s;;;%s;%s;%s;%s\n' \
|
||||
"$(date '+%Y-%m-%d')" "$minuten" "$(saeubern "$meilenstein")" \
|
||||
"$(saeubern "$art")" "$notiz" >> "$CSV"
|
||||
printf 'Nachgetragen: %s Minuten auf %s (%s)\n' "$minuten" "$meilenstein" "$art"
|
||||
}
|
||||
|
||||
befehl_summe() {
|
||||
local filter="${1:-}"
|
||||
[ -f "$CSV" ] || fehler "Noch keine Zeiten erfasst ($CSV fehlt)."
|
||||
|
||||
awk -F';' -v filter="$filter" '
|
||||
NR == 1 { next }
|
||||
$5 == "" { next }
|
||||
filter != "" && $5 != filter { next }
|
||||
{
|
||||
schluessel = $5 ";" $6
|
||||
summe[schluessel] += $4
|
||||
je_meilenstein[$5] += $4
|
||||
gesamt += $4
|
||||
if (!($5 in gesehen)) { gesehen[$5] = 1; reihenfolge[++anzahl] = $5 }
|
||||
}
|
||||
END {
|
||||
if (gesamt == 0) { print "Keine Einträge gefunden."; exit }
|
||||
printf "%-14s %-14s %10s %10s\n", "Meilenstein", "Art", "Minuten", "Stunden"
|
||||
printf "%s\n", "------------------------------------------------------"
|
||||
for (i = 1; i <= anzahl; i++) {
|
||||
m = reihenfolge[i]
|
||||
for (s in summe) {
|
||||
split(s, teil, ";")
|
||||
if (teil[1] == m) {
|
||||
printf "%-14s %-14s %10d %10.2f\n", m, teil[2], summe[s], summe[s] / 60
|
||||
}
|
||||
}
|
||||
printf "%-14s %-14s %10d %10.2f\n", m, "= Summe", je_meilenstein[m], je_meilenstein[m] / 60
|
||||
printf "%s\n", "------------------------------------------------------"
|
||||
}
|
||||
printf "%-29s %10d %10.2f\n", "Gesamt", gesamt, gesamt / 60
|
||||
}
|
||||
' "$CSV"
|
||||
}
|
||||
|
||||
befehl_status() {
|
||||
if [ -f "$OFFEN" ]; then
|
||||
local zeile datum start meilenstein notiz
|
||||
zeile="$(cat "$OFFEN")"
|
||||
IFS=';' read -r datum start meilenstein notiz <<< "$zeile"
|
||||
printf 'Offen seit %s %s Uhr: %s%s\n' "$datum" "$start" "$meilenstein" "${notiz:+ – $notiz}"
|
||||
else
|
||||
printf 'Kein offener Eintrag.\n'
|
||||
fi
|
||||
}
|
||||
|
||||
befehl="${1:-hilfe}"
|
||||
shift || true
|
||||
|
||||
case "$befehl" in
|
||||
start) befehl_start "$@" ;;
|
||||
stop) befehl_stop "$@" ;;
|
||||
nachtrag) befehl_nachtrag "$@" ;;
|
||||
summe) befehl_summe "$@" ;;
|
||||
status) befehl_status ;;
|
||||
hilfe|-h|--hilfe|--help) hilfe ;;
|
||||
*) fehler "Unbekannter Befehl '$befehl'. 'zeit.sh hilfe' zeigt die Übersicht." ;;
|
||||
esac
|
||||
Reference in New Issue
Block a user