refactor: standardize RolesTable component formatting and improve code readability
This commit is contained in:
parent
f4838422c2
commit
2cfce21323
@ -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<Set<string>>(
|
||||
new Set(),
|
||||
);
|
||||
// const [expandedResources, setExpandedResources] = useState<Set<string>>(
|
||||
// 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<string>,
|
||||
): 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<string>,
|
||||
// ): 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<string>();
|
||||
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<string>();
|
||||
// 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 */}
|
||||
<div className="pb-4">
|
||||
<div
|
||||
className="
|
||||
flex flex-col items-start gap-3 self-stretch
|
||||
p-4 rounded-[8px]
|
||||
border border-[#D1D5DB]
|
||||
bg-white
|
||||
"
|
||||
>
|
||||
{/* Header Section */}
|
||||
<div className="flex flex-col items-start self-stretch">
|
||||
<div className="flex flex-col items-start self-stretch">
|
||||
<h3 className="text-[13px] font-semibold text-[#111827]">
|
||||
Permissions
|
||||
</h3>
|
||||
<p className="text-[11px] text-[#6B7280]">
|
||||
Select allowed actions for this role by module.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{errors.permissions && (
|
||||
<p className="text-sm text-[#ef4444]">
|
||||
{errors.permissions.message}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{/* Table */}
|
||||
<div className="w-full overflow-x-auto border border-[#E5E7EB] rounded-[8px]">
|
||||
<table className="w-full border-collapse">
|
||||
{/* Table Header */}
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
className="
|
||||
w-[204px] h-[53px]
|
||||
px-3 py-[19px]
|
||||
text-left text-[11px]
|
||||
font-medium uppercase
|
||||
text-[#6B7280]
|
||||
border-b border-[#D1D5DB]
|
||||
bg-[#F9F9F9]
|
||||
"
|
||||
>
|
||||
Platform Services
|
||||
</th>
|
||||
|
||||
{["View", "Create", "Edit", "Delete"].map((action) => (
|
||||
<th
|
||||
key={action}
|
||||
className="w-[120px] px-3 py-[10px] text-center text-[11px] font-medium text-[#6B7280] border-b border-[#D1D5DB] bg-[#F9F9F9]"
|
||||
>
|
||||
<div className="flex flex-col items-center gap-1">
|
||||
<span>{action}</span>
|
||||
</div>
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
{/* Table Body */}
|
||||
<tbody>
|
||||
{Array.from(availableResourcesAndActions.entries()).map(
|
||||
([resource, actions]) => (
|
||||
<tr key={resource}>
|
||||
{/* Resource Name */}
|
||||
<td
|
||||
className="
|
||||
w-[204px]
|
||||
h-[53px]
|
||||
px-3 py-[19px]
|
||||
border-b border-[#E5E7EB]
|
||||
text-[14px]
|
||||
text-[#111827]
|
||||
bg-white
|
||||
"
|
||||
>
|
||||
{resource.replace(/_/g, " ")}
|
||||
</td>
|
||||
|
||||
{/* 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 (
|
||||
<td
|
||||
key={`${resource}-${action}`}
|
||||
className="
|
||||
w-[120px]
|
||||
px-3 py-[10px]
|
||||
text-center
|
||||
border-b border-[#E5E7EB]
|
||||
bg-white
|
||||
"
|
||||
>
|
||||
<div className="flex justify-center items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
disabled={!isAvailable}
|
||||
checked={isAvailable ? isChecked : false}
|
||||
onChange={(e) =>
|
||||
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
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
);
|
||||
})}
|
||||
</tr>
|
||||
),
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* <div className="pb-4">
|
||||
<label className="flex items-center gap-1.5 text-[13px] font-medium text-[#0e1b2a] mb-3">
|
||||
<span>Permissions</span>
|
||||
</label>
|
||||
@ -631,7 +785,7 @@ export const EditRoleModal = ({
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div> */}
|
||||
</form>
|
||||
)}
|
||||
</Modal>
|
||||
|
||||
@ -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<Set<string>>(
|
||||
new Set(),
|
||||
);
|
||||
// const [expandedResources, setExpandedResources] = useState<Set<string>>(
|
||||
// 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<string>,
|
||||
): 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<string>,
|
||||
// ): 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 */}
|
||||
<div className="pb-4">
|
||||
<div
|
||||
className="
|
||||
flex flex-col items-start gap-3 self-stretch
|
||||
p-4 rounded-[8px]
|
||||
border border-[#D1D5DB]
|
||||
bg-white
|
||||
"
|
||||
>
|
||||
{/* Header Section */}
|
||||
<div className="flex flex-col items-start self-stretch">
|
||||
<div className="flex flex-col items-start self-stretch">
|
||||
<h3 className="text-[13px] font-semibold text-[#111827]">
|
||||
Permissions
|
||||
</h3>
|
||||
<p className="text-[11px] text-[#6B7280]">
|
||||
Select allowed actions for this role by module.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{errors.permissions && (
|
||||
<p className="text-sm text-[#ef4444]">
|
||||
{errors.permissions.message}
|
||||
</p>
|
||||
)}
|
||||
|
||||
{/* Table */}
|
||||
<div className="w-full overflow-x-auto border border-[#E5E7EB] rounded-[8px]">
|
||||
<table className="w-full border-collapse">
|
||||
{/* Table Header */}
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
className="
|
||||
w-[204px] h-[53px]
|
||||
px-3 py-[10px]
|
||||
text-left text-[11px]
|
||||
font-medium uppercase
|
||||
text-[#6B7280]
|
||||
border-b border-[#D1D5DB]
|
||||
bg-[#F9F9F9]
|
||||
"
|
||||
>
|
||||
Platform Services
|
||||
</th>
|
||||
|
||||
{["View", "Create", "Edit", "Delete"].map((action) => (
|
||||
<th
|
||||
key={action}
|
||||
className="w-[120px] px-3 py-[10px] text-center text-[11px] font-medium text-[#6B7280] border-b border-[#D1D5DB] bg-[#F9F9F9]"
|
||||
>
|
||||
<div className="flex flex-col items-center gap-1">
|
||||
<span>{action}</span>
|
||||
</div>
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
{/* Table Body */}
|
||||
<tbody>
|
||||
{Array.from(availableResourcesAndActions.entries()).map(
|
||||
([resource, actions]) => (
|
||||
<tr key={resource}>
|
||||
{/* Resource Name */}
|
||||
<td
|
||||
className="
|
||||
w-[204px]
|
||||
h-[53px]
|
||||
px-3 py-[19px]
|
||||
border-b border-[#E5E7EB]
|
||||
text-[14px]
|
||||
text-[#111827]
|
||||
bg-white
|
||||
"
|
||||
>
|
||||
{resource.replace(/_/g, " ")}
|
||||
</td>
|
||||
|
||||
{/* 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 (
|
||||
<td
|
||||
key={`${resource}-${action}`}
|
||||
className="
|
||||
w-[120px]
|
||||
px-3 py-[10px]
|
||||
text-center
|
||||
border-b border-[#E5E7EB]
|
||||
bg-white
|
||||
"
|
||||
>
|
||||
<div className="flex justify-center items-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
disabled={!isAvailable}
|
||||
checked={isAvailable ? isChecked : false}
|
||||
onChange={(e) =>
|
||||
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
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
);
|
||||
})}
|
||||
</tr>
|
||||
),
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* <div className="pb-4">
|
||||
<label className="flex items-center gap-1.5 text-[13px] font-medium text-[#0e1b2a] mb-3">
|
||||
<span>Permissions</span>
|
||||
</label>
|
||||
@ -511,7 +662,7 @@ export const NewRoleModal = ({
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div> */}
|
||||
</form>
|
||||
</Modal>
|
||||
);
|
||||
|
||||
@ -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,11 +53,8 @@ interface RolesTableProps {
|
||||
compact?: boolean; // Compact mode for tabs (default: false)
|
||||
}
|
||||
|
||||
export const RolesTable = forwardRef<RolesTableRef, RolesTableProps>(({
|
||||
tenantId,
|
||||
showHeader = true,
|
||||
compact = false,
|
||||
}, ref): ReactElement => {
|
||||
export const RolesTable = forwardRef<RolesTableRef, RolesTableProps>(
|
||||
({ tenantId, showHeader = true, compact = false }, ref): ReactElement => {
|
||||
// const { primaryColor } = useAppTheme();
|
||||
const { canCreate, canUpdate, canDelete } = usePermissions();
|
||||
const [roles, setRoles] = useState<Role[]>([]);
|
||||
@ -111,7 +114,13 @@ export const RolesTable = forwardRef<RolesTableRef, RolesTableProps>(({
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
const response = tenantId
|
||||
? await roleService.getByTenant(tenantId, page, itemsPerPage, sortBy, searchQuery)
|
||||
? await roleService.getByTenant(
|
||||
tenantId,
|
||||
page,
|
||||
itemsPerPage,
|
||||
sortBy,
|
||||
searchQuery,
|
||||
)
|
||||
: await roleService.getAll(page, itemsPerPage, sortBy, searchQuery);
|
||||
if (response.success) {
|
||||
setRoles(response.data);
|
||||
@ -144,7 +153,9 @@ export const RolesTable = forwardRef<RolesTableRef, RolesTableProps>(({
|
||||
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`;
|
||||
const description = response.message
|
||||
? undefined
|
||||
: `${data.name} has been added`;
|
||||
showToast.success(message, description);
|
||||
setIsModalOpen(false);
|
||||
await fetchRoles(currentPage, limit, orderBy);
|
||||
@ -169,16 +180,21 @@ export const RolesTable = forwardRef<RolesTableRef, RolesTableProps>(({
|
||||
};
|
||||
|
||||
// Update role handler
|
||||
const handleUpdateRole = async (id: string, data: UpdateRoleRequest): Promise<void> => {
|
||||
const handleUpdateRole = async (
|
||||
id: string,
|
||||
data: UpdateRoleRequest,
|
||||
): Promise<void> => {
|
||||
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`;
|
||||
const description = response.message
|
||||
? undefined
|
||||
: `${data.name} has been updated`;
|
||||
showToast.success(message, description);
|
||||
setEditModalOpen(false);
|
||||
setSelectedRoleId(null);
|
||||
setSelectedRoleName('');
|
||||
setSelectedRoleName("");
|
||||
await fetchRoles(currentPage, limit, orderBy);
|
||||
} catch (err: any) {
|
||||
throw err;
|
||||
@ -203,7 +219,7 @@ export const RolesTable = forwardRef<RolesTableRef, RolesTableProps>(({
|
||||
await roleService.delete(selectedRoleId);
|
||||
setDeleteModalOpen(false);
|
||||
setSelectedRoleId(null);
|
||||
setSelectedRoleName('');
|
||||
setSelectedRoleName("");
|
||||
await fetchRoles(currentPage, limit, orderBy);
|
||||
} catch (err: any) {
|
||||
throw err; // Let the modal handle the error display
|
||||
@ -224,7 +240,9 @@ export const RolesTable = forwardRef<RolesTableRef, RolesTableProps>(({
|
||||
key: "name",
|
||||
label: "Name",
|
||||
render: (role) => (
|
||||
<span className="text-sm font-normal text-[#0f1724]">{role.name}</span>
|
||||
<span className="text-sm font-normal text-[#0f1724]">
|
||||
{role.name}
|
||||
</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
@ -232,6 +250,15 @@ export const RolesTable = forwardRef<RolesTableRef, RolesTableProps>(({
|
||||
label: "Code",
|
||||
render: (role) => <CodeBadge label={role.code} />,
|
||||
},
|
||||
{
|
||||
key: "description",
|
||||
label: "Description",
|
||||
render: (role) => (
|
||||
<span className="text-sm font-normal text-[#6b7280]">
|
||||
{role.description || "N/A"}
|
||||
</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "scope",
|
||||
label: "Scope",
|
||||
@ -250,15 +277,6 @@ export const RolesTable = forwardRef<RolesTableRef, RolesTableProps>(({
|
||||
</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "description",
|
||||
label: "Description",
|
||||
render: (role) => (
|
||||
<span className="text-sm font-normal text-[#6b7280]">
|
||||
{role.description || "N/A"}
|
||||
</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "created_at",
|
||||
label: "Created Date",
|
||||
@ -300,7 +318,9 @@ export const RolesTable = forwardRef<RolesTableRef, RolesTableProps>(({
|
||||
<h3 className="text-sm font-medium text-[#0f1724] truncate">
|
||||
{role.name}
|
||||
</h3>
|
||||
<p className="text-xs text-[#6b7280] mt-0.5 font-mono">{role.code}</p>
|
||||
<p className="text-xs text-[#6b7280] mt-0.5 font-mono">
|
||||
{role.code}
|
||||
</p>
|
||||
</div>
|
||||
<ActionDropdown
|
||||
onView={() => handleViewRole(role.id)}
|
||||
@ -340,7 +360,9 @@ export const RolesTable = forwardRef<RolesTableRef, RolesTableProps>(({
|
||||
{role.description && (
|
||||
<div className="col-span-2">
|
||||
<span className="text-[#9aa6b2]">Description:</span>
|
||||
<p className="text-[#0f1724] font-normal mt-1">{role.description}</p>
|
||||
<p className="text-[#0f1724] font-normal mt-1">
|
||||
{role.description}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@ -474,9 +496,15 @@ export const RolesTable = forwardRef<RolesTableRef, RolesTableProps>(({
|
||||
{ value: ["code", "asc"], label: "Code (A-Z)" },
|
||||
{ value: ["code", "desc"], label: "Code (Z-A)" },
|
||||
{ value: ["created_at", "asc"], label: "Created (Oldest)" },
|
||||
{ value: ["created_at", "desc"], label: "Created (Newest)" },
|
||||
{
|
||||
value: ["created_at", "desc"],
|
||||
label: "Created (Newest)",
|
||||
},
|
||||
{ value: ["updated_at", "asc"], label: "Updated (Oldest)" },
|
||||
{ value: ["updated_at", "desc"], label: "Updated (Newest)" },
|
||||
{
|
||||
value: ["updated_at", "desc"],
|
||||
label: "Updated (Newest)",
|
||||
},
|
||||
]}
|
||||
value={orderBy}
|
||||
onChange={(value) => {
|
||||
@ -568,7 +596,7 @@ export const RolesTable = forwardRef<RolesTableRef, RolesTableProps>(({
|
||||
onClose={() => {
|
||||
setEditModalOpen(false);
|
||||
setSelectedRoleId(null);
|
||||
setSelectedRoleName('');
|
||||
setSelectedRoleName("");
|
||||
}}
|
||||
roleId={selectedRoleId}
|
||||
onLoadRole={loadRole}
|
||||
@ -582,7 +610,7 @@ export const RolesTable = forwardRef<RolesTableRef, RolesTableProps>(({
|
||||
onClose={() => {
|
||||
setDeleteModalOpen(false);
|
||||
setSelectedRoleId(null);
|
||||
setSelectedRoleName('');
|
||||
setSelectedRoleName("");
|
||||
}}
|
||||
onConfirm={handleConfirmDelete}
|
||||
title="Delete Role"
|
||||
@ -592,4 +620,5 @@ export const RolesTable = forwardRef<RolesTableRef, RolesTableProps>(({
|
||||
/>
|
||||
</>
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
@ -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 = () => {
|
||||
<div className="flex items-center justify-center">
|
||||
<Icon
|
||||
className="w-[20px] h-[20px]"
|
||||
color={primaryColor}
|
||||
color={action.btncolor}
|
||||
strokeWidth={2}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -306,13 +306,13 @@ const LandingPage = (): ReactElement => {
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main className="flex-1 flex flex-col items-center px-6 py-16 md:py-24">
|
||||
<main className="flex-1 flex flex-col items-center px-6 py-6 md:py-10">
|
||||
{/* Welcome Section */}
|
||||
<div className="text-center mb-16 md:mb-24 animate-in fade-in slide-in-from-bottom-4 duration-700">
|
||||
<h1 className="text-4xl md:text-5xl font-bold text-[#0f1724] mb-4">
|
||||
<div className="text-center mb-4 md:mb-12 animate-in fade-in slide-in-from-bottom-4 duration-700">
|
||||
<h1 className="text-3xl font-bold text-[#0f1724] mb-4">
|
||||
Welcome back, {getUserName()}
|
||||
</h1>
|
||||
<p className="text-[#64748b] text-lg max-w-2xl mx-auto leading-relaxed">
|
||||
<p className="text-[#64748b] text-md max-w-2xl mx-auto leading-relaxed">
|
||||
Select a module below to access your workspace. You can switch between modules anytime from the main navigation.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@ -22,6 +22,7 @@ export interface QuickAction {
|
||||
icon: LucideIcon;
|
||||
label: string;
|
||||
onClick: () => void;
|
||||
btncolor: string;
|
||||
}
|
||||
|
||||
export interface HealthMetric {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user