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
+54
View File
@@ -0,0 +1,54 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 260 900" width="260" height="900" preserveAspectRatio="xMidYMax slice">
<!--
Rainy, Seitenleiste: nasse Huegel mit Pfuetzen, von unten hereinwachsend.
Wie bei den anderen Themes sitzt das Motiv unten - oben liegen die
Menuepunkte unter einem dichten Schleier (index.css). Der Regen selbst ist
nicht gezeichnet, er faellt lebendig ueber den Bildschirm; hier stehen nur
die Ringe, die er in den Pfuetzen hinterlaesst.
-->
<defs>
<linearGradient id="dunst" x1="0" y1="0" x2="0" y2="1">
<stop offset="0" stop-color="#8fa1b6" stop-opacity="0" />
<stop offset="1" stop-color="#8fa1b6" stop-opacity="0.45" />
</linearGradient>
<filter id="weich" x="-20%" y="-20%" width="140%" height="140%">
<feGaussianBlur stdDeviation="12" />
</filter>
</defs>
<!-- Nebel ueber dem Boden -->
<rect y="540" width="260" height="360" fill="url(#dunst)" filter="url(#weich)" />
<!-- Hintere Huegel -->
<path d="M0 740 C 50 700 100 712 140 724 C 180 736 220 706 260 712 L260 900 L0 900 Z" fill="#7d8ea3" opacity="0.45" />
<!-- Vordere Huegel -->
<path d="M0 792 C 60 770 110 786 160 778 C 200 772 236 782 260 776 L260 900 L0 900 Z" fill="#56667b" opacity="0.8" />
<!-- Pfuetzen: der Himmel spiegelt sich hell darin -->
<g fill="#b9c7d6" opacity="0.45">
<ellipse cx="62" cy="838" rx="42" ry="7" />
<ellipse cx="184" cy="858" rx="52" ry="8" />
</g>
<!-- Ringe, die der Regen in den Pfuetzen zieht -->
<g fill="none" stroke="#eef3f8" stroke-width="1.2" opacity="0.4">
<ellipse cx="52" cy="838" rx="12" ry="2.4" />
<ellipse cx="52" cy="838" rx="22" ry="4" opacity="0.5" />
<ellipse cx="80" cy="838" rx="6" ry="1.4" />
<ellipse cx="170" cy="858" rx="16" ry="3" />
<ellipse cx="170" cy="858" rx="28" ry="5" opacity="0.45" />
<ellipse cx="206" cy="857" rx="8" ry="1.8" />
</g>
<!-- Graeser am Pfuetzenrand, dunkel und nass -->
<g stroke="#3e4b5c" stroke-width="2" stroke-linecap="round" fill="none" opacity="0.35">
<path d="M14 830 Q 10 808 20 794" />
<path d="M22 832 Q 28 810 22 796" />
<path d="M118 848 Q 112 824 124 810" />
<path d="M128 850 Q 134 826 128 812" />
<path d="M238 846 Q 232 824 244 810" />
<path d="M248 848 Q 254 826 250 812" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB