diff --git a/src/db.js b/src/db.js index 8429269..ba3280e 100644 --- a/src/db.js +++ b/src/db.js @@ -23,8 +23,9 @@ export async function initDb() { name TEXT NOT NULL, description TEXT, base_path TEXT NOT NULL, - icon TEXT NOT NULL DEFAULT '📋', + icon TEXT NOT NULL DEFAULT 'ClipboardList', theme_color TEXT NOT NULL DEFAULT '#1e3a5f', + category VARCHAR(100), active BOOLEAN NOT NULL DEFAULT TRUE, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); @@ -37,17 +38,20 @@ export async function initDb() { ); `) + // Add category column to existing installs + await pool.query(`ALTER TABLE apps ADD COLUMN IF NOT EXISTS category VARCHAR(100)`) + // Seed built-in apps await pool.query(` - INSERT INTO apps (slug, name, description, base_path, icon, theme_color) + INSERT INTO apps (slug, name, description, base_path, icon, theme_color, category) VALUES - ('noticeboard', 'Noticeboard', 'Staff notices and announcements', '/notices', 'ClipboardList', '#1e3a5f'), - ('kitchen', 'Kitchen Flash','Invoice processing and GP tracking', '/kitchen', 'ChefHat', '#e85d04'), - ('cashup', 'Cash Up', 'Hotel daily cashing up', '/cashup', 'Banknote', '#6b2d8b'), - ('housekeeping','Housekeeping', 'Room status and task management', '/hk', 'BedDouble', '#2d6a4f'), - ('forecasting', 'Forecasting', 'Revenue forecasting and reporting', '/forecast','TrendingUp', '#0077b6'), - ('rates', 'Rate Scraper', 'Competitor rate monitoring', '/rates', 'Tag', '#7b4f00') - ON CONFLICT (slug) DO UPDATE SET icon = EXCLUDED.icon + ('noticeboard', 'Noticeboard', 'Staff notices and announcements', '/notices', 'ClipboardList', '#1e3a5f', NULL), + ('kitchen', 'Kitchen Flash','Invoice processing and GP tracking', '/kitchen', 'ChefHat', '#e85d04', 'Kitchen'), + ('cashup', 'Cash Up', 'Hotel daily cashing up', '/cashup', 'Banknote', '#6b2d8b', 'Finance'), + ('housekeeping','Housekeeping', 'Room status and task management', '/hk', 'BedDouble', '#2d6a4f', 'Housekeeping'), + ('forecasting', 'Forecasting', 'Revenue forecasting and reporting', '/forecast','TrendingUp', '#0077b6', 'Finance'), + ('rates', 'Rate Scraper', 'Competitor rate monitoring', '/rates', 'Tag', '#7b4f00', 'Finance') + ON CONFLICT (slug) DO UPDATE SET icon = EXCLUDED.icon, category = EXCLUDED.category `) // Seed first admin user if table is empty diff --git a/src/routes/auth.js b/src/routes/auth.js index 50d96cd..6344f15 100644 --- a/src/routes/auth.js +++ b/src/routes/auth.js @@ -32,17 +32,17 @@ async function getUserWithApps(userId) { // only the apps explicitly granted to them via user_app_perms. const { rows: apps } = user.is_admin ? await pool.query( - `SELECT a.slug, a.name, a.description, a.base_path, a.icon, a.theme_color + `SELECT a.slug, a.name, a.description, a.base_path, a.icon, a.theme_color, a.category FROM apps a WHERE a.active = true - ORDER BY a.name` + ORDER BY a.category NULLS FIRST, a.name` ) : await pool.query( - `SELECT a.slug, a.name, a.description, a.base_path, a.icon, a.theme_color + `SELECT a.slug, a.name, a.description, a.base_path, a.icon, a.theme_color, a.category FROM apps a JOIN user_app_perms p ON p.app_id = a.id WHERE p.user_id = $1 AND a.active = true - ORDER BY a.name`, + ORDER BY a.category NULLS FIRST, a.name`, [userId] )