diff --git a/inventory/src/components/settings/UserList.tsx b/inventory/src/components/settings/UserList.tsx
index 17bbf97..1b4d479 100644
--- a/inventory/src/components/settings/UserList.tsx
+++ b/inventory/src/components/settings/UserList.tsx
@@ -20,6 +20,8 @@ interface UserListProps {
}
export function UserList({ users, onEdit, onDelete }: UserListProps) {
+ console.log("Rendering user list with users:", users);
+
if (users.length === 0) {
return (
diff --git a/inventory/src/components/settings/UserManagement.tsx b/inventory/src/components/settings/UserManagement.tsx
index 151b307..0db495c 100644
--- a/inventory/src/components/settings/UserManagement.tsx
+++ b/inventory/src/components/settings/UserManagement.tsx
@@ -6,7 +6,6 @@ import { UserList } from "./UserList";
import { UserForm } from "./UserForm";
import config from "@/config";
import { AuthContext } from "@/contexts/AuthContext";
-import { usePermissions } from "@/hooks/usePermissions";
import { ShieldAlert } from "lucide-react";
interface User {
@@ -33,8 +32,7 @@ interface PermissionCategory {
}
export function UserManagement() {
- const { token, fetchCurrentUser } = useContext(AuthContext);
- const { hasPermission } = usePermissions();
+ const { token, fetchCurrentUser, user } = useContext(AuthContext);
const [users, setUsers] = useState
([]);
const [selectedUser, setSelectedUser] = useState(null);
const [isAddingUser, setIsAddingUser] = useState(false);
@@ -137,10 +135,47 @@ export function UserManagement() {
});
if (!response.ok) {
- throw new Error('Failed to fetch user details');
+ if (response.status === 401) {
+ throw new Error('Authentication failed. Please log in again.');
+ } else if (response.status === 403) {
+ throw new Error('You don\'t have permission to edit users.');
+ } else {
+ throw new Error(`Failed to fetch user details (${response.status})`);
+ }
}
const userData = await response.json();
+ console.log("Fetched user data for editing:", userData);
+
+ // Ensure permissions is always an array
+ if (!userData.permissions) {
+ userData.permissions = [];
+ }
+
+ // Make sure the permissions are in the right format for the form
+ // The server might send either an array of permission objects or just permission codes
+ if (userData.permissions && userData.permissions.length > 0) {
+ // Check if permissions are objects with id property
+ if (typeof userData.permissions[0] === 'string') {
+ // If we just have permission codes, we need to convert them to objects with ids
+ // by looking them up in the permissions data
+ const permissionObjects = [];
+
+ // Go through each permission category
+ for (const category of permissions) {
+ // For each permission in the category
+ for (const permission of category.permissions) {
+ // If this permission's code is in the user's permission codes
+ if (userData.permissions.includes(permission.code)) {
+ permissionObjects.push(permission);
+ }
+ }
+ }
+
+ userData.permissions = permissionObjects;
+ }
+ }
+
setSelectedUser(userData);
setIsAddingUser(false);
} catch (err) {
@@ -155,38 +190,68 @@ export function UserManagement() {
};
const handleSaveUser = async (userData: any) => {
+ console.log("Saving user data:", userData);
+
+ // Format permissions for the API - convert from permission objects to IDs
+ let formattedUserData = { ...userData };
+
+ if (userData.permissions && Array.isArray(userData.permissions)) {
+ // Check if permissions are objects (from the form) and convert to IDs for the API
+ if (userData.permissions.length > 0 && typeof userData.permissions[0] === 'object') {
+ // The backend expects permission IDs, not just the code strings
+ formattedUserData.permissions = userData.permissions.map(p => p.id);
+ }
+ }
+
+ console.log("Formatted user data for API:", formattedUserData);
+
+ setLoading(true);
+
try {
- setLoading(true);
-
+ // Use PUT for updating, POST for creating
+ const method = userData.id ? 'PUT' : 'POST';
const endpoint = userData.id
? `${config.authUrl}/users/${userData.id}`
: `${config.authUrl}/users`;
- const method = userData.id ? 'PUT' : 'POST';
+ console.log(`${method} request to ${endpoint}`);
const response = await fetch(endpoint, {
method,
headers: {
- 'Authorization': `Bearer ${token}`,
- 'Content-Type': 'application/json'
+ 'Content-Type': 'application/json',
+ 'Authorization': `Bearer ${token}`
},
- body: JSON.stringify(userData)
+ body: JSON.stringify(formattedUserData)
});
- if (!response.ok) {
- const errorData = await response.json();
- throw new Error(errorData.error || 'Failed to save user');
+ let responseData;
+
+ try {
+ responseData = await response.json();
+ } catch (e) {
+ console.error("Error parsing response JSON:", e);
+ throw new Error("Invalid response from server");
}
- // Refresh user list after a successful save
- await fetchData();
+ if (!response.ok) {
+ console.error("Error response from server:", responseData);
+ throw new Error(responseData.error || responseData.message || `Failed to save user (${response.status})`);
+ }
- // Reset form state
+ console.log("Server response after saving user:", responseData);
+
+ // Reset the form state
setSelectedUser(null);
setIsAddingUser(false);
+
+ // Refresh the user list
+ fetchData();
+
} catch (err) {
const errorMessage = err instanceof Error ? err.message : 'Failed to save user';
setError(errorMessage);
+ } finally {
setLoading(false);
}
};
diff --git a/inventory/src/contexts/AuthContext.tsx b/inventory/src/contexts/AuthContext.tsx
index bd2c88a..621aeff 100644
--- a/inventory/src/contexts/AuthContext.tsx
+++ b/inventory/src/contexts/AuthContext.tsx
@@ -64,6 +64,9 @@ export function AuthProvider({ children }: { children: ReactNode }) {
}
const userData = await response.json();
+ console.log("Fetched current user data:", userData);
+ console.log("User permissions:", userData.permissions);
+
setUser(userData);
// Ensure we have the sessionStorage isLoggedIn flag set
sessionStorage.setItem('isLoggedIn', 'true');
@@ -113,6 +116,8 @@ export function AuthProvider({ children }: { children: ReactNode }) {
}
const data = await response.json();
+ console.log("Login successful, received data:", data);
+ console.log("User permissions:", data.user?.permissions);
localStorage.setItem('token', data.token);
sessionStorage.setItem('isLoggedIn', 'true');
diff --git a/inventory/src/hooks/usePagePermission.ts b/inventory/src/hooks/usePagePermission.ts
deleted file mode 100644
index 87c67ae..0000000
--- a/inventory/src/hooks/usePagePermission.ts
+++ /dev/null
@@ -1,101 +0,0 @@
-import { usePermissions } from './usePermissions';
-import { useNavigate } from 'react-router-dom';
-import { useEffect, useState } from 'react';
-
-interface PagePermissionConfig {
- // The permission required to access the specific page
- permission?: string;
-
- // Array of permissions where ANY must be present
- anyPermissions?: string[];
-
- // Array of permissions where ALL must be present
- allPermissions?: string[];
-
- // Whether this page is admin-only
- adminOnly?: boolean;
-
- // Page identifier for page-specific access check
- page?: string;
-
- // Redirect path if permission check fails
- redirectTo?: string;
-}
-
-/**
- * Hook to check if a user has permission to access a specific page
- * Will automatically redirect if permission is denied
- */
-export function usePagePermission(config: PagePermissionConfig) {
- const {
- permission,
- anyPermissions,
- allPermissions,
- adminOnly = false,
- page,
- redirectTo = '/'
- } = config;
-
- const navigate = useNavigate();
- const {
- hasPermission,
- hasPageAccess,
- hasAnyPermission,
- hasAllPermissions,
- isAdmin
- } = usePermissions();
-
- const [hasAccess, setHasAccess] = useState(null);
-
- useEffect(() => {
- // Check permissions
- let permitted = true;
-
- // Admin check
- if (adminOnly && !isAdmin) {
- permitted = false;
- }
-
- // Page access check
- if (page && !hasPageAccess(page)) {
- permitted = false;
- }
-
- // Single permission check
- if (permission && !hasPermission(permission)) {
- permitted = false;
- }
-
- // Any permissions check
- if (anyPermissions && !hasAnyPermission(anyPermissions)) {
- permitted = false;
- }
-
- // All permissions check
- if (allPermissions && !hasAllPermissions(allPermissions)) {
- permitted = false;
- }
-
- setHasAccess(permitted);
-
- // Redirect if no permission
- if (permitted === false) {
- navigate(redirectTo);
- }
- }, [
- permission,
- anyPermissions,
- allPermissions,
- adminOnly,
- page,
- redirectTo,
- hasPermission,
- hasPageAccess,
- hasAnyPermission,
- hasAllPermissions,
- isAdmin,
- navigate
- ]);
-
- return hasAccess;
-}
\ No newline at end of file
diff --git a/inventory/src/hooks/usePermissions.ts b/inventory/src/hooks/usePermissions.ts
deleted file mode 100644
index 82ad1a0..0000000
--- a/inventory/src/hooks/usePermissions.ts
+++ /dev/null
@@ -1,80 +0,0 @@
-import { useContext, useCallback } from "react";
-import { AuthContext } from "@/contexts/AuthContext";
-
-/**
- * Hook for checking user permissions
- * @returns Functions for checking permissions
- */
-export function usePermissions() {
- const { user } = useContext(AuthContext);
-
- /**
- * Check if the user has a specific permission
- * @param permissionCode The permission code to check
- * @returns Whether the user has the permission
- */
- const hasPermission = useCallback((permissionCode: string): boolean => {
- // If not authenticated or no permissions, return false
- if (!user || !user.permissions) {
- return false;
- }
-
- // Admin users have all permissions
- if (user.is_admin) {
- return true;
- }
-
- // Check specific permission
- return user.permissions.includes(permissionCode);
- }, [user]);
-
- /**
- * Check if the user has access to a specific page
- * @param pageName The page name (e.g., 'products', 'settings')
- * @returns Whether the user has access to the page
- */
- const hasPageAccess = useCallback((pageName: string): boolean => {
- return hasPermission(`access:${pageName.toLowerCase()}`);
- }, [hasPermission]);
-
- /**
- * Check if the user has ANY of the specified permissions
- * @param permissionCodes Array of permission codes to check
- * @returns Whether the user has any of the permissions
- */
- const hasAnyPermission = useCallback((permissionCodes: string[]): boolean => {
- // If admin or no permissions to check, return true
- if (!permissionCodes.length || (user && user.is_admin)) {
- return true;
- }
-
- return permissionCodes.some(code => hasPermission(code));
- }, [user, hasPermission]);
-
- /**
- * Check if the user has ALL of the specified permissions
- * @param permissionCodes Array of permission codes to check
- * @returns Whether the user has all the permissions
- */
- const hasAllPermissions = useCallback((permissionCodes: string[]): boolean => {
- // If no permissions to check, return true
- if (!permissionCodes.length) {
- return true;
- }
-
- // If admin, return true
- if (user && user.is_admin) {
- return true;
- }
-
- return permissionCodes.every(code => hasPermission(code));
- }, [user, hasPermission]);
-
- return {
- hasPermission,
- hasPageAccess,
- hasAnyPermission,
- hasAllPermissions,
- isAdmin: user?.is_admin || false
- };
-}
\ No newline at end of file
diff --git a/inventory/src/pages/Login.tsx b/inventory/src/pages/Login.tsx
index be2da22..665037f 100644
--- a/inventory/src/pages/Login.tsx
+++ b/inventory/src/pages/Login.tsx
@@ -1,13 +1,12 @@
-import { useState } from "react";
+import { useState, useContext } from "react";
import { useNavigate, useSearchParams } from "react-router-dom";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { toast } from "sonner";
-import config from "../config";
import { Loader2, Box } from "lucide-react";
-import { motion } from "motion/react";
-
+import { motion } from "framer-motion";
+import { AuthContext } from "@/contexts/AuthContext";
export function Login() {
const [username, setUsername] = useState("");
@@ -15,59 +14,22 @@ export function Login() {
const [isLoading, setIsLoading] = useState(false);
const navigate = useNavigate();
const [searchParams] = useSearchParams();
+ const { login } = useContext(AuthContext);
const handleLogin = async (e: React.FormEvent) => {
e.preventDefault();
setIsLoading(true);
try {
- const url = `${config.authUrl}/login`;
- console.log("Making login request:", {
- url,
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- },
- body: { username, password },
- config,
- });
-
- const response = await fetch(url, {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- },
- body: JSON.stringify({ username, password }),
- credentials: "include",
- });
-
- console.log("Login response status:", response.status);
-
- if (!response.ok) {
- const data = await response
- .json()
- .catch(() => ({ error: "Failed to parse error response" }));
- console.error("Login failed:", data);
- throw new Error(data.error || "Login failed");
- }
-
- const data = await response.json();
- console.log("Login successful:", data);
-
- localStorage.setItem("token", data.token);
- sessionStorage.setItem("isLoggedIn", "true");
- toast.success("Successfully logged in");
-
- // Get the redirect URL from the URL parameters, defaulting to "/"
- const redirectTo = searchParams.get("redirect") || "/"
+ await login(username, password);
- // Navigate to the redirect URL after successful login
- navigate(redirectTo)
+ // Login successful, redirect to the requested page or home
+ const redirectTo = searchParams.get("redirect") || "/";
+ navigate(redirectTo);
} catch (error) {
+ const message = error instanceof Error ? error.message : "Login failed";
+ toast.error(message);
console.error("Login error:", error);
- toast.error(
- error instanceof Error ? error.message : "An unexpected error occurred"
- );
} finally {
setIsLoading(false);
}
diff --git a/inventory/src/pages/Settings.tsx b/inventory/src/pages/Settings.tsx
index 3573075..48cabac 100644
--- a/inventory/src/pages/Settings.tsx
+++ b/inventory/src/pages/Settings.tsx
@@ -5,25 +5,11 @@ import { PerformanceMetrics } from "@/components/settings/PerformanceMetrics";
import { CalculationSettings } from "@/components/settings/CalculationSettings";
import { TemplateManagement } from "@/components/settings/TemplateManagement";
import { UserManagement } from "@/components/settings/UserManagement";
-import { motion } from 'motion/react';
-import { PermissionGuard } from "@/components/common/PermissionGuard";
+import { motion } from 'framer-motion';
import { Alert, AlertDescription } from "@/components/ui/alert";
-import { usePagePermission } from "@/hooks/usePagePermission";
-import { useEffect } from "react";
+import { Protected } from "@/components/auth/Protected";
export function Settings() {
- // Check if the user has permission to access the Settings page
- // This will automatically redirect if permission is denied
- const hasAccess = usePagePermission({
- page: 'settings',
- redirectTo: '/'
- });
-
- // Prevent flash of content before redirect
- if (hasAccess === false) {
- return null;
- }
-
return (
@@ -37,20 +23,22 @@ export function Settings() {
Performance Metrics
-
- Calculation Settings
-
+
+
+ Calculation Settings
+
+
Template Management
-
User Management
-
+
@@ -66,7 +54,18 @@ export function Settings() {
-
+
+
+ You don't have permission to access Calculation Settings.
+
+
+ }
+ >
+
+
@@ -74,8 +73,8 @@ export function Settings() {
-
@@ -85,7 +84,7 @@ export function Settings() {
}
>
-
+