-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #9 from Hasnainahmad04/feat/board
feat: created basic ui for kanban board
- Loading branch information
Showing
26 changed files
with
7,171 additions
and
4,592 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import prisma from '@/prisma/client'; | ||
|
||
export const getIssueDetail = async (id: number) => { | ||
const issue = await prisma.issue.findUnique({ where: { id } }); | ||
return issue; | ||
}; | ||
|
||
export const getAllIssues = async () => { | ||
const issues = await prisma.issue.findMany(); | ||
return issues; | ||
}; |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
'use client'; | ||
|
||
import type { DragEndEvent } from '@dnd-kit/core'; | ||
import type { Issue, Status } from '@prisma/client'; | ||
import React, { useState } from 'react'; | ||
|
||
import useUpdateIssue from '@/hooks/issue/useUpdateIssue'; | ||
|
||
import Column from './Column'; | ||
import { KanbanBoard, KanbanBoardContainer } from './Kanban'; | ||
import KanbanCard from './KanbanCard'; | ||
import KanbanItem from './KanbanItem'; | ||
|
||
type Props = { | ||
data: Issue[]; | ||
}; | ||
|
||
const Board = ({ data }: React.PropsWithChildren<Props>) => { | ||
const initialState: Record<Status, Issue[]> = { | ||
TODO: [], | ||
BACKLOG: [], | ||
IN_PROGRESS: [], | ||
DONE: [], | ||
CANCELLED: [], | ||
}; | ||
const { mutate: updateIssueStatus } = useUpdateIssue(); | ||
const [taskStages, setTaskStages] = useState({ | ||
...initialState, | ||
...Object.groupBy(data, (item) => item.status), | ||
}); | ||
|
||
const handleOnDragEnd = (event: DragEndEvent) => { | ||
const status = event.over?.id as undefined | Status | null; | ||
const taskId = event.active.id as string; | ||
const issue = event.active.data.current?.issue as Issue; | ||
const prevStatus = event.active.data.current?.prevStatus as Status; | ||
|
||
if (status) { | ||
const state = { ...taskStages }; | ||
const prevStatusState = [...state[prevStatus]]; | ||
const updatedPrevState = prevStatusState.filter( | ||
(item) => item.id !== Number(taskId), | ||
); | ||
state[prevStatus] = updatedPrevState; | ||
state[status].push({ ...issue, status }); | ||
|
||
setTaskStages(state); | ||
updateIssueStatus( | ||
{ id: Number(taskId), data: { ...issue, status } }, | ||
|
||
{ | ||
onSuccess: () => { | ||
alert('Status Updated'); | ||
}, | ||
onError: () => { | ||
setTaskStages(taskStages); | ||
}, | ||
}, | ||
); | ||
} | ||
}; | ||
|
||
return ( | ||
<KanbanBoardContainer> | ||
<KanbanBoard onDragEnd={handleOnDragEnd}> | ||
{taskStages && | ||
Object.entries(taskStages).map(([status, issues]) => ( | ||
<Column key={status} id={status} title={status as Status}> | ||
{issues.map((issue) => ( | ||
<KanbanItem | ||
key={issue.id} | ||
id={issue.id.toString()} | ||
data={{ issue, prevStatus: status }} | ||
> | ||
<KanbanCard issue={issue} /> | ||
</KanbanItem> | ||
))} | ||
</Column> | ||
))} | ||
</KanbanBoard> | ||
</KanbanBoardContainer> | ||
); | ||
}; | ||
|
||
export default Board; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import type { UseDroppableArguments } from '@dnd-kit/core'; | ||
import { useDroppable } from '@dnd-kit/core'; | ||
import type { Status } from '@prisma/client'; | ||
|
||
import { cn } from '@/lib/utils'; | ||
|
||
type Props = { | ||
id: string; | ||
title: Status; | ||
data?: UseDroppableArguments['data']; | ||
}; | ||
|
||
const statusMetadata: Record<Status, { title: string; background: string }> = { | ||
TODO: { background: 'bg-neutral-500', title: 'Todo' }, | ||
IN_PROGRESS: { background: 'bg-sky-500', title: 'In Progress' }, | ||
CANCELLED: { background: 'bg-red-500', title: 'Cancelled' }, | ||
DONE: { background: 'bg-green-500', title: 'Done' }, | ||
BACKLOG: { background: 'bg-yellow-500', title: 'Backlog' }, | ||
}; | ||
|
||
const Column = ({ | ||
id, | ||
title, | ||
data, | ||
children, | ||
}: React.PropsWithChildren<Props>) => { | ||
const { isOver, setNodeRef, active } = useDroppable({ id, data }); | ||
|
||
return ( | ||
<div ref={setNodeRef} className="flex w-96 flex-col px-4"> | ||
<div | ||
className={cn( | ||
'flex w-full items-center justify-between rounded-lg px-3 py-2', | ||
statusMetadata[title].background, | ||
)} | ||
> | ||
<span | ||
className="truncate text-xs font-bold uppercase text-white" | ||
title={title} | ||
> | ||
{statusMetadata[title].title} | ||
</span> | ||
{/* {Boolean(count) && ( | ||
<span className="inline-flex size-6 items-center justify-center rounded-full bg-neutral-100 text-xs text-zinc-800"> | ||
{count} | ||
</span> | ||
)} */} | ||
</div> | ||
<div | ||
style={{ overflowY: active ? 'unset' : 'auto' }} | ||
className={`column-scrollbar flex h-screen flex-col gap-2 rounded-md border-2 border-dashed p-1 ${isOver ? 'border-gray-500' : 'border-transparent'}`} | ||
> | ||
{children} | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
export default Column; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import type { DragEndEvent } from '@dnd-kit/core'; | ||
import { | ||
DndContext, | ||
MouseSensor, | ||
TouchSensor, | ||
useSensor, | ||
useSensors, | ||
} from '@dnd-kit/core'; | ||
import React from 'react'; | ||
|
||
export const KanbanBoardContainer = ({ children }: React.PropsWithChildren) => { | ||
return ( | ||
<div className="flex h-[100vh-64px] w-full flex-col"> | ||
<div className="flex h-[90vh] w-full overflow-auto p-8">{children}</div> | ||
</div> | ||
); | ||
}; | ||
|
||
type Props = { | ||
onDragEnd: (event: DragEndEvent) => void; | ||
}; | ||
|
||
export const KanbanBoard = ({ | ||
children, | ||
onDragEnd, | ||
}: React.PropsWithChildren<Props>) => { | ||
const mouseSensor = useSensor(MouseSensor, { | ||
activationConstraint: { | ||
distance: 5, | ||
}, | ||
}); | ||
|
||
const touchSensor = useSensor(TouchSensor, { | ||
activationConstraint: { | ||
distance: 5, | ||
}, | ||
}); | ||
|
||
const sensors = useSensors(mouseSensor, touchSensor); | ||
|
||
return ( | ||
<DndContext onDragEnd={onDragEnd} sensors={sensors}> | ||
{children} | ||
</DndContext> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import type { Issue } from '@prisma/client'; | ||
|
||
const KanbanCard = ({ issue }: { issue: Issue }) => { | ||
return ( | ||
<div className="divide-y rounded-lg border border-neutral-200 bg-white"> | ||
<span className="block p-2 text-sm font-medium text-zinc-900"> | ||
{issue.title} | ||
</span> | ||
</div> | ||
); | ||
}; | ||
|
||
export default KanbanCard; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import type { UseDraggableArguments } from '@dnd-kit/core'; | ||
import { DragOverlay, useDraggable } from '@dnd-kit/core'; | ||
|
||
interface Props { | ||
id: string; | ||
data?: UseDraggableArguments['data']; | ||
} | ||
|
||
const KanbanItem = ({ children, id, data }: React.PropsWithChildren<Props>) => { | ||
const { attributes, listeners, setNodeRef, active } = useDraggable({ | ||
id, | ||
data, | ||
}); | ||
|
||
return ( | ||
<div className="relative"> | ||
<div | ||
ref={setNodeRef} | ||
{...attributes} | ||
{...listeners} | ||
className="relative cursor-grab rounded-lg" | ||
style={{ opacity: active && active.id !== id ? 0.5 : 1 }} | ||
> | ||
{active?.id === id && ( | ||
<DragOverlay zIndex={1000}> | ||
<div | ||
className="cursor-grabbing rounded-lg" | ||
style={{ boxShadow: 'rgba(149, 157, 165, 0.2) 0px 8px 24px' }} | ||
> | ||
{children} | ||
</div> | ||
</DragOverlay> | ||
)} | ||
{children} | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
export default KanbanItem; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { getAllIssues } from '@/app/actions/issue'; | ||
|
||
import Board from './Board'; | ||
|
||
export default async function Page() { | ||
const data = await getAllIssues(); | ||
|
||
return <Board data={data} />; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.