PrepTrack — Offline-first PWA zur Vorratsverwaltung. React 18 + TypeScript, Vite 6, Tailwind CSS 3, Zustand, Dexie.js (IndexedDB). Sprachen: Deutsch (de), Englisch (en), Portugiesisch (pt), Arabisch (ar), Italienisch (it), Französisch (fr). Lizenz: Apache 2.0.
npm run dev # Dev-Server (localhost:5173)
npm run build # Production Build (tsc + vite)
npm run test # Vitest (15 Tests, src/lib/utils.test.ts)
npm run preview # Build lokal testen
npx tsc --noEmit # Type-Check ohne Build
Routing über Zustand Store (useAppStore.currentPage), NICHT React Router.
Pages: dashboard | products | add | scanner | settings | stats
src/
├── App.tsx # Router: rendert Page basierend auf currentPage
├── main.tsx # Entry, seedDefaults(), SW-Handler
├── sw-handler.ts # Service Worker Update-Benachrichtigung
├── types/index.ts # Alle Interfaces, Types, Konstanten
├── store/useAppStore.ts # Zustand: Navigation, Filter, Editing, Scan
├── lib/
│ ├── db.ts # Dexie DB, CRUD, Export/Import, seedDefaults
│ ├── utils.ts # MHD-Logik, Formatierung, compressImage, lookupBarcode
│ ├── utils.test.ts # Unit Tests
│ └── notifications.ts # Lokale Push-Benachrichtigungen
├── hooks/
│ ├── useDarkMode.ts # Dark/Light Toggle (localStorage)
│ ├── useOnlineStatus.ts # Online/Offline Detection
│ └── usePWAInstall.ts # PWA Install Prompt + iOS Detection
└── components/
├── Dashboard.tsx # StatRings, MHD-Balken, dringende Produkte
├── ProductList.tsx # Liste mit Suche/Filter, Archiv, CRUD
├── ProductForm.tsx # Add/Edit mit Draft-Persist (sessionStorage)
├── BarcodeScanner.tsx # ZXing Scanner, Duplikat-Check, API-Lookup
├── Statistics.tsx # Verbrauchsstatistiken
├── Settings.tsx # Theme, Notifications, Lagerorte, Export/Import
├── Navigation.tsx # Bottom Nav (6 Items)
├── StatRing.tsx # SVG Kreisdiagramm
├── OfflineBanner.tsx # Offline-Indikator (Framer Motion)
├── PWAInstallPrompt.tsx # Install-Hinweis
└── ImageCaptureModal.tsx # Kamera-Modal (aktuell nicht verwendet)
DB-Name: PrepTrackDB, aktuell Version 2.
products: ++id, name, barcode, category, storageLocation, expiryDate, archived, createdAt
storageLocations: ++id, name
consumptionLogs: ++id, productId, consumedAt
notificationSchedules: ++id, productId, notifyAt, sent, [productId+daysBefore]
Product-Felder: id?, name, barcode?, category, storageLocation, quantity, unit, expiryDate, expiryPrecision, photo?, minStock?, notes?, archived, createdAt, updatedAt
10 Kategorien: konserven, wasser, medizin, werkzeug, hygiene, lebensmittel, getranke, elektronik, kleidung, sonstiges 10 Einheiten: Stück, Liter, kg, g, ml, Packung, Dose, Flasche, Karton, Palette 8 Standard-Lagerorte: Keller, Garage, Küche, Dachboden, Vorratsraum, Bunker, Auto, Gartenhaus
useAppStore (Zustand):
currentPage/setPage(page)— Navigationfilters/setFilter(key, value)— Produktliste-FiltereditingProductId/setEditingProductId(id)— Bearbeitung (setzt Page auf 'add')scannedData/navigateToAddWithScan(data)— Scanner → FormularnotificationsEnabled/setNotificationsEnabled(enabled)— localStorage-persistent
Beim App-Start: getInitialPage() prüft sessionStorage auf Form-Draft (Kamera-Reload-Fix).
- Form-Draft-Persist: ProductForm speichert State in sessionStorage bevor Kamera öffnet (Mobile-PWA wird aus RAM entladen). Store startet auf 'add' wenn Draft existiert.
- Kamera-Button: Aktuell per
className="hidden"deaktiviert, Code bleibt erhalten. - Version: Wird aus
package.jsonimportiert (import { version } from '../../package.json'), erscheint in Settings + JSON-Export. - Export: JSON ohne Fotos (Platzhalter
[FOTO]), CSV mit BOM für Umlaute. - Import: Duplikat-Erkennung (Name + MHD + Lagerort).
ImportResultKlasse für Teil-Erfolg. - BarcodeScanner: Lazy-loaded (
React.lazy). Nutzt@zxing/browser, sucht Rückkamera. Duplikat-Popup wenn Barcode schon existiert. Online: Open Food Facts API Lookup. - Notifications: Lokal via
NotificationAPI. Checker läuft alle 6h. 30/14/7/3/1 Tage vor MHD. - Dark Mode: CSS-Klasse auf
<html>, localStorage-persistent, Default: dark.
- Vite base:
./lokal,/Prepper_Log/für GitHub Pages (GITHUB_PAGESenv var) - PWA:
vite-plugin-pwamit autoUpdate, Workbox für API/Font/Image Caching - CI/CD:
.github/workflows/deploy.yml— Push auf main → Build + Test + Deploy auf GitHub Pages - Tailwind: Custom primary/olive/khaki Palette, Fonts: Inter, Bebas Neue, JetBrains Mono
- Alle UI-Texte auf Deutsch mit korrekten Umlauten (ä, ö, ü, ß)
- Tailwind-Klassen, keine separaten CSS-Dateien (außer globals in index.css)
- Lucide React Icons, keine anderen Icon-Libraries
noUnusedLocals: truein tsconfig — unbenutzte Imports/Variablen = Build-Fehler- Semikolon-frei bei Tailwind-Klassen, Standard TypeScript-Formatting