beads-kanban-ui 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -222
- package/package.json +18 -55
- package/.designs/beads-kanban-ui-bj0.md +0 -73
- package/.designs/beads-kanban-ui-qxq.md +0 -144
- package/.designs/epic-support.md +0 -282
- package/.env.local.example +0 -2
- package/.eslintrc.json +0 -3
- package/.gitattributes +0 -3
- package/.github/workflows/release.yml +0 -123
- package/.history/README_20260121193710.md +0 -227
- package/.history/README_20260121193918.md +0 -227
- package/.history/README_20260121193921.md +0 -227
- package/.history/README_20260121193933.md +0 -227
- package/.history/README_20260121193934.md +0 -227
- package/.history/README_20260121193944.md +0 -227
- package/.history/README_20260121193953.md +0 -227
- package/.history/src/app/page_20260121133429.tsx +0 -134
- package/.history/src/app/page_20260121133928.tsx +0 -134
- package/.history/src/app/page_20260121144850.tsx +0 -138
- package/.history/src/app/page_20260121144854.tsx +0 -138
- package/.history/src/app/page_20260121144858.tsx +0 -138
- package/.history/src/app/page_20260121144902.tsx +0 -138
- package/.history/src/app/page_20260121144906.tsx +0 -138
- package/.history/src/app/page_20260121144911.tsx +0 -138
- package/.history/src/app/page_20260121144928.tsx +0 -138
- package/.playwright-mcp/.playwright-mcp/morphing-dialog-wheel-scroll-fix.png +0 -0
- package/.playwright-mcp/beams-test.png +0 -0
- package/.playwright-mcp/card-verification.png +0 -0
- package/.playwright-mcp/design-doc-dialog-fix-verification.png +0 -0
- package/.playwright-mcp/dialog-width-test.png +0 -0
- package/.playwright-mcp/homepage.png +0 -0
- package/.playwright-mcp/morphing-dialog-expanded.png +0 -0
- package/.playwright-mcp/morphing-dialog-fixes-final.png +0 -0
- package/.playwright-mcp/morphing-dialog-open.png +0 -0
- package/.playwright-mcp/page-2026-01-21T14-08-31-529Z.png +0 -0
- package/.playwright-mcp/page-2026-01-21T14-09-23-431Z.png +0 -0
- package/.playwright-mcp/page-2026-01-21T14-10-28-773Z.png +0 -0
- package/.playwright-mcp/page-2026-01-21T14-10-47-432Z.png +0 -0
- package/.playwright-mcp/page-2026-01-21T14-11-12-350Z.png +0 -0
- package/.playwright-mcp/screenshot-after-click.png +0 -0
- package/.playwright-mcp/screenshot-after-dialog-click.png +0 -0
- package/.playwright-mcp/sheet-restored-after-dialog-close.png +0 -0
- package/.playwright-mcp/test-1-sheet-open-with-overlay.png +0 -0
- package/.playwright-mcp/test-2-morphing-dialog-with-overlay.png +0 -0
- package/.playwright-mcp/test-3-sheet-open-dark-overlay.png +0 -0
- package/.playwright-mcp/test-4-morphing-dialog-with-dark-overlay.png +0 -0
- package/.playwright-mcp/test-5-morphing-dialog-scrolled.png +0 -0
- package/.playwright-mcp/test-6-sheet-restored-after-dialog-close.png +0 -0
- package/.playwright-mcp/wheel-scroll-fixed.png +0 -0
- package/Screenshots/bead-detail.png +0 -0
- package/Screenshots/dashboard.png +0 -0
- package/Screenshots/kanban-board.png +0 -0
- package/components.json +0 -27
- package/logo/logo.svg +0 -1
- package/next.config.js +0 -9
- package/npm/README.md +0 -37
- package/npm/package.json +0 -20
- package/postcss.config.js +0 -6
- package/public/logo.svg +0 -1
- package/restart.sh +0 -5
- package/server/Cargo.lock +0 -1685
- package/server/Cargo.toml +0 -24
- package/server/src/db.rs +0 -570
- package/server/src/main.rs +0 -141
- package/server/src/routes/beads.rs +0 -413
- package/server/src/routes/cli.rs +0 -150
- package/server/src/routes/fs.rs +0 -360
- package/server/src/routes/git.rs +0 -169
- package/server/src/routes/mod.rs +0 -107
- package/server/src/routes/projects.rs +0 -177
- package/server/src/routes/watch.rs +0 -211
- package/src/app/globals.css +0 -101
- package/src/app/layout.tsx +0 -36
- package/src/app/page.tsx +0 -348
- package/src/app/project/kanban-board.tsx +0 -356
- package/src/app/project/page.tsx +0 -18
- package/src/app/settings/page.tsx +0 -224
- package/src/components/Beams.css +0 -5
- package/src/components/Beams.jsx +0 -307
- package/src/components/Galaxy.css +0 -5
- package/src/components/Galaxy.jsx +0 -333
- package/src/components/activity-timeline.tsx +0 -172
- package/src/components/add-project-dialog.tsx +0 -219
- package/src/components/bead-card.tsx +0 -196
- package/src/components/bead-detail.tsx +0 -306
- package/src/components/color-picker.tsx +0 -101
- package/src/components/comment-input.tsx +0 -155
- package/src/components/comment-list.tsx +0 -147
- package/src/components/dependency-badge.tsx +0 -106
- package/src/components/design-doc-dialog.tsx +0 -58
- package/src/components/design-doc-preview.tsx +0 -97
- package/src/components/design-doc-viewer.tsx +0 -199
- package/src/components/editable-project-name.tsx +0 -178
- package/src/components/epic-card.tsx +0 -263
- package/src/components/folder-browser.tsx +0 -273
- package/src/components/footer.tsx +0 -27
- package/src/components/kanban/default.tsx +0 -184
- package/src/components/kanban-column.tsx +0 -167
- package/src/components/project-card.tsx +0 -191
- package/src/components/quick-filter-bar.tsx +0 -279
- package/src/components/scan-directory-dialog.tsx +0 -368
- package/src/components/status-donut.tsx +0 -197
- package/src/components/subtask-list.tsx +0 -128
- package/src/components/tag-picker.tsx +0 -252
- package/src/components/ui/.gitkeep +0 -0
- package/src/components/ui/alert-dialog.tsx +0 -141
- package/src/components/ui/avatar.tsx +0 -67
- package/src/components/ui/badge.tsx +0 -230
- package/src/components/ui/button.tsx +0 -433
- package/src/components/ui/card/index.tsx +0 -24
- package/src/components/ui/card/roiui-card.module.css +0 -197
- package/src/components/ui/card/roiui-card.tsx +0 -154
- package/src/components/ui/card/shadcn-card.tsx +0 -76
- package/src/components/ui/chart.tsx +0 -369
- package/src/components/ui/dialog.tsx +0 -122
- package/src/components/ui/dropdown-menu.tsx +0 -201
- package/src/components/ui/input.tsx +0 -22
- package/src/components/ui/kanban.tsx +0 -522
- package/src/components/ui/morphing-dialog.tsx +0 -457
- package/src/components/ui/popover.tsx +0 -33
- package/src/components/ui/progress.tsx +0 -28
- package/src/components/ui/scroll-area.tsx +0 -48
- package/src/components/ui/select.tsx +0 -159
- package/src/components/ui/separator.tsx +0 -31
- package/src/components/ui/sheet.tsx +0 -142
- package/src/components/ui/skeleton.tsx +0 -15
- package/src/components/ui/toast.tsx +0 -129
- package/src/components/ui/toaster.tsx +0 -35
- package/src/components/ui/tooltip.tsx +0 -30
- package/src/hooks/.gitkeep +0 -0
- package/src/hooks/use-bead-filters.ts +0 -261
- package/src/hooks/use-beads.ts +0 -162
- package/src/hooks/use-branch-statuses.ts +0 -161
- package/src/hooks/use-epics.ts +0 -173
- package/src/hooks/use-file-watcher.ts +0 -111
- package/src/hooks/use-keyboard-navigation.ts +0 -282
- package/src/hooks/use-project.ts +0 -61
- package/src/hooks/use-projects.ts +0 -93
- package/src/hooks/use-toast.ts +0 -194
- package/src/hooks/useClickOutside.tsx +0 -26
- package/src/lib/.gitkeep +0 -0
- package/src/lib/api.ts +0 -186
- package/src/lib/beads-parser.ts +0 -252
- package/src/lib/cli.ts +0 -193
- package/src/lib/db.ts +0 -145
- package/src/lib/design-doc.ts +0 -74
- package/src/lib/epic-parser.ts +0 -242
- package/src/lib/git.ts +0 -102
- package/src/lib/utils.ts +0 -12
- package/src/types/index.ts +0 -107
- package/tailwind.config.ts +0 -85
- package/tsconfig.json +0 -26
- /package/{npm/bin → bin}/cli.js +0 -0
- /package/{npm/scripts → scripts}/postinstall.js +0 -0
package/src/app/page.tsx
DELETED
|
@@ -1,348 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { useMemo, useState } from "react";
|
|
4
|
-
import Link from "next/link";
|
|
5
|
-
import { Plus, ChevronDown, FolderPlus, FolderSearch, Github, Settings, Search, X } from "lucide-react";
|
|
6
|
-
import { ProjectCard } from "@/components/project-card";
|
|
7
|
-
import { AddProjectDialog } from "@/components/add-project-dialog";
|
|
8
|
-
import { ScanDirectoryDialog } from "@/components/scan-directory-dialog";
|
|
9
|
-
import { useProjects } from "@/hooks/use-projects";
|
|
10
|
-
import { useToast } from "@/hooks/use-toast";
|
|
11
|
-
import { Skeleton } from "@/components/ui/skeleton";
|
|
12
|
-
import { Button, ButtonArrow } from "@/components/ui/button";
|
|
13
|
-
import { Input } from "@/components/ui/input";
|
|
14
|
-
import { Badge } from "@/components/ui/badge";
|
|
15
|
-
import {
|
|
16
|
-
DropdownMenu,
|
|
17
|
-
DropdownMenuContent,
|
|
18
|
-
DropdownMenuItem,
|
|
19
|
-
DropdownMenuTrigger,
|
|
20
|
-
} from "@/components/ui/dropdown-menu";
|
|
21
|
-
|
|
22
|
-
export default function ProjectsPage() {
|
|
23
|
-
const [isAddDialogOpen, setIsAddDialogOpen] = useState(false);
|
|
24
|
-
const [isScanDialogOpen, setIsScanDialogOpen] = useState(false);
|
|
25
|
-
const [searchQuery, setSearchQuery] = useState("");
|
|
26
|
-
const [selectedTagIds, setSelectedTagIds] = useState<string[]>([]);
|
|
27
|
-
const { projects, isLoading, error, addProject, updateProjectTags } = useProjects();
|
|
28
|
-
const { toast } = useToast();
|
|
29
|
-
|
|
30
|
-
// Get all unique tags across projects
|
|
31
|
-
const allTags = useMemo(() => {
|
|
32
|
-
const tagMap = new Map<string, { id: string; name: string; color: string }>();
|
|
33
|
-
projects.forEach((project) => {
|
|
34
|
-
project.tags.forEach((tag) => {
|
|
35
|
-
if (!tagMap.has(tag.id)) {
|
|
36
|
-
tagMap.set(tag.id, tag);
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
|
-
});
|
|
40
|
-
return Array.from(tagMap.values()).sort((a, b) => a.name.localeCompare(b.name));
|
|
41
|
-
}, [projects]);
|
|
42
|
-
|
|
43
|
-
// Filter projects by search query and selected tags (AND logic)
|
|
44
|
-
const filteredProjects = useMemo(() => {
|
|
45
|
-
return projects.filter((project) => {
|
|
46
|
-
// Search filter - match name or path
|
|
47
|
-
const searchLower = searchQuery.toLowerCase().trim();
|
|
48
|
-
const matchesSearch = searchLower === "" ||
|
|
49
|
-
project.name.toLowerCase().includes(searchLower) ||
|
|
50
|
-
project.path.toLowerCase().includes(searchLower);
|
|
51
|
-
|
|
52
|
-
// Tag filter - AND logic: project must have ALL selected tags
|
|
53
|
-
const matchesTags = selectedTagIds.length === 0 ||
|
|
54
|
-
selectedTagIds.every((tagId) =>
|
|
55
|
-
project.tags.some((tag) => tag.id === tagId)
|
|
56
|
-
);
|
|
57
|
-
|
|
58
|
-
return matchesSearch && matchesTags;
|
|
59
|
-
});
|
|
60
|
-
}, [projects, searchQuery, selectedTagIds]);
|
|
61
|
-
|
|
62
|
-
const toggleTag = (tagId: string) => {
|
|
63
|
-
setSelectedTagIds((prev) =>
|
|
64
|
-
prev.includes(tagId)
|
|
65
|
-
? prev.filter((id) => id !== tagId)
|
|
66
|
-
: [...prev, tagId]
|
|
67
|
-
);
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
const clearFilters = () => {
|
|
71
|
-
setSearchQuery("");
|
|
72
|
-
setSelectedTagIds([]);
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
const hasActiveFilters = searchQuery.trim() !== "" || selectedTagIds.length > 0;
|
|
76
|
-
|
|
77
|
-
const handleAddProject = async (input: { name: string; path: string }) => {
|
|
78
|
-
// Check for duplicate
|
|
79
|
-
const isDuplicate = projects.some(p => p.path === input.path);
|
|
80
|
-
if (isDuplicate) {
|
|
81
|
-
toast({
|
|
82
|
-
title: "Project already exists",
|
|
83
|
-
description: "This project is already in your dashboard.",
|
|
84
|
-
variant: "destructive",
|
|
85
|
-
});
|
|
86
|
-
throw new Error("Project already exists"); // Let the dialog know it failed
|
|
87
|
-
}
|
|
88
|
-
await addProject(input);
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
const handleAddMultipleProjects = async (projectsToAdd: { name: string; path: string }[]) => {
|
|
92
|
-
// Filter out projects that already exist
|
|
93
|
-
const existingPaths = new Set(projects.map(p => p.path));
|
|
94
|
-
const newProjects = projectsToAdd.filter(p => !existingPaths.has(p.path));
|
|
95
|
-
const duplicateCount = projectsToAdd.length - newProjects.length;
|
|
96
|
-
|
|
97
|
-
// If all are duplicates, show error
|
|
98
|
-
if (newProjects.length === 0) {
|
|
99
|
-
toast({
|
|
100
|
-
title: "All projects already exist",
|
|
101
|
-
description: `${duplicateCount === 1 ? "This project is" : "All " + duplicateCount + " projects are"} already in your dashboard.`,
|
|
102
|
-
variant: "destructive",
|
|
103
|
-
});
|
|
104
|
-
throw new Error("All projects already exist");
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Add new projects
|
|
108
|
-
for (const project of newProjects) {
|
|
109
|
-
await addProject(project);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// If some were duplicates, notify the user
|
|
113
|
-
if (duplicateCount > 0) {
|
|
114
|
-
toast({
|
|
115
|
-
title: "Some projects skipped",
|
|
116
|
-
description: `${duplicateCount} duplicate project${duplicateCount > 1 ? "s were" : " was"} skipped.`,
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
return (
|
|
122
|
-
<div className="dark flex min-h-dvh flex-col bg-[#0a0a0a]">
|
|
123
|
-
{/* Navigation Bar */}
|
|
124
|
-
<nav className="sticky top-0 z-30 border-b border-zinc-800 bg-[#0a0a0a]/80 backdrop-blur-sm">
|
|
125
|
-
<div className="mx-auto flex max-w-[1200px] items-center justify-center gap-4 px-6 py-2">
|
|
126
|
-
<a
|
|
127
|
-
href="https://github.com/AvivK5498/Beads-Kanban-UI"
|
|
128
|
-
target="_blank"
|
|
129
|
-
rel="noopener noreferrer"
|
|
130
|
-
aria-label="GitHub repository"
|
|
131
|
-
className="rounded-md p-2 text-zinc-400 transition-colors duration-150 hover:text-zinc-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-400 focus-visible:ring-offset-2 focus-visible:ring-offset-[#0a0a0a]"
|
|
132
|
-
>
|
|
133
|
-
<Github className="h-5 w-5" aria-hidden="true" />
|
|
134
|
-
</a>
|
|
135
|
-
<Link
|
|
136
|
-
href="/settings"
|
|
137
|
-
aria-label="Settings"
|
|
138
|
-
className="rounded-md p-2 text-zinc-400 transition-colors duration-150 hover:text-zinc-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-400 focus-visible:ring-offset-2 focus-visible:ring-offset-[#0a0a0a]"
|
|
139
|
-
>
|
|
140
|
-
<Settings className="h-5 w-5" aria-hidden="true" />
|
|
141
|
-
</Link>
|
|
142
|
-
</div>
|
|
143
|
-
</nav>
|
|
144
|
-
|
|
145
|
-
{/* Hero Section - pushed down with padding */}
|
|
146
|
-
<main className="flex flex-col items-center px-6 pt-32">
|
|
147
|
-
{/* Centered Heading with Space Grotesk */}
|
|
148
|
-
<h1 className="mb-12 text-center text-balance font-heading text-4xl font-bold tracking-tight text-white sm:text-5xl">
|
|
149
|
-
Manage Your Beads Projects
|
|
150
|
-
</h1>
|
|
151
|
-
|
|
152
|
-
<div className="w-full max-w-[1200px]">
|
|
153
|
-
{/* Add Project Dropdown */}
|
|
154
|
-
<div className="mb-6 flex justify-end">
|
|
155
|
-
<DropdownMenu>
|
|
156
|
-
<DropdownMenuTrigger asChild>
|
|
157
|
-
<Button variant="mono" size="md">
|
|
158
|
-
<Plus aria-hidden="true" />
|
|
159
|
-
Add Project
|
|
160
|
-
<ButtonArrow icon={ChevronDown} />
|
|
161
|
-
</Button>
|
|
162
|
-
</DropdownMenuTrigger>
|
|
163
|
-
<DropdownMenuContent align="end">
|
|
164
|
-
<DropdownMenuItem onSelect={() => setIsAddDialogOpen(true)}>
|
|
165
|
-
<FolderPlus aria-hidden="true" />
|
|
166
|
-
Add Project
|
|
167
|
-
</DropdownMenuItem>
|
|
168
|
-
<DropdownMenuItem onSelect={() => setIsScanDialogOpen(true)}>
|
|
169
|
-
<FolderSearch aria-hidden="true" />
|
|
170
|
-
Scan Directory
|
|
171
|
-
</DropdownMenuItem>
|
|
172
|
-
</DropdownMenuContent>
|
|
173
|
-
</DropdownMenu>
|
|
174
|
-
</div>
|
|
175
|
-
|
|
176
|
-
{/* Search and Filter Bar */}
|
|
177
|
-
{projects.length > 0 && (
|
|
178
|
-
<div className="mb-6 space-y-3">
|
|
179
|
-
{/* Search Input */}
|
|
180
|
-
<div className="relative">
|
|
181
|
-
<Search className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-zinc-500" aria-hidden="true" />
|
|
182
|
-
<Input
|
|
183
|
-
type="search"
|
|
184
|
-
placeholder="Search projects..."
|
|
185
|
-
value={searchQuery}
|
|
186
|
-
onChange={(e) => setSearchQuery(e.target.value)}
|
|
187
|
-
className="pl-10 bg-zinc-900/50 border-zinc-700"
|
|
188
|
-
aria-label="Search projects"
|
|
189
|
-
/>
|
|
190
|
-
</div>
|
|
191
|
-
|
|
192
|
-
{/* Tag Filter Chips */}
|
|
193
|
-
{allTags.length > 0 && (
|
|
194
|
-
<div className="flex flex-wrap items-center gap-2">
|
|
195
|
-
<span className="text-xs text-zinc-500">Filter by tag:</span>
|
|
196
|
-
{allTags.map((tag) => {
|
|
197
|
-
const isSelected = selectedTagIds.includes(tag.id);
|
|
198
|
-
return (
|
|
199
|
-
<button
|
|
200
|
-
key={tag.id}
|
|
201
|
-
type="button"
|
|
202
|
-
onClick={() => toggleTag(tag.id)}
|
|
203
|
-
className="transition-opacity"
|
|
204
|
-
aria-pressed={isSelected}
|
|
205
|
-
aria-label={`Filter by ${tag.name}`}
|
|
206
|
-
>
|
|
207
|
-
<Badge
|
|
208
|
-
variant={isSelected ? "primary" : "outline"}
|
|
209
|
-
size="sm"
|
|
210
|
-
style={
|
|
211
|
-
isSelected
|
|
212
|
-
? {
|
|
213
|
-
backgroundColor: tag.color,
|
|
214
|
-
color: "#fff",
|
|
215
|
-
borderColor: tag.color,
|
|
216
|
-
}
|
|
217
|
-
: {
|
|
218
|
-
backgroundColor: `${tag.color}10`,
|
|
219
|
-
color: tag.color,
|
|
220
|
-
borderColor: `${tag.color}50`,
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
>
|
|
224
|
-
{tag.name}
|
|
225
|
-
</Badge>
|
|
226
|
-
</button>
|
|
227
|
-
);
|
|
228
|
-
})}
|
|
229
|
-
{hasActiveFilters && (
|
|
230
|
-
<button
|
|
231
|
-
type="button"
|
|
232
|
-
onClick={clearFilters}
|
|
233
|
-
className="ml-2 flex items-center gap-1 text-xs text-zinc-500 hover:text-zinc-300 transition-colors"
|
|
234
|
-
aria-label="Clear all filters"
|
|
235
|
-
>
|
|
236
|
-
<X className="h-3 w-3" aria-hidden="true" />
|
|
237
|
-
Clear
|
|
238
|
-
</button>
|
|
239
|
-
)}
|
|
240
|
-
</div>
|
|
241
|
-
)}
|
|
242
|
-
|
|
243
|
-
{/* Results count when filtering */}
|
|
244
|
-
{hasActiveFilters && (
|
|
245
|
-
<p className="text-xs text-zinc-500">
|
|
246
|
-
Showing {filteredProjects.length} of {projects.length} project{projects.length !== 1 ? "s" : ""}
|
|
247
|
-
</p>
|
|
248
|
-
)}
|
|
249
|
-
</div>
|
|
250
|
-
)}
|
|
251
|
-
|
|
252
|
-
{isLoading ? (
|
|
253
|
-
<div role="status" aria-label="Loading projects" className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
|
254
|
-
{[1, 2, 3].map((i) => (
|
|
255
|
-
<div key={i} className="rounded-xl border border-zinc-800 bg-zinc-900/70 p-4">
|
|
256
|
-
<div className="mb-3 flex gap-1.5">
|
|
257
|
-
<Skeleton className="h-5 w-16" />
|
|
258
|
-
<Skeleton className="h-5 w-12" />
|
|
259
|
-
</div>
|
|
260
|
-
<Skeleton className="h-5 w-40" />
|
|
261
|
-
<Skeleton className="mt-2 h-4 w-48" />
|
|
262
|
-
<Skeleton className="mt-4 h-4 w-32" />
|
|
263
|
-
<Skeleton className="mt-2 h-3 w-28" />
|
|
264
|
-
</div>
|
|
265
|
-
))}
|
|
266
|
-
</div>
|
|
267
|
-
) : error ? (
|
|
268
|
-
<div role="alert" className="rounded-lg border border-red-800/50 bg-red-950/70 p-6 text-center">
|
|
269
|
-
<p className="text-red-400">Error loading projects: {error.message}</p>
|
|
270
|
-
<p className="mt-2 text-sm text-red-500">
|
|
271
|
-
Make sure the Tauri backend is running.
|
|
272
|
-
</p>
|
|
273
|
-
</div>
|
|
274
|
-
) : filteredProjects.length === 0 ? (
|
|
275
|
-
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
|
276
|
-
<div className="rounded-lg border border-dashed border-zinc-700 bg-zinc-900/70 p-6 text-center text-zinc-400">
|
|
277
|
-
{hasActiveFilters ? (
|
|
278
|
-
<>
|
|
279
|
-
<p>No matching projects</p>
|
|
280
|
-
<p className="mt-1 text-sm text-zinc-500">Try adjusting your search or filters</p>
|
|
281
|
-
</>
|
|
282
|
-
) : (
|
|
283
|
-
<>
|
|
284
|
-
<p>No projects yet</p>
|
|
285
|
-
<p className="mt-1 text-sm text-zinc-500">Click the Add Project button above to get started</p>
|
|
286
|
-
</>
|
|
287
|
-
)}
|
|
288
|
-
</div>
|
|
289
|
-
</div>
|
|
290
|
-
) : (
|
|
291
|
-
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
|
292
|
-
{filteredProjects.map((project) => (
|
|
293
|
-
<ProjectCard
|
|
294
|
-
key={project.id}
|
|
295
|
-
id={project.id}
|
|
296
|
-
name={project.name}
|
|
297
|
-
path={project.path}
|
|
298
|
-
tags={project.tags}
|
|
299
|
-
beadCounts={project.beadCounts}
|
|
300
|
-
onTagsChange={(tags) => updateProjectTags(project.id, tags)}
|
|
301
|
-
/>
|
|
302
|
-
))}
|
|
303
|
-
</div>
|
|
304
|
-
)}
|
|
305
|
-
</div>
|
|
306
|
-
</main>
|
|
307
|
-
|
|
308
|
-
{/* Footer */}
|
|
309
|
-
<footer className="mt-auto border-t border-zinc-800 py-6">
|
|
310
|
-
<div className="mx-auto flex max-w-[1200px] items-center justify-center gap-4 px-6">
|
|
311
|
-
<a
|
|
312
|
-
href="https://github.com/AvivK5498/beads-kanban-ui"
|
|
313
|
-
target="_blank"
|
|
314
|
-
rel="noopener noreferrer"
|
|
315
|
-
className="flex items-center gap-2 text-sm text-zinc-500 transition-colors hover:text-zinc-300"
|
|
316
|
-
>
|
|
317
|
-
<Github className="h-4 w-4" aria-hidden="true" />
|
|
318
|
-
<span>Beads Kanban UI</span>
|
|
319
|
-
</a>
|
|
320
|
-
<span className="text-zinc-600" aria-hidden="true">·</span>
|
|
321
|
-
<a
|
|
322
|
-
href="https://github.com/steveyegge/beads"
|
|
323
|
-
target="_blank"
|
|
324
|
-
rel="noopener noreferrer"
|
|
325
|
-
className="flex items-center gap-2 text-sm text-zinc-500 transition-colors hover:text-zinc-300"
|
|
326
|
-
>
|
|
327
|
-
<Github className="h-4 w-4" aria-hidden="true" />
|
|
328
|
-
<span>Beads CLI</span>
|
|
329
|
-
</a>
|
|
330
|
-
</div>
|
|
331
|
-
</footer>
|
|
332
|
-
|
|
333
|
-
{/* Add Project Dialog */}
|
|
334
|
-
<AddProjectDialog
|
|
335
|
-
open={isAddDialogOpen}
|
|
336
|
-
onOpenChange={setIsAddDialogOpen}
|
|
337
|
-
onAddProject={handleAddProject}
|
|
338
|
-
/>
|
|
339
|
-
|
|
340
|
-
{/* Scan Directory Dialog */}
|
|
341
|
-
<ScanDirectoryDialog
|
|
342
|
-
open={isScanDialogOpen}
|
|
343
|
-
onOpenChange={setIsScanDialogOpen}
|
|
344
|
-
onAddProjects={handleAddMultipleProjects}
|
|
345
|
-
/>
|
|
346
|
-
</div>
|
|
347
|
-
);
|
|
348
|
-
}
|