Table view
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Table({ className, ...props }: React.ComponentProps<"table">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="table-container"
|
||||
className="relative w-full overflow-x-auto"
|
||||
>
|
||||
<table
|
||||
data-slot="table"
|
||||
className={cn("w-full caption-bottom text-sm", className)}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function TableHeader({ className, ...props }: React.ComponentProps<"thead">) {
|
||||
return (
|
||||
<thead
|
||||
data-slot="table-header"
|
||||
className={cn("[&_tr]:border-b", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function TableBody({ className, ...props }: React.ComponentProps<"tbody">) {
|
||||
return (
|
||||
<tbody
|
||||
data-slot="table-body"
|
||||
className={cn("[&_tr:last-child]:border-0", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) {
|
||||
return (
|
||||
<tfoot
|
||||
data-slot="table-footer"
|
||||
className={cn(
|
||||
"bg-muted/50 border-t font-medium [&>tr]:last:border-b-0",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
|
||||
return (
|
||||
<tr
|
||||
data-slot="table-row"
|
||||
className={cn(
|
||||
"hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function TableHead({ className, ...props }: React.ComponentProps<"th">) {
|
||||
return (
|
||||
<th
|
||||
data-slot="table-head"
|
||||
className={cn(
|
||||
"text-foreground h-10 px-2 text-left align-middle font-medium whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function TableCell({ className, ...props }: React.ComponentProps<"td">) {
|
||||
return (
|
||||
<td
|
||||
data-slot="table-cell"
|
||||
className={cn(
|
||||
"p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function TableCaption({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<"caption">) {
|
||||
return (
|
||||
<caption
|
||||
data-slot="table-caption"
|
||||
className={cn("text-muted-foreground mt-4 text-sm", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
Table,
|
||||
TableHeader,
|
||||
TableBody,
|
||||
TableFooter,
|
||||
TableHead,
|
||||
TableRow,
|
||||
TableCell,
|
||||
TableCaption,
|
||||
}
|
||||
@@ -4,24 +4,27 @@ import { SetState, Task } from "@/lib/types";
|
||||
import { Color } from "@/lib/ui/color";
|
||||
import BoardView from "./board/BoardView";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "../ui/tabs";
|
||||
import { TableView } from "./table/TableView";
|
||||
|
||||
export type TaskViewProps = {
|
||||
tags: Record<string, { color: Color; name: string }>;
|
||||
tasks: Task[];
|
||||
setTasks: SetState<Task[]>;
|
||||
createTask: (status: string) => void;
|
||||
};
|
||||
|
||||
export default function TaskView({
|
||||
tags,
|
||||
tasks,
|
||||
setTasks,
|
||||
createTask,
|
||||
}: {
|
||||
tags: Record<string, { color: Color; name: string }>;
|
||||
tasks: Task[];
|
||||
setTasks: SetState<Task[]>;
|
||||
createTask: (status: string) => void;
|
||||
}) {
|
||||
}: TaskViewProps) {
|
||||
return (
|
||||
<div className="px-6 pt-3">
|
||||
<Tabs defaultValue="board">
|
||||
<TabsList>
|
||||
<TabsTrigger value="board">Board</TabsTrigger>
|
||||
<TabsTrigger value="list">List</TabsTrigger>
|
||||
<TabsTrigger value="table">Table</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="board">
|
||||
<BoardView
|
||||
@@ -31,8 +34,8 @@ export default function TaskView({
|
||||
tags={tags}
|
||||
/>
|
||||
</TabsContent>
|
||||
<TabsContent value="list">
|
||||
<BoardView
|
||||
<TabsContent value="table">
|
||||
<TableView
|
||||
tasks={tasks}
|
||||
setTasks={setTasks}
|
||||
createTask={createTask}
|
||||
|
||||
@@ -4,11 +4,10 @@ import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { DragDropContext, Droppable, Draggable } from "@hello-pangea/dnd";
|
||||
import { PlusIcon } from "lucide-react";
|
||||
import { SetState, Task } from "@/lib/types";
|
||||
import BoardTask from "./Task";
|
||||
import { Color } from "@/lib/ui/color";
|
||||
import { TaskViewProps } from "../TaskView";
|
||||
|
||||
const columnLabels: Record<string, string> = {
|
||||
const statusLabels: Record<string, string> = {
|
||||
todo: "To do",
|
||||
inprogress: "In progress",
|
||||
done: "Done",
|
||||
@@ -19,12 +18,7 @@ export default function BoardView({
|
||||
tasks,
|
||||
setTasks,
|
||||
createTask,
|
||||
}: {
|
||||
tags: Record<string, { color: Color; name: string }>;
|
||||
tasks: Task[];
|
||||
setTasks: SetState<Task[]>;
|
||||
createTask: (status: string) => void;
|
||||
}) {
|
||||
}: TaskViewProps) {
|
||||
function onDragEnd(result: any) {
|
||||
const { draggableId, source, destination } = result;
|
||||
if (!destination) return;
|
||||
@@ -56,7 +50,7 @@ export default function BoardView({
|
||||
<div className="w-full h-full flex flex-col gap-6">
|
||||
<DragDropContext onDragEnd={onDragEnd}>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 items-start">
|
||||
{Object.keys(columnLabels).map((status) => (
|
||||
{Object.keys(statusLabels).map((status) => (
|
||||
<Droppable key={status} droppableId={status}>
|
||||
{(provided) => (
|
||||
<Card
|
||||
@@ -66,7 +60,7 @@ export default function BoardView({
|
||||
>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center justify-between">
|
||||
<span>{columnLabels[status]}</span>
|
||||
<span>{statusLabels[status]}</span>
|
||||
<Button
|
||||
size="sm"
|
||||
className="ml-auto"
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
"use client";
|
||||
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCaption,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { TaskViewProps } from "../TaskView";
|
||||
|
||||
export function TableView({ tasks, setTasks }: TaskViewProps) {
|
||||
return (
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="min-w-[200px]">Title</TableHead>
|
||||
<TableHead>Status</TableHead>
|
||||
<TableHead>Description</TableHead>
|
||||
<TableHead>Tags</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{tasks.length === 0 ? (
|
||||
<TableRow>
|
||||
<TableCell
|
||||
colSpan={5}
|
||||
className="text-center text-muted-foreground"
|
||||
>
|
||||
No tasks yet
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : (
|
||||
tasks.map((task) => (
|
||||
<TableRow key={task.id}>
|
||||
<TableCell className="font-medium">{task.title}</TableCell>
|
||||
<TableCell>
|
||||
<Badge
|
||||
variant={
|
||||
task.status === "done"
|
||||
? "default"
|
||||
: task.status === "in-progress"
|
||||
? "secondary"
|
||||
: "outline"
|
||||
}
|
||||
>
|
||||
{task.status}
|
||||
</Badge>
|
||||
</TableCell>
|
||||
<TableCell className="max-w-[250px] truncate">
|
||||
{task.description || "-"}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{task.tags?.map((tag) => (
|
||||
<Badge key={tag} variant="outline">
|
||||
{tag}
|
||||
</Badge>
|
||||
))}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user