diff --git a/distribution_fe/app/(dashboard)/users/roles/[id]/page.tsx b/distribution_fe/app/(dashboard)/users/roles/[id]/page.tsx
new file mode 100644
index 0000000..6e0c424
--- /dev/null
+++ b/distribution_fe/app/(dashboard)/users/roles/[id]/page.tsx
@@ -0,0 +1,117 @@
+'use client'
+
+import { useEffect, useState } from 'react'
+import { useParams, useRouter } from 'next/navigation'
+import { HugeiconsIcon } from '@hugeicons/react'
+import { ArrowLeft01Icon, PencilEdit01Icon } from '@hugeicons/core-free-icons'
+
+import { Badge } from '@/components/ui/badge'
+import { Button } from '@/components/ui/button'
+import { toast } from '@/components/ui/toast'
+import { roles as rolesApi, getApiError } from '@/lib/api/index'
+import { PERMISSION_CATEGORIES } from '@/config/permissions'
+import type { RoleResponse, PermissionResponse } from '@/types/api'
+
+const fmtDate = (v: string) =>
+ new Date(v).toLocaleDateString('en-GB', { day: '2-digit', month: 'short', year: 'numeric' })
+
+// 'orders.view' -> 'View Orders', 'loading-sheet.manage' -> 'Manage Loading Sheet'
+function permissionLabel(code: string): string {
+ const [resource, action] = code.split('.')
+ const title = (s: string) => s.split(/[-_]/).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(' ')
+ return action ? `${title(action)} ${title(resource)}` : title(resource)
+}
+
+function Field({ label, children }: { label: string; children: React.ReactNode }) {
+ return (
+
+ {label}
+ {children}
+
+ )
+}
+
+export default function ViewRolePage() {
+ const { id } = useParams<{ id: string }>()
+ const router = useRouter()
+ const goBack = () => router.push('/users/roles')
+
+ const [role, setRole] = useState(null)
+ const [permissions, setPermissions] = useState([])
+ const [loading, setLoading] = useState(true)
+
+ useEffect(() => {
+ Promise.all([rolesApi.get(id), rolesApi.getPermissions(id)])
+ .then(([r, perms]) => { setRole(r); setPermissions(perms) })
+ .catch((err) => toast.add({ title: 'Failed to load role', description: getApiError(err), type: 'error' }))
+ .finally(() => setLoading(false))
+ }, [id])
+
+ return (
+
+
+
+
+
+
Role Details
+
View role details and permissions.
+
+
+ {role && (
+
+ )}
+
+
+
+ {loading && (
+
+ {Array.from({ length: 4 }).map((_, i) => (
+
+ ))}
+
+ )}
+ {!loading && role && (
+
+
+ {role.name}
+ {role.level}
+
+ {role.isActive ? 'Active' : 'Inactive'}
+
+ {fmtDate(role.createdAt)}
+
+
+
+
Permissions
+ {permissions.length === 0 ? (
+
No permissions assigned to this role.
+ ) : (
+
+ {PERMISSION_CATEGORIES.map((cat) => {
+ const catPerms = permissions.filter((p) => cat.codes.includes(p.code))
+ if (catPerms.length === 0) return null
+ return (
+
+
{cat.label}
+
+ {catPerms.map((p) => (
+ {permissionLabel(p.code)}
+ ))}
+
+
+ )
+ })}
+
+ )}
+
+
+ )}
+
+
+ )
+}
diff --git a/distribution_fe/app/(dashboard)/users/roles/_components/role-view-sheet.tsx b/distribution_fe/app/(dashboard)/users/roles/_components/role-view-sheet.tsx
deleted file mode 100644
index c6405b1..0000000
--- a/distribution_fe/app/(dashboard)/users/roles/_components/role-view-sheet.tsx
+++ /dev/null
@@ -1,77 +0,0 @@
-'use client'
-
-import { useEffect, useState } from 'react'
-import { Badge } from '@/components/ui/badge'
-import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'
-import { toast } from '@/components/ui/toast'
-import { roles as rolesApi, getApiError } from '@/lib/api/index'
-import type { RoleResponse, PermissionResponse } from '@/types/api'
-
-const fmtDate = (v: string) =>
- new Date(v).toLocaleDateString('en-GB', { day: '2-digit', month: 'short', year: 'numeric' })
-
-interface RoleViewSheetProps {
- open: boolean
- onClose: () => void
- role: RoleResponse | null
-}
-
-function Row({ label, value }: { label: string; value: React.ReactNode }) {
- return (
-
- {label}
- {value}
-
- )
-}
-
-export function RoleViewSheet({ open, onClose, role }: RoleViewSheetProps) {
- const [permissions, setPermissions] = useState([])
- const [loading, setLoading] = useState(false)
-
- useEffect(() => {
- if (!open || !role) return
- setLoading(true)
- rolesApi.getPermissions(role.id)
- .then(setPermissions)
- .catch((err) => toast.add({ title: 'Failed to load permissions', description: getApiError(err), type: 'error' }))
- .finally(() => setLoading(false))
- }, [open, role])
-
- if (!role) return null
-
- return (
-
- )
-}
diff --git a/distribution_fe/app/(dashboard)/users/roles/_components/roles-client.tsx b/distribution_fe/app/(dashboard)/users/roles/_components/roles-client.tsx
index f757c5e..d8e4d79 100644
--- a/distribution_fe/app/(dashboard)/users/roles/_components/roles-client.tsx
+++ b/distribution_fe/app/(dashboard)/users/roles/_components/roles-client.tsx
@@ -13,8 +13,6 @@ import { toast } from '@/components/ui/toast'
import { roles as rolesApi, getApiError } from '@/lib/api/index'
import type { RoleResponse } from '@/types/api'
-import { RoleViewSheet } from './role-view-sheet'
-
export function RolesClient() {
const router = useRouter()
const [data, setData] = useState([])
@@ -22,9 +20,6 @@ export function RolesClient() {
const [total, setTotal] = useState(0)
const [pageSize, setPageSize] = useState(20)
- const [viewOpen, setViewOpen] = useState(false)
- const [viewing, setViewing] = useState(null)
-
const fetchData = useCallback(async () => {
setLoading(true)
try {
@@ -42,7 +37,7 @@ export function RolesClient() {
function openCreate() { router.push('/users/roles/new') }
function openEdit(role: RoleResponse) { router.push(`/users/roles/${role.id}/edit`) }
- function openView(role: RoleResponse) { setViewing(role); setViewOpen(true) }
+ function openView(role: RoleResponse) { router.push(`/users/roles/${role.id}`) }
async function handleDeactivate(role: RoleResponse) {
try {
@@ -120,12 +115,6 @@ export function RolesClient() {
totalCount={total}
onServerSideChange={({ page: p }) => setPageSize(p.pageSize)}
/>
-
- setViewOpen(false)}
- role={viewing}
- />
>
)
}