From ce75496770ad28f6c844216e5c2bae2d3ca8c48e Mon Sep 17 00:00:00 2001 From: Matt Date: Sun, 23 Mar 2025 17:18:31 -0400 Subject: [PATCH] Clean up unused permissions, take user to first page/component they can access --- inventory-server/auth/schema.sql | 69 ++------- inventory/src/App.tsx | 6 + .../components/auth/FirstAccessiblePage.tsx | 44 ++++++ inventory/src/pages/Settings.tsx | 141 +++++++++++++++--- 4 files changed, 184 insertions(+), 76 deletions(-) create mode 100644 inventory/src/components/auth/FirstAccessiblePage.tsx diff --git a/inventory-server/auth/schema.sql b/inventory-server/auth/schema.sql index 836b17b..13f1508 100644 --- a/inventory-server/auth/schema.sql +++ b/inventory-server/auth/schema.sql @@ -2,10 +2,14 @@ CREATE TABLE users ( id SERIAL PRIMARY KEY, username VARCHAR(255) NOT NULL UNIQUE, password VARCHAR(255) NOT NULL, + email VARCHAR UNIQUE, + is_admin BOOLEAN DEFAULT FALSE, + is_active BOOLEAN DEFAULT TRUE, + last_login TIMESTAMP WITH TIME ZONE, + updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); - -- Function to update the updated_at timestamp CREATE OR REPLACE FUNCTION update_updated_at_column() RETURNS TRIGGER AS $$ @@ -18,14 +22,6 @@ $$ language 'plpgsql'; -- Sequence and defined type for users table if not exists CREATE SEQUENCE IF NOT EXISTS users_id_seq; --- Update users table with new fields -ALTER TABLE "public"."users" - ADD COLUMN IF NOT EXISTS "email" varchar UNIQUE, - ADD COLUMN IF NOT EXISTS "is_admin" boolean DEFAULT FALSE, - ADD COLUMN IF NOT EXISTS "is_active" boolean DEFAULT TRUE, - ADD COLUMN IF NOT EXISTS "last_login" timestamp with time zone, - ADD COLUMN IF NOT EXISTS "updated_at" timestamp with time zone DEFAULT CURRENT_TIMESTAMP; - -- Create permissions table CREATE TABLE IF NOT EXISTS "public"."permissions" ( "id" SERIAL PRIMARY KEY, @@ -58,8 +54,7 @@ CREATE TRIGGER update_permissions_updated_at FOR EACH ROW EXECUTE FUNCTION update_updated_at_column(); --- Insert default permissions by page --- Core page access permissions +-- Insert default permissions by page - only the ones used in application INSERT INTO permissions (name, code, description, category) VALUES ('Dashboard Access', 'access:dashboard', 'Can access the Dashboard page', 'Pages'), ('Products Access', 'access:products', 'Can access the Products page', 'Pages'), @@ -73,52 +68,14 @@ INSERT INTO permissions (name, code, description, category) VALUES ('AI Validation Debug Access', 'access:ai_validation_debug', 'Can access the AI Validation Debug page', 'Pages') ON CONFLICT (code) DO NOTHING; --- Granular permissions for Products -INSERT INTO permissions (name, code, description, category) VALUES - ('View Products', 'view:products', 'Can view product listings', 'Products'), - ('Create Products', 'create:products', 'Can create new products', 'Products'), - ('Edit Products', 'edit:products', 'Can edit product details', 'Products'), - ('Delete Products', 'delete:products', 'Can delete products', 'Products') -ON CONFLICT (code) DO NOTHING; - --- Granular permissions for Categories -INSERT INTO permissions (name, code, description, category) VALUES - ('View Categories', 'view:categories', 'Can view categories', 'Categories'), - ('Create Categories', 'create:categories', 'Can create new categories', 'Categories'), - ('Edit Categories', 'edit:categories', 'Can edit categories', 'Categories'), - ('Delete Categories', 'delete:categories', 'Can delete categories', 'Categories') -ON CONFLICT (code) DO NOTHING; - --- Granular permissions for Vendors -INSERT INTO permissions (name, code, description, category) VALUES - ('View Vendors', 'view:vendors', 'Can view vendors', 'Vendors'), - ('Create Vendors', 'create:vendors', 'Can create new vendors', 'Vendors'), - ('Edit Vendors', 'edit:vendors', 'Can edit vendors', 'Vendors'), - ('Delete Vendors', 'delete:vendors', 'Can delete vendors', 'Vendors') -ON CONFLICT (code) DO NOTHING; - --- Granular permissions for Purchase Orders -INSERT INTO permissions (name, code, description, category) VALUES - ('View Purchase Orders', 'view:purchase_orders', 'Can view purchase orders', 'Purchase Orders'), - ('Create Purchase Orders', 'create:purchase_orders', 'Can create new purchase orders', 'Purchase Orders'), - ('Edit Purchase Orders', 'edit:purchase_orders', 'Can edit purchase orders', 'Purchase Orders'), - ('Delete Purchase Orders', 'delete:purchase_orders', 'Can delete purchase orders', 'Purchase Orders') -ON CONFLICT (code) DO NOTHING; - --- User management permissions -INSERT INTO permissions (name, code, description, category) VALUES - ('View Users', 'view:users', 'Can view user accounts', 'Users'), - ('Create Users', 'create:users', 'Can create user accounts', 'Users'), - ('Edit Users', 'edit:users', 'Can modify user accounts', 'Users'), - ('Delete Users', 'delete:users', 'Can delete user accounts', 'Users'), - ('Manage Permissions', 'manage:permissions', 'Can assign permissions to users', 'Users') -ON CONFLICT (code) DO NOTHING; - --- System permissions +-- Settings section permissions INSERT INTO permissions (name, code, description, category) VALUES - ('Run Calculations', 'run:calculations', 'Can trigger system calculations', 'System'), - ('Import Data', 'import:data', 'Can import data into the system', 'System'), - ('System Settings', 'edit:system_settings', 'Can modify system settings', 'System') + ('Data Management', 'settings:data_management', 'Access to the Data Management settings section', 'Settings'), + ('Stock Management', 'settings:stock_management', 'Access to the Stock Management settings section', 'Settings'), + ('Performance Metrics', 'settings:performance_metrics', 'Access to the Performance Metrics settings section', 'Settings'), + ('Calculation Settings', 'settings:calculation_settings', 'Access to the Calculation Settings section', 'Settings'), + ('Template Management', 'settings:templates', 'Access to the Template Management settings section', 'Settings'), + ('User Management', 'settings:user_management', 'Access to the User Management settings section', 'Settings') ON CONFLICT (code) DO NOTHING; -- Set any existing users as admin diff --git a/inventory/src/App.tsx b/inventory/src/App.tsx index 4609f6a..a6868f4 100644 --- a/inventory/src/App.tsx +++ b/inventory/src/App.tsx @@ -18,6 +18,7 @@ import { Import } from '@/pages/Import'; import { AiValidationDebug } from "@/pages/AiValidationDebug" import { AuthProvider } from './contexts/AuthContext'; import { Protected } from './components/auth/Protected'; +import { FirstAccessiblePage } from './components/auth/FirstAccessiblePage'; const queryClient = new QueryClient(); @@ -78,6 +79,11 @@ function App() { }> + }> + + + } /> diff --git a/inventory/src/components/auth/FirstAccessiblePage.tsx b/inventory/src/components/auth/FirstAccessiblePage.tsx new file mode 100644 index 0000000..15fcf8d --- /dev/null +++ b/inventory/src/components/auth/FirstAccessiblePage.tsx @@ -0,0 +1,44 @@ +import { useContext } from "react"; +import { Navigate } from "react-router-dom"; +import { AuthContext } from "@/contexts/AuthContext"; + +// Define available pages in order of priority +const PAGES = [ + { path: "/products", permission: "access:products" }, + { path: "/categories", permission: "access:categories" }, + { path: "/vendors", permission: "access:vendors" }, + { path: "/purchase-orders", permission: "access:purchase_orders" }, + { path: "/analytics", permission: "access:analytics" }, + { path: "/forecasting", permission: "access:forecasting" }, + { path: "/import", permission: "access:import" }, + { path: "/settings", permission: "access:settings" }, + { path: "/ai-validation/debug", permission: "access:ai_validation_debug" } +]; + +export function FirstAccessiblePage() { + const { user } = useContext(AuthContext); + + // If user isn't loaded yet, don't render anything + if (!user) { + return null; + } + + // Admin users have access to all pages, so this component + // shouldn't be rendering for them (handled by App.tsx) + if (user.is_admin) { + return null; + } + + // Find the first page the user has access to + const firstAccessiblePage = PAGES.find(page => { + return user.permissions?.includes(page.permission); + }); + + // If we found a page, redirect to it + if (firstAccessiblePage) { + return ; + } + + // If user has no access to any page, redirect to login + return ; +} diff --git a/inventory/src/pages/Settings.tsx b/inventory/src/pages/Settings.tsx index 48cabac..35b3f46 100644 --- a/inventory/src/pages/Settings.tsx +++ b/inventory/src/pages/Settings.tsx @@ -8,33 +8,90 @@ import { UserManagement } from "@/components/settings/UserManagement"; import { motion } from 'framer-motion'; import { Alert, AlertDescription } from "@/components/ui/alert"; import { Protected } from "@/components/auth/Protected"; +import { useContext, useMemo } from "react"; +import { AuthContext } from "@/contexts/AuthContext"; + +// Define available settings tabs with their permission requirements +const SETTINGS_TABS = [ + { id: "data-management", permission: "settings:data_management", label: "Data Management" }, + { id: "stock-management", permission: "settings:stock_management", label: "Stock Management" }, + { id: "performance-metrics", permission: "settings:performance_metrics", label: "Performance Metrics" }, + { id: "calculation-settings", permission: "settings:calculation_settings", label: "Calculation Settings" }, + { id: "templates", permission: "settings:templates", label: "Template Management" }, + { id: "user-management", permission: "settings:user_management", label: "User Management" } +]; export function Settings() { + const { user } = useContext(AuthContext); + + // Determine the first tab the user has access to + const defaultTab = useMemo(() => { + // Admin users have access to all tabs + if (user?.is_admin) { + return SETTINGS_TABS[0].id; + } + + // Find the first tab the user has permission to access + const firstAccessibleTab = SETTINGS_TABS.find(tab => + user?.permissions?.includes(tab.permission) + ); + + // Return the ID of the first accessible tab, or first tab as fallback + return firstAccessibleTab?.id || SETTINGS_TABS[0].id; + }, [user]); + + // Check if user has access to any tab + const hasAccessToAnyTab = useMemo(() => { + if (user?.is_admin) return true; + return SETTINGS_TABS.some(tab => user?.permissions?.includes(tab.permission)); + }, [user]); + + // If user doesn't have access to any tabs, show a helpful message + if (!hasAccessToAnyTab) { + return ( + +
+

Settings

+
+ + + You don't have permission to access any settings. Please contact an administrator for assistance. + + +
+ ); + } + return (

Settings

- + - Data Management - Stock Management - - Performance Metrics - - + + Data Management + + + Stock Management + + + + Performance Metrics + + + Calculation Settings - - Template Management - - + + + Template Management + + + User Management @@ -42,20 +99,53 @@ export function Settings() { - + + + You don't have permission to access Data Management. + + + } + > + + - + + + You don't have permission to access Stock Management. + + + } + > + + - + + + You don't have permission to access Performance Metrics. + + + } + > + + @@ -69,12 +159,23 @@ export function Settings() { - + + + You don't have permission to access Template Management. + + + } + > + +