Components
Loading preview...
Here is IssueGrid component
npx shadcn@latest add https://21st.dev/r/ln-dev7/issue-grid// demo.tsx
'use client';
import * as React from 'react';
import { DndProvider, useDrop } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
import IssueGrid, { CustomDragLayer, IssueDragType } from '@/components/ui/issue-grid';
import type { Issue } from '@/components/ui/issue-grid';
import { Circle, CircleDot, Signal, Component } from 'lucide-react';
const initialIssues: Record<string, Issue[]> = {
todo: [
{
id: '1',
identifier: 'PROJ-101',
title: 'Implement user authentication flow',
status: { icon: Circle },
priority: { icon: Signal },
labels: [{ name: 'Feature', color: '#0e8a16' }],
project: { name: 'WebApp', icon: Component },
assignee: { name: 'Alice', avatarUrl: 'https://i.pravatar.cc/150?u=a' },
createdAt: '2023-10-26T10:00:00Z',
},
{
id: '2',
identifier: 'PROJ-102',
title: 'Fix critical bug in payment gateway',
status: { icon: Circle },
priority: { icon: Signal },
labels: [{ name: 'Bug', color: '#d73a4a' }],
assignee: null,
createdAt: '2023-10-25T14:30:00Z',
},
],
inProgress: [
{
id: '3',
identifier: 'PROJ-103',
title: 'Design new marketing landing page',
status: { icon: CircleDot },
priority: { icon: Signal },
labels: [{ name: 'Design', color: '#d876e3' }],
project: { name: 'Website', icon: Component },
assignee: { name: 'Bob', avatarUrl: 'https://i.pravatar.cc/150?u=b' },
createdAt: '2023-10-24T09:00:00Z',
},
],
};
const Column: React.FC<{
title: string;
status: string;
issues: Issue[];
className?: string;
}> = ({ title, status, issues, className }) => {
const [, drop] = useDrop(() => ({
accept: IssueDragType,
drop: () => ({ status }),
}));
return (
<div ref={drop} className="flex h-full w-80 shrink-0 flex-col">
<div className="mb-4 flex items-center justify-between">
<h3 className="font-semibold">{title}</h3>
<span className="text-sm text-muted-foreground">{issues.length}</span>
</div>
<div className="flex flex-col gap-3 overflow-y-auto">{issues.map((issue) => (
<IssueGrid key={issue.id} issue={issue} />
))}</div>
</div>
);
};
const IssueGridDemo = () => {
const [issues, setIssues] = React.useState(initialIssues);
const handleDrop = (item: Issue, status: string) => {
setIssues((prev) => {
const newIssues = { ...prev };
// Remove from all lists
Object.keys(newIssues).forEach((key) => {
newIssues[key] = newIssues[key].filter((i) => i.id !== item.id);
});
// Add to the new list
newIssues[status] = [...newIssues[status], item];
return newIssues;
});
};
return (
<DndProvider backend={HTML5Backend}>
<CustomDragLayer />
<div className="flex w-full items-center justify-center p-4">
<div className="flex gap-8">
<Column title="To Do" status="todo" issues={issues.todo} />
<Column title="In Progress" status="inProgress" issues={issues.inProgress} />
</div>
</div>
</DndProvider>
);
};
export { IssueGridDemo as DemoOne };