Skip to content

Commit

Permalink
feat(fe): add admin problem detail (#1524)
Browse files Browse the repository at this point in the history
* feat(fe): add problem detail layout

* feat(fe): add submission table

* feat(fe): add username filter input

* feat(fe): add filter for userID Search

* fix: change user to username, change id to underscore

* fix: edit datatable input placeholder

* fix: delete files of frontend-client

---------

Co-authored-by: jiho <[email protected]>
  • Loading branch information
B0XERCAT and jihorobert authored Mar 11, 2024
1 parent 634e1b4 commit 5fb21d1
Show file tree
Hide file tree
Showing 6 changed files with 962 additions and 666 deletions.
59 changes: 59 additions & 0 deletions apps/frontend/app/admin/problem/[id]/_components/Columns.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
'use client'

import type { SubmissionItem } from '@/types/type'
import type { ColumnDef } from '@tanstack/react-table'
import dayjs from 'dayjs'

export const columns: ColumnDef<SubmissionItem>[] = [
{
header: '#',
accessorKey: 'id',
cell: ({ row }) => <p className="text-sm">{row.original.id}</p>
},
{
id: 'username',
header: () => 'User ID',
accessorKey: 'username',
cell: ({ row }) => row.original.user.username,
// submission userID Search Filter
filterFn: (row, _, value) => {
const users = row.original.user
return users.username.includes(value)
}
},
{
header: () => 'Result',
accessorKey: 'result',
cell: ({ row }) => {
return row.original.result === 'Accepted' ? (
<p className="text-green-500">{row.original.result}</p>
) : row.original.result === 'Judging' ? (
<p className="text-gray-500">{row.original.result}</p>
) : (
<p className="text-red-500">{row.original.result}</p>
)
}
},
{
header: () => 'Language',
accessorKey: 'language',
cell: ({ row }) => row.original.language
},
{
header: () => 'Submission Time',
accessorKey: 'createTime',
cell: ({ row }) =>
dayjs(row.original.createTime).format('YYYY-MM-DD HH:mm:ss')
},
{
header: () => 'Code Size',
accessorKey: 'codeSize',
cell: ({ row }) => {
return row.original.codeSize === null ? (
<p>N/A</p>
) : (
<p>{row.original.codeSize} B</p>
)
}
}
]
169 changes: 169 additions & 0 deletions apps/frontend/app/admin/problem/[id]/_components/DataTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
'use client'

import { Input } from '@/components/ui/input'
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow
} from '@/components/ui/table'
import { cn } from '@/lib/utils'
import type { ColumnDef } from '@tanstack/react-table'
import {
flexRender,
getCoreRowModel,
getFilteredRowModel,
useReactTable
} from '@tanstack/react-table'
import type { Route } from 'next'
import Link from 'next/link'
import { useRouter } from 'next/navigation'

interface DataTableProps<TData, TValue> {
columns: ColumnDef<TData, TValue>[]
data: TData[]
headerStyle: {
[key: string]: string
}
problemId: number
}

/**
* @param columns
* columns to be displayed
* @param data
* data to be displayed
* @param headerStyle
* tailwindcss class name for each header
* @param name
* name of the table, used for routing
* @example
* ```tsx
* // page.tsx
* <DataTable data={data} columns={columns} headerStyle={{
* title: 'text-left w-2/4 md:w-4/6',
* createdBy: 'w-1/4 md:w-1/6',
* createTime: 'w-1/4 md:w-1/6'
* }}
* name="notice"
* />
* ```
* ```tsx
* // _components/Columns.tsx
* import type { Notice } from '@/types/type'
* export const columns: ColumnDef<Notice, string>[] = [
* {
* header: 'Title',
* accessorKey: 'title',
* cell: (row) => row.original.title,
* },
* ...
* ]
* ```
*/

interface Item {
id: number
}

export default function DataTable<TData extends Item, TValue>({
columns,
data,
headerStyle,
problemId
}: DataTableProps<TData, TValue>) {
const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
getFilteredRowModel: getFilteredRowModel()
})
const router = useRouter()

return (
<div>
<Input
placeholder="Search User"
value={(table.getColumn('username')?.getFilterValue() as string) ?? ''}
onChange={(event) => {
table.getColumn('username')?.setFilterValue(event.target.value)
}}
className="h-10 w-[150px] lg:w-[250px]"
/>
<Table className="table-fixed">
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow className="hover:bg-gray-200/40" key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<TableHead
key={header.id}
className={cn(
'border-b border-gray-400 text-center text-xs font-semibold',
headerStyle[header.id]
)}
style={{ padding: 0 }}
>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
)
})}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => {
const href =
`/problem/${problemId}/submission/${row.original.id}` as Route
return (
<TableRow
key={row.id}
data-state={row.getIsSelected() && 'selected'}
className="cursor-pointer border-t border-gray-400 hover:bg-gray-200/40 hover:font-semibold"
onClick={() => {
router.push(href)
}}
>
{row.getVisibleCells().map((cell) => (
<TableCell
key={cell.id}
style={{
paddingTop: 10,
paddingBottom: 10,
paddingLeft: 2,
paddingRight: 2
}}
>
<div className="text-center text-xs">
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</div>
{/* for prefetch */}
<Link href={href} />
</TableCell>
))}
</TableRow>
)
})
) : (
<TableRow className="hover:bg-gray-200/40">
<TableCell colSpan={columns.length} className="h-24 text-center">
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
)
}
Loading

0 comments on commit 5fb21d1

Please sign in to comment.