diff --git a/logs/err-0.log b/logs/err-0.log
index 56e7e80..fe985c1 100644
--- a/logs/err-0.log
+++ b/logs/err-0.log
@@ -31,3 +31,36 @@ Error: Could not find a production build in the '.next' directory. Try building
at setupFsCheck (/home/alex/git/warme/node_modules/next/dist/server/lib/router-utils/filesystem.js:151:19)
at async initialize (/home/alex/git/warme/node_modules/next/dist/server/lib/router-server.js:64:23)
at async NextCustomServer.prepare (/home/alex/git/warme/node_modules/next/dist/server/next.js:242:28)
+2025-10-30T19:41:07: ⨯ Error: Input Buffer is empty
+ at Sharp._createInputDescriptor (/home/alex/git/warme/node_modules/sharp/lib/input.js:68:13)
+ at new Sharp (/home/alex/git/warme/node_modules/sharp/lib/constructor.js:403:29)
+ at Sharp (/home/alex/git/warme/node_modules/sharp/lib/constructor.js:212:12)
+ at detectContentType (/home/alex/git/warme/node_modules/next/dist/server/image-optimizer.js:359:28)
+ at imageOptimizer (/home/alex/git/warme/node_modules/next/dist/server/image-optimizer.js:767:32)
+ at NextNodeServer.imageOptimizer (/home/alex/git/warme/node_modules/next/dist/server/next-server.js:655:20)
+ at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
+ at async cacheEntry.imageResponseCache.get.incrementalCache (/home/alex/git/warme/node_modules/next/dist/server/next-server.js:182:65)
+ at async /home/alex/git/warme/node_modules/next/dist/server/response-cache/index.js:90:36
+ at async /home/alex/git/warme/node_modules/next/dist/lib/batcher.js:45:32
+2025-10-30T19:41:29: ⨯ Error: Input Buffer is empty
+ at Sharp._createInputDescriptor (/home/alex/git/warme/node_modules/sharp/lib/input.js:68:13)
+ at new Sharp (/home/alex/git/warme/node_modules/sharp/lib/constructor.js:403:29)
+ at Sharp (/home/alex/git/warme/node_modules/sharp/lib/constructor.js:212:12)
+ at detectContentType (/home/alex/git/warme/node_modules/next/dist/server/image-optimizer.js:359:28)
+ at imageOptimizer (/home/alex/git/warme/node_modules/next/dist/server/image-optimizer.js:767:32)
+ at NextNodeServer.imageOptimizer (/home/alex/git/warme/node_modules/next/dist/server/next-server.js:655:20)
+ at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
+ at async cacheEntry.imageResponseCache.get.incrementalCache (/home/alex/git/warme/node_modules/next/dist/server/next-server.js:182:65)
+ at async /home/alex/git/warme/node_modules/next/dist/server/response-cache/index.js:90:36
+ at async /home/alex/git/warme/node_modules/next/dist/lib/batcher.js:45:32
+2025-10-30T19:42:20: ⨯ Error: Input Buffer is empty
+ at Sharp._createInputDescriptor (/home/alex/git/warme/node_modules/sharp/lib/input.js:68:13)
+ at new Sharp (/home/alex/git/warme/node_modules/sharp/lib/constructor.js:403:29)
+ at Sharp (/home/alex/git/warme/node_modules/sharp/lib/constructor.js:212:12)
+ at detectContentType (/home/alex/git/warme/node_modules/next/dist/server/image-optimizer.js:359:28)
+ at imageOptimizer (/home/alex/git/warme/node_modules/next/dist/server/image-optimizer.js:767:32)
+ at NextNodeServer.imageOptimizer (/home/alex/git/warme/node_modules/next/dist/server/next-server.js:655:20)
+ at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
+ at async cacheEntry.imageResponseCache.get.incrementalCache (/home/alex/git/warme/node_modules/next/dist/server/next-server.js:182:65)
+ at async /home/alex/git/warme/node_modules/next/dist/server/response-cache/index.js:90:36
+ at async /home/alex/git/warme/node_modules/next/dist/lib/batcher.js:45:32
diff --git a/logs/out-0.log b/logs/out-0.log
index f0dbe7d..3243740 100644
--- a/logs/out-0.log
+++ b/logs/out-0.log
@@ -6,3 +6,10 @@
2025-10-30T19:31:44: > Ready on http://localhost:3000
2025-10-30T19:34:15: > Ready on http://localhost:3000
2025-10-30T19:38:44: > Ready on http://localhost:3000
+2025-10-30T19:40:45: > Ready on http://localhost:3000
+2025-10-30T19:42:05: > Ready on http://localhost:3000
+2025-10-30T19:50:39: > Ready on http://localhost:3000
+2025-10-30T19:54:37: > Ready on http://localhost:3000
+2025-10-30T19:58:29: > Ready on http://localhost:3000
+2025-10-31T19:17:21: > Ready on http://localhost:3000
+2025-10-31T20:05:46: > Ready on http://localhost:3000
diff --git a/package.json b/package.json
index add4a00..56d05db 100644
--- a/package.json
+++ b/package.json
@@ -18,6 +18,9 @@
"pm2:logs": "pm2 logs warme --lines 100 --nostream",
"pm2:delete": "pm2 delete warme"
},
+ "migration": {
+ "addFeatured": "node scripts/add-featured-column.mjs"
+ },
"dependencies": {
"class-variance-authority": "^0.7.0",
"clsx": "^2.0.0",
diff --git a/scripts/README.md b/scripts/README.md
new file mode 100644
index 0000000..3b60156
--- /dev/null
+++ b/scripts/README.md
@@ -0,0 +1,48 @@
+Run database migrations in this repo
+
+This repository includes a small, idempotent migration to add a `featured` column to the `menus` table.
+
+Script
+- `scripts/add-featured-column.mjs` — checks information_schema and adds `featured TINYINT(1) NOT NULL DEFAULT 0` if missing.
+
+How to run
+
+From the project root, set your DB connection environment variables and run the script. Example (zsh):
+
+```bash
+export DB_HOST=localhost
+export DB_USER=root
+export DB_PASSWORD='your-password'
+export DB_NAME=warme
+
+# run the migration
+node scripts/add-featured-column.mjs
+```
+
+If the script can't connect, confirm your environment variables and that the MySQL/MariaDB server is reachable from this machine.
+
+SQL alternative
+
+If you prefer to run SQL directly in your DB client, run:
+
+```sql
+SELECT COUNT(*) AS cnt
+FROM information_schema.columns
+WHERE table_schema = 'your_db_name' AND table_name = 'menus' AND column_name = 'featured';
+
+-- If cnt = 0
+ALTER TABLE menus ADD COLUMN featured TINYINT(1) NOT NULL DEFAULT 0;
+UPDATE menus SET featured = 0 WHERE featured IS NULL;
+```
+
+Verification
+
+After running, check with:
+
+```bash
+mysql -h "$DB_HOST" -u "$DB_USER" -p"$DB_PASSWORD" -D "$DB_NAME" -e "DESCRIBE menus;"
+```
+
+Notes
+- The migration is idempotent — running it multiple times is safe.
+- I can't run it from CI/this environment without database credentials. Run it locally on your machine or on a host that can reach the DB.
diff --git a/scripts/add-featured-column.mjs b/scripts/add-featured-column.mjs
new file mode 100644
index 0000000..9cea87e
--- /dev/null
+++ b/scripts/add-featured-column.mjs
@@ -0,0 +1,38 @@
+import { pool } from '../src/lib/db.js';
+
+// Idempotent migration using the project's DB pool: add `featured` TINYINT(1) to `menus` if missing
+// Usage: node scripts/add-featured-column.mjs
+
+async function columnExists(connection) {
+ const dbName = process.env.DB_NAME || 'warme';
+ const [rows] = await connection.query(
+ `SELECT COUNT(*) as cnt FROM information_schema.columns WHERE table_schema = ? AND table_name = 'menus' AND column_name = 'featured'`,
+ [dbName]
+ );
+ return rows[0].cnt > 0;
+}
+
+async function run() {
+ const connection = await pool.getConnection();
+ try {
+ const exists = await columnExists(connection);
+ if (exists) {
+ console.log('Column `featured` already exists on `menus`. Nothing to do.');
+ return;
+ }
+
+ console.log('Adding `featured` column to `menus`...');
+ await connection.query(`ALTER TABLE menus ADD COLUMN featured TINYINT(1) NOT NULL DEFAULT 0`);
+ console.log('Column added. Ensuring existing rows have 0...');
+ await connection.query(`UPDATE menus SET featured = 0 WHERE featured IS NULL`);
+ console.log('Migration completed successfully.');
+ } finally {
+ connection.release();
+ await pool.end();
+ }
+}
+
+run().then(() => process.exit(0)).catch((err) => {
+ console.error('Migration failed:', err);
+ process.exit(1);
+});
diff --git a/src/app/admin/availability/page.tsx b/src/app/admin/availability/page.tsx
index 37e3c93..5060d5c 100644
--- a/src/app/admin/availability/page.tsx
+++ b/src/app/admin/availability/page.tsx
@@ -3,14 +3,53 @@
import { useEffect, useState } from 'react'
import { DayPicker } from 'react-day-picker'
import { da } from 'date-fns/locale'
+import { ToastProvider, useToast } from '@/components/ui/toast'
type Weekly = { id: string; weekday: number; start_time: string; end_time: string; note?: string }
type Blocked = { id: string; block_date: string; note?: string }
+function BlockedList({ blocked, onDelete }: { blocked: Blocked[]; onDelete: (id: string) => void }) {
+ if (!blocked || blocked.length === 0) return
(m as any).featured) as Menu[])
+ .slice(0, 4)
+ .length > 0 ? (menus.filter((m: Menu) => (m as any).featured) as Menu[]).slice(0, 4) : menus.slice(0, 4)
return (
diff --git a/src/components/ui/image-placeholder.tsx b/src/components/ui/image-placeholder.tsx
index 6a37e71..e6cf3e0 100644
--- a/src/components/ui/image-placeholder.tsx
+++ b/src/components/ui/image-placeholder.tsx
@@ -19,13 +19,23 @@ export function ImagePlaceholder({ label, src, className }: ImagePlaceholderProp
return (
{src ? (
-
+ // If the image is a local static asset under /img we render a plain
+ // tag to avoid triggering Next.js' image optimizer which has
+ // caused errors in some environments. For external images we keep
+ // using the optimized component.
+ src.startsWith('/img/') ? (
+ // fill the container using absolute positioning when parent is
+ // positioned, otherwise let w-full/h-full handle sizing
+
+ ) : (
+
+ )
) : (