diff --git a/.agents/TODO.md b/.agents/TODO.md index 8331ede..953b718 100644 --- a/.agents/TODO.md +++ b/.agents/TODO.md @@ -28,12 +28,6 @@ Dieses Dokument enthält den aktuellen Entwicklungsstand und detaillierte Aufgab - Touch-Swipe-Karussell direkt auf index.php (unter Landing-Page) - Filteralgorithmus nach Benutzer-Ernährungszielen - Mikro-Animationen & Quick-Save Funktion -- [/] **Phase 6: Firebase Firestore Datenbank-Migration & Seeding** - - Firebase CLI-Login & Projekt-Initialisierung - - .env-Konfiguration mit Firebase App Credentials - - Client-seitiger "Seed Firestore"-Button in admin.php - - Firestore REST API Integration in helpers.php - - Echtzeit-Zwei-Wege-Sync bei Rezeptänderungen in admin.php --- @@ -144,29 +138,6 @@ Ein hochgradig interaktives, touch-freundliches Karussell direkt auf der Startse --- -### 🔥 Phase 6: Firebase Firestore Datenbank-Migration & Seeding -Migration des lokalen JSON-Flachdateispeichers auf eine skalierbare, cloudbasierte Firestore-Struktur. - -- [/] **Firebase-Projekt & CLI-Setup** - - [x] Firebase-Verzeichnis-Umgebung initialisieren und anbinden - - [/] Firebase CLI Login durchführen und aktiven Account verknüpfen - - [ ] Firebase Project & Web App ermitteln oder neu anlegen - - [ ] Lokale `.env`-Konfiguration mit Firebase SDK Parametern befüllen -- [ ] **Client-seitiges Daten-Seeding** - - [ ] "Seed Firestore"-Aktion im Admin-Bereich (`admin.php`) einbauen - - [ ] Batch-Dokumentenupload der `recipes.json`-Einträge in die Firestore Collection `recipes` über JS SDK - - [ ] Firestore Security Rules für den öffentlichen Lesezugriff anpassen -- [ ] **Firestore Server-seitiger Abruf (PHP REST API)** - - [ ] Firestore REST API Parser-Helfer in `helpers.php` implementieren (Transformation von Firestore Key-Maps in Standard-Arrays) - - [ ] `load_recipes()` auf REST API GET `/recipes` umstellen - - [ ] Neue Funktion `load_recipe_by_slug($slug)` für gezielten Einzelabruf implementieren und in `recipe.php` integrieren -- [ ] **Zwei-Wege-Echtzeit-Synchronisierung (Admin)** - - [ ] `save_recipes()` so anpassen, dass lokales JSON als Fallback-Backup erhalten bleibt - - [ ] Client-seitigen Trigger nach erfolgreichem PHP-Speichern in `admin.php` ausführen, um Änderungen sofort in Firestore zu spiegeln - - [ ] Funktionstest: Hinzufügen, Editieren und Löschen von Rezepten über die Admin-Konsole verifizieren - ---- - ## 📈 Status-Legende - `[ ]` Noch nicht begonnen - `[/]` In Bearbeitung diff --git a/.agents/brain.md b/.agents/brain.md index 9d54165..5ae699f 100644 --- a/.agents/brain.md +++ b/.agents/brain.md @@ -32,10 +32,6 @@ This document summarizes the architectural knowledge, conventions, and learnings ## 5. Completed Milestones - **Phase 3 (Nutrition):** Implemented. Recipes now store `calories`, `protein`, `carbs`, and `fat`. Admin panel handles inputs, and the UI displays them beautifully. - **Phase 4 (Interactive Cooking Mode):** Implemented. Recipes now support step-by-step looping background videos and interactive timers (`step_videos`, `step_timers`). The UI utilizes a fullscreen overlay slider with Vanilla JS logic. -- **Phase 6 (Firestore Database Migration):** Migrated recipes database from `data/recipes.json` to Firebase Firestore. - - *Zero-Dependency REST API Read:* Server-side read requests in `helpers.php` use native PHP cURL to query the Firestore REST API `/documents/recipes`. Complex Firestore nested type maps are dynamically parsed into clean standard associative arrays using custom decoders. - - *Dynamic Local Fallback:* In case of rate limits, network failures, or missing `.env` config, all lookup functions automatically fail back to the local `recipes.json` flat-file, guaranteeing 100% database availability and site resilience. - - *Browser-Driven Seeding & Auto-Sync:* Admin seeding and real-time updates are executed client-side in `admin.php` via the authenticated Firebase Client SDK, keeping the local file and Cloud Firestore perfectly in sync without server-side OAuth2 keys. ## 6. Next Steps -According to `TODO.md`, the next major feature block is completing the database seeding (by clicking "Seed Firestore" on `admin.php` in the browser) and verifying all recipe updates reflect in real-time. Afterwards, we can proceed to **Phase 5 (PWA & Offline Support)**. +According to `TODO.md`, the next major feature block is **Phase 5 (PWA & Offline Support)**, which involves service workers, manifest files, and enabling the app to be installable on mobile devices. diff --git a/.firebaserc b/.firebaserc deleted file mode 100644 index 8d0ccc0..0000000 --- a/.firebaserc +++ /dev/null @@ -1,5 +0,0 @@ -{ - "projects": { - "default": "flixcooks" - } -} diff --git a/admin.php b/admin.php index f632277..1902a51 100644 --- a/admin.php +++ b/admin.php @@ -367,18 +367,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'delet .list-item { padding: 12px 0; border-bottom: 1px solid rgba(255,255,255,0.08); display: flex; justify-content: space-between; align-items: center; } .pill { background: rgba(255,255,255,0.06); color: var(--ink); } - - - - -
@@ -388,9 +376,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'delet Recipes Site settings
- - - View site Back @@ -653,66 +638,5 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'delet - - - - - - - - - - - - - diff --git a/helpers.php b/helpers.php index 5379f03..d6b4099 100644 --- a/helpers.php +++ b/helpers.php @@ -7,46 +7,7 @@ if (session_status() === PHP_SESSION_NONE) { require_once __DIR__ . '/config.php'; -function decode_firestore_value($value) { - if (!is_array($value)) return $value; - if (isset($value['stringValue'])) return $value['stringValue']; - if (isset($value['integerValue'])) return (int)$value['integerValue']; - if (isset($value['doubleValue'])) return (float)$value['doubleValue']; - if (isset($value['booleanValue'])) return (bool)$value['booleanValue']; - if (isset($value['nullValue'])) return null; - if (isset($value['arrayValue']['values'])) { - return array_map('decode_firestore_value', $value['arrayValue']['values']); - } - if (isset($value['mapValue']['fields'])) { - return array_map('decode_firestore_value', $value['mapValue']['fields']); - } - return $value; -} - -function decode_firestore_document(array $doc): array { - if (!isset($doc['fields'])) return []; - return array_map('decode_firestore_value', $doc['fields']); -} - -function firestore_get(string $url): ?array { - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, $url); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_TIMEOUT, 10); - curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); - $response = curl_exec($ch); - $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); - curl_close($ch); - - if ($httpCode !== 200 || !$response) { - return null; - } - - return json_decode($response, true); -} - -function load_recipes_local(): array { +function load_recipes(): array { $path = __DIR__ . '/data/recipes.json'; if (!file_exists($path)) { return []; @@ -67,54 +28,6 @@ function load_recipes_local(): array { return $data; } -function load_recipes(): array { - $config = get_firebase_config(); - $projectId = $config['projectId'] ?? ''; - if (empty($projectId)) { - return load_recipes_local(); - } - - $url = "https://firestore.googleapis.com/v1/projects/{$projectId}/databases/(default)/documents/recipes?pageSize=100"; - $res = firestore_get($url); - if (!$res || !isset($res['documents'])) { - return load_recipes_local(); - } - - $recipes = []; - foreach ($res['documents'] as $doc) { - $decoded = decode_firestore_document($doc); - if (!empty($decoded)) { - if (!empty($decoded['hero']) && is_string($decoded['hero'])) { - $decoded['hero'] = normalize_asset_path($decoded['hero']); - } - $recipes[] = $decoded; - } - } - - return $recipes; -} - -function load_recipe_by_slug(string $slug): ?array { - $config = get_firebase_config(); - $projectId = $config['projectId'] ?? ''; - if (empty($projectId)) { - return find_recipe_by_slug(load_recipes_local(), $slug); - } - - $url = "https://firestore.googleapis.com/v1/projects/{$projectId}/databases/(default)/documents/recipes/" . urlencode($slug); - $res = firestore_get($url); - if (!$res || isset($res['error'])) { - return find_recipe_by_slug(load_recipes_local(), $slug); - } - - $decoded = decode_firestore_document($res); - if (!empty($decoded['hero']) && is_string($decoded['hero'])) { - $decoded['hero'] = normalize_asset_path($decoded['hero']); - } - - return $decoded; -} - function save_recipes(array $recipes): bool { $path = __DIR__ . '/data/recipes.json'; $json = json_encode($recipes, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); diff --git a/recipe.php b/recipe.php index 0259b04..2aeaca2 100644 --- a/recipe.php +++ b/recipe.php @@ -48,7 +48,7 @@ $t = $copy[$lang]; $allRecipes = load_recipes(); $slug = isset($_GET['slug']) ? trim($_GET['slug']) : ''; -$recipeRaw = $slug ? load_recipe_by_slug($slug) : null; +$recipeRaw = $slug ? find_recipe_by_slug($allRecipes, $slug) : null; $recipe = $recipeRaw ? localize_recipe($recipeRaw, $lang) : null; $allRecipesLocal = localize_recipes($allRecipes, $lang);