Skip to content

Commit 0672911

Browse files
authored
Merge pull request #87 from techulus/feat/delete-board
Add support for deleting roadmap board
2 parents 94df84b + 4df0647 commit 0672911

File tree

4 files changed

+68
-38
lines changed

4 files changed

+68
-38
lines changed

apps/page/pages/_sites/[site]/roadmap/[roadmap_slug].tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -260,10 +260,10 @@ export default function RoadmapPage({
260260
handleVote(item.id);
261261
}}
262262
disabled={votingItems.has(item.id)}
263-
className={`flex items-center text-xs px-2 py-1 rounded transition-colors ${
263+
className={`flex items-center text-xs px-2 py-1 rounded border transition-colors ${
264264
votes[item.id]?.voted
265-
? "bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200"
266-
: "text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-600"
265+
? "bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200 border-blue-300 dark:border-blue-700"
266+
: "text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-600 border-gray-200 dark:border-gray-600 hover:border-gray-300 dark:hover:border-gray-500"
267267
} ${
268268
votingItems.has(item.id)
269269
? "opacity-50 cursor-not-allowed"
@@ -420,10 +420,10 @@ export default function RoadmapPage({
420420
disabled={
421421
!selectedItem || votingItems.has(selectedItem.id)
422422
}
423-
className={`flex items-center text-sm px-3 py-1.5 rounded-lg transition-colors ${
423+
className={`flex items-center text-sm px-3 py-1.5 rounded-lg border transition-colors ${
424424
selectedItem && votes[selectedItem.id]?.voted
425-
? "bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200"
426-
: "text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-600"
425+
? "bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200 border-blue-300 dark:border-blue-700"
426+
: "text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-600 border-gray-200 dark:border-gray-600 hover:border-gray-300 dark:hover:border-gray-500"
427427
} ${
428428
!selectedItem || votingItems.has(selectedItem.id)
429429
? "opacity-50 cursor-not-allowed"

apps/web/components/roadmap/RoadmapItem.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ export default function RoadmapItem({
8686
</span>
8787
)}
8888
</div>
89-
<div className="flex items-center text-xs text-gray-500 dark:text-gray-400">
89+
<div className="flex items-center text-xs text-gray-500 dark:text-gray-400 border border-gray-200 dark:border-gray-600 rounded-md px-2 py-1 hover:border-gray-300 dark:hover:border-gray-500 transition-colors">
9090
<svg className="mr-1 h-4 w-4" fill="currentColor" viewBox="0 0 20 20">
9191
<path
9292
fillRule="evenodd"

apps/web/pages/pages/[page_id]/roadmap/[board_id]/settings.tsx

Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { PencilIcon, PlusIcon, TrashIcon } from "@heroicons/react/solid";
88
import { InferGetServerSidePropsType } from "next";
99
import { useRouter } from "next/router";
1010
import { useEffect, useMemo, useState, type JSX } from "react";
11+
import ConfirmDeleteDialog from "../../../../../components/dialogs/confirm-delete-dialog.component";
1112
import SwitchComponent from "../../../../../components/forms/switch.component";
1213
import AuthLayout from "../../../../../components/layout/auth-layout.component";
1314
import Page from "../../../../../components/layout/page.component";
@@ -153,6 +154,10 @@ export default function BoardSettings({
153154
const [draggedColumn, setDraggedColumn] = useState(null);
154155
const [dragOverIndex, setDragOverIndex] = useState(null);
155156

157+
// Delete board state
158+
const [showDeleteDialog, setShowDeleteDialog] = useState(false);
159+
const [isDeletingBoard, setIsDeletingBoard] = useState(false);
160+
156161
const tabs = [
157162
{
158163
name: "Settings",
@@ -303,7 +308,7 @@ export default function BoardSettings({
303308
}
304309
};
305310

306-
const handleUpdateCategory = async (categoryId) => {
311+
const handleUpdateCategory = async (categoryId: string) => {
307312
if (!categoryToEdit.trim() || !isPageOwner) return;
308313

309314
try {
@@ -355,7 +360,7 @@ export default function BoardSettings({
355360
}
356361
};
357362

358-
const handleDeleteCategory = async (categoryId) => {
363+
const handleDeleteCategory = async (categoryId: string) => {
359364
// Check if category has items
360365
try {
361366
const { data: itemsWithCategory, error: checkError } = await supabase
@@ -438,7 +443,7 @@ export default function BoardSettings({
438443
}
439444
};
440445

441-
const handleUpdateColumn = async (columnId) => {
446+
const handleUpdateColumn = async (columnId: string) => {
442447
if (!columnToEdit.trim() || !isPageOwner) return;
443448

444449
try {
@@ -478,7 +483,7 @@ export default function BoardSettings({
478483
}
479484
};
480485

481-
const handleDeleteColumn = async (columnId) => {
486+
const handleDeleteColumn = async (columnId: string) => {
482487
// Check if stage has items
483488
try {
484489
const { data: itemsInColumn, error: checkError } = await supabase
@@ -607,6 +612,38 @@ export default function BoardSettings({
607612
setDragOverIndex(null);
608613
};
609614

615+
// Delete board function
616+
const handleDeleteBoard = async () => {
617+
if (!isPageOwner) return;
618+
619+
setIsDeletingBoard(true);
620+
try {
621+
const { error } = await supabase
622+
.from("roadmap_boards")
623+
.delete()
624+
.eq("id", board.id)
625+
.eq("page_id", page_id);
626+
627+
if (error) throw error;
628+
629+
// Create audit log for board deletion
630+
await createAuditLog(supabase, {
631+
page_id: page_id,
632+
actor_id: user.id,
633+
action: `Deleted Roadmap Board: ${board.title}`,
634+
changes: { board: board },
635+
});
636+
637+
// Redirect to roadmap list
638+
router.push(`/pages/${page_id}/roadmap`);
639+
} catch (error) {
640+
console.error("Error deleting board:", error);
641+
alert("Failed to delete board");
642+
setIsDeletingBoard(false);
643+
}
644+
setShowDeleteDialog(false);
645+
};
646+
610647
if (!page_id || !board || !isPageOwner) {
611648
return <div>Access denied</div>;
612649
}
@@ -767,7 +804,15 @@ export default function BoardSettings({
767804
/>
768805
</div>
769806

770-
<div className="flex justify-end">
807+
<div className="flex justify-between items-center">
808+
<button
809+
type="button"
810+
onClick={() => setShowDeleteDialog(true)}
811+
className="inline-flex items-center justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-red-600 hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500"
812+
>
813+
<TrashIcon className="h-4 w-4 mr-2" />
814+
Delete Board
815+
</button>
771816
<button
772817
type="submit"
773818
disabled={isSavingBoard}
@@ -1044,6 +1089,17 @@ export default function BoardSettings({
10441089
</div>
10451090
)}
10461091
</div>
1092+
1093+
{/* Delete Board Confirmation Dialog */}
1094+
<ConfirmDeleteDialog
1095+
itemName={board.title}
1096+
open={showDeleteDialog}
1097+
setOpen={setShowDeleteDialog}
1098+
highRiskAction={true}
1099+
riskVerificationText={board.title}
1100+
deleteCallback={handleDeleteBoard}
1101+
processing={isDeletingBoard}
1102+
/>
10471103
</Page>
10481104
);
10491105
}

packages/supabase/migrations/18_roadmap.sql

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -111,32 +111,6 @@ alter table roadmap_votes add constraint unique_item_visitor unique (item_id, vi
111111

112112
alter table roadmap_votes enable row level security;
113113

114-
-- Function to get vote count for roadmap items
115-
CREATE OR REPLACE FUNCTION roadmap_item_votes_count(itemid uuid)
116-
RETURNS bigint
117-
AS $$
118-
BEGIN
119-
RETURN (
120-
SELECT COUNT(*)
121-
FROM roadmap_votes
122-
WHERE item_id = itemid
123-
);
124-
END;
125-
$$ LANGUAGE 'plpgsql';
126-
127-
-- Function to check if a visitor has voted for an item
128-
CREATE OR REPLACE FUNCTION roadmap_item_has_voted(itemid uuid, visitorid uuid)
129-
RETURNS boolean
130-
AS $$
131-
BEGIN
132-
RETURN EXISTS (
133-
SELECT 1
134-
FROM roadmap_votes
135-
WHERE item_id = itemid AND visitor_id = visitorid
136-
);
137-
END;
138-
$$ LANGUAGE 'plpgsql';
139-
140114
-- Function to initialize default columns for a roadmap board
141115
CREATE OR REPLACE FUNCTION initialize_roadmap_columns(board_id uuid)
142116
RETURNS void

0 commit comments

Comments
 (0)