Phase 1 & 2: full-stack family dashboard scaffold
- pnpm monorepo (apps/client + apps/server) - Server: Express + node:sqlite with numbered migration runner, REST API for all 9 features (members, events, chores, shopping, meals, messages, countdowns, photos, settings) - Client: React 18 + Vite + TypeScript + Tailwind + Framer Motion + Zustand - Theme system: dark/light + 5 accent colors, CSS custom properties, anti-FOUC script, ThemeToggle on every surface - AppShell: collapsible sidebar, animated route transitions, mobile drawer - Phase 2 features: Calendar (custom month grid, event chips, add/edit modal), Chores (card grid, complete/reset, member filter, streaks), Shopping (multi-list tabs, animated check-off, quick-add bar, member assign) - Family member CRUD with avatar, color picker - Settings page: theme/accent, photo folder, slideshow, weather, date/time - Docker: multi-stage Dockerfile, docker-compose.yml, entrypoint with PUID/PGID - Unraid: CA XML template, CLI install script, UNRAID.md guide - .gitignore covering node_modules, dist, db files, secrets, build artifacts Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
37
apps/server/src/routes/meals.ts
Normal file
37
apps/server/src/routes/meals.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { Router } from 'express';
|
||||
import db from '../db/db';
|
||||
|
||||
const router = Router();
|
||||
|
||||
// Get meals for a date range (e.g. ?start=2024-01-01&end=2024-01-31)
|
||||
router.get('/', (req, res) => {
|
||||
const { start, end } = req.query as { start?: string; end?: string };
|
||||
if (start && end) {
|
||||
return res.json(db.prepare('SELECT * FROM meals WHERE date >= ? AND date <= ? ORDER BY date ASC').all(start, end));
|
||||
}
|
||||
res.json(db.prepare('SELECT * FROM meals ORDER BY date ASC').all());
|
||||
});
|
||||
|
||||
router.get('/:date', (req, res) => {
|
||||
const row = db.prepare('SELECT * FROM meals WHERE date = ?').get(req.params.date);
|
||||
if (!row) return res.status(404).json({ error: 'No meal for this date' });
|
||||
res.json(row);
|
||||
});
|
||||
|
||||
router.put('/:date', (req, res) => {
|
||||
const { title, description, recipe_url } = req.body as { title: string; description?: string; recipe_url?: string };
|
||||
if (!title?.trim()) return res.status(400).json({ error: 'Title is required' });
|
||||
db.prepare(`
|
||||
INSERT INTO meals (date, title, description, recipe_url) VALUES (?, ?, ?, ?)
|
||||
ON CONFLICT(date) DO UPDATE SET title=excluded.title, description=excluded.description, recipe_url=excluded.recipe_url
|
||||
`).run(req.params.date, title.trim(), description ?? null, recipe_url ?? null);
|
||||
res.json(db.prepare('SELECT * FROM meals WHERE date = ?').get(req.params.date));
|
||||
});
|
||||
|
||||
router.delete('/:date', (req, res) => {
|
||||
const result = db.prepare('DELETE FROM meals WHERE date = ?').run(req.params.date);
|
||||
if (result.changes === 0) return res.status(404).json({ error: 'No meal for this date' });
|
||||
res.status(204).end();
|
||||
});
|
||||
|
||||
export default router;
|
||||
Reference in New Issue
Block a user