IssueTable
import { IssueStatusBadge } from '@/app/components'
import { Issue, Status } from '@prisma/client'
import { ArrowDownIcon, ArrowUpIcon } from '@radix-ui/react-icons'
import { Table } from '@radix-ui/themes'
import { default as Link, default as NextLink } from 'next/link'
export interface issueQuery {
status: Status,
orderBy: keyof Issue,
dir: 'asc' | 'desc',
page: string
}
interface Props {
searchParams: issueQuery
issues: Issue[]
}
const IssueTable = ({ searchParams, issues }: Props) => {
return (
<Table.Root variant='surface'>
<Table.Header>
<Table.Row>
{columns.map(column => (
<Table.ColumnHeaderCell className={column.className} key={column.value}>
<NextLink href={{
query: {
...searchParams,
orderBy: column.value,
dir: column.value === searchParams.orderBy ?
searchParams.dir === 'asc' ? 'desc' : 'asc'
: 'asc'
}
}}>{column.label}</NextLink>
{column.value === searchParams.orderBy && (searchParams.dir === 'asc' ? <ArrowUpIcon className="inline" /> : <ArrowDownIcon className="inline" />)}
</Table.ColumnHeaderCell>
))}
</Table.Row>
</Table.Header>
<Table.Body>
{issues.map(issue => (
<Table.Row key={issue.id}>
<Table.Cell>
<Link href={`/issues/${issue.id}`}>
{issue.title}
</Link>
<div className='block md:hidden'><IssueStatusBadge status={issue.status} /></div>
</Table.Cell>
<Table.Cell className='hidden md:table-cell'><IssueStatusBadge status={issue.status} /></Table.Cell>
<Table.Cell className='hidden md:table-cell'>{issue.createdAt.toDateString()}</Table.Cell>
</Table.Row>
))}
</Table.Body>
</Table.Root>
)
}
const columns: {
label: string,
value: keyof Issue
className?: string
}[] = [
{ label: 'Issue', value: 'title' },
{ label: 'Status', value: 'status', className: 'hidden md:table-cell' },
{ label: 'Created', value: 'createdAt', className: 'hidden md:table-cell' }
]
export const columnNames = columns.map(column => column.value);
export default IssueTable
list page.tsx
import Pagination from "@/app/components/Pagination";
import prisma from '@/prisma/client';
import { Status } from "@prisma/client";
import IssueActions from './IssueActions';
import IssueTable, { columnNames, issueQuery } from "./IssueTable";
import { Flex } from "@radix-ui/themes";
import { Metadata } from "next";
interface Props {
searchParams: issueQuery
}
const IssuesPage = async ({ searchParams }: Props) => {
const statuses = Object.values(Status);
const status = statuses.includes(searchParams.status) ? searchParams.status : undefined;
const where = { status };
const orderBy = columnNames.includes(searchParams.orderBy) ? { [searchParams.orderBy]: searchParams.dir } : undefined;
const page = parseInt(searchParams.page) || 1;
const pageSize = 10;
const issues = await prisma.issue.findMany({
where,
orderBy,
skip: ((page - 1) * pageSize),
take: pageSize
});
const issueCount = await prisma.issue.count({ where });
return (
<Flex direction='column' gap='3'>
<IssueActions />
<IssueTable searchParams={searchParams} issues={issues} />
<Pagination pageSize={pageSize} currentPage={page} itemCount={issueCount} />
</Flex>
)
}
export const dynamic = 'force-dynamic';
export const metadata: Metadata = {
title: 'Issue Tracker - Issue List',
description: 'View all project issues'
}
export default IssuesPage