21 Commits

Author SHA1 Message Date
christian
cb4aee7722 feat(vorgang): Bulk-Erfassung mehrerer Positionen pro Vorgang
Backend:
- POST /api/lieferungen/bulk und /api/ruecknahmen/bulk
- Multipart: kunde_id, positionen (JSON-Array), foto (eins fuer alle)
- Transaktion: entweder alle Positionen oder keine
- Validierung: artikel_id + anzahl >= 1, Kunde + alle Artikel im Tenant
- Antwortet mit { count, items, foto_url }

Frontend (VorgangForm):
- Mehrere Positions-Zeilen mit + Hinzufuegen / x Entfernen
- Pro Zeile: Artikel-Dropdown + Anzahl-Input
- Ein einziger Foto-Upload fuer den gesamten Vorgang
- Erfolgs-/Fehler-Meldung als gefaerbtes Banner
- Reset nach erfolgreicher Erfassung

api/client.js: createLieferungBulk / createRuecknahmeBulk (FormData mit JSON-positionen)
2026-06-03 12:57:41 +00:00
christian
a7f74eaf1c feat: Produktpflege mit Bildupload + Beschreibung + Aktiv-Status
Backend (routes/artikel.js):
- 3 neue Spalten via Auto-Migration (bootstrap.js):
  - bild_url VARCHAR(500)
  - beschreibung TEXT
  - aktiv TINYINT(1) DEFAULT 1
- POST/PUT/DELETE Routes jetzt unter requireRole(admin)
- GET unterstuetzt ?all=1 fuer inaktive Produkte (Produktpflege-Ansicht)
- POST /artikel/:id/bild und DELETE /artikel/:id/bild fuer Bildverwaltung
- DELETE faengt FK-Constraint ab (Hinweis: stattdessen inaktiv setzen)

Frontend:
- Neue Komponente Produkte.jsx als Kartenraster
  - Bildupload, Beschreibung, Pfandbetrag, Aktiv-Toggle, Edit/Loeschen
  - Inaktive werden ausgegraut dargestellt
- TopNav: neuer Admin-Menupunkt Produkte (FaTags Icon)
- Dashboard.jsx: Route eingebaut
- client.js: api.getArtikel(true) fuer inaktive, uploadArtikelBild, deleteArtikelBild
2026-06-02 09:44:14 +00:00
christian
5f6d536401 feat: Backup-Skript mit 20 Tage Aufbewahrung + Restore
scripts/backup.sh:
- MySQL-Dump (utf8mb4, single-transaction, routines, triggers) -> gz
- Uploads-Volume per alpine-tar -> tar.gz
- Loescht Backups aelter als 20 Tage (find -mtime)
- Liest MYSQL_PASSWORD via grep aus .env (kein source -
  .env enthaelt MAIL_FROM mit <> die bash sonst als Redirect deutet)
- Konfigurierbar via env PROJECT_DIR/BACKUP_DIR/RETENTION_DAYS

scripts/restore.sh:
- Listet verfuegbare Backups bei Aufruf ohne Argument
- Modi: full (default), db-only, uploads-only
- Sicherheitsabfrage vor Ueberschreiben

Cron auf Server: taeglich 03:00 -> /var/log/pfandsystem-demo-backup.log
Backup-Ziel: /root/backups/pfandsystem-demo/
2026-05-27 06:48:59 +00:00
christian
3228fe4ab3 feat(demo): Mail-Benachrichtigungen bei Demo-Ablauf
Neue Mail-Templates (lib/mail.js):
- sendDemoExpiryReminderMail: 7 Tage vor Ablauf
- sendDemoLastDayMail: 1 Tag vor Ablauf (Letzter-Tag-Reminder)
- sendDemoExpiredMail: am Ablauftag (setzt Tenant-Status abgelaufen)

Cron-Job (lib/expiryJob.js):
- node-cron taeglich 08:00 UTC (anpassbar via CRON_EXPIRY_AT)
- 3 Windows pruefen, idempotent ueber notified_*_at Timestamp-Spalten
- BETWEEN-Fenster [n-1, n] Tage fuer Robustheit

DB-Migration (bootstrap.js):
- Beim Backend-Start automatisches ALTER TABLE fuer
  notified_7d_at, notified_1d_at, notified_expired_at (idempotent)

Manueller Test: docker exec pfandsystem-demo-backend node -e "
  import(\"./lib/expiryJob.js\").then(m => m.runExpiryCheck())"
2026-05-26 15:59:25 +00:00
christian
d9c10211d8 style(topnav): Demo-Badge aus Header entfernt (bleibt im Account-Menue) 2026-05-26 15:32:21 +00:00
christian
3df819247a style(login): Demo-Anfrage-Hinweis aus Footer entfernt 2026-05-26 15:06:02 +00:00
christian
148a638f5e fix(api-client): aussagekraeftige Fehlermeldungen
- JSON-Parse-Fehler zeigt jetzt HTTP-Status + Body-Snippet
- Validation-Errors (express-validator errors-Array) werden lesbar
  als Eingabe ungueltig: msg1, msg2 ausgegeben statt HTTP 400
2026-05-26 15:01:47 +00:00
christian
0265b43010 fix: weitere fehlende Umlaute (Hinzufuegen, Eintraege, Passwoerter, erhaelt, enthaelt etc) 2026-05-26 14:58:08 +00:00
christian
f8a2d5cb0a fix(dashboard): Ruecknahmen-View-Match auf ASCII-Slug
activeView-String in TopNav ist "ruecknahmen" (matcht API-Route), aber
Dashboard.jsx hat durch das Umlaut-Replacement "rücknahmen" bekommen ->
Komponente wurde nie gerendert.
2026-05-26 14:54:56 +00:00
christian
a4794dc60c fix(topnav): kein horizontaler Scroller mehr im Desktop-Header
- nav: overflow-x-auto raus, flex-1 raus, kompaktes gap-0.5
- NavBtn: nur Icon (groesser: 16px) auf md/lg, Label erst ab xl (>=1280px)
- title-Attribut fuer Tooltip beim Icon-Only-Mode
- Trennlinie zwischen Haupt- und Verwaltungs-Items kompakter
2026-05-26 14:44:58 +00:00
christian
261357a44e fix(seed+data): Demo-Daten konsequent mit Umlauten
- seed.js: "Cafe Sonnenschein" -> "Café Sonnenschein"
- Bestehende DB-Daten aller 3 Tenants per REPLACE() korrigiert:
  - kunden.name:   Baeckerei -> Bäckerei, Cafe -> Café
  - kunden.adresse: Seestrasse/Bahnhofstrasse/Hauptstrasse/Industriestrasse -> ß-Variante
  - anlieferungshinweis: Hintertuer/Schluesselkasten/ueber/fuer -> Unicode
  - geraete.typ: Kuehlschrank -> Kühlschrank
  - artikel.bezeichnung: Brotkorb gross -> Brotkorb groß
2026-05-26 14:42:29 +00:00
christian
6d00e7e703 feat(geraete): Barcode/QR-Scanner per Smartphone-Kamera
- Neue Komponente BarcodeScanner (Modal mit Video-Stream + Such-Rahmen)
- @zxing/browser als Dependency (dynamisch importiert -> eigener Chunk,
  nicht im Initial-Bundle)
- Im Geraete-Formular Scan-Button neben dem Seriennummer-Input
- Auto-Erkennung: erster Treffer fuellt Feld + schliesst Modal
- Bevorzugt Rueckkamera (facingMode environment), Kamera-Switch-Button
  wenn mehrere Cams verfuegbar
- Fehlerbehandlung: NotAllowedError (Permission denied) mit Hinweis-Text
- Mobile-friendly: aspect-square Video, Touch-Targets

Unterstuetzte Formate: QR, Code128, Code39, EAN-8/13, UPC-A/E, Data Matrix u.a.
2026-05-26 14:29:06 +00:00
christian
0f9fdf8fab fix(pwa): clientsClaim + skipWaiting fuer sofortiges Update bei neuen Deploys
Der bisherige autoUpdate-Mode hat den neuen SW erst beim naechsten
Navigations-Event aktiviert -> alte Bundles (mit ASCII-Umlauten) blieben
gecached. Mit clientsClaim+skipWaiting wird der neue SW sofort uebernommen
und cleanupOutdatedCaches entfernt veraltete Caches.
2026-05-26 14:15:50 +00:00
christian
1f7c0b4c40 fix: letzter Umlaut in Mail-Template (aendere -> ändere) 2026-05-26 14:02:25 +00:00
christian
0535fbe72a fix: Umlaute, Foto-404, Account-Menu responsive
Bugs:
- nginx: /uploads/ wurde von der .jpg/.png-Regex-Location geschluckt
  -> ^~ Prefix gibt /uploads/ und /api/ Vorrang vor Regex
  -> Fotos werden jetzt korrekt vom Backend ausgeliefert
- TopNav menu-item Klasse war via <style>@apply definiert -
  Tailwind verarbeitet @apply nur in .css-Dateien, nicht in JSX-style-Tags
  -> alle Klassen direkt am Element + neuer responsive Aufbau
- TopNav mobile-friendlier: Brand-Truncate, Demo-Badge erst ab sm,
  Dropdown max-w fuer Viewport, Touch-Outside-Close, groessere Touch-Targets

Umlaute:
- Alle ASCII-Substitutionen (fuer, ueber, aendern, loeschen, Stueck, Geraete,
  Ruecknahme, Hintertuer, Schluessel, Kuehlschrank, Pruefe, gueltig, etc.)
  konsequent auf Unicode (für, über, ä, ö, ü, ß) umgestellt.
- 26 Dateien betroffen, SQL-Identifier + Import-Pfade + API-Routen ASCII gelassen.
- seed.js: Baeckerei -> Baeckerei.. ah Baeckerei -> Bäckerei
2026-05-26 14:01:30 +00:00
christian
9726d70c41 chore: rebrand zu Dockly-Pfandsystem
Anzeige-Name ueberall umgestellt:
- index.html title, Login-H1, TopNav, SuperAdmin-Header
- Mail-Subject, HTML-Body, Default-MAIL_FROM
- PWA Manifest name (short_name: Dockly-Pfand wegen Laengen-Limit)
2026-05-26 13:43:46 +00:00
christian
712afdd253 style: pfandlogo.png statt .jpg (transparente Version)
- public/pfandlogo.png ersetzt pfandlogo.jpg
- Alle Referenzen umgestellt (Login, TopNav, SuperAdmin, index.html, manifest.json)
- favicon type=image/png
2026-05-26 13:42:21 +00:00
christian
25ede6f2a3 style: Keyfarbe #051e23 + Pfandlogo einbauen
- Tailwind brand-Palette neu verankert: #051e23 = brand-900 (Keyfarbe),
  brand-50..950 als dunkles Teal-Spektrum (warm)
- btn-primary: bg-brand-900 hover:bg-brand-800 (statt indigo)
- input/badge: focus/text auf brand-700/900
- public/pfandlogo.jpg eingebaut als:
  - Favicon + Apple-Touch-Icon
  - Login (zentral, statt P-Box)
  - TopNav-Header (statt P-Box)
  - SuperAdmin-Header (statt P-Box)
- index.html theme-color #051e23
- manifest.json theme_color #051e23 + Logo als zusaetzliches Icon
2026-05-26 13:37:39 +00:00
christian
51aa85df6f feat(frontend): Tailwind UI + Whitelabel + Multi-Tenant Components
Tech:
- Tailwind 3.4 + PostCSS + Autoprefixer
- Inter via rsms.me, Slate + Indigo (brand-* Farbpalette)
- Komponenten-Klassen .btn-{primary|secondary|danger|ghost}, .input, .label, .card, .badge-*

Visuelles Refresh:
- Logo entfernt - Wortmarke Pfandsystem mit Mono-Box
- Login: zentriertes Card-Layout mit Tenant-Slug-Toggle
- TopNav: cleanes Header mit Tabs + User-Menue, Demo-Badge
- Konsistente Card/Table/Badge-Patterns ueber alle Views
- Mobile-First (Tabellen scrollen, Filter wrap)

Komponenten neu/umgebaut:
- Auth/Login.jsx        - Tenant-Slug aus URL-Param ?tenant=...
- Auth/PasswordChange.jsx
- Dashboard/Dashboard.jsx
- Dashboard/TopNav.jsx  - inkl. Mobile-Hamburger
- Dashboard/Account.jsx (neu)
- Dashboard/AdminDashboard.jsx - Soll-Ist mit CSV-Export
- Dashboard/VorgangsListe.jsx
- Dashboard/BildModal.jsx
- Eingabe/VorgangForm.jsx - Kunden-Info-Popup mit Hinweis+Bildern+Maps
- KundenVerwaltung.jsx - inkl. Anlieferungshinweis, Lieferzeit, Bilder-Modal
- MitarbeiterVerwaltung.jsx - Aktiv-Toggle, Fahrer-Rolle
- Geraete.jsx (neu) - CRUD mit Status-Badges, Filter
- TourPlanung.jsx (neu) - Sortiert nach Lieferzeit, Navi-Button, Done-Persistenz
- SuperAdmin.jsx (neu) - Tenant-Liste + Demo-Anlage-Modal + Verlaengern

API Client:
- relative URL /api (Traefik-Proxy)
- Tenant-Slug im Login
- Endpoints fuer tenants, geraete, kunden-bilder

Sonstiges:
- nginx.conf: SSL raus (Traefik macht TLS), Host-Lock raus, Gzip, immutable Cache
- docker-compose: Frontend baut via Dockerfile (multi-stage)
- Manifest/Theme: #4338ca (Indigo)
- Aufgeraeumt: alte Supabase-Reste, Logout-Komponente (jetzt im Menu),
               nicht mehr noetige Deploy-Skripte
2026-05-26 13:18:13 +00:00
christian
43c75e5dd3 feat(backend): multi-tenant refactor + Geraete + Kundenbilder
Middleware:
- requireTenant prueft tenant_id, Status und Demo-Ablauf (autom. Status-Update)
- requireSuperAdmin fuer cross-tenant Endpoints
- JWT enthaelt jetzt tenant_id

Auth:
- Login: SuperAdmin (tenant_id=NULL) ODER tenant-scoped User
- Demo-Ablaufs-Check beim Login

Neue Routes:
- /api/tenants  (SuperAdmin: Demo-Accounts anlegen mit 30-Tage-Default,
                 Mail-Invite via Resend, Sample-Daten-Seed automatisch,
                 verlaengern, deaktivieren)
- /api/geraete  (Seriennummern-Verwaltung mit Kunde-Zuordnung)
- /api/kunden-bilder/:kunde_id (Foto-Upload, max 5/Kunde)

Bestehende Routes:
- alle tenant_id-scoped (SELECT/UPDATE/DELETE)
- kunden: anlieferungshinweis + lieferzeit_bis Felder
- Cross-tenant-Refs (kunde_id/artikel_id) werden gegen Tenant validiert

Lib:
- lib/mail.js (Resend, mit Dev-Fallback)
- lib/seed.js (5 Kunden, 3 Artikel, 6 Lieferungen, 2 Geraete)
- scripts/bootstrap.js (legt SuperAdmin aus env-Vars beim Start an)

server.js: neue Routes + bootstrap-Call
2026-05-26 12:47:17 +00:00
christian
d86c898455 chore: initial scaffold (Whitelabel-Fork von Pfandsystem)
- Source-Copy von /root/entwicklung/pfandsystem
- docker-compose.yml umgeschrieben: pfandsystem-demo-* Container, Ports 3308/8083/3003 (alle localhost)
- Traefik-Label fuer pfandsystem.dockly.de
- Erweitertes Multi-Tenant-Schema (tenants, tenant_id auf allen Tabellen)
- Neue Tabellen: kunden_bilder, geraete
- Neue Spalten: kunden.anlieferungshinweis, kunden.lieferzeit_bis
- Rollen: superadmin, admin, user, fahrer
- .env.example + README

Code-Refactor (Tailwind, Multi-Tenant-Middleware, Erweiterungen) folgt.
2026-05-26 12:39:17 +00:00