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>
This commit is contained in:
schakal00783
2026-09-17 22:52:20 +02:00
co-authored by Claude Opus 5
commit ef676a95f4
104 changed files with 21030 additions and 0 deletions
+66
View File
@@ -0,0 +1,66 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 65 225" width="65" height="225" shape-rendering="crispEdges" preserveAspectRatio="xMidYMax slice">
<!--
Pixel Art, Seitenleiste: Steinmauer, von unten aufgemauert.
Der viewBox ist winzig (65 x 225, eine Einheit = ein Bildpunkt), damit die
Mauer beim Hochskalieren blockig bleibt; shape-rendering="crispEdges" haelt
die Kanten hart. Oben ist die Grafik leer - dort stehen die Menuepunkte.
Nach oben hin wird die Mauer **stufenweise** durchsichtig, Reihe um Reihe.
Ein weicher Verlauf waere hier ein Stilbruch: Pixelgrafik kennt keine
Zwischentoene, sie staffelt.
Jeder Stein ist 12 x 6 Punkte, versetzt gemauert, mit heller Kante oben und
dunkler unten - damit taeuscht Pixelgrafik Tiefe vor.
-->
<defs>
<g id="stein">
<rect width="12" height="6" fill="#8a8f7a" />
<rect width="12" height="1" fill="#a8ad96" />
<rect y="5" width="12" height="1" fill="#5f6553" />
<rect x="11" width="1" height="6" fill="#5f6553" />
</g>
<!-- Eine ganze Reihe; der Versatz kommt beim Aufruf -->
<g id="reihe">
<use href="#stein" x="0" /><use href="#stein" x="13" /><use href="#stein" x="26" />
<use href="#stein" x="39" /><use href="#stein" x="52" />
</g>
</defs>
<!-- Reihen von unten nach oben, jede zweite um einen halben Stein versetzt -->
<use href="#reihe" y="219" opacity="1" />
<use href="#reihe" x="-6" y="212" opacity="1" />
<use href="#reihe" y="205" opacity="1" />
<use href="#reihe" x="-6" y="198" opacity="1" />
<use href="#reihe" y="191" opacity="0.9" />
<use href="#reihe" x="-6" y="184" opacity="0.8" />
<use href="#reihe" y="177" opacity="0.7" />
<use href="#reihe" x="-6" y="170" opacity="0.6" />
<use href="#reihe" y="163" opacity="0.5" />
<use href="#reihe" x="-6" y="156" opacity="0.4" />
<use href="#reihe" y="149" opacity="0.32" />
<use href="#reihe" x="-6" y="142" opacity="0.24" />
<use href="#reihe" y="135" opacity="0.18" />
<use href="#reihe" x="-6" y="128" opacity="0.13" />
<use href="#reihe" y="121" opacity="0.09" />
<use href="#reihe" x="-6" y="114" opacity="0.06" />
<use href="#reihe" y="107" opacity="0.03" />
<!-- Moos in den Fugen: sonst wirkt die Mauer steril -->
<g fill="#6f9c4a">
<rect x="4" y="218" width="3" height="1" />
<rect x="30" y="211" width="4" height="1" />
<rect x="17" y="204" width="2" height="1" />
<rect x="46" y="197" width="3" height="1" />
<rect x="9" y="190" width="4" height="1" opacity="0.9" />
<rect x="55" y="183" width="3" height="1" opacity="0.8" />
<rect x="24" y="176" width="2" height="1" opacity="0.7" />
<rect x="38" y="162" width="3" height="1" opacity="0.5" />
</g>
<!-- Zwei ausgebrochene Steine, damit die Mauer nicht zu regelmaessig ist -->
<g fill="#5f6553">
<rect x="26" y="219" width="12" height="6" />
<rect x="7" y="170" width="12" height="6" opacity="0.6" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB