Clean up unused permissions, take user to first page/component they can access
This commit is contained in:
@@ -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
|
||||
-- Settings section permissions
|
||||
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
|
||||
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
|
||||
|
||||
@@ -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() {
|
||||
<MainLayout />
|
||||
</RequireAuth>
|
||||
}>
|
||||
<Route index element={
|
||||
<Protected page="dashboard" fallback={<FirstAccessiblePage />}>
|
||||
<Dashboard />
|
||||
</Protected>
|
||||
} />
|
||||
<Route path="/" element={
|
||||
<Protected page="dashboard">
|
||||
<Dashboard />
|
||||
|
||||
44
inventory/src/components/auth/FirstAccessiblePage.tsx
Normal file
44
inventory/src/components/auth/FirstAccessiblePage.tsx
Normal file
@@ -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 <Navigate to={firstAccessiblePage.path} replace />;
|
||||
}
|
||||
|
||||
// If user has no access to any page, redirect to login
|
||||
return <Navigate to="/login" replace />;
|
||||
}
|
||||
@@ -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 (
|
||||
<motion.div layout className="container mx-auto py-6">
|
||||
<div className="mb-6">
|
||||
<h1 className="text-3xl font-bold">Settings</h1>
|
||||
</div>
|
||||
<Alert>
|
||||
<AlertDescription>
|
||||
You don't have permission to access any settings. Please contact an administrator for assistance.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<motion.div layout className="container mx-auto py-6">
|
||||
<div className="mb-6">
|
||||
<h1 className="text-3xl font-bold">Settings</h1>
|
||||
</div>
|
||||
|
||||
<Tabs defaultValue="data-management" className="space-y-4">
|
||||
<Tabs defaultValue={defaultTab} className="space-y-4">
|
||||
<TabsList>
|
||||
<TabsTrigger value="data-management">Data Management</TabsTrigger>
|
||||
<TabsTrigger value="stock-management">Stock Management</TabsTrigger>
|
||||
<TabsTrigger value="performance-metrics">
|
||||
Performance Metrics
|
||||
</TabsTrigger>
|
||||
<Protected permission="edit:system_settings">
|
||||
<Protected permission="settings:data_management">
|
||||
<TabsTrigger value="data-management">Data Management</TabsTrigger>
|
||||
</Protected>
|
||||
<Protected permission="settings:stock_management">
|
||||
<TabsTrigger value="stock-management">Stock Management</TabsTrigger>
|
||||
</Protected>
|
||||
<Protected permission="settings:performance_metrics">
|
||||
<TabsTrigger value="performance-metrics">
|
||||
Performance Metrics
|
||||
</TabsTrigger>
|
||||
</Protected>
|
||||
<Protected permission="settings:calculation_settings">
|
||||
<TabsTrigger value="calculation-settings">
|
||||
Calculation Settings
|
||||
</TabsTrigger>
|
||||
</Protected>
|
||||
<TabsTrigger value="templates">
|
||||
Template Management
|
||||
</TabsTrigger>
|
||||
<Protected
|
||||
permission="view:users"
|
||||
fallback={null}
|
||||
>
|
||||
<Protected permission="settings:templates">
|
||||
<TabsTrigger value="templates">
|
||||
Template Management
|
||||
</TabsTrigger>
|
||||
</Protected>
|
||||
<Protected permission="settings:user_management">
|
||||
<TabsTrigger value="user-management">
|
||||
User Management
|
||||
</TabsTrigger>
|
||||
@@ -42,20 +99,53 @@ export function Settings() {
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="data-management">
|
||||
<DataManagement />
|
||||
<Protected
|
||||
permission="settings:data_management"
|
||||
fallback={
|
||||
<Alert>
|
||||
<AlertDescription>
|
||||
You don't have permission to access Data Management.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
}
|
||||
>
|
||||
<DataManagement />
|
||||
</Protected>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="stock-management">
|
||||
<StockManagement />
|
||||
<Protected
|
||||
permission="settings:stock_management"
|
||||
fallback={
|
||||
<Alert>
|
||||
<AlertDescription>
|
||||
You don't have permission to access Stock Management.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
}
|
||||
>
|
||||
<StockManagement />
|
||||
</Protected>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="performance-metrics">
|
||||
<PerformanceMetrics />
|
||||
<Protected
|
||||
permission="settings:performance_metrics"
|
||||
fallback={
|
||||
<Alert>
|
||||
<AlertDescription>
|
||||
You don't have permission to access Performance Metrics.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
}
|
||||
>
|
||||
<PerformanceMetrics />
|
||||
</Protected>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="calculation-settings">
|
||||
<Protected
|
||||
permission="edit:system_settings"
|
||||
permission="settings:calculation_settings"
|
||||
fallback={
|
||||
<Alert>
|
||||
<AlertDescription>
|
||||
@@ -69,12 +159,23 @@ export function Settings() {
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="templates">
|
||||
<TemplateManagement />
|
||||
<Protected
|
||||
permission="settings:templates"
|
||||
fallback={
|
||||
<Alert>
|
||||
<AlertDescription>
|
||||
You don't have permission to access Template Management.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
}
|
||||
>
|
||||
<TemplateManagement />
|
||||
</Protected>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="user-management">
|
||||
<Protected
|
||||
permission="view:users"
|
||||
permission="settings:user_management"
|
||||
fallback={
|
||||
<Alert>
|
||||
<AlertDescription>
|
||||
|
||||
Reference in New Issue
Block a user