22
33import { useState } from "react" ;
44import { useRouter } from "next/navigation" ;
5- import { useDisclosure , addToast } from "@heroui/react" ;
6-
7- import { deleteCategory } from "@/actions/categories" ;
8- import { DeleteModal , HeadingDashboard } from "@/components/ui" ;
9-
10- import type { Profile } from "@/types" ;
11- import { ProfileTable } from "@/components/profiles" ;
12- import { updateProfileRole } from "@/actions/profiles" ;
5+ import {
6+ useDisclosure ,
7+ addToast ,
8+ Image ,
9+ Chip ,
10+ Dropdown ,
11+ DropdownTrigger ,
12+ Button ,
13+ DropdownMenu ,
14+ DropdownItem ,
15+ } from "@heroui/react" ;
16+ import { deleteProfileByAdmin , updateProfileRole } from "@/actions/profiles" ;
17+ import { DeleteModal , HeadingDashboard , Table } from "@/components/ui" ;
18+ import { ChevronDown , Trash2 } from "lucide-react" ;
19+ import { capitalize } from "@/lib/utils" ;
20+ import { ROLE_OPTIONS } from "@/config/constants" ;
21+ import type { Profile , TableAction , TableColumn } from "@/types" ;
1322
1423type Props = { profiles : Profile [ ] } ;
1524
1625export default function ProfilesPageClient ( { profiles } : Props ) {
1726 const router = useRouter ( ) ;
27+
28+ // States
1829 const [ profileToDelete , setProfileToDelete ] = useState < string | null > ( null ) ;
1930 const [ loading , setLoading ] = useState < boolean > ( false ) ;
2031
32+ // Hooks
2133 const {
2234 isOpen : isConfirmOpen ,
2335 onOpen : onConfirmOpen ,
2436 onClose : onConfirmClose ,
2537 } = useDisclosure ( ) ;
2638
39+ // Functions
2740 const handleRoleChange = async ( id : string , newRole : string ) => {
2841 try {
2942 await updateProfileRole ( id , newRole ) ;
@@ -50,7 +63,7 @@ export default function ProfilesPageClient({ profiles }: Props) {
5063 if ( ! profileToDelete ) return ;
5164 setLoading ( true ) ;
5265 try {
53- await deleteCategory ( profileToDelete ) ;
66+ await deleteProfileByAdmin ( profileToDelete ) ;
5467 addToast ( {
5568 title : "Deleted" ,
5669 description : "Profile deleted successfully." ,
@@ -66,16 +79,117 @@ export default function ProfilesPageClient({ profiles }: Props) {
6679 }
6780 } ;
6881
82+ // Constants
83+ const columns : TableColumn [ ] = [
84+ { name : "Avatar" , uid : "avatar_url" } ,
85+ { name : "Full Name" , uid : "full_name" , sortable : true } ,
86+ { name : "Username" , uid : "username" , sortable : true } ,
87+ { name : "Created At" , uid : "created_at" , sortable : true } ,
88+ { name : "Role" , uid : "role" } ,
89+ { name : "Status" , uid : "status" } ,
90+ ] ;
91+
92+ const roleOptions = [
93+ { uid : "user" , name : "User" } ,
94+ { uid : "admin" , name : "Admin" } ,
95+ ] ;
96+
97+ const actions : TableAction [ ] = [
98+ {
99+ key : "delete" ,
100+ label : "Delete profile" ,
101+ icon : < Trash2 size = { 18 } /> ,
102+ color : "danger" ,
103+ shortcut : "⌘D" ,
104+ onAction : ( profile ) => handleDelete ( profile . id ) ,
105+ } ,
106+ ] ;
107+
69108 return (
70109 < div className = "flex flex-col gap-4" >
71110 { /* Heading */ }
72111 < HeadingDashboard title = "Profiles" count = { profiles . length } />
73112
74113 { /* Table */ }
75- < ProfileTable
76- profiles = { profiles }
77- handleRoleChange = { handleRoleChange }
78- handleDelete = { handleDelete }
114+ < Table
115+ title = "Profiles table"
116+ data = { profiles }
117+ columns = { columns }
118+ actions = { actions }
119+ searchPlaceholder = "Search by full name or username..."
120+ searchKeys = { [ "full_name" , "username" ] }
121+ defaultRowsPerPage = { 10 }
122+ rowsPerPageOptions = { [ 10 , 25 , 50 , 100 ] }
123+ filterKey = "role"
124+ filterLabel = "Role"
125+ filterOptions = { roleOptions }
126+ cellRenderer = { ( { item, columnKey, value } ) => {
127+ switch ( columnKey ) {
128+ case "avatar_url" :
129+ return (
130+ < Image
131+ radius = "sm"
132+ src = { value }
133+ alt = "Current image"
134+ width = { 24 }
135+ height = { 24 }
136+ className = "border-2 border-neutral-200 dark:border-neutral-800"
137+ />
138+ ) ;
139+ case "created_at" :
140+ return new Date ( value ) . toLocaleDateString ( ) ;
141+ case "role" :
142+ return (
143+ < Dropdown
144+ classNames = { {
145+ content :
146+ "bg-white dark:bg-neutral-950 border-2 border-neutral-200 dark:border-neutral-800" ,
147+ } }
148+ >
149+ < DropdownTrigger >
150+ < Button
151+ size = "sm"
152+ variant = "bordered"
153+ className = "min-w-[110px] border-2 border-neutral-200 dark:border-neutral-800"
154+ endContent = { < ChevronDown size = { 16 } /> }
155+ >
156+ { capitalize ( value ) }
157+ </ Button >
158+ </ DropdownTrigger >
159+ < DropdownMenu
160+ aria-label = "Select Role"
161+ disallowEmptySelection
162+ selectionMode = "single"
163+ onAction = { ( key ) =>
164+ handleRoleChange ( item . id , key . toString ( ) )
165+ }
166+ >
167+ { ROLE_OPTIONS . map ( ( role ) => (
168+ < DropdownItem key = { role . key } > { role . label } </ DropdownItem >
169+ ) ) }
170+ </ DropdownMenu >
171+ </ Dropdown >
172+ ) ;
173+ case "status" :
174+ return (
175+ < Chip
176+ size = "sm"
177+ color = {
178+ value === "active"
179+ ? "success"
180+ : value === "inactive"
181+ ? "danger"
182+ : "default"
183+ }
184+ variant = "flat"
185+ >
186+ { capitalize ( value ) }
187+ </ Chip >
188+ ) ;
189+ default :
190+ return value ;
191+ }
192+ } }
79193 />
80194
81195 { /* Delete Modal */ }
0 commit comments