From 2cfce213239ac8074b2b32feeb31d41d535fdec8 Mon Sep 17 00:00:00 2001 From: Yashwin Date: Fri, 8 May 2026 14:34:24 +0530 Subject: [PATCH] refactor: standardize RolesTable component formatting and improve code readability --- src/components/shared/EditRoleModal.tsx | 288 ++++-- src/components/shared/NewRoleModal.tsx | 221 ++++- src/components/superadmin/RolesTable.tsx | 911 +++++++++--------- .../dashboard/components/QuickActions.tsx | 15 +- src/pages/tenant/LandingPage.tsx | 8 +- src/types/dashboard.ts | 1 + 6 files changed, 893 insertions(+), 551 deletions(-) diff --git a/src/components/shared/EditRoleModal.tsx b/src/components/shared/EditRoleModal.tsx index 479773c..4f77abd 100644 --- a/src/components/shared/EditRoleModal.tsx +++ b/src/components/shared/EditRoleModal.tsx @@ -3,7 +3,7 @@ import type { ReactElement } from "react"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { z } from "zod"; -import { Loader2, ChevronDown, ChevronRight } from "lucide-react"; +import { Loader2 } from "lucide-react"; import { Modal, FormField, @@ -116,9 +116,9 @@ export const EditRoleModal = ({ const [selectedPermissions, setSelectedPermissions] = useState< Array<{ resource: string; action: string }> >([]); - const [expandedResources, setExpandedResources] = useState>( - new Set(), - ); + // const [expandedResources, setExpandedResources] = useState>( + // new Set(), + // ); const { register, @@ -185,38 +185,38 @@ export const EditRoleModal = ({ return resourceMap; }, [permissions]); - // Check if a resource has any selected actions - const hasSelectedActions = ( - resource: string, - actions: Set, - ): boolean => { - return Array.from(actions).some((action) => { - return selectedPermissions.some((p) => { - // Check for exact match - if (p.resource === resource && p.action === action) return true; - // Check for wildcard resource with exact action - if (p.resource === "*" && p.action === action) return true; - // Check for exact resource with wildcard action - if (p.resource === resource && p.action === "*") return true; - // Check for wildcard resource with wildcard action - if (p.resource === "*" && p.action === "*") return true; - return false; - }); - }); - }; + // // Check if a resource has any selected actions + // const hasSelectedActions = ( + // resource: string, + // actions: Set, + // ): boolean => { + // return Array.from(actions).some((action) => { + // return selectedPermissions.some((p) => { + // // Check for exact match + // if (p.resource === resource && p.action === action) return true; + // // Check for wildcard resource with exact action + // if (p.resource === "*" && p.action === action) return true; + // // Check for exact resource with wildcard action + // if (p.resource === resource && p.action === "*") return true; + // // Check for wildcard resource with wildcard action + // if (p.resource === "*" && p.action === "*") return true; + // return false; + // }); + // }); + // }; - // Toggle resource expansion - const toggleResource = (resource: string) => { - setExpandedResources((prev) => { - const newSet = new Set(prev); - if (newSet.has(resource)) { - newSet.delete(resource); - } else { - newSet.add(resource); - } - return newSet; - }); - }; + // // Toggle resource expansion + // const toggleResource = (resource: string) => { + // setExpandedResources((prev) => { + // const newSet = new Set(prev); + // if (newSet.has(resource)) { + // newSet.delete(resource); + // } else { + // newSet.add(resource); + // } + // return newSet; + // }); + // }; // Handle permission checkbox change const handlePermissionChange = ( @@ -252,37 +252,37 @@ export const EditRoleModal = ({ }, [selectedPermissions, setValue]); // Expand resources that have selected permissions when role is loaded - useEffect(() => { - if ( - selectedPermissions.length > 0 && - availableResourcesAndActions.size > 0 - ) { - const resourcesWithPermissions = new Set(); - selectedPermissions.forEach((perm) => { - if (perm.resource === "*") { - // If wildcard resource, expand all available resources - availableResourcesAndActions.forEach((_, resource) => { - resourcesWithPermissions.add(resource); - }); - } else if (availableResourcesAndActions.has(perm.resource)) { - // Only expand if resource exists in available resources - resourcesWithPermissions.add(perm.resource); - } - }); - // Only update if we have resources to expand and they're not already expanded - if (resourcesWithPermissions.size > 0) { - setExpandedResources((prev) => { - const newSet = new Set(prev); - resourcesWithPermissions.forEach((resource) => { - if (!newSet.has(resource)) { - newSet.add(resource); - } - }); - return newSet; - }); - } - } - }, [selectedPermissions, availableResourcesAndActions]); + // useEffect(() => { + // if ( + // selectedPermissions.length > 0 && + // availableResourcesAndActions.size > 0 + // ) { + // const resourcesWithPermissions = new Set(); + // selectedPermissions.forEach((perm) => { + // if (perm.resource === "*") { + // // If wildcard resource, expand all available resources + // availableResourcesAndActions.forEach((_, resource) => { + // resourcesWithPermissions.add(resource); + // }); + // } else if (availableResourcesAndActions.has(perm.resource)) { + // // Only expand if resource exists in available resources + // resourcesWithPermissions.add(perm.resource); + // } + // }); + // // Only update if we have resources to expand and they're not already expanded + // if (resourcesWithPermissions.size > 0) { + // setExpandedResources((prev) => { + // const newSet = new Set(prev); + // resourcesWithPermissions.forEach((resource) => { + // if (!newSet.has(resource)) { + // newSet.add(resource); + // } + // }); + // return newSet; + // }); + // } + // } + // }, [selectedPermissions, availableResourcesAndActions]); // Load role data when modal opens - only load once per roleId useEffect(() => { @@ -505,6 +505,160 @@ export const EditRoleModal = ({ {/* Permissions Section */}
+
+ {/* Header Section */} +
+
+

+ Permissions +

+

+ Select allowed actions for this role by module. +

+
+
+ + {errors.permissions && ( +

+ {errors.permissions.message} +

+ )} + + {/* Table */} +
+ + {/* Table Header */} + + + + + {["View", "Create", "Edit", "Delete"].map((action) => ( + + ))} + + + + {/* Table Body */} + + {Array.from(availableResourcesAndActions.entries()).map( + ([resource, actions]) => ( + + {/* Resource Name */} + + + {/* Action Columns */} + {[ + "read", + "create", + "update", + "delete", + // "approve", + // "admin", + ].map((action) => { + const isChecked = selectedPermissions.some((p) => { + if ( + p.resource === resource && + p.action === action + ) + return true; + + if (p.resource === "*" && p.action === action) + return true; + + if (p.resource === resource && p.action === "*") + return true; + + if (p.resource === "*" && p.action === "*") + return true; + + return false; + }); + + const isAvailable = actions.has(action); + + return ( + + ); + })} + + ), + )} + +
+ Platform Services + +
+ {action} +
+
+ {resource.replace(/_/g, " ")} + +
+ + handlePermissionChange( + resource, + action, + e.target.checked, + ) + } + className=" + w-4 h-4 rounded + border-[#D1D5DB] + text-[#0F3CC9] + focus:ring-[#0F3CC9] + disabled:opacity-30 + disabled:cursor-not-allowed + " + /> +
+
+
+
+
+ {/*
@@ -631,7 +785,7 @@ export const EditRoleModal = ({
)} - + */} )} diff --git a/src/components/shared/NewRoleModal.tsx b/src/components/shared/NewRoleModal.tsx index 2779973..56ef862 100644 --- a/src/components/shared/NewRoleModal.tsx +++ b/src/components/shared/NewRoleModal.tsx @@ -3,7 +3,7 @@ import type { ReactElement } from "react"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { z } from "zod"; -import { ChevronDown, ChevronRight } from "lucide-react"; +// import { ChevronDown, ChevronRight } from "lucide-react"; import { Modal, FormField, @@ -108,9 +108,9 @@ export const NewRoleModal = ({ const [selectedPermissions, setSelectedPermissions] = useState< Array<{ resource: string; action: string }> >([]); - const [expandedResources, setExpandedResources] = useState>( - new Set(), - ); + // const [expandedResources, setExpandedResources] = useState>( + // new Set(), + // ); const { register, @@ -193,37 +193,37 @@ export const NewRoleModal = ({ }, [permissions]); // Check if a resource has any selected actions - const hasSelectedActions = ( - resource: string, - actions: Set, - ): boolean => { - return Array.from(actions).some((action) => { - return selectedPermissions.some((p) => { - // Check for exact match - if (p.resource === resource && p.action === action) return true; - // Check for wildcard resource with exact action - if (p.resource === "*" && p.action === action) return true; - // Check for exact resource with wildcard action - if (p.resource === resource && p.action === "*") return true; - // Check for wildcard resource with wildcard action - if (p.resource === "*" && p.action === "*") return true; - return false; - }); - }); - }; + // const hasSelectedActions = ( + // resource: string, + // actions: Set, + // ): boolean => { + // return Array.from(actions).some((action) => { + // return selectedPermissions.some((p) => { + // // Check for exact match + // if (p.resource === resource && p.action === action) return true; + // // Check for wildcard resource with exact action + // if (p.resource === "*" && p.action === action) return true; + // // Check for exact resource with wildcard action + // if (p.resource === resource && p.action === "*") return true; + // // Check for wildcard resource with wildcard action + // if (p.resource === "*" && p.action === "*") return true; + // return false; + // }); + // }); + // }; - // Toggle resource expansion - const toggleResource = (resource: string) => { - setExpandedResources((prev) => { - const newSet = new Set(prev); - if (newSet.has(resource)) { - newSet.delete(resource); - } else { - newSet.add(resource); - } - return newSet; - }); - }; + // // Toggle resource expansion + // const toggleResource = (resource: string) => { + // setExpandedResources((prev) => { + // const newSet = new Set(prev); + // if (newSet.has(resource)) { + // newSet.delete(resource); + // } else { + // newSet.add(resource); + // } + // return newSet; + // }); + // }; // Handle permission checkbox change const handlePermissionChange = ( @@ -397,6 +397,157 @@ export const NewRoleModal = ({ {/* Permissions Section */}
+
+ {/* Header Section */} +
+
+

+ Permissions +

+

+ Select allowed actions for this role by module. +

+
+
+ + {errors.permissions && ( +

+ {errors.permissions.message} +

+ )} + + {/* Table */} +
+ + {/* Table Header */} + + + + + {["View", "Create", "Edit", "Delete"].map((action) => ( + + ))} + + + + {/* Table Body */} + + {Array.from(availableResourcesAndActions.entries()).map( + ([resource, actions]) => ( + + {/* Resource Name */} + + + {/* Action Columns */} + {[ + "read", + "create", + "update", + "delete", + // "approve", + // "admin", + ].map((action) => { + const isChecked = selectedPermissions.some((p) => { + if (p.resource === resource && p.action === action) + return true; + + if (p.resource === "*" && p.action === action) + return true; + + if (p.resource === resource && p.action === "*") + return true; + + if (p.resource === "*" && p.action === "*") + return true; + + return false; + }); + + const isAvailable = actions.has(action); + + return ( + + ); + })} + + ), + )} + +
+ Platform Services + +
+ {action} +
+
+ {resource.replace(/_/g, " ")} + +
+ + handlePermissionChange( + resource, + action, + e.target.checked, + ) + } + className=" + w-4 h-4 rounded + border-[#D1D5DB] + text-[#0F3CC9] + focus:ring-[#0F3CC9] + disabled:opacity-30 + disabled:cursor-not-allowed + " + /> +
+
+
+
+
+ {/*
@@ -511,7 +662,7 @@ export const NewRoleModal = ({
)} - + */} ); diff --git a/src/components/superadmin/RolesTable.tsx b/src/components/superadmin/RolesTable.tsx index 13247c7..110304d 100644 --- a/src/components/superadmin/RolesTable.tsx +++ b/src/components/superadmin/RolesTable.tsx @@ -1,4 +1,10 @@ -import { useState, useEffect, type ReactElement, forwardRef, useImperativeHandle } from "react"; +import { + useState, + useEffect, + type ReactElement, + forwardRef, + useImperativeHandle, +} from "react"; import { PrimaryButton, StatusBadge, @@ -47,233 +53,275 @@ interface RolesTableProps { compact?: boolean; // Compact mode for tabs (default: false) } -export const RolesTable = forwardRef(({ - tenantId, - showHeader = true, - compact = false, -}, ref): ReactElement => { - // const { primaryColor } = useAppTheme(); - const { canCreate, canUpdate, canDelete } = usePermissions(); - const [roles, setRoles] = useState([]); - const [isLoading, setIsLoading] = useState(true); - const [error, setError] = useState(null); - const [isModalOpen, setIsModalOpen] = useState(false); - const [isCreating, setIsCreating] = useState(false); +export const RolesTable = forwardRef( + ({ tenantId, showHeader = true, compact = false }, ref): ReactElement => { + // const { primaryColor } = useAppTheme(); + const { canCreate, canUpdate, canDelete } = usePermissions(); + const [roles, setRoles] = useState([]); + const [isLoading, setIsLoading] = useState(true); + const [error, setError] = useState(null); + const [isModalOpen, setIsModalOpen] = useState(false); + const [isCreating, setIsCreating] = useState(false); - // Expose imperative methods - useImperativeHandle(ref, () => ({ - openNewModal: () => setIsModalOpen(true), - refresh: () => fetchRoles(currentPage, limit, orderBy, debouncedSearch), - })); + // Expose imperative methods + useImperativeHandle(ref, () => ({ + openNewModal: () => setIsModalOpen(true), + refresh: () => fetchRoles(currentPage, limit, orderBy, debouncedSearch), + })); - // Pagination state - const [currentPage, setCurrentPage] = useState(1); - const [limit, setLimit] = useState(5); - const [pagination, setPagination] = useState<{ - page: number; - limit: number; - total: number; - totalPages: number; - hasMore: boolean; - }>({ - page: 1, - limit: 5, - total: 0, - totalPages: 1, - hasMore: false, - }); + // Pagination state + const [currentPage, setCurrentPage] = useState(1); + const [limit, setLimit] = useState(5); + const [pagination, setPagination] = useState<{ + page: number; + limit: number; + total: number; + totalPages: number; + hasMore: boolean; + }>({ + page: 1, + limit: 5, + total: 0, + totalPages: 1, + hasMore: false, + }); - // Filter state - // const [scopeFilter, setScopeFilter] = useState(null); - const [orderBy, setOrderBy] = useState(null); + // Filter state + // const [scopeFilter, setScopeFilter] = useState(null); + const [orderBy, setOrderBy] = useState(null); - // Search state - const [search, setSearch] = useState(""); - const [debouncedSearch, setDebouncedSearch] = useState(""); + // Search state + const [search, setSearch] = useState(""); + const [debouncedSearch, setDebouncedSearch] = useState(""); - // View, Edit, Delete modals - const [viewModalOpen, setViewModalOpen] = useState(false); - const [editModalOpen, setEditModalOpen] = useState(false); - const [deleteModalOpen, setDeleteModalOpen] = useState(false); - const [selectedRoleId, setSelectedRoleId] = useState(null); - const [selectedRoleName, setSelectedRoleName] = useState(""); - const [isUpdating, setIsUpdating] = useState(false); - const [isDeleting, setIsDeleting] = useState(false); + // View, Edit, Delete modals + const [viewModalOpen, setViewModalOpen] = useState(false); + const [editModalOpen, setEditModalOpen] = useState(false); + const [deleteModalOpen, setDeleteModalOpen] = useState(false); + const [selectedRoleId, setSelectedRoleId] = useState(null); + const [selectedRoleName, setSelectedRoleName] = useState(""); + const [isUpdating, setIsUpdating] = useState(false); + const [isDeleting, setIsDeleting] = useState(false); - const fetchRoles = async ( - page: number, - itemsPerPage: number, - // scope: string | null = null, - sortBy: string[] | null = null, - searchQuery: string | null = null, - ): Promise => { - try { - setIsLoading(true); - setError(null); - const response = tenantId - ? await roleService.getByTenant(tenantId, page, itemsPerPage, sortBy, searchQuery) - : await roleService.getAll(page, itemsPerPage, sortBy, searchQuery); - if (response.success) { - setRoles(response.data); - setPagination(response.pagination); - } else { - setError("Failed to load roles"); + const fetchRoles = async ( + page: number, + itemsPerPage: number, + // scope: string | null = null, + sortBy: string[] | null = null, + searchQuery: string | null = null, + ): Promise => { + try { + setIsLoading(true); + setError(null); + const response = tenantId + ? await roleService.getByTenant( + tenantId, + page, + itemsPerPage, + sortBy, + searchQuery, + ) + : await roleService.getAll(page, itemsPerPage, sortBy, searchQuery); + if (response.success) { + setRoles(response.data); + setPagination(response.pagination); + } else { + setError("Failed to load roles"); + } + } catch (err: any) { + setError(err?.response?.data?.error?.message || "Failed to load roles"); + } finally { + setIsLoading(false); } - } catch (err: any) { - setError(err?.response?.data?.error?.message || "Failed to load roles"); - } finally { - setIsLoading(false); - } - }; + }; - // Handle search debouncing - useEffect(() => { - const timer = setTimeout(() => { - setDebouncedSearch(search); - if (search) setCurrentPage(1); - }, 500); - return () => clearTimeout(timer); - }, [search]); + // Handle search debouncing + useEffect(() => { + const timer = setTimeout(() => { + setDebouncedSearch(search); + if (search) setCurrentPage(1); + }, 500); + return () => clearTimeout(timer); + }, [search]); - useEffect(() => { - fetchRoles(currentPage, limit, orderBy, debouncedSearch); - }, [currentPage, limit, orderBy, debouncedSearch, tenantId]); + useEffect(() => { + fetchRoles(currentPage, limit, orderBy, debouncedSearch); + }, [currentPage, limit, orderBy, debouncedSearch, tenantId]); - const handleCreateRole = async (data: CreateRoleRequest): Promise => { - try { - setIsCreating(true); - const response = await roleService.create(data); - const message = response.message || `Role created successfully`; - const description = response.message ? undefined : `${data.name} has been added`; - showToast.success(message, description); - setIsModalOpen(false); - await fetchRoles(currentPage, limit, orderBy); - } catch (err: any) { - throw err; - } finally { - setIsCreating(false); - } - }; + const handleCreateRole = async (data: CreateRoleRequest): Promise => { + try { + setIsCreating(true); + const response = await roleService.create(data); + const message = response.message || `Role created successfully`; + const description = response.message + ? undefined + : `${data.name} has been added`; + showToast.success(message, description); + setIsModalOpen(false); + await fetchRoles(currentPage, limit, orderBy); + } catch (err: any) { + throw err; + } finally { + setIsCreating(false); + } + }; - // View role handler - const handleViewRole = (roleId: string): void => { - setSelectedRoleId(roleId); - setViewModalOpen(true); - }; + // View role handler + const handleViewRole = (roleId: string): void => { + setSelectedRoleId(roleId); + setViewModalOpen(true); + }; - // Edit role handler - const handleEditRole = (roleId: string, roleName: string): void => { - setSelectedRoleId(roleId); - setSelectedRoleName(roleName); - setEditModalOpen(true); - }; + // Edit role handler + const handleEditRole = (roleId: string, roleName: string): void => { + setSelectedRoleId(roleId); + setSelectedRoleName(roleName); + setEditModalOpen(true); + }; - // Update role handler - const handleUpdateRole = async (id: string, data: UpdateRoleRequest): Promise => { - try { - setIsUpdating(true); - const response = await roleService.update(id, data); - const message = response.message || `Role updated successfully`; - const description = response.message ? undefined : `${data.name} has been updated`; - showToast.success(message, description); - setEditModalOpen(false); - setSelectedRoleId(null); - setSelectedRoleName(''); - await fetchRoles(currentPage, limit, orderBy); - } catch (err: any) { - throw err; - } finally { - setIsUpdating(false); - } - }; + // Update role handler + const handleUpdateRole = async ( + id: string, + data: UpdateRoleRequest, + ): Promise => { + try { + setIsUpdating(true); + const response = await roleService.update(id, data); + const message = response.message || `Role updated successfully`; + const description = response.message + ? undefined + : `${data.name} has been updated`; + showToast.success(message, description); + setEditModalOpen(false); + setSelectedRoleId(null); + setSelectedRoleName(""); + await fetchRoles(currentPage, limit, orderBy); + } catch (err: any) { + throw err; + } finally { + setIsUpdating(false); + } + }; - // Delete role handler - const handleDeleteRole = (roleId: string, roleName: string): void => { - setSelectedRoleId(roleId); - setSelectedRoleName(roleName); - setDeleteModalOpen(true); - }; + // Delete role handler + const handleDeleteRole = (roleId: string, roleName: string): void => { + setSelectedRoleId(roleId); + setSelectedRoleName(roleName); + setDeleteModalOpen(true); + }; - // Confirm delete handler - const handleConfirmDelete = async (): Promise => { - if (!selectedRoleId) return; + // Confirm delete handler + const handleConfirmDelete = async (): Promise => { + if (!selectedRoleId) return; - try { - setIsDeleting(true); - await roleService.delete(selectedRoleId); - setDeleteModalOpen(false); - setSelectedRoleId(null); - setSelectedRoleName(''); - await fetchRoles(currentPage, limit, orderBy); - } catch (err: any) { - throw err; // Let the modal handle the error display - } finally { - setIsDeleting(false); - } - }; + try { + setIsDeleting(true); + await roleService.delete(selectedRoleId); + setDeleteModalOpen(false); + setSelectedRoleId(null); + setSelectedRoleName(""); + await fetchRoles(currentPage, limit, orderBy); + } catch (err: any) { + throw err; // Let the modal handle the error display + } finally { + setIsDeleting(false); + } + }; - // Load role for view/edit - const loadRole = async (id: string): Promise => { - const response = await roleService.getById(id); - return response.data; - }; + // Load role for view/edit + const loadRole = async (id: string): Promise => { + const response = await roleService.getById(id); + return response.data; + }; - // Table columns - const columns: Column[] = [ - { - key: "name", - label: "Name", - render: (role) => ( - {role.name} - ), - }, - { - key: "code", - label: "Code", - render: (role) => , - }, - { - key: "scope", - label: "Scope", - render: (role) => ( - - {role.scope} - - ), - }, - { - key: "user_count", - label: "Users", - render: (role) => ( - - {role.user_count || 0} - - ), - }, - { - key: "description", - label: "Description", - render: (role) => ( - - {role.description || "N/A"} - - ), - }, - { - key: "created_at", - label: "Created Date", - render: (role) => ( - - {formatDate(role.created_at)} - - ), - }, - { - key: "actions", - label: "Actions", - align: "right", - render: (role) => ( -
+ // Table columns + const columns: Column[] = [ + { + key: "name", + label: "Name", + render: (role) => ( + + {role.name} + + ), + }, + { + key: "code", + label: "Code", + render: (role) => , + }, + { + key: "description", + label: "Description", + render: (role) => ( + + {role.description || "N/A"} + + ), + }, + { + key: "scope", + label: "Scope", + render: (role) => ( + + {role.scope} + + ), + }, + { + key: "user_count", + label: "Users", + render: (role) => ( + + {role.user_count || 0} + + ), + }, + { + key: "created_at", + label: "Created Date", + render: (role) => ( + + {formatDate(role.created_at)} + + ), + }, + { + key: "actions", + label: "Actions", + align: "right", + render: (role) => ( +
+ handleViewRole(role.id)} + onEdit={ + canUpdate("roles") + ? () => handleEditRole(role.id, role.name) + : undefined + } + onDelete={ + canDelete("roles") + ? () => handleDeleteRole(role.id, role.name) + : undefined + } + /> +
+ ), + }, + ]; + + // Mobile card renderer + const mobileCardRenderer = (role: Role) => ( +
+
+
+

+ {role.name} +

+

+ {role.code} +

+
handleViewRole(role.id)} onEdit={ @@ -288,90 +336,214 @@ export const RolesTable = forwardRef(({ } />
- ), - }, - ]; - - // Mobile card renderer - const mobileCardRenderer = (role: Role) => ( -
-
-
-

- {role.name} -

-

{role.code}

-
- handleViewRole(role.id)} - onEdit={ - canUpdate("roles") - ? () => handleEditRole(role.id, role.name) - : undefined - } - onDelete={ - canDelete("roles") - ? () => handleDeleteRole(role.id, role.name) - : undefined - } - /> -
-
-
- Scope: -
- - {role.scope} - -
-
-
- Users: -

- {role.user_count || 0} -

-
-
- Created: -

- {formatDate(role.created_at)} -

-
- {role.description && ( -
- Description: -

{role.description}

-
- )} -
-
- ); - - if (compact) { - // Compact mode for tabs - return ( - <> -
-
-

-
- - {canCreate("roles") && ( - setIsModalOpen(true)} - > - - New Role - - )} +
+
+ Scope: +
+ + {role.scope} +
+
+ Users: +

+ {role.user_count || 0} +

+
+
+ Created: +

+ {formatDate(role.created_at)} +

+
+ {role.description && ( +
+ Description: +

+ {role.description} +

+
+ )} +
+
+ ); + + if (compact) { + // Compact mode for tabs + return ( + <> +
+
+

+
+ + {canCreate("roles") && ( + setIsModalOpen(true)} + > + + New Role + + )} +
+
+ role.id} + mobileCardRenderer={mobileCardRenderer} + emptyMessage="No roles found" + isLoading={isLoading} + error={error} + /> + {pagination.totalPages > 0 && ( + { + setCurrentPage(page); + }} + onLimitChange={(newLimit: number) => { + setLimit(newLimit); + setCurrentPage(1); + }} + /> + )} +
+ + {/* Modals */} + setIsModalOpen(false)} + onSubmit={handleCreateRole} + isLoading={isCreating} + defaultTenantId={tenantId || undefined} + /> + + { + setViewModalOpen(false); + setSelectedRoleId(null); + }} + roleId={selectedRoleId} + onLoadRole={loadRole} + /> + + { + setEditModalOpen(false); + setSelectedRoleId(null); + setSelectedRoleName(""); + }} + roleId={selectedRoleId} + onLoadRole={loadRole} + onSubmit={handleUpdateRole} + isLoading={isUpdating} + defaultTenantId={tenantId || undefined} + /> + + { + setDeleteModalOpen(false); + setSelectedRoleId(null); + setSelectedRoleName(""); + }} + onConfirm={handleConfirmDelete} + title="Delete Role" + message={`Are you sure you want to delete this role`} + itemName={selectedRoleName} + isLoading={isDeleting} + /> + + ); + } + + // Full mode with header + return ( + <> + {/* Table Container */} +
+ {/* Table Header with Filters */} + {showHeader && ( +
+ {/* Filters */} +
+ {/* Global Search */} + + + {/* Sort Filter */} + { + setOrderBy(value as string[] | null); + setCurrentPage(1); + }} + placeholder="Default" + showIcon + icon={} + /> +
+ + {/* Actions */} +
+ {/* Export Button */} + {/* */} + + {/* New Role Button */} + {canCreate("roles") && ( + setIsModalOpen(true)} + > + + New Role + + )} +
+
+ )} + + {/* Data Table */} (({ isLoading={isLoading} error={error} /> - {pagination.totalPages > 0 && ( + + {/* Table Footer with Pagination */} + {pagination.total > 0 && ( (({ }} onLimitChange={(newLimit: number) => { setLimit(newLimit); - setCurrentPage(1); + setCurrentPage(1); // Reset to first page when limit changes }} /> )} @@ -446,150 +620,5 @@ export const RolesTable = forwardRef(({ /> ); - } - - // Full mode with header - return ( - <> - {/* Table Container */} -
- {/* Table Header with Filters */} - {showHeader && ( -
- {/* Filters */} -
- {/* Global Search */} - - - {/* Sort Filter */} - { - setOrderBy(value as string[] | null); - setCurrentPage(1); - }} - placeholder="Default" - showIcon - icon={} - /> -
- - {/* Actions */} -
- {/* Export Button */} - {/* */} - - {/* New Role Button */} - {canCreate("roles") && ( - setIsModalOpen(true)} - > - - New Role - - )} -
-
- )} - - {/* Data Table */} - role.id} - mobileCardRenderer={mobileCardRenderer} - emptyMessage="No roles found" - isLoading={isLoading} - error={error} - /> - - {/* Table Footer with Pagination */} - {pagination.total > 0 && ( - { - setCurrentPage(page); - }} - onLimitChange={(newLimit: number) => { - setLimit(newLimit); - setCurrentPage(1); // Reset to first page when limit changes - }} - /> - )} -
- - {/* Modals */} - setIsModalOpen(false)} - onSubmit={handleCreateRole} - isLoading={isCreating} - defaultTenantId={tenantId || undefined} - /> - - { - setViewModalOpen(false); - setSelectedRoleId(null); - }} - roleId={selectedRoleId} - onLoadRole={loadRole} - /> - - { - setEditModalOpen(false); - setSelectedRoleId(null); - setSelectedRoleName(''); - }} - roleId={selectedRoleId} - onLoadRole={loadRole} - onSubmit={handleUpdateRole} - isLoading={isUpdating} - defaultTenantId={tenantId || undefined} - /> - - { - setDeleteModalOpen(false); - setSelectedRoleId(null); - setSelectedRoleName(''); - }} - onConfirm={handleConfirmDelete} - title="Delete Role" - message={`Are you sure you want to delete this role`} - itemName={selectedRoleName} - isLoading={isDeleting} - /> - - ); -}); + }, +); diff --git a/src/features/dashboard/components/QuickActions.tsx b/src/features/dashboard/components/QuickActions.tsx index 77ddec5..9ce3be4 100644 --- a/src/features/dashboard/components/QuickActions.tsx +++ b/src/features/dashboard/components/QuickActions.tsx @@ -9,10 +9,10 @@ import { } from "lucide-react"; import { useAppSelector } from "@/hooks/redux-hooks"; import type { QuickAction } from "@/types/dashboard"; -import { useAppTheme } from "@/hooks/useAppTheme"; +// import { useAppTheme } from "@/hooks/useAppTheme"; export const QuickActions = () => { - const { primaryColor } = useAppTheme(); + // const { primaryColor } = useAppTheme(); const navigate = useNavigate(); const { roles, permissions } = useAppSelector((state) => state.auth); @@ -32,17 +32,20 @@ export const QuickActions = () => { { icon: Plus, label: "New Tenant", + btncolor: "#4C89FA", onClick: () => navigate("/tenants/create-wizard"), }, - { icon: UserPlus, label: "Module", onClick: () => navigate("/modules") }, + { icon: UserPlus, label: "Module", btncolor: "#16C784", onClick: () => navigate("/modules") }, { icon: Shield, label: "Notification", + btncolor: "#FCA004", onClick: () => navigate("/notifications"), }, { icon: Settings, label: "Audit Logs", + btncolor: "#6B7280", onClick: () => navigate("/audit-logs"), }, ]; @@ -51,21 +54,25 @@ export const QuickActions = () => { hasPermission("users", "create") && { icon: UserPlus, label: "New User", + btncolor: "#4C89FA", onClick: () => navigate("/tenant/users"), }, hasPermission("roles", "create") && { icon: Shield, label: "New Role", + btncolor: "#16C784", onClick: () => navigate("/tenant/roles"), }, hasPermission("departments", "create") && { icon: Building2, label: "New Dept", + btncolor: "#FCA004", onClick: () => navigate("/tenant/departments"), }, hasPermission("designations", "create") && { icon: BadgeCheck, label: "New Desig", + btncolor: "#6B7280", onClick: () => navigate("/tenant/designations"), }, ].filter(Boolean) as QuickAction[]; @@ -107,7 +114,7 @@ export const QuickActions = () => {
diff --git a/src/pages/tenant/LandingPage.tsx b/src/pages/tenant/LandingPage.tsx index 45f6885..3c2d58a 100644 --- a/src/pages/tenant/LandingPage.tsx +++ b/src/pages/tenant/LandingPage.tsx @@ -306,13 +306,13 @@ const LandingPage = (): ReactElement => {
-
+
{/* Welcome Section */} -
-

+
+

Welcome back, {getUserName()}

-

+

Select a module below to access your workspace. You can switch between modules anytime from the main navigation.

diff --git a/src/types/dashboard.ts b/src/types/dashboard.ts index 720294d..f3efb76 100644 --- a/src/types/dashboard.ts +++ b/src/types/dashboard.ts @@ -22,6 +22,7 @@ export interface QuickAction { icon: LucideIcon; label: string; onClick: () => void; + btncolor: string; } export interface HealthMetric {