Commit Graph
18 Commits
Author SHA1 Message Date
schakal00783andClaude Opus 5 7e7188d476 feat(saetze): Satz je Task plus Import-Endpunkt fuer Harvest
Satz je Task
  Ein Task kann einen eigenen Satz tragen; leer bedeutet weiter "Satz des
  Budgettopfs" bzw. bei Abruf "Satz des Projekts". Damit lassen sich
  "remote" und "vor Ort" im selben Projekt abrechnen, ohne dafuer getrennte
  Projekte anzulegen.

  Das Budget bleibt in Stunden - gemischte Saetze aendern nichts daran, wann
  ein Topf voll ist. Der Umsatz wird je Task-Satz aufsummiert: loadProjects
  gruppiert den Topfverbrauch nach Task-Satz und gibt das Ergebnis an
  potStats, Abruf rechnet je Task. Mitarbeiter sehen keine Saetze, auch
  nicht die am Task.

Import
  POST /api/imports/entries (nur Administrator) schreibt Buchungen fuer
  beliebige Personen - POST /api/entries bucht bewusst nur auf den
  angemeldeten Benutzer. Der Import laeuft durch dieselben Regeln: Projekt
  aktiv, Person zugewiesen, Topf passend zum Datum, maximal 24 h am Tag.
  dry_run schreibt nichts.

  Jede uebernommene Buchung traegt ihre harvest_id (unique). Ein zweiter
  Lauf legt nichts doppelt an. DELETE /api/imports/entries entfernt genau
  die uebernommenen Zeiten; handgetippte bleiben unberuehrt.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-21 11:37:26 +02:00
schakal00783andClaude Opus 5 7606df9280 fix(export): "Summe" im PDF nicht mehr in der schmalen Spalte quetschen
Die Summenzeile des Stundennachweises trug ihre Beschriftung in der Spalte
"Tag". Die ist rund 40 Punkt breit, also brach "Summe" dort in drei Zeilen
um ("Su/mm/e"), waehrend die Zahlen rechts daneben in einer Zeile standen.

drawRow laesst in der Summenzeile eine Beschriftung jetzt in die leeren
Spalten dahinter laufen und setzt sie einzeilig. Gefuellte Spalten werden
nicht einverleibt, die Zahlen bleiben also an ihrem Platz. Das gilt fuer
alle PDF-Tabellen, nicht nur den Stundennachweis; Excel und CSV waren nie
betroffen.

Rueckbau: diesen Commit reverten - die Aenderung steckt allein in
export/render.js.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-21 10:11:27 +02:00
schakal00783andClaude Opus 5 9576d18abc feat(profil): eigenes Profilbild hochladen
Jeder setzt sein Bild unter "Mein Konto", Vorgesetzte und Administratoren
auch fuer die Benutzer, die sie verwalten. Wo bisher die Initialen standen,
steht jetzt das Bild - Seitenleiste, Kopfzeile, Team, Dashboard, Reports,
Projekte, Kunden und die Buchungsliste. Ohne Bild bleiben die Initialen.

Der Browser verkleinert das Bild vor dem Hochladen auf ein Quadrat von
256 px und schneidet mittig zu (lib/imageCompress.ts): aus einem 4-MB-Foto
werden wenige Kilobyte, und der Server muss kein Bild verarbeiten.

Gespeichert wird es als Data-URL in pulse_users.avatar (LONGTEXT). Kein
Upload-Verzeichnis, das gesichert und beim Deployment am Leben gehalten
werden muesste.

Geprueft wird serverseitig in lib/avatar.js: nur PNG, JPEG, GIF, WebP und
AVIF als Data-URL, hoechstens 2 MB. SVG ist bewusst nicht erlaubt, es kann
Skripte tragen. Externe http(s)-Adressen ebenso wenig - sonst laedt jeder
Seitenaufruf ein Bild von einem fremden Server und verraet, wer wann mit
Pulse arbeitet.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-19 23:06:02 +02:00
schakal00783andClaude Opus 5 2d3d843988 feat(design): gruenes "P" als Browsersymbol
Ersetzt das perinco-Punktzeichen im Reiter durch ein gruenes P.

- favicon-p.svg: gezeichnete Form statt gesetzter Schrift, damit sie auf
  jedem Rechner gleich aussieht und in jeder Groesse scharf bleibt. Auf
  dunklen Reiterleisten schaltet sie per prefers-color-scheme in ein
  helleres Gruen.
- favicon-p-32.png und favicon-p-180.png als Rueckfall fuer Browser ohne
  SVG-Symbol, apple-touch-icon-p.png mit weisser Flaeche, weil iOS
  durchsichtige Symbole auf Schwarz legt.

Eigene Dateinamen statt favicon.png: Apache laesst Bilder ein Jahr im
Cache liegen, unter gleichem Namen saehe man das neue Symbol sonst erst
nach einem harten Neuladen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-18 16:33:42 +02:00
schakal00783andClaude Opus 5 d9d46fea6a feat(pixel): 8-Bit-Handwerker laeuft einmal pro Minute durch die Seitenleiste
Im Theme "Pixel Art" laeuft eine kleine Figur ueber die Steinmauer der
Seitenleiste, macht einen Huepfer und verschwindet wieder nach links.
Auftritt alle 60 Sekunden, der erste nach 6 Sekunden.

Die Figur ist eine eigene Zeichnung - gruene Muetze, Schutzbrille,
Latzhose - und steht als Pixelraster 14 x 16 direkt im Code, ohne
Bilddatei. Bewusst kein Sprite aus einem kommerziellen Spiel: solche
Figuren und Grafiken sind geschuetzt und gehoeren nicht in eine
produktiv genutzte Anwendung.

Technik wie bei den Wetter-Ebenen: ein Canvas genau ueber der
Seitenleiste, ohne Klickfang, unter den Dialogen, per ResizeObserver an
die ein- und ausklappbare Breite gebunden. Zwischen zwei Auftritten
laeuft ein Timer statt eines Animationsframes. Bei "Bewegung reduzieren"
und unterhalb der Tabletbreite bleibt alles aus.

Rueckbau: diesen Commit reverten, oder die Datei loeschen und die eine
Zeile in lib/theme.tsx entfernen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-18 12:10:19 +02:00
schakal00783andClaude Opus 5 3b818bd1c6 fix(projekte): Kacheln als durchgehendes Raster statt Kundengruppen
Jede Kundenueberschrift begann eine neue Rasterzeile. Bei einem Projekt je
Kunde stand dadurch eine Kachel allein in der Zeile und rechts blieb der
Platz leer - auf der Produktion mit fuenf Kunden untereinander sichtbar.

Die Kacheln laufen jetzt in einem Raster, sortiert nach Kunde und Projekt.
Der Kundenname steht dafuer neben dem Projektcode auf der Kachel selbst.
Die Detailansicht steht wie in der Liste unter der Uebersicht.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-18 11:49:36 +02:00
schakal00783andClaude Opus 5 be37d89d53 feat(projekte): Listenansicht der Projektuebersicht
Umschalter Karten/Liste neben dem Statusfilter, die Wahl merkt sich der
Browser. Die Liste zeigt alle gefilterten Projekte flach ueber die Kunden
hinweg und sortierbar: Projekt mit Code, Toepfen und Tasks, Kunde, Typ,
gebuchte Stunden und Tage, Budget mit freiem Rest, Verbrauchsbalken,
offener Betrag mit erbrachtem Umsatz und das Team. Beträge nur ab
Vorgesetztem.

Ein Klick auf eine Zeile oeffnet dieselbe Detailansicht wie eine Kachel,
in der Liste unterhalb der Tabelle. Dafuer ist der Detailblock einmal
aufgebaut und wird in beiden Ansichten verwendet.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-18 11:28:42 +02:00
schakal00783andClaude Opus 5 133fca9ef6 fix(dialoge): Dialoge an den body haengen, damit sie nicht in Karten haengen bleiben
Die Einblendanimationen laufen mit animation-fill-mode: both. Danach behaelt
das Element den Wert des letzten Keyframes, bei transform also eine
Einheitsmatrix statt "none". Das genuegt, um einen Bezugsrahmen fuer
position: fixed zu erzeugen: Ein Dialog unterhalb einer solchen Karte hat
sein Overlay nur noch ueber der Karte, deren overflow: hidden schneidet ihn
ab. Sichtbar beim Bearbeiten eines Budgettopfs in der Projektansicht - das
Fenster klebte an der Detailkarte und die Schaltflaechen waren abgeschnitten.

Modal und ConfirmDialog rendern jetzt per createPortal in document.body.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-18 11:21:00 +02:00
schakal00783andClaude Opus 5 ef7171496f feat(ansichten): Listenansicht fuer Buchungen, Report und Kunden
Neue Komponente EntryList zeigt einzelne Buchungen als sortierbare Tabelle
mit Datum, Kunde, Projekt, Task und der Zeit getrennt nach billable und
non-billable, inklusive Summen.

- Buchung: Umschalter Kalender/Liste neben Woche/Monat/Jahr, Auswahl wird
  im Browser gemerkt. Klick auf eine Zeile oeffnet die Buchung.
- Reports: Klick auf einen Mitarbeiter oeffnet dessen Buchungen des
  Reportzeitraums als Liste; ein gesetzter Kundenfilter wird uebernommen.
- Kunden: Umschalter Karten/Liste mit Ort, Projekten, Stunden und Umsatz.
- GET /api/entries akzeptiert dafuer customer_id, Segmented nimmt Icons,
  Modal kennt die Groesse 2xl.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-18 10:14:48 +02:00
schakal00783andClaude Opus 5 8c93df11d9 feat(projekte): Projekttyp "Abruf" fuer Abrechnung nach Aufwand
Abruf braucht keinen Budgettopf: es gibt kein zugesagtes Volumen und kein
Ablaufdatum, gebucht wird, was anfaellt. Der Satz kann deshalb nicht am
Topf haengen und steht am Projekt (pulse_projects.rate, Stunde oder Tag).
Der Umsatz sind die abrechenbaren Stunden mal diesem Satz; offen ist
nichts.

- Migration ergaenzt den ENUM-Wert und die Spalte rate
- Anlegen verlangt einen Satz und nimmt keine Toepfe an; ein Topf
  nachtraeglich anzulegen wird abgewiesen
- Wechsel auf Abruf nur, solange auf den Toepfen nichts gebucht ist -
  sonst wuerden Buchungen ihr Budget verlieren; leere Toepfe fallen weg
- Buchungsdialog, Projektkarte, Projektdetail und Projektreport zeigen
  Satz und Umsatz statt Budgetbalken

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-18 00:13:35 +02:00
schakal00783andClaude Opus 5 2af516ec20 fix(design): Logo ueber Vite einbinden, damit Aenderungen sofort ankommen
Das Logo lag in public/ und hatte immer denselben Namen. Apache gibt
Bildern ein Jahr Cache-Zeit mit - eine neue Fassung war deshalb erst
nach hartem Neuladen zu sehen, obwohl der Server sie schon auslieferte.

Jetzt liegt es in src/assets/ und wird importiert: Vite haengt einen Hash
an den Dateinamen (assets/pulse-logo-<hash>.png), der sich bei jeder
Aenderung mitaendert.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-18 00:01:46 +02:00
schakal00783andClaude Opus 5 2daec61557 chore: Logo-Rohvorlagen konsequent ausschliessen
/Pulse-Logo.png deckte nur die erste Vorlage ab, Pulse-Logo2.png landete
dadurch im Repo. Jetzt greift /Pulse-Logo*.png.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-17 23:58:15 +02:00
schakal00783andClaude Opus 5 7999e0d6c2 style(design): Pulse-Logo durch die zweite Fassung ersetzt
Pulse-Logo2.png war bereits freigestellt, deshalb nur zugeschnitten und
auf 160 px Hoehe skaliert (73 KB). Angezeigt 46 px hoch, 159 px breit.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-17 23:57:58 +02:00
schakal00783andClaude Opus 5 6a4112d8ab fix(design): Trennlinie der Seitenleiste wieder buendig zum Header
Das groessere Logo machte die Logo-Zeile 79 px hoch, der Header blieb bei
71,5 px - die Linie lief schief. Beide haengen jetzt an
--app-bar-height (4,5 rem); das Logo begrenzt nur seine maximale Hoehe.
So bleibt es buendig, auch wenn Logo oder Titel sich aendern.

Dazu die neu zugeschnittene Logovorlage uebernommen: weniger Freiflaeche,
freigestellt und auf 160 px Hoehe skaliert.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-17 23:39:08 +02:00
schakal00783andClaude Opus 5 104aebbbe6 style(design): Pulse-Logo 15 % groesser (40 -> 46 px)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-17 23:34:07 +02:00
schakal00783andClaude Opus 5 7c8823dbca feat(design): Pulse-Logo in der Seitenleiste, Wellen bewegen sich sichtbar
Oben links steht jetzt das Pulse-Logo statt des perinco-Logos; der
Schriftzug "Pulse" daneben ist damit doppelt und entfaellt. Das Logo ist
freigestellt (grauer Grund und Bildrauschen entfernt, auf 140 px Hoehe
zugeschnitten) und wird im Dunkelmodus nicht umgefaerbt - die Wuerfel
sind gruen und sollen gruen bleiben.

Die schaukelnden Huegel im hellen Aura lagen als Hintergrundbild und
wurden ueber background-position bewegt; bei diesen breiten Formen war
das nicht zu sehen. Sie liegen nun in einer eigenen Ebene und wandern
per transform 26 px hin und her, dazu 5 px auf und ab.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-17 23:30:02 +02:00
schakal00783andClaude Opus 5 413104ae73 docs: Themes und Gitea-Repos im README
Uebersicht der sieben Farbwelten mit ihren Animationen, dazu die vier
Stellen, die ein neues Theme braucht - die Pruefung im Backend wurde
beim Nachziehen von Rainy, Snow und Aura dreimal vergessen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-17 22:53:37 +02:00
schakal00783andClaude Opus 5 ef676a95f4 feat: perinco Pulse - Zeiterfassung fuer perinco
Erste Fassung, live unter https://pulse.perinco.de.

Frontend React 19 + TypeScript + Tailwind 4 (Vite), Backend Express mit
mysql2 auf MariaDB, Exporte als PDF (pdfkit), Excel (exceljs) und CSV.
Aufbau, Designsystem und die sieben Themes kommen aus dem perinco CRM.

Fachlich:
- Buchung mit Wochen-, Monats- und Jahresansicht, Zeiten als 1:23 oder 1,5
- Projekte mit n Budgettoepfen: Laufzeit, Volumen, Sperre bei Ablauf oder
  aufgebrauchtem Budget, Buchung nur auf den passenden Topf
- Kunden, Team mit Hierarchie, Reports im Harvest-Stil, Monatsexporte
- Rollen user/lead/management/admin, doppelt geprueft (Menue und Backend)
- SSO gegen Microsoft Entra ID, Uebernahme der AD-Benutzer per Drag & Drop

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-17 22:52:20 +02:00