Skip to content

Commit 8ebd1cc

Browse files
authored
Merge pull request #119 from ArjinAlbay/main
Fix kanban remove and delete task sync issues
2 parents 2c74652 + d75dcc8 commit 8ebd1cc

File tree

7 files changed

+467
-18
lines changed

7 files changed

+467
-18
lines changed

package-lock.json

Lines changed: 35 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"@radix-ui/react-slot": "^1.2.3",
2222
"@radix-ui/react-switch": "^1.2.5",
2323
"@radix-ui/react-tabs": "^1.1.12",
24+
"@radix-ui/react-tooltip": "^1.2.8",
2425
"@tanstack/react-table": "^8.21.3",
2526
"chart.js": "^4.5.0",
2627
"class-variance-authority": "^0.7.1",

src/app/action-required/page.tsx

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,10 @@ import {
2727
LucideIcon,
2828
} from "lucide-react";
2929
import { useActionItemsStore } from "@/stores";
30+
import type { ActionItem as StoreActionItem } from "@/stores/actionItems";
3031
import { PageHeader } from "@/components/layout/PageHeader";
3132
import { SearchModal } from "@/components/search/SearchModal";
33+
import { AddToKanbanButton } from "@/components/action-required/AddToKanbanButton";
3234

3335
interface ActionItem {
3436
id: string | number;
@@ -142,11 +144,12 @@ function ActionRequiredContent() {
142144
<Table>
143145
<TableHeader>
144146
<TableRow>
145-
<TableHead className="w-[40%]">Title / Repository</TableHead>
146-
<TableHead className="w-[15%]">Priority</TableHead>
147-
<TableHead className="w-[15%]">Activity</TableHead>
148-
<TableHead className="w-[15%]">Repo Popularity</TableHead>
149-
<TableHead className="w-[15%]">Type</TableHead>
147+
<TableHead className="w-[35%]">Title / Repository</TableHead>
148+
<TableHead className="w-[12%]">Priority</TableHead>
149+
<TableHead className="w-[12%]">Activity</TableHead>
150+
<TableHead className="w-[13%]">Repo Popularity</TableHead>
151+
<TableHead className="w-[13%]">Type</TableHead>
152+
<TableHead className="w-[15%]">Actions</TableHead>
150153
</TableRow>
151154
</TableHeader>
152155
<TableBody>
@@ -170,6 +173,9 @@ function ActionRequiredContent() {
170173
<TableCell>
171174
<div className="w-16 h-6 bg-gray-300 dark:bg-gray-600 rounded animate-pulse" />
172175
</TableCell>
176+
<TableCell>
177+
<div className="w-8 h-8 bg-gray-300 dark:bg-gray-600 rounded animate-pulse" />
178+
</TableCell>
173179
</TableRow>
174180
))}
175181
</TableBody>
@@ -215,11 +221,12 @@ function ActionRequiredContent() {
215221
<Table>
216222
<TableHeader>
217223
<TableRow>
218-
<TableHead className="w-[40%]">Title / Repository</TableHead>
219-
<TableHead className="w-[15%]">Priority</TableHead>
220-
<TableHead className="w-[15%]">Activity</TableHead>
221-
<TableHead className="w-[15%]">Repo Popularity</TableHead>
222-
<TableHead className="w-[15%]">Type</TableHead>
224+
<TableHead className="w-[35%]">Title / Repository</TableHead>
225+
<TableHead className="w-[12%]">Priority</TableHead>
226+
<TableHead className="w-[12%]">Activity</TableHead>
227+
<TableHead className="w-[13%]">Repo Popularity</TableHead>
228+
<TableHead className="w-[13%]">Type</TableHead>
229+
<TableHead className="w-[15%]">Actions</TableHead>
223230
</TableRow>
224231
</TableHeader>
225232
<TableBody>
@@ -284,6 +291,9 @@ function ActionRequiredContent() {
284291
{item.type === "pullRequest" ? "PR" : "Issue"}
285292
</Badge>
286293
</TableCell>
294+
<TableCell>
295+
<AddToKanbanButton item={item as StoreActionItem} />
296+
</TableCell>
287297
</TableRow>
288298
))}
289299
</TableBody>
Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
"use client";
2+
3+
import { useState } from "react";
4+
import { Button } from "@/components/ui/button";
5+
import {
6+
Dialog,
7+
DialogContent,
8+
DialogDescription,
9+
DialogHeader,
10+
DialogTitle,
11+
DialogTrigger,
12+
DialogFooter,
13+
} from "@/components/ui/dialog";
14+
import {
15+
Select,
16+
SelectContent,
17+
SelectItem,
18+
SelectTrigger,
19+
SelectValue,
20+
} from "@/components/ui/select";
21+
import { Textarea } from "@/components/ui/textarea";
22+
import { Label } from "@/components/ui/label";
23+
import {
24+
Tooltip,
25+
TooltipContent,
26+
TooltipProvider,
27+
TooltipTrigger,
28+
} from "@/components/ui/tooltip";
29+
import { Plus, CheckCircle } from "lucide-react";
30+
import { useKanbanStore } from "@/stores";
31+
import type {
32+
ActionItem,
33+
AssignedItem,
34+
MentionItem,
35+
StalePR,
36+
} from "@/stores/actionItems";
37+
import { useRouter } from "next/navigation";
38+
39+
interface AddToKanbanButtonProps {
40+
item: ActionItem | AssignedItem | MentionItem | StalePR;
41+
}
42+
43+
export function AddToKanbanButton({ item }: AddToKanbanButtonProps) {
44+
const [open, setOpen] = useState(false);
45+
const [confirmOpen, setConfirmOpen] = useState(false);
46+
const [notes, setNotes] = useState("");
47+
const [selectedColumn, setSelectedColumn] = useState("todo");
48+
const [isAdding, setIsAdding] = useState(false);
49+
const [isRemoving, setIsRemoving] = useState(false);
50+
const [showSuccess, setShowSuccess] = useState(false);
51+
52+
const {
53+
addTaskFromActionItem,
54+
columns,
55+
columnOrder,
56+
isActionItemAdded,
57+
removeActionItemFromKanban,
58+
} = useKanbanStore();
59+
const router = useRouter();
60+
61+
const isAlreadyAdded = isActionItemAdded(item.id.toString());
62+
63+
const handleAdd = () => {
64+
setIsAdding(true);
65+
try {
66+
addTaskFromActionItem(item, notes, selectedColumn);
67+
setShowSuccess(true);
68+
setTimeout(() => {
69+
setOpen(false);
70+
setNotes("");
71+
setSelectedColumn("todo");
72+
setShowSuccess(false);
73+
}, 1500);
74+
} catch (error) {
75+
console.error("Failed to add task to kanban:", error);
76+
} finally {
77+
setIsAdding(false);
78+
}
79+
};
80+
81+
const handleRemove = () => {
82+
setIsRemoving(true);
83+
try {
84+
removeActionItemFromKanban(item.id.toString());
85+
setConfirmOpen(false);
86+
} catch (error) {
87+
console.error("Failed to remove task from kanban:", error);
88+
} finally {
89+
setIsRemoving(false);
90+
}
91+
};
92+
93+
const handleViewKanban = () => {
94+
setOpen(false);
95+
router.push("/dashboard");
96+
};
97+
98+
if (isAlreadyAdded) {
99+
return (
100+
<TooltipProvider delayDuration={0}>
101+
<Tooltip>
102+
<TooltipTrigger asChild>
103+
<Button
104+
variant="ghost"
105+
size="sm"
106+
className="h-8 w-8 p-0 text-green-600 dark:text-green-400 hover:text-green-700 dark:hover:text-green-300"
107+
onClick={() => setConfirmOpen(true)}
108+
>
109+
<CheckCircle className="h-4 w-4" />
110+
</Button>
111+
</TooltipTrigger>
112+
<TooltipContent>
113+
<p>Click to remove from Kanban</p>
114+
</TooltipContent>
115+
</Tooltip>
116+
117+
<Dialog open={confirmOpen} onOpenChange={setConfirmOpen}>
118+
<DialogContent className="sm:max-w-[425px]">
119+
<DialogHeader>
120+
<DialogTitle>Remove from Kanban?</DialogTitle>
121+
<DialogDescription>
122+
This will remove the task from your personal Kanban board. You
123+
can add it back later if needed.
124+
</DialogDescription>
125+
</DialogHeader>
126+
127+
<div className="py-4">
128+
<div className="p-3 rounded-md bg-gray-50 dark:bg-gray-800 border">
129+
<p className="font-medium">{item.title}</p>
130+
<p className="text-sm text-gray-500 dark:text-gray-400 mt-1">
131+
{item.repo}
132+
</p>
133+
</div>
134+
</div>
135+
136+
<DialogFooter>
137+
<Button
138+
variant="outline"
139+
onClick={() => setConfirmOpen(false)}
140+
disabled={isRemoving}
141+
>
142+
Cancel
143+
</Button>
144+
<Button
145+
variant="destructive"
146+
onClick={handleRemove}
147+
disabled={isRemoving}
148+
>
149+
{isRemoving ? "Removing..." : "Remove from Kanban"}
150+
</Button>
151+
</DialogFooter>
152+
</DialogContent>
153+
</Dialog>
154+
</TooltipProvider>
155+
);
156+
}
157+
158+
return (
159+
<TooltipProvider delayDuration={0}>
160+
<Tooltip>
161+
<Dialog open={open} onOpenChange={setOpen}>
162+
<TooltipTrigger asChild>
163+
<DialogTrigger asChild>
164+
<Button
165+
variant="ghost"
166+
size="sm"
167+
className="h-8 w-8 p-0"
168+
>
169+
<Plus className="h-4 w-4" />
170+
</Button>
171+
</DialogTrigger>
172+
</TooltipTrigger>
173+
<TooltipContent>
174+
<p>Add to Kanban</p>
175+
</TooltipContent>
176+
<DialogContent className="sm:max-w-[525px]">
177+
<DialogHeader>
178+
<DialogTitle>Add to Personal Kanban</DialogTitle>
179+
<DialogDescription>
180+
Add this item to your personal task board on the Dashboard
181+
</DialogDescription>
182+
</DialogHeader>
183+
184+
{showSuccess ? (
185+
<div className="flex flex-col items-center justify-center py-8 space-y-4">
186+
<CheckCircle className="w-16 h-16 text-green-500" />
187+
<p className="text-lg font-medium">Added to Kanban!</p>
188+
<Button variant="outline" onClick={handleViewKanban}>
189+
View Dashboard
190+
</Button>
191+
</div>
192+
) : (
193+
<>
194+
<div className="space-y-4 py-4">
195+
<div className="space-y-2">
196+
<Label className="text-sm font-medium">Task</Label>
197+
<div className="p-3 rounded-md bg-gray-50 dark:bg-gray-800 border">
198+
<p className="font-medium">{item.title}</p>
199+
<p className="text-sm text-gray-500 dark:text-gray-400 mt-1">
200+
{item.repo}
201+
</p>
202+
</div>
203+
</div>
204+
205+
<div className="space-y-2">
206+
<Label htmlFor="column" className="text-sm font-medium">
207+
Column
208+
</Label>
209+
<Select
210+
value={selectedColumn}
211+
onValueChange={setSelectedColumn}
212+
>
213+
<SelectTrigger id="column">
214+
<SelectValue placeholder="Select column" />
215+
</SelectTrigger>
216+
<SelectContent>
217+
{columnOrder.map((columnId: string) => {
218+
const column = columns[columnId];
219+
return (
220+
<SelectItem key={columnId} value={columnId}>
221+
<div className="flex items-center gap-2">
222+
<div
223+
className="w-3 h-3 rounded-full"
224+
style={{ backgroundColor: column.color }}
225+
/>
226+
{column.title}
227+
</div>
228+
</SelectItem>
229+
);
230+
})}
231+
</SelectContent>
232+
</Select>
233+
</div>
234+
235+
<div className="space-y-2">
236+
<Label htmlFor="notes" className="text-sm font-medium">
237+
Notes (Optional)
238+
</Label>
239+
<Textarea
240+
id="notes"
241+
placeholder="Add notes about this task..."
242+
value={notes}
243+
onChange={(e) => setNotes(e.target.value)}
244+
rows={4}
245+
className="resize-none"
246+
/>
247+
</div>
248+
</div>
249+
250+
<DialogFooter>
251+
<Button
252+
variant="outline"
253+
onClick={() => setOpen(false)}
254+
disabled={isAdding}
255+
>
256+
Cancel
257+
</Button>
258+
<Button onClick={handleAdd} disabled={isAdding}>
259+
{isAdding ? "Adding..." : "Add to Kanban"}
260+
</Button>
261+
</DialogFooter>
262+
</>
263+
)}
264+
</DialogContent>
265+
</Dialog>
266+
</Tooltip>
267+
</TooltipProvider>
268+
);
269+
}

0 commit comments

Comments
 (0)