Skip to content

Commit

Permalink
테이블 디자인 작업 (#56)
Browse files Browse the repository at this point in the history
* style: 테이블 스타일 작업
레이아웃 구성
색상 및 아이콘 추가
메인 페이지 미흡한 부분 수정

* style: 마크다운 스크롤 스타일 변경
  • Loading branch information
jgjgill authored Dec 29, 2023
1 parent 8acf5e3 commit 44f5c70
Show file tree
Hide file tree
Showing 9 changed files with 143 additions and 73 deletions.
9 changes: 2 additions & 7 deletions app/(main)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,8 @@ export default async function Page() {
return (
<HydrationBoundary state={dehydrate(queryClient)}>
<BaseHeader />

<QuizTable userId={user?.id} />

<div className="h-16">
<div className="fixed bottom-0 h-16 w-[28rem] bg-white">
혹시 몰라서 추가해 본 바텀시트...
</div>
<div className="px-5 py-4">
<QuizTable userId={user?.id} />
</div>
</HydrationBoundary>
);
Expand Down
15 changes: 14 additions & 1 deletion app/(main)/quiz-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,18 @@ type QuizTableProps = {
export default function QuizTable({ userId }: QuizTableProps) {
const { data: quizzes } = useGetQuizzes(userId);

return <div>{quizzes && <DataTable columns={columns} data={quizzes} />}</div>;
const quizzesLength = quizzes.length;
const solvedQuizziesLength = quizzes.filter((quiz) => quiz.success).length;

return (
<div>
<h2 className="text-lg font-bold">전체 퀴즈</h2>
<p className="text-slate-500">
수록된 {quizzesLength}개의 문제 중 {solvedQuizziesLength}개를 풀었어요!
({Math.round((solvedQuizziesLength / quizzesLength) * 100)}%)
</p>

{quizzes && <DataTable columns={columns} data={quizzes} />}
</div>
);
}
13 changes: 12 additions & 1 deletion app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@
@apply my-2;
}

.markdown code::before, .markdown code::after {
.markdown code::before,
.markdown code::after {
display: none;
}

Expand All @@ -97,3 +98,13 @@
#app-screen {
@apply relative lg:left-1/2 mx-auto lg:mx-0 min-h-screen max-w-md;
}

pre::-webkit-scrollbar {
height: 5px;
}

pre::-webkit-scrollbar-thumb {
background: hsl(var(--background));
border: 2px solid hsl(var(--background));
border-radius: 12px 12px 12px 12px;
}
Binary file added assets/images/check-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/search-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/images/x-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion components/common/markdown/markdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export default function Markdown({
return language ? (
<SyntaxHighlighter
{...rest}
customStyle={{ padding: '0', overflow: 'auto' }}
customStyle={{ overflow: 'auto' }}
language={language}
style={style}
ref={null}
Expand Down
52 changes: 39 additions & 13 deletions components/quiz/table/columns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,40 @@ import {
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import type { Quiz, QuizTable } from '@/libs/models';
import { cn } from '@/libs/utils';
import { Column, ColumnDef } from '@tanstack/react-table';
import Link from 'next/link';
import { cva } from 'class-variance-authority';
import Image from 'next/image';
import XIcon from '@/assets/images/x-icon.png';
import CheckIcon from '@/assets/images/check-icon.png';

export const columns: ColumnDef<QuizTable>[] = [
{
accessorKey: 'success',
header: () => <div className="text-left">상태</div>,
header: () => <div className="w-full text-center">상태</div>,
cell: ({ row }) => {
const { success } = row.original;

return <div>{JSON.stringify(success)}</div>;
const solveIcon = success ? CheckIcon : XIcon;
const solveAlt = success ? '성공' : '실패';

return (
<div>
{success !== undefined && <Image src={solveIcon} alt={solveAlt} />}
</div>
);
},
},

{
accessorKey: 'title',
header: '제목',
cell: ({ row }) => {
const { title, summary, id } = row.original;
const { title, summary } = row.original;

return (
<div className="max-w-[13rem]">
<h3 className="truncate">
<Link className="text-base font-semibold" href={`/quizzes/${id}`}>
{title}
</Link>
</h3>
<div className="w-[10rem]">
<h3 className="truncate text-base font-semibold">{title}</h3>
<p className="truncate text-blue-500">{summary}</p>
</div>
);
Expand All @@ -53,16 +61,34 @@ export const columns: ColumnDef<QuizTable>[] = [
accessorKey: 'difficulty',
header: ({ column }) => (
<DropdownMenu>
<DropdownMenuTrigger className="w-full">난이도</DropdownMenuTrigger>
<DropdownMenuTrigger className="w-full pr-4">
난이도
</DropdownMenuTrigger>
<DropdownMenuContent>
<Filter column={column} />
</DropdownMenuContent>
</DropdownMenu>
),
cell: ({ row }) => {
const difficulty = String(row.getValue('difficulty'));
const difficulty = String(row.getValue('difficulty')) as
| '쉬움'
| '보통'
| '어려움';
const difficultyColor = cva(' -mx-4 pr-4 text-center', {
variants: {
color: {
쉬움: 'text-green-500',
보통: 'text-orange-500',
어려움: 'text-red-500',
},
},
});

return <div className="text-center">{difficulty}</div>;
return (
<div className={cn(difficultyColor({ color: difficulty }))}>
{difficulty}
</div>
);
},
filterFn: 'arrIncludesSome',
},
Expand Down
125 changes: 75 additions & 50 deletions components/quiz/table/data-table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,18 @@ import {
getPaginationRowModel,
useReactTable,
} from '@tanstack/react-table';
import Image from 'next/image';
import { useState } from 'react';
import SearchIcon from '@/assets/images/search-icon.png';
import { useRouter } from 'next/navigation';
import { QuizTable } from '@/libs/models';

type DataTableProps<TData, TValue> = {
columns: ColumnDef<TData, TValue>[];
data: TData[];
};

export default function DataTable<TData, TValue>({
export default function DataTable<TData extends QuizTable, TValue>({
columns,
data,
}: DataTableProps<TData, TValue>) {
Expand All @@ -35,64 +39,85 @@ export default function DataTable<TData, TValue>({
data,
columns,
state: { columnFilters },
initialState: { pagination: { pageSize: 2 } },
initialState: { pagination: { pageSize: 5 } },
onColumnFiltersChange: setColumnFilters,
getCoreRowModel: getCoreRowModel(),
getFilteredRowModel: getFilteredRowModel(),
getPaginationRowModel: getPaginationRowModel(),
});

const router = useRouter();

return (
<div className="mt-2">
<input
type="text"
className="mb-2.5 w-full border"
placeholder="퀴즈를 검색해 보세요!"
onChange={(e) => {
table.getColumn('title')?.setFilterValue(e.target.value);
}}
/>
<div className="mt-3.5">
<div className="relative mb-2.5">
<Image
src={SearchIcon}
alt="검색"
className="absolute left-2.5 top-1/2 -translate-y-1/2"
width={20}
height={20}
/>
<input
type="text"
className="h-full w-full border py-3 pl-10 pr-2"
placeholder="퀴즈를 검색해 보세요!"
onChange={(e) => {
table.getColumn('title')?.setFilterValue(e.target.value);
}}
/>
</div>

<Table className="border">
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<TableHead key={header.id}>
{flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
);
})}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
data-state={row.getIsSelected() && 'selected'}
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</TableCell>
))}
<div className="min-h-[450px]">
<Table className="mb-4 border bg-white">
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<TableHead key={header.id}>
{flexRender(
header.column.columnDef.header,
header.getContext()
)}
</TableHead>
);
})}
</TableRow>
))
) : (
<TableRow>
<TableCell colSpan={columns.length} className="h-24 text-center">
해당되는 퀴즈가 없습니다.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
onClick={() => router.push(`/quizzes/${row.original.id}`)}
data-state={row.getIsSelected() && 'selected'}
className="h-10 cursor-pointer"
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id} className="">
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell
colSpan={columns.length}
className="h-24 text-center"
>
해당되는 퀴즈가 없습니다.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>

<div>
<Button
Expand Down

0 comments on commit 44f5c70

Please sign in to comment.