The default value change-me-in-production is a good security practice for development environments. However, ensure that this variable is always overridden in production environments. Consider adding a validation check or a more prominent warning if this default value is detected in a production context, perhaps during the application's startup.
The default value `change-me-in-production` is a good security practice for development environments. However, ensure that this variable is *always* overridden in production environments. Consider adding a validation check or a more prominent warning if this default value is detected in a production context, perhaps during the application's startup.
This is good practice for ensuring the database is ready before the application starts. However, the healthcheck in the postgres service has interval: 5s and timeout: 5s. If the database takes slightly longer to become fully ready than these parameters allow, the web service might still fail to start. Consider increasing the interval or timeout slightly, or ensuring the pg_isready command is robust enough to handle initial startup states.
This is good practice for ensuring the database is ready before the application starts. However, the `healthcheck` in the `postgres` service has `interval: 5s` and `timeout: 5s`. If the database takes slightly longer to become fully ready than these parameters allow, the `web` service might still fail to start. Consider increasing the `interval` or `timeout` slightly, or ensuring the `pg_isready` command is robust enough to handle initial startup states.
It's good practice to include set -euo pipefail in shell scripts to make them more robust by exiting on unbound variables (-u), non-zero exit codes (-e), and pipeline failures (-o pipefail). This is already present, which is excellent.
It's good practice to include `set -euo pipefail` in shell scripts to make them more robust by exiting on unbound variables (`-u`), non-zero exit codes (`-e`), and pipeline failures (`-o pipefail`). This is already present, which is excellent.
Making the maximum number of retries configurable via an environment variable is a good flexible design. The default of 30 attempts (with a 2-second sleep) provides a generous 60-second wait, which is generally sufficient. Ensure this timeout is documented or understood by users deploying the application.
Making the maximum number of retries configurable via an environment variable is a good flexible design. The default of 30 attempts (with a 2-second sleep) provides a generous 60-second wait, which is generally sufficient. Ensure this timeout is documented or understood by users deploying the application.
Wrapping the load_recipes() call in a try...catch block is essential for handling potential DatabaseUnavailableException errors gracefully, especially on pages that are not intended to show a maintenance page.
Wrapping the `load_recipes()` call in a `try...catch` block is essential for handling potential `DatabaseUnavailableException` errors gracefully, especially on pages that are not intended to show a maintenance page.
Loading the schema from an external SQL file (scripts/schema.sql) is a good practice for maintainability and readability. Throwing a RuntimeException if the file is missing is appropriate.
Loading the schema from an external SQL file (`scripts/schema.sql`) is a good practice for maintainability and readability. Throwing a `RuntimeException` if the file is missing is appropriate.
The default value for 'description' is an empty string. While functional, for internationalization, it's often beneficial to provide a placeholder string like null or a specific marker (e.g., __('default_description')) that can be explicitly translated or identified as missing. This can help in debugging or ensuring all fields are eventually populated.
The default value for 'description' is an empty string. While functional, for internationalization, it's often beneficial to provide a placeholder string like `null` or a specific marker (e.g., `__('default_description')`) that can be explicitly translated or identified as missing. This can help in debugging or ensuring all fields are eventually populated.
Similar to the 'description', an empty string for 'category' might be better represented by null or a translatable placeholder to distinguish between an intentionally empty category and a missing one.
Similar to the 'description', an empty string for 'category' might be better represented by `null` or a translatable placeholder to distinguish between an intentionally empty category and a missing one.
Similar to 'description' and 'category', an empty string for 'difficulty' could be improved by using null or a translatable placeholder. This helps differentiate between an unset difficulty and a deliberately empty one.
Similar to 'description' and 'category', an empty string for 'difficulty' could be improved by using `null` or a translatable placeholder. This helps differentiate between an unset difficulty and a deliberately empty one.
It's good that step_videos is initialized as an empty array. However, the save_recipe function inserts video_url from the input directly. If the input step_videos array contains null or empty strings, they will be inserted as such. Consider adding a trim or filter for empty strings here if they are not intended to be stored.
It's good that `step_videos` is initialized as an empty array. However, the `save_recipe` function inserts `video_url` from the input directly. If the input `step_videos` array contains `null` or empty strings, they will be inserted as such. Consider adding a trim or filter for empty strings here if they are not intended to be stored.
Similar to step_videos, step_timers is initialized as an empty array. The save_recipe function converts timer raw values to int or null. If the input array contains non-numeric strings, they will result in null. Ensure that any non-numeric or empty string values are handled consistently, perhaps by filtering them out before insertion.
Similar to `step_videos`, `step_timers` is initialized as an empty array. The `save_recipe` function converts timer raw values to `int` or `null`. If the input array contains non-numeric strings, they will result in `null`. Ensure that any non-numeric or empty string values are handled consistently, perhaps by filtering them out before insertion.
This function correctly extracts base recipe data from a database row, handling potential missing keys with default values. The explicit type casting ((int), (bool)) is good for data integrity.
This function correctly extracts base recipe data from a database row, handling potential missing keys with default values. The explicit type casting (`(int)`, `(bool)`) is good for data integrity.
This function attempts to decode legacy JSONB values. It handles arrays, strings, and objects that can be JSON encoded. However, it might be beneficial to add explicit handling or error logging for unexpected data types passed to $value to prevent potential TypeError or other runtime errors if the input is not as expected.
This function attempts to decode legacy JSONB values. It handles arrays, strings, and objects that can be JSON encoded. However, it might be beneficial to add explicit handling or error logging for unexpected data types passed to `$value` to prevent potential `TypeError` or other runtime errors if the input is not as expected.
This check correctly identifies if the legacy 'data' column exists, preventing unnecessary migration steps. This is a robust way to handle schema evolution.
This check correctly identifies if the legacy 'data' column exists, preventing unnecessary migration steps. This is a robust way to handle schema evolution.
Dropping existing tables before applying the schema is a common migration strategy. However, if this script is ever run on a production database with existing data, this will result in data loss. Ensure this is only intended for development/testing environments or that a proper migration system is in place for production.
Dropping existing tables before applying the schema is a common migration strategy. However, if this script is ever run on a production database with existing data, this will result in data loss. Ensure this is only intended for development/testing environments or that a proper migration system is in place for production.
Using a static variable $ready to ensure schema application only runs once per request is an efficient optimization. This prevents redundant database operations.
Using a static variable `$ready` to ensure schema application only runs once per request is an efficient optimization. This prevents redundant database operations.
This function is crucial for database connectivity and schema management. The fallback logic for DATABASE_URL being unset is good. Throwing a DatabaseUnavailableException is a clear way to signal a critical error.
This function is crucial for database connectivity and schema management. The fallback logic for `DATABASE_URL` being unset is good. Throwing a `DatabaseUnavailableException` is a clear way to signal a critical error.
This function orchestrates the loading of all recipe data from the database, including translations, tags, ingredients, etc. It's well-structured, though the multiple SELECT statements could potentially be optimized if performance becomes an issue with a very large number of recipes or related data.
This function orchestrates the loading of all recipe data from the database, including translations, tags, ingredients, etc. It's well-structured, though the multiple `SELECT` statements could potentially be optimized if performance becomes an issue with a very large number of recipes or related data.
This function handles saving a recipe to the database, including its translations and related items. It uses transactions for atomicity, which is excellent. However, the repeated calls to DELETE for all related tables before inserting new data can be inefficient for updates. If a recipe is updated frequently, consider an UPSERT approach or more targeted updates for related data rather than full deletes and re-inserts.
This function handles saving a recipe to the database, including its translations and related items. It uses transactions for atomicity, which is excellent. However, the repeated calls to `DELETE` for all related tables before inserting new data can be inefficient for updates. If a recipe is updated frequently, consider an `UPSERT` approach or more targeted updates for related data rather than full deletes and re-inserts.
Clearing all other featured recipes when a new one is marked as featured is a good logic for ensuring only one recipe is featured at a time. This prevents ambiguity.
Clearing all other featured recipes when a new one is marked as featured is a good logic for ensuring only one recipe is featured at a time. This prevents ambiguity.
These delete statements are executed for every save, even if the data hasn't changed. This can be inefficient. If the goal is to update, consider only deleting/inserting what has changed, or using ON CONFLICT clauses where applicable in the INSERT statements for related tables if the database supports it.
These delete statements are executed for every save, even if the data hasn't changed. This can be inefficient. If the goal is to update, consider only deleting/inserting what has changed, or using `ON CONFLICT` clauses where applicable in the `INSERT` statements for related tables if the database supports it.
Hardcoding languages en and de might become problematic if more languages are added. It would be more maintainable to fetch the list of supported languages from a configuration or a dedicated table if the application grows.
Hardcoding languages `en` and `de` might become problematic if more languages are added. It would be more maintainable to fetch the list of supported languages from a configuration or a dedicated table if the application grows.
The ternary operator ($timerRaw !== '' && is_numeric($timerRaw)) ? (int) $timerRaw : null; correctly handles non-numeric or empty timer values. This is good input sanitization.
The ternary operator `($timerRaw !== '' && is_numeric($timerRaw)) ? (int) $timerRaw : null;` correctly handles non-numeric or empty timer values. This is good input sanitization.
Loading all recipes on every page request might be inefficient if the recipe list is very large and rarely changes. Consider caching the recipe data if performance becomes an issue, especially for pages like index.php and login.php where the full list might not be immediately necessary for rendering.
Loading all recipes on every page request might be inefficient if the recipe list is very large and rarely changes. Consider caching the recipe data if performance becomes an issue, especially for pages like `index.php` and `login.php` where the full list might not be immediately necessary for rendering.
This JavaScript function filters recipes based on user goals. The logic for filtering appears sound. However, hardcoding the goal names ('weight_loss', 'muscle_gain', 'healthy') and the associated tag/category keywords could be made more configurable or data-driven, especially if more goals are to be added.
This JavaScript function filters recipes based on user goals. The logic for filtering appears sound. However, hardcoding the goal names (`'weight_loss'`, `'muscle_gain'`, `'healthy'`) and the associated tag/category keywords could be made more configurable or data-driven, especially if more goals are to be added.
This fallback ensures that if filtering results in an empty list, the full recipe bank is shown. This is a good user experience to prevent a blank carousel.
This fallback ensures that if filtering results in an empty list, the full recipe bank is shown. This is a good user experience to prevent a blank carousel.
The original code had Firebase auth logic. This new implementation seems to rely on window.fcLocal for local storage of goals and favorites. The removal of the Firebase auth observer is a significant architectural change. Ensure that the authentication mechanism and data persistence strategy (local vs. server-side) are clearly defined and intended.
The original code had Firebase auth logic. This new implementation seems to rely on `window.fcLocal` for local storage of goals and favorites. The removal of the Firebase auth observer is a significant architectural change. Ensure that the authentication mechanism and data persistence strategy (local vs. server-side) are clearly defined and intended.
Accessing window.fcLocal without a check could lead to a TypeError if fcLocal is not defined. The check window.fcLocal ? ... : '' correctly handles this potential issue.
Accessing `window.fcLocal` without a check could lead to a `TypeError` if `fcLocal` is not defined. The check `window.fcLocal ? ... : ''` correctly handles this potential issue.
This check ensures that favorite rendering only proceeds if window.fcLocal is available, preventing potential errors. This is good defensive programming.
This check ensures that favorite rendering only proceeds if `window.fcLocal` is available, preventing potential errors. This is good defensive programming.
This function dynamically renders the user's favorite recipes. It correctly handles cases where window.fcLocal is not available or when there are no favorites.
This function dynamically renders the user's favorite recipes. It correctly handles cases where `window.fcLocal` is not available or when there are no favorites.
Iterating through favorite slugs and then finding the corresponding recipe in recipeBank is a straightforward approach. If recipeBank becomes very large, this linear search (.find()) could become a performance bottleneck. Consider using a map or object for faster lookups if performance is critical.
Iterating through favorite slugs and then finding the corresponding recipe in `recipeBank` is a straightforward approach. If `recipeBank` becomes very large, this linear search (`.find()`) could become a performance bottleneck. Consider using a map or object for faster lookups if performance is critical.
This DOMContentLoaded listener initializes the UI based on local storage. It correctly checks for window.fcLocal before accessing its methods. The logic for updating the goal select box and rendering favorites seems sound.
This `DOMContentLoaded` listener initializes the UI based on local storage. It correctly checks for `window.fcLocal` before accessing its methods. The logic for updating the goal select box and rendering favorites seems sound.
This line assumes window.fcLocal is always defined. Similar to the index.php script, it would be safer to use const goal = window.fcLocal ? window.fcLocal.getGoal() : ''; to handle cases where fcLocal might not be initialized.
This line assumes `window.fcLocal` is always defined. Similar to the `index.php` script, it would be safer to use `const goal = window.fcLocal ? window.fcLocal.getGoal() : '';` to handle cases where `fcLocal` might not be initialized.
It's good to select the element before trying to set its value. The if (goal && select) check correctly handles cases where either the goal isn't set in local storage or the element doesn't exist on the page.
It's good to select the element before trying to set its value. The `if (goal && select)` check correctly handles cases where either the goal isn't set in local storage or the element doesn't exist on the page.
Including a general stylesheet like /assets/style.css on a maintenance page is good for consistent branding and layout. Ensure this file is available and correctly linked even when the main application might be having issues.
Including a general stylesheet like `/assets/style.css` on a maintenance page is good for consistent branding and layout. Ensure this file is available and correctly linked even when the main application might be having issues.
Displaying the specific $dbError message within a <code> block is excellent for debugging and providing actionable information to the user about what went wrong.
Displaying the specific `$dbError` message within a `<code>` block is excellent for debugging and providing actionable information to the user about what went wrong.
Providing explicit, step-by-step instructions for local debugging on a maintenance page is a very user-friendly and helpful practice. This significantly aids users in resolving common local setup issues.
Providing explicit, step-by-step instructions for local debugging on a maintenance page is a very user-friendly and helpful practice. This significantly aids users in resolving common local setup issues.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
gemini-code-review-action comments
@@ -0,0 +14,4 @@- "8080:80"environment:DATABASE_URL: postgresql://flixcooks:flixcooks_prod@postgres:5432/flixcooksFLIXCOOKS_ADMIN_KEY: ${FLIXCOOKS_ADMIN_KEY:-change-me-in-production}The default value
change-me-in-productionis a good security practice for development environments. However, ensure that this variable is always overridden in production environments. Consider adding a validation check or a more prominent warning if this default value is detected in a production context, perhaps during the application's startup.@@ -0,0 +18,4 @@RUN_DB_SEED: ${RUN_DB_SEED:-false}depends_on:postgres:condition: service_healthyThis is good practice for ensuring the database is ready before the application starts. However, the
healthcheckin thepostgresservice hasinterval: 5sandtimeout: 5s. If the database takes slightly longer to become fully ready than these parameters allow, thewebservice might still fail to start. Consider increasing theintervalortimeoutslightly, or ensuring thepg_isreadycommand is robust enough to handle initial startup states.@@ -0,0 +1,28 @@#!/bin/bashIt's good practice to include
set -euo pipefailin shell scripts to make them more robust by exiting on unbound variables (-u), non-zero exit codes (-e), and pipeline failures (-o pipefail). This is already present, which is excellent.@@ -0,0 +5,4 @@echo "[flixcooks] Waiting for database..."TRIES=0MAX_TRIES="${DB_WAIT_MAX_TRIES:-30}"Making the maximum number of retries configurable via an environment variable is a good flexible design. The default of 30 attempts (with a 2-second sleep) provides a generous 60-second wait, which is generally sufficient. Ensure this timeout is documented or understood by users deploying the application.
@@ -0,0 +6,4 @@require __DIR__ . '/config.php';try {Wrapping the
load_recipes()call in atry...catchblock is essential for handling potentialDatabaseUnavailableExceptionerrors gracefully, especially on pages that are not intended to show a maintenance page.@@ -12,0 +22,4 @@$stmt->execute([$table, $column]);return (bool) $stmt->fetchColumn();}This is a concise and efficient way to check for the existence of a column in
information_schema.columns.@@ -12,1 +25,4 @@function apply_recipe_schema(PDO $pdo): void {$path = __DIR__ . '/scripts/schema.sql';if (!file_exists($path)) {Loading the schema from an external SQL file (
scripts/schema.sql) is a good practice for maintainability and readability. Throwing aRuntimeExceptionif the file is missing is appropriate.@@ -18,0 +37,4 @@'description' => '','category' => '','difficulty' => '','tags' => [],This helper function provides a clean default structure for internationalized recipe data, ensuring consistency.
@@ -18,0 +40,4 @@'tags' => [],'ingredients' => [],'utensils' => [],'steps' => [],The default value for 'description' is an empty string. While functional, for internationalization, it's often beneficial to provide a placeholder string like
nullor a specific marker (e.g.,__('default_description')) that can be explicitly translated or identified as missing. This can help in debugging or ensuring all fields are eventually populated.@@ -18,0 +41,4 @@'ingredients' => [],'utensils' => [],'steps' => [],'step_videos' => [],Similar to the 'description', an empty string for 'category' might be better represented by
nullor a translatable placeholder to distinguish between an intentionally empty category and a missing one.@@ -18,0 +42,4 @@'utensils' => [],'steps' => [],'step_videos' => [],'step_timers' => [],Similar to 'description' and 'category', an empty string for 'difficulty' could be improved by using
nullor a translatable placeholder. This helps differentiate between an unset difficulty and a deliberately empty one.@@ -18,0 +47,4 @@}function recipe_base_from_row(array $row): array {return [It's good that
step_videosis initialized as an empty array. However, thesave_recipefunction insertsvideo_urlfrom the input directly. If the inputstep_videosarray containsnullor empty strings, they will be inserted as such. Consider adding a trim or filter for empty strings here if they are not intended to be stored.@@ -18,0 +48,4 @@function recipe_base_from_row(array $row): array {return ['slug' => $row['slug'],Similar to
step_videos,step_timersis initialized as an empty array. Thesave_recipefunction converts timer raw values tointornull. If the input array contains non-numeric strings, they will result innull. Ensure that any non-numeric or empty string values are handled consistently, perhaps by filtering them out before insertion.@@ -18,0 +52,4 @@'hero' => $row['hero'] ?? '','prep_time' => (int) ($row['prep_time'] ?? 0),'cook_time' => (int) ($row['cook_time'] ?? 0),'total_time' => (int) ($row['total_time'] ?? 0),This function correctly extracts base recipe data from a database row, handling potential missing keys with default values. The explicit type casting (
(int),(bool)) is good for data integrity.@@ -18,0 +75,4 @@}if (is_string($value)) {$decoded = json_decode($value, true);return is_array($decoded) ? $decoded : null;This function attempts to decode legacy JSONB values. It handles arrays, strings, and objects that can be JSON encoded. However, it might be beneficial to add explicit handling or error logging for unexpected data types passed to
$valueto prevent potentialTypeErroror other runtime errors if the input is not as expected.@@ -18,0 +91,4 @@$legacy = [];$stmt = $pdo->query('SELECT slug, data FROM recipes');while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {This check correctly identifies if the legacy 'data' column exists, preventing unnecessary migration steps. This is a robust way to handle schema evolution.
@@ -18,0 +104,4 @@$pdo->exec('DROP TABLE IF EXISTS recipe_tags CASCADE');$pdo->exec('DROP TABLE IF EXISTS recipe_translations CASCADE');$pdo->exec('DROP TABLE IF EXISTS recipes CASCADE');Dropping existing tables before applying the schema is a common migration strategy. However, if this script is ever run on a production database with existing data, this will result in data loss. Ensure this is only intended for development/testing environments or that a proper migration system is in place for production.
@@ -18,0 +118,4 @@return;}if (db_table_has_column($pdo, 'recipes', 'data') && !db_table_has_column($pdo, 'recipes', 'calories')) {Using a static variable
$readyto ensure schema application only runs once per request is an efficient optimization. This prevents redundant database operations.@@ -18,0 +134,4 @@$message = getenv('DATABASE_URL')? 'Database connection failed. Check DATABASE_URL and that Postgres is running.': 'DATABASE_URL is not set in .env.';throw new DatabaseUnavailableException($message);This function is crucial for database connectivity and schema management. The fallback logic for
DATABASE_URLbeing unset is good. Throwing aDatabaseUnavailableExceptionis a clear way to signal a critical error.@@ -18,0 +151,4 @@'SELECT * FROM recipes ORDER BY featured DESC, updated_at DESC, slug ASC');while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {$recipes[$row['slug']] = recipe_base_from_row($row);This function orchestrates the loading of all recipe data from the database, including translations, tags, ingredients, etc. It's well-structured, though the multiple
SELECTstatements could potentially be optimized if performance becomes an issue with a very large number of recipes or related data.@@ -35,0 +258,4 @@try {$pdo->beginTransaction();if (!empty($recipe['featured'])) {This function handles saving a recipe to the database, including its translations and related items. It uses transactions for atomicity, which is excellent. However, the repeated calls to
DELETEfor all related tables before inserting new data can be inefficient for updates. If a recipe is updated frequently, consider anUPSERTapproach or more targeted updates for related data rather than full deletes and re-inserts.@@ -35,0 +270,4 @@:slug, :hero, :prep_time, :cook_time, :total_time, :servings,:featured, :coming_soon, :calories, :protein, :carbs, :fat, NOW())ON CONFLICT (slug) DO UPDATE SETClearing all other featured recipes when a new one is marked as featured is a good logic for ensuring only one recipe is featured at a time. This prevents ambiguity.
@@ -35,0 +310,4 @@);$tagStmt = $pdo->prepare('INSERT INTO recipe_tags (recipe_slug, lang, tag, sort_order) VALUES (?, ?, ?, ?)');These delete statements are executed for every save, even if the data hasn't changed. This can be inefficient. If the goal is to update, consider only deleting/inserting what has changed, or using
ON CONFLICTclauses where applicable in theINSERTstatements for related tables if the database supports it.@@ -35,0 +334,4 @@]);$tagOrder = 0;foreach (array_values($block['tags'] ?? []) as $tag) {Hardcoding languages
enanddemight become problematic if more languages are added. It would be more maintainable to fetch the list of supported languages from a configuration or a dedicated table if the application grows.@@ -35,0 +379,4 @@if ($pdo->inTransaction()) {$pdo->rollBack();}error_log('save_recipe failed: ' . $e->getMessage());The ternary operator
($timerRaw !== '' && is_numeric($timerRaw)) ? (int) $timerRaw : null;correctly handles non-numeric or empty timer values. This is good input sanitization.@@ -2,2 +2,4 @@require __DIR__ . '/helpers.php';try {$allRecipes = load_recipes();Loading all recipes on every page request might be inefficient if the recipe list is very large and rarely changes. Consider caching the recipe data if performance becomes an issue, especially for pages like
index.phpandlogin.phpwhere the full list might not be immediately necessary for rendering.This JavaScript function filters recipes based on user goals. The logic for filtering appears sound. However, hardcoding the goal names (
'weight_loss','muscle_gain','healthy') and the associated tag/category keywords could be made more configurable or data-driven, especially if more goals are to be added.This fallback ensures that if filtering results in an empty list, the full recipe bank is shown. This is a good user experience to prevent a blank carousel.
The original code had Firebase auth logic. This new implementation seems to rely on
window.fcLocalfor local storage of goals and favorites. The removal of the Firebase auth observer is a significant architectural change. Ensure that the authentication mechanism and data persistence strategy (local vs. server-side) are clearly defined and intended.Accessing
window.fcLocalwithout a check could lead to aTypeErroriffcLocalis not defined. The checkwindow.fcLocal ? ... : ''correctly handles this potential issue.@@ -46,9 +52,9 @@ $copy = ['your_goal' => 'Dein Ernährungsziel','saved_recipes' => 'Deine gespeicherten Favoriten',This function maps internal goal identifiers to user-friendly labels. It's a clean way to handle translations and display logic.
@@ -519,3 +449,3 @@}function handleLogin(e) {function handleSaveGoal(e) {This check ensures that favorite rendering only proceeds if
window.fcLocalis available, preventing potential errors. This is good defensive programming.This check prevents errors if
window.fcLocalisn't defined, ensuring the script doesn't crash. It's good defensive programming.@@ -544,1 +455,4 @@window.fcLocal.setGoal(goal);document.getElementById('profileGoalVal').textContent = goalLabel(goal);showAlert(langText.goal_saved);}This function dynamically renders the user's favorite recipes. It correctly handles cases where
window.fcLocalis not available or when there are no favorites.Iterating through favorite slugs and then finding the corresponding recipe in
recipeBankis a straightforward approach. IfrecipeBankbecomes very large, this linear search (.find()) could become a performance bottleneck. Consider using a map or object for faster lookups if performance is critical.This
DOMContentLoadedlistener initializes the UI based on local storage. It correctly checks forwindow.fcLocalbefore accessing its methods. The logic for updating the goal select box and rendering favorites seems sound.This line assumes
window.fcLocalis always defined. Similar to theindex.phpscript, it would be safer to useconst goal = window.fcLocal ? window.fcLocal.getGoal() : '';to handle cases wherefcLocalmight not be initialized.It's good to select the element before trying to set its value. The
if (goal && select)check correctly handles cases where either the goal isn't set in local storage or the element doesn't exist on the page.@@ -0,0 +4,4 @@<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>FlixCooks – Datenbank nicht verfügbar</title><link rel="stylesheet" href="/assets/style.css">Including a general stylesheet like
/assets/style.csson a maintenance page is good for consistent branding and layout. Ensure this file is available and correctly linked even when the main application might be having issues.@@ -0,0 +37,4 @@<div class="db-error-card"><h1>Datenbank nicht verfügbar</h1><p>FlixCooks benötigt eine laufende PostgreSQL-Verbindung. Ohne Datenbank werden keine Rezepte angezeigt.</p><?php if (!empty($dbError)): ?>Displaying the specific
$dbErrormessage within a<code>block is excellent for debugging and providing actionable information to the user about what went wrong.@@ -0,0 +41,4 @@<code><?php echo htmlspecialchars($dbError, ENT_QUOTES, 'UTF-8'); ?></code><?php endif; ?><p><strong>Lokal prüfen:</strong></p><ol style="text-align: left; margin: 0 auto; max-width: 22rem;">Providing explicit, step-by-step instructions for local debugging on a maintenance page is a very user-friendly and helpful practice. This significantly aids users in resolving common local setup issues.