Zeitraumwahl im Kundenreport (Standard Gesamt). Stunden, Team, Tasks,
Projektstunden und Exporte beziehen sich auf den Zeitraum; Diagramm je
Tag/Woche/Monat nach Laenge. Zeitraumwahl als PeriodPicker ausgelagert,
auch im Zeitreport genutzt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Der schwebende Plus-Button sass rechts unten und verdeckte den
Kopier-Knopf der Buchungskarten. Jetzt mittig, dazu Abstand am
Seitenende, damit die letzte Karte ueber den Button scrollen kann.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Das Kopier-Icon (Liste, Wochenansicht) und "Kopieren" im Bearbeiten-Dialog
oeffnen jetzt einen kleinen Monatskalender. Dort werden beliebige Tage
an- und abgewaehlt, auch ueber Monate hinweg, und in einem Schritt gebucht.
- Tage ausserhalb der Projektlaufzeit bzw. ohne Budgettopf sind gesperrt.
- Urlaub/Feiertage sind markiert, bereits gebuchte Stunden je Tag sichtbar.
- "Werktage" waehlt Mo-Fr des Monats ohne Urlaub und Feiertag.
- Buchungen laufen nacheinander; fehlgeschlagene Tage bleiben ausgewaehlt.
- Der Vorlage-Modus im Buchungsdialog entfaellt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Duplizieren-Button im Bearbeiten-Dialog und Kopier-Icon je Buchung in
Liste und Wochenansicht. Die Kopie oeffnet den Dialog als neue Buchung
fuer heute mit Projekt, Task, Notiz und Zeit der Vorlage. Inaktive
Projekte oder Tasks fallen auf die Standardauswahl zurueck.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- HRworks /absences erlaubt hoechstens ein Jahr je Aufruf; der Kalender
reicht weiter und fragt jetzt in Abschnitten von 365 Tagen.
- HRworks liefert zu Feiertagen keinen Namen. Der Name kommt jetzt aus
dem Datum (feste Feiertage, Ostern-abhaengige, Buss- und Bettag).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Neuer Menuepunkt "Kalender" fuer alle Rollen: genehmigte Urlaube aller
Mitarbeiter, auch in der Zukunft, dazu Feiertage. Monatsansicht (Standard)
und Zeitleiste, filterbar nach Team und Name.
- Eigene Tabelle pulse_calendar_days, gefuellt von lib/calendarSync.js im
HRworks-Lauf (Fenster bis 400 Tage voraus). Buchungen entstehen weiter
erst am jeweiligen Tag.
- Nur validatedOk und nur freigegebene Arten (calendar_types, Haken in den
HRworks-Einstellungen). Krankmeldungen werden dafuer nicht abgefragt.
- GET /api/calendar fuer alle Angemeldeten, ohne Mails und Profilbilder.
- Timer laeuft zusaetzlich um 12:00 Uhr.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Den Fix aus dem Theme Mobile unter der sm-Grenze (40rem) fuer alle Themes:
Segmented bekommt min-width: max-content, Knoepfe mit Symbol zeigen nur das
Symbol. Ab sm stehen die Umschalter mit flex-none, Desktop und Tablet
bleiben unveraendert.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Segmented hat Spalten mit minmax(0, 1fr) und damit keine Mindestbreite;
neben dem Mitarbeiter-Auswahlfeld liefen die Beschriftungen auf dem Handy
uebereinander. Im Theme Mobile jetzt min-width: max-content, Knoepfe mit
Symbol nur als Symbol, Innenpolster fest 0.25rem passend zum Marker.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Perinco-Farben, aber rund 16 % dichter: --spacing 0.21rem statt 0.25rem,
kleinere Ueberschriften (lg bis 5xl), Kopfleiste 3.5rem, flachere Schatten.
text-sm/base bleiben, damit iOS beim Tippen in Felder nicht zoomt.
Dunkel mit echtem Schwarz fuer OLED.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bisher musste der Administrator den Modellnamen abtippen - ein Tippfehler
fiel erst beim Test oder bei der ersten Frage auf, und welche Modelle der
Schluessel ueberhaupt darf, war in Pulse nicht zu sehen.
Einstellungen -> KI-Assistent zeigt jetzt ein Auswahlfeld. Die Liste kommt
ueber POST /settings/ai/models live von OpenAI (/v1/models), neueste
zuerst; Embedding-, Bild-, Audio-, Realtime-, Such- und
Moderationsmodelle fliegen raus. "Liste laden" nimmt einen gerade
eingetippten, noch nicht gespeicherten Schluessel mit. Das eingestellte
Modell bleibt waehlbar, auch wenn OpenAI es nicht mehr listet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bisher ging ein Stundennachweis nur von einer Person aus. Fuer die
Rechnung an den Kunden ist aber der Auftrag die Klammer: wer alles auf
Auxil gebucht hat, steht nirgends auf einem Blatt.
Auf der Kundenseite oeffnet ein Knopf je Karte und Zeile den
Kundennachweis: Monat, Format und wahlweise einzelne Projekte des
Kunden; ohne Auswahl sind alle enthalten. Das Dokument zeigt je Zeile
den Berater und fasst am Ende nach Berater (Tage, Dauer, Stunden,
optional Betrag) und nach Projekt und Task zusammen.
Dafuer nimmt /exports/timesheet ein ?project_ids fuer mehrere Projekte,
und loadEntries filtert entsprechend. Die Berater-Zusammenfassung
erscheint nur, wenn mehr als eine Person beteiligt war.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A8ZiAB96iSmQhDrK4Jpi2R
Wer in der Buchung einen Kollegen ausgewaehlt hatte und dann exportierte,
bekam im Dialog die eigenen Kunden und Projekte zur Auswahl - fuer einen
Nachweis, der jemand anderem gehoert.
Der Dialog laedt die Projekte jetzt zu der Person, fuer die der Nachweis
entsteht, und laedt neu, wenn man die Person darin wechselt; die alte
Kunden- und Projektauswahl wird dabei verworfen, weil sie nicht mehr
passt. Bei "Alle Mitarbeiter" stehen alle sichtbaren Projekte zur Wahl.
Dafuer nimmt GET /projects ein ?user_id an. Wer fremde Projekte sehen
darf, entscheidet dieselbe Sicht wie bei den Zeiten: Mitarbeiter bekommen
403, Vorgesetzte nur ihr Team, Management alle. Die Sammelauswahl heisst
fuer Management "Alle Mitarbeiter" statt "Alle meine Mitarbeiter" - sie
sieht ohnehin jeden.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Die Umstellung auf eine Woche stand bisher nur im Code und in der
Commit-Nachricht. Im README fehlte ueberhaupt eine Aussage dazu, wann
jemand ausgeloggt wird - eine Frage, die im Betrieb regelmaessig kommt.
Dazu die Werte, die nur in der Produktions-.env stehen und ein Deployment
ueberdauern, samt Hinweis, dass eine Aenderung dort einen Neustart des
Dienstes braucht.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Zwoelf Stunden bedeuteten in der Praxis, dass sich jeder mindestens
einmal am Tag neu anmeldet - bei SSO ein zusaetzlicher Umweg ohne
Gewinn. Die Frist laeuft weiterhin ab der Anmeldung und verlaengert sich
nicht bei Aktivitaet.
Auf dem Server steht der Wert in der .env; hier ziehen Vorgabe und
Beispieldatei nach, damit eine neue Installation nicht wieder bei zwoelf
Stunden anfaengt.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Die Auswahl im Exportdialog kam aus allen zugewiesenen Projekten - also
auch aus archivierten und von stillgelegten Kunden. In der Liste stand
damit etwa "Top IT Services", wo seit 2025 nichts mehr laeuft.
Waehlbar sind jetzt nur laufende Projekte aktiver Kunden. Dafuer liefert
loadProjects den Status des Kunden mit; ohne ihn liesse sich "nur aktive
Kunden" in der Oberflaeche nicht entscheiden.
Die Zeiten aus archivierten Projekten bleiben erreichbar: ohne Filter
exportiert der Nachweis alles, was die Person im Monat gebucht hat, und
der Projektreport hat seinen eigenen Export.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Mit den 25 stillgelegten Kunden aus der Harvest-Historie war "Aktiv"
oder "Alle" zu grob: wer nachsehen will, wen wir stillgelegt haben,
musste ihn zwischen allen 63 suchen.
Kundenliste und Kundenreport haben jetzt drei Stufen - Aktiv, Inaktiv,
Alle. Im Kundenreport bleibt der gewaehlte Kunde in der Auswahl stehen,
auch wenn der Filter ihn sonst ausblenden wuerde; stillgelegte sind mit
"(inaktiv)" gekennzeichnet. Im Zeitreport stehen sie unter "Inaktiv" in
einer eigenen Gruppe, damit sie die Auswahl nicht zuwachsen lassen - die
Historie bleibt ja auswertbar.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Management und Administration koennen oben rechts eine Frage in Worten
stellen - "wie viele Stunden im August", "welches Projekt hat noch
Budget", "wie ist die Auslastung".
Aufbau: das Modell bekommt eine Uebersicht (Kunden, laufende Projekte,
Eckdaten) und vier Werkzeuge - auswertung, projekte, buchungen,
personen. Damit fragt es gezielt nach, statt dass ihm der ganze Bestand
vorgelegt wird; 19.000 Buchungen passen in keinen Kontext. Die Werkzeuge
rechnen mit loadEntries und loadProjects, also mit denselben Funktionen
wie die Reports - der Assistent kann so keine zweite Wahrheit erzeugen.
Was einfliesst, begrenzt visibleUserIds wie ueberall sonst.
Benutzt wird die Responses-API von OpenAI: die neueren Modelle nehmen
Werkzeuge in /v1/chat/completions nur ohne Nachdenken an. Mit
"store: false" bleibt nichts bei OpenAI liegen, der Verlauf geht deshalb
je Runde komplett mit. Der Schluessel liegt verschluesselt in den
Einstellungen (AES-256-GCM wie das SSO-Secret) und wird nie ausgeliefert;
das Modell ist einstellbar, Vorgabe gpt-5.6-luna.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Die Reporttabelle lief mit table-auto: ein langer Projektname wie
"HAYS - Globales Template und Implementierung: ... _Fresenius -
01.10.2025-31.07.2026" zog die Namensspalte auf und schob Stunden und
Betrag aus dem sichtbaren Bereich. Das truncate am Namen half nicht,
weil die Spalte keine Breite hatte, an der es haette greifen koennen.
Die Tabelle steht jetzt auf table-fixed mit festen Breiten fuer die
Zahlenspalten; der Name bekommt den Rest und bricht um (break-words).
Die Zahlen selbst bleiben einzeilig.
Dazu die Kennzahlenkarte: ihre vier Spalten mit den grossen Betraegen
passten ab lg nicht mehr in die Breite, der rechte Rand wurde von
overflow-x-hidden abgeschnitten. Sie bricht jetzt auf zwei Spalten um
und zeigt vier erst ab xl.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Im Zeitreport stehen interne Projekte und Abwesenheiten zwischen den
Kundenzahlen: 2025 sind das 19.451 von 45.762 Stunden, und die Auslastung
zeigt 146 Prozent, weil Urlaub und Krankheit mitzaehlen.
Ein Haken neben der Billable-Aufteilung blendet Projekte vom Typ
"nicht abrechenbar" aus. Er wirkt auf den ganzen Report - Summen,
Auslastung, alle vier Gruppierungen - und auf die Exporte, die denselben
Report bauen; im PDF-Kopf steht dann "Nicht abrechenbare Projekte:
ausgeblendet". Gefiltert wird in loadEntries, damit Kunden-, Projekt- und
Taskliste dieselbe Grundmenge sehen.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Beim Nachziehen der Harvest-Historie sind zwei Regeln im Weg gestanden,
die fuer eine Uebernahme nicht passen:
- Beim Anlegen verlangte die API auch fuer nicht abrechenbare Projekte
einen Satz, obwohl validateProject ihn direkt danach verwirft. Nur
Abruf rechnet ueber den Satz am Projekt ab, also gilt die Pflicht auch
nur dort.
- Der Importweg liess hoechstens 24 Stunden je Tag zu. In Harvest stehen
Sammelbuchungen ("Nachbuchung DORA September und Oktober", 80 Stunden
auf einem Tag). Sie abzulehnen verliert die Stunden und verfaelscht das
Restbudget, sie zu verteilen erfindet Leistungsdaten - deshalb kommen
sie so herein, wie sie gebucht wurden. Eine Buchung darf jetzt bis zu
31 Tage umfassen, damit Zahlendreher weiter auffallen. Im
Buchungsdialog bleibt die Tagesgrenze bestehen.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Die Uebernahme der Harvest-Historie ist an drei Stellen haengen geblieben:
- resolvePot hat abgelaufene Toepfe grundsaetzlich gesperrt. Fuer eine
historische Buchung ist das falsch - dort zaehlt, ob der Topf am
Leistungstag lief. Der Importweg setzt jetzt `historic`, das Volumen
wird weiter geprueft, damit Ueberbuchungen auffallen.
- In der SSO-Verwaltung fehlte die Ablagezone fuer die Rolle "Externe";
Konten liessen sich dort nicht zuordnen.
- In buildProjectDetail und der Kundenauswertung stand ein Rest der
Auslastungsaenderung (bisHeute/capTo) ohne Bezug im Scope. Beide
Reportseiten liefen dadurch auf 500.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Auslastung
Verglichen werden jetzt die bis heute gebuchten Stunden mit der Kapazitaet
bis heute. Die Kapazitaet endete schon immer am heutigen Tag; wurde im
Voraus gebucht - eine ganze Woche Urlaub am Montag - standen 40 Stunden
gegen 8 Stunden Kapazitaet und die Wochenansicht zeigte 500 Prozent. Die
Spalte "Stunden" zeigt weiter den ganzen Zeitraum.
Tasks
Das Feld zum Hinzufuegen lief aus der Karte heraus: die Zeile darf jetzt
umbrechen, und die Breitenangaben tragen "!", weil inputClass w-full
mitbringt und sonst gewinnt. Der Satz eines Tasks steht als schmales Feld
direkt in der Zeile und wird beim Verlassen gespeichert - vorher lag er
hinter einem Abfragedialog und war praktisch nicht zu finden.
Export der Buchungen
Kunde und Projekt kommen nur noch aus den eigenen Zuweisungen, auch fuer
Vorgesetzte. Eine Liste aller Projekte des Hauses war dort nur Suchaufwand.
Budgettopf
Neues Feld Bestellnummer (optional). Es erscheint im Stundennachweis als
eigene Kopfzeile, wenn die gebuchten Toepfe eine tragen.
Rolle "Externe"
Gleiche Rechte wie "Mitarbeiter" und derselbe Rang, die Rolle trennt nur,
wer zum Haus gehoert. Sie setzt is_external automatisch, damit Liste und
Kennzeichnung nicht widersprechen. Vorgesetzte duerfen sie vergeben.
Stellen, die bisher role === 'user' pruefen, fragen nun hasRole(lead) ab -
sonst haetten Externe alle Projekte gesehen.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Urlaub, Krankheit, Elternzeit und Feiertage stehen in HRworks. Pulse holt
sie einmal taeglich und bucht sie auf das nicht abrechenbare Projekt
"Abwesenheiten". Nach HRworks geht nichts zurueck, es wird nur gelesen.
Die Stunden rechnet HRworks selbst: available-working-hours liefert je
Person und Tag das Soll und die verfuegbaren Stunden, die Differenz sind
die Abwesenheitsstunden. Damit stimmen Teilzeit und halbe Tage ohne
eigenen Kalender. relatedEvents nennt den Auslöser; Vorrang hat der
Feiertag, dann Krankheit, dann Abwesenheit.
Abgesprochene Regeln: nur genehmigte Abwesenheiten (validatedOk), bei
Krankmeldungen auch offene Bescheinigungen; nur bis heute buchen, der
Rest waechst taeglich nach; halbe Tage feste 4 Stunden; jeder aktive
Mitarbeiter wird dem Projekt zugewiesen, auch neue.
Jede Buchung traegt hrworks_ref = "<Nummer>@<Tag>" (unique). Ein zweiter
Lauf aendert nichts, Stornos und Verschiebungen verschwinden im naechsten
Lauf, DELETE /api/hrworks/entries entfernt genau diese Buchungen.
Nicht abrechenbare Projekte brauchen keinen Budgettopf mehr. Ein Topf
traegt Volumen, Laufzeit und Satz - das hat nur bei Billable und
Festpreis Sinn. Am Abwesenheitsprojekt war er nur ein Ablaufdatum, das
irgendwann Buchungen blockiert haette.
Taeglicher Lauf ueber systemd (perinco-pulse-hrworks.timer, 5:30 Uhr
Berliner Zeit, Persistent). Bedienung unter Einstellungen ->
Abwesenheiten (HRworks) mit Verbindungstest und Probelauf; der geheime
Schluessel liegt AES-verschluesselt in der Datenbank.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Der Dialog war 1152 Punkte breit, die Tabelle brauchte mehr - "Billable
Zeit" und "Non-billable Zeit" lagen ausserhalb und waren nur durch
seitliches Scrollen zu erreichen, was niemand sieht.
- Neue Dialogbreite 3xl: bis 1600 Punkte, hoechstens 95 % des Fensters.
- Die Tabelle liegt jetzt auf festen Spaltenbreiten (table-fixed): Datum,
Mitarbeiter und die beiden Zeitspalten bekommen ihre Breite, Kunde und
Projekt einen Anteil, den Rest nimmt der Task.
- Lange Kunden-, Projekt- und Tasknamen samt Notiz brechen um statt
abgeschnitten zu werden; die Zahlen brechen nie.
Geprueft bei 1920, 1440 und 1280 Punkten: Tabelle passt ohne Querscrollen,
die letzte Spalte liegt im Dialog. Auf dem Handy scrollt die Tabelle wie
vorher innerhalb des Dialogs, die Seite selbst nicht.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
/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>
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>
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>
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>
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>
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>