From a20f5eff8507d68f153739291744cef539483971 Mon Sep 17 00:00:00 2001 From: Hafid Nur <73023445+hafidnrzs@users.noreply.github.com> Date: Sun, 30 Nov 2025 16:55:11 +0700 Subject: [PATCH] test datatable with mock data --- .../(protected)/hackathon-dashboard/page.tsx | 6 +- .../app/(protected)/hackathon-teams/page.tsx | 257 +++++++++++++++++- .../app/(protected)/hackathon-users/page.tsx | 181 +++++++++++- .../backoffice/src/app/(protected)/layout.tsx | 4 +- .../src/components/modal-delete-user.tsx | 41 +++ .../src/components/modal-suspend-or-ban.tsx | 42 +++ .../src/components/modal-user-detail.tsx | 47 ++++ .../backoffice-sidebar/backoffice-sidebar.tsx | 12 +- 8 files changed, 574 insertions(+), 16 deletions(-) create mode 100644 apps/backoffice/src/components/modal-delete-user.tsx create mode 100644 apps/backoffice/src/components/modal-suspend-or-ban.tsx create mode 100644 apps/backoffice/src/components/modal-user-detail.tsx diff --git a/apps/backoffice/src/app/(protected)/hackathon-dashboard/page.tsx b/apps/backoffice/src/app/(protected)/hackathon-dashboard/page.tsx index 61bb59b..190ff52 100644 --- a/apps/backoffice/src/app/(protected)/hackathon-dashboard/page.tsx +++ b/apps/backoffice/src/app/(protected)/hackathon-dashboard/page.tsx @@ -7,8 +7,10 @@ export const HackathonDashboardPage: FC = (): ReactElement => {

Hackathon Dashboard

-
-

Boilerplate page for hackathon dashboard. Add KPIs and actions here.

+
+

+ Boilerplate page for hackathon dashboard. Add KPIs and actions here. +

); diff --git a/apps/backoffice/src/app/(protected)/hackathon-teams/page.tsx b/apps/backoffice/src/app/(protected)/hackathon-teams/page.tsx index 59d9cc7..afe4a71 100644 --- a/apps/backoffice/src/app/(protected)/hackathon-teams/page.tsx +++ b/apps/backoffice/src/app/(protected)/hackathon-teams/page.tsx @@ -1,15 +1,266 @@ -import { FC, ReactElement } from 'react'; +import { FC, ReactElement, useState } from 'react'; +import ModalUserDetail from '../../../components/modal-user-detail'; +import ModalSuspendOrBan from '../../../components/modal-suspend-or-ban'; +import ModalDeleteUser from '../../../components/modal-delete-user'; +import { DataTable } from '@imphnen-frontend-service/ui/organisms'; +import { + ColumnDef, + getCoreRowModel, + getPaginationRowModel, + PaginationState, + RowSelectionState, + useReactTable, +} from '@tanstack/react-table'; +import { Button } from '@imphnen-frontend-service/ui/atoms'; +import { cn } from '@imphnen-frontend-service/utils'; export const HackathonTeamsPage: FC = (): ReactElement => { + const [showDeleteModal, setShowDeleteModal] = useState(false); + const [showDetailModal, setShowDetailModal] = useState(false); + const [showSuspendModal, setShowSuspendModal] = useState(false); + + const [rowSelection, setRowSelection] = useState({}); + const [pagination, setPagination] = useState({ + pageIndex: 0, + pageSize: 9, + }); + + const mockData: TeamType[] = Array.from({ length: 90 }, (_, i) => ({ + id: `team-${i + 1}`, + name: `Team ${i + 1} - ${i % 3 === 0 ? 'Innovators' : 'Hackers'}`, + city: i % 2 === 0 ? 'Jakarta' : 'Bandung', + visibility: i % 4 === 0 ? 'private' : 'public', + member_count: Math.floor(Math.random() * 4) + 1, + has_submission: i % 3 !== 0, + created_at: new Date().toISOString(), + updated_at: new Date().toISOString(), + leader: { + user: { + fullname: `Leader User ${i}`, + email: `leader${i}@example.com`, + }, + }, + })); + + interface TeamType { + id: string; + name: string; + city: string; + visibility: 'public' | 'private'; + member_count: number; + has_submission: boolean; + created_at: string; + updated_at: string; + leader?: { + user: { + fullname: string; + email: string; + }; + }; + } + + const columns: ColumnDef[] = [ + { + id: 'select', + meta: { cellClassName: cn('w-12') }, + header: ({ table }) => ( + + ), + cell: ({ row }) => ( + + ), + }, + { + accessorKey: 'name', + header: 'Team Name', + cell: ({ row }) => ( +
+
{row.original.name}
+
{row.original.id}
+
+ ), + }, + { + accessorKey: 'city', + header: 'City', + }, + { + accessorKey: 'visibility', + header: 'Visibility', + cell: ({ row }) => { + const isPublic = row.original.visibility === 'public'; + return ( + + {isPublic ? 'Public' : 'Private'} + + ); + }, + }, + { + accessorKey: 'member_count', + header: 'Members', + cell: ({ row }) => ( +
+ {row.getValue('member_count')} + members +
+ ), + }, + { + id: 'leader', + header: 'Leader', + cell: ({ row }) => { + const leader = row.original.leader?.user; + return leader ? ( +
+
+ {leader.fullname} +
+
{leader.email}
+
+ ) : ( + No leader + ); + }, + }, + { + accessorKey: 'has_submission', + header: 'Submission', + cell: ({ row }) => { + const hasSubmission = row.original.has_submission; + return ( + + {hasSubmission ? 'Yes' : 'No'} + + ); + }, + }, + { + accessorKey: 'updated_at', + header: 'Last Updated', + cell: ({ row }) => { + return ( + + {new Date(row.original.updated_at).toLocaleDateString()} + + ); + }, + }, + { + id: 'actions', + header: 'Action', + meta: { cellClassName: cn('w-48') }, + cell: ({ row }) => ( +
+ + | + +
+ ), + }, + ]; + + const table = useReactTable({ + data: mockData, + columns, + state: { + pagination, + rowSelection, + }, + enableRowSelection: true, + onRowSelectionChange: setRowSelection, + getCoreRowModel: getCoreRowModel(), + getPaginationRowModel: getPaginationRowModel(), + onPaginationChange: setPagination, + pageCount: Math.ceil(mockData.length / pagination.pageSize), + manualPagination: false, + }); + return (

Hackathon Teams

-
-

Boilerplate page for managing teams. Add table, filters, and forms here.

+ {/* Filters and actions */} +
+
+ + + +
+ + {/* Table */} +
+ + {/* Modals extracted into shared backoffice components */} + setShowDetailModal(false)} + /> + setShowSuspendModal(false)} + /> + setShowDeleteModal(false)} + />
); }; diff --git a/apps/backoffice/src/app/(protected)/hackathon-users/page.tsx b/apps/backoffice/src/app/(protected)/hackathon-users/page.tsx index 9707728..3816975 100644 --- a/apps/backoffice/src/app/(protected)/hackathon-users/page.tsx +++ b/apps/backoffice/src/app/(protected)/hackathon-users/page.tsx @@ -1,15 +1,190 @@ -import { FC, ReactElement } from 'react'; +import { FC, ReactElement, useState } from 'react'; +import ModalUserDetail from '../../../components/modal-user-detail'; +import ModalSuspendOrBan from '../../../components/modal-suspend-or-ban'; +import ModalDeleteUser from '../../../components/modal-delete-user'; +import { DataTable } from '@imphnen-frontend-service/ui/organisms'; +import { + ColumnDef, + getCoreRowModel, + getPaginationRowModel, + PaginationState, + RowSelectionState, + useReactTable, +} from '@tanstack/react-table'; +import { Button } from '@imphnen-frontend-service/ui/atoms'; +import { cn } from '@imphnen-frontend-service/utils'; +import { SearchOutlined } from '@ant-design/icons'; export const HackathonUsersPage: FC = (): ReactElement => { + const [showDeleteModal, setShowDeleteModal] = useState(false); + const [showDetailModal, setShowDetailModal] = useState(false); + const [showSuspendModal, setShowSuspendModal] = useState(false); + + const [rowSelection, setRowSelection] = useState({}); + const [pagination, setPagination] = useState({ + pageIndex: 0, + pageSize: 9, + }); + + const mockData: any[] = Array.from({ length: 90 }, (_, i) => ({ + id: i + 1, + name: i % 3 === 0 ? 'Ahmad Wijuana' : 'Sofia Wijuana', + email: 'fullname23@gmail.com', + rating: 4.5, + status: i % 2 === 0 ? 'active' : 'inactive', + })); + + type UserStatus = 'active' | 'inactive'; + + interface UserType { + id: number; + name: string; + email: string; + rating: number; + status: UserStatus; + } + + const columns: ColumnDef[] = [ + { + id: 'select', + meta: { cellClassName: cn('w-20') }, + header: ({ table }) => ( + + ), + cell: ({ row }) => ( + + ), + }, + { + id: 'name', + header: 'Name', + accessorKey: 'name', + }, + { + id: 'email', + header: 'Email', + accessorKey: 'email', + }, + { + id: 'rating', + header: 'Rating', + accessorKey: 'rating', + }, + { + id: 'status', + header: 'Status', + accessorKey: 'status', + cell: ({ row }) => { + const status = row.original.status; + const statusColors: Record = { + active: 'bg-success-200 text-success-500', + inactive: 'bg-danger-200 text-danger-500', + }; + const statusText: Record = { + active: 'Active', + inactive: 'Inactive', + }; + return ( +
+ {statusText[status]} +
+ ); + }, + }, + { + header: 'Action', + meta: { cellClassName: cn('w-72') }, + cell: ({ row }) => ( + + ), + }, + ]; + + const table = useReactTable({ + data: mockData, + columns, + state: { + pagination, + rowSelection, + }, + enableRowSelection: true, + onRowSelectionChange: setRowSelection, + getCoreRowModel: getCoreRowModel(), + getPaginationRowModel: getPaginationRowModel(), + onPaginationChange: setPagination, + pageCount: Math.ceil(mockData.length / pagination.pageSize), + manualPagination: false, + }); + return (

Hackathon Users

-
-

Boilerplate page for managing users. Add table, filters, and forms here.

+ {/* Filters and actions */} +
+
+ + + +
+ + {/* Table */} +
+ + {/* Modals extracted into shared backoffice components */} + setShowDetailModal(false)} + /> + setShowSuspendModal(false)} + /> + setShowDeleteModal(false)} + />
); }; diff --git a/apps/backoffice/src/app/(protected)/layout.tsx b/apps/backoffice/src/app/(protected)/layout.tsx index 78df939..a1fb13e 100644 --- a/apps/backoffice/src/app/(protected)/layout.tsx +++ b/apps/backoffice/src/app/(protected)/layout.tsx @@ -16,14 +16,14 @@ export const AppLayout: FC = (): ReactElement => { {/* Sticky top header */}
{/* Mobile menu button (shown on small screens) */} + +
+

+ Are you sure you want to delete this user? This action cannot be + undone. +

+
+ + +
+
+ + + + ); +}; + +export default ModalDeleteUser; diff --git a/apps/backoffice/src/components/modal-suspend-or-ban.tsx b/apps/backoffice/src/components/modal-suspend-or-ban.tsx new file mode 100644 index 0000000..517685d --- /dev/null +++ b/apps/backoffice/src/components/modal-suspend-or-ban.tsx @@ -0,0 +1,42 @@ +import { FC } from 'react'; + +interface ModalProps { + isOpen: boolean; + onClose: () => void; +} + +const ModalSuspendOrBan: FC = ({ isOpen, onClose }) => { + if (!isOpen) return null; + return ( +
+
+
+
+
+

Suspend or Ban User

+ +
+
+ +