Skip to content

Commit b1101ed

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/lint-fixes-ui-enhancements-01AWVJeHqCUTLDm4MCfh6hTH
2 parents 35bd289 + d7209f5 commit b1101ed

35 files changed

+5095
-254
lines changed

package-lock.json

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

package.json

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"@dnd-kit/sortable": "^10.0.0",
1414
"@dnd-kit/utilities": "^3.2.2",
1515
"@radix-ui/react-avatar": "^1.1.10",
16+
"@radix-ui/react-checkbox": "^1.3.3",
1617
"@radix-ui/react-collapsible": "^1.1.11",
1718
"@radix-ui/react-dialog": "^1.1.14",
1819
"@radix-ui/react-label": "^2.1.7",
@@ -23,19 +24,28 @@
2324
"@radix-ui/react-tabs": "^1.1.12",
2425
"@radix-ui/react-tooltip": "^1.2.8",
2526
"@tanstack/react-table": "^8.21.3",
27+
"@types/react-syntax-highlighter": "^15.5.13",
2628
"chart.js": "^4.5.0",
2729
"class-variance-authority": "^0.7.1",
2830
"clsx": "^2.1.1",
31+
"cmdk": "^1.1.1",
2932
"date-fns": "^4.1.0",
3033
"echarts": "^5.6.0",
3134
"echarts-for-react": "^3.0.2",
35+
"framer-motion": "^12.23.24",
36+
"github-markdown-css": "^5.8.1",
3237
"lucide-react": "^0.525.0",
3338
"next": "15.3.4",
3439
"next-auth": "^4.24.11",
3540
"react": "^19.0.0",
3641
"react-chartjs-2": "^5.3.0",
3742
"react-dom": "^19.0.0",
43+
"react-markdown": "^10.1.0",
44+
"react-resizable-panels": "^3.0.6",
45+
"react-syntax-highlighter": "^16.1.0",
3846
"recharts": "^3.1.0",
47+
"rehype-raw": "^7.0.0",
48+
"remark-gfm": "^4.0.1",
3949
"tailwind-merge": "^3.3.1",
4050
"zustand": "^5.0.6"
4151
},
@@ -50,6 +60,6 @@
5060
"eslint-config-next": "15.3.4",
5161
"tailwindcss": "^4",
5262
"tw-animate-css": "^1.3.4",
53-
"typescript": "^5"
63+
"typescript": "5.9.3"
5464
}
5565
}

src/app/action-required/page.tsx

Lines changed: 187 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
11
"use client";
22

3-
import { useEffect, Suspense, useCallback, useMemo } from "react";
3+
import { useEffect, Suspense, useCallback, useMemo, useState } from "react";
44
import { useSearchParams, useRouter } from "next/navigation";
55
import { Layout } from "@/components/layout/Layout";
66
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
77
import { Button } from "@/components/ui/button";
88
import { Badge } from "@/components/ui/badge";
9+
import {
10+
Select,
11+
SelectContent,
12+
SelectItem,
13+
SelectTrigger,
14+
SelectValue,
15+
} from "@/components/ui/select";
16+
import { Checkbox } from "@/components/ui/checkbox";
917
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
1018
import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar";
1119
import {
@@ -25,8 +33,14 @@ import {
2533
ExternalLink,
2634
RefreshCw,
2735
LucideIcon,
36+
GitMerge,
37+
AlertCircle,
38+
CheckCircle2,
39+
XCircle,
40+
Loader2,
41+
Plus,
2842
} from "lucide-react";
29-
import { useActionItemsStore } from "@/stores";
43+
import { useActionItemsStore, useKanbanStore } from "@/stores";
3044
import type { ActionItem as StoreActionItem } from "@/stores/actionItems";
3145
import { PageHeader } from "@/components/layout/PageHeader";
3246
import { SearchModal } from "@/components/search/SearchModal";
@@ -48,6 +62,13 @@ interface ActionItem {
4862
updatedAt: string;
4963
comments?: number;
5064
stars?: number;
65+
additions?: number;
66+
deletions?: number;
67+
language?: string;
68+
mergeable?: "MERGEABLE" | "CONFLICTING" | "UNKNOWN";
69+
statusCheckRollup?: {
70+
state: "SUCCESS" | "FAILURE" | "PENDING" | "EXPECTED";
71+
};
5172
}
5273

5374
const VALID_TABS = ["assigned", "mentions", "stale"] as const;
@@ -198,6 +219,61 @@ function ActionRequiredContent() {
198219
const isLoading = loading[type];
199220
const error = errors[type];
200221

222+
const [selectedRepo, setSelectedRepo] = useState<string>("all");
223+
const [selectedLanguage, setSelectedLanguage] = useState<string>("all");
224+
const [selectedItems, setSelectedItems] = useState<Set<string>>(new Set());
225+
const { addTaskFromActionItem } = useKanbanStore();
226+
227+
const repositories = useMemo(() => {
228+
const repos = new Set(items.map((item: ActionItem) => item.repo));
229+
return Array.from(repos).sort();
230+
}, [items]);
231+
232+
const languages = useMemo(() => {
233+
const langs = new Set(
234+
items
235+
.map((item: ActionItem) => item.language)
236+
.filter((lang): lang is string => !!lang)
237+
);
238+
return Array.from(langs).sort();
239+
}, [items]);
240+
241+
const filteredItems = useMemo(() => {
242+
return items.filter((item: ActionItem) => {
243+
if (selectedRepo !== "all" && item.repo !== selectedRepo) return false;
244+
if (selectedLanguage !== "all" && item.language !== selectedLanguage) return false;
245+
return true;
246+
});
247+
}, [items, selectedRepo, selectedLanguage]);
248+
249+
const toggleSelectAll = () => {
250+
if (selectedItems.size === filteredItems.length) {
251+
setSelectedItems(new Set());
252+
} else {
253+
setSelectedItems(new Set(filteredItems.map((item: ActionItem) => item.id.toString())));
254+
}
255+
};
256+
257+
const toggleSelectItem = (itemId: string) => {
258+
const newSelected = new Set(selectedItems);
259+
if (newSelected.has(itemId)) {
260+
newSelected.delete(itemId);
261+
} else {
262+
newSelected.add(itemId);
263+
}
264+
setSelectedItems(newSelected);
265+
};
266+
267+
const handleBulkAddToKanban = () => {
268+
const itemsToAdd = filteredItems.filter((item: ActionItem) =>
269+
selectedItems.has(item.id.toString())
270+
);
271+
itemsToAdd.forEach((item: ActionItem) => {
272+
addTaskFromActionItem(item as StoreActionItem, "", "todo");
273+
});
274+
setSelectedItems(new Set());
275+
};
276+
201277
if (isLoading) {
202278
return (
203279
<div className="rounded-md border">
@@ -284,25 +360,84 @@ function ActionRequiredContent() {
284360
}
285361

286362
return (
287-
<div className="rounded-md border">
288-
<Table>
289-
<TableHeader>
290-
<TableRow>
291-
<TableHead className="w-[30%]">Title / Repository</TableHead>
292-
<TableHead className="w-[10%]">Author</TableHead>
293-
<TableHead className="w-[18%]">Labels</TableHead>
294-
<TableHead className="w-[10%]">Priority</TableHead>
295-
<TableHead className="w-[8%]">Activity</TableHead>
296-
<TableHead className="w-[10%]">Updated</TableHead>
297-
<TableHead className="w-[14%]">Actions</TableHead>
298-
</TableRow>
299-
</TableHeader>
300-
<TableBody>
301-
{items.map((item: ActionItem) => (
363+
<div className="space-y-4">
364+
<div className="flex items-center gap-4 flex-wrap">
365+
<div className="flex items-center gap-2">
366+
<label className="text-sm font-medium text-gray-700 dark:text-gray-300">Repository:</label>
367+
<Select value={selectedRepo} onValueChange={setSelectedRepo}>
368+
<SelectTrigger className="w-[200px]">
369+
<SelectValue placeholder="All Repositories" />
370+
</SelectTrigger>
371+
<SelectContent>
372+
<SelectItem value="all">All Repositories</SelectItem>
373+
{repositories.map((repo) => (
374+
<SelectItem key={repo} value={repo}>
375+
{repo}
376+
</SelectItem>
377+
))}
378+
</SelectContent>
379+
</Select>
380+
</div>
381+
{languages.length > 0 && (
382+
<div className="flex items-center gap-2">
383+
<label className="text-sm font-medium text-gray-700 dark:text-gray-300">Language:</label>
384+
<Select value={selectedLanguage} onValueChange={setSelectedLanguage}>
385+
<SelectTrigger className="w-[150px]">
386+
<SelectValue placeholder="All Languages" />
387+
</SelectTrigger>
388+
<SelectContent>
389+
<SelectItem value="all">All Languages</SelectItem>
390+
{languages.map((lang) => (
391+
<SelectItem key={lang} value={lang}>
392+
{lang}
393+
</SelectItem>
394+
))}
395+
</SelectContent>
396+
</Select>
397+
</div>
398+
)}
399+
{selectedItems.size > 0 && (
400+
<div className="flex items-center gap-2 ml-auto">
401+
<Badge variant="secondary">{selectedItems.size} selected</Badge>
402+
<Button size="sm" onClick={handleBulkAddToKanban}>
403+
<Plus className="w-4 h-4 mr-2" />
404+
Bulk Add to Kanban
405+
</Button>
406+
</div>
407+
)}
408+
</div>
409+
<div className="rounded-md border">
410+
<Table>
411+
<TableHeader>
412+
<TableRow>
413+
<TableHead className="w-[3%]">
414+
<Checkbox
415+
checked={selectedItems.size === filteredItems.length && filteredItems.length > 0}
416+
onCheckedChange={toggleSelectAll}
417+
/>
418+
</TableHead>
419+
<TableHead className="w-[27%]">Title / Repository</TableHead>
420+
<TableHead className="w-[10%]">Author</TableHead>
421+
<TableHead className="w-[15%]">Labels</TableHead>
422+
<TableHead className="w-[8%]">Priority</TableHead>
423+
<TableHead className="w-[8%]">Size</TableHead>
424+
<TableHead className="w-[6%]">Activity</TableHead>
425+
<TableHead className="w-[9%]">Updated</TableHead>
426+
<TableHead className="w-[14%]">Actions</TableHead>
427+
</TableRow>
428+
</TableHeader>
429+
<TableBody>
430+
{filteredItems.map((item: ActionItem) => (
302431
<TableRow
303432
key={item.id}
304-
className={type === "stale" ? getStaleRowClassName(item.daysOld) : ""}
433+
className={getStaleRowClassName(item.daysOld)}
305434
>
435+
<TableCell>
436+
<Checkbox
437+
checked={selectedItems.has(item.id.toString())}
438+
onCheckedChange={() => toggleSelectItem(item.id.toString())}
439+
/>
440+
</TableCell>
306441
<TableCell>
307442
<div className="flex items-center gap-2">
308443
<div className="min-w-0 flex-1">
@@ -326,6 +461,26 @@ function ActionRequiredContent() {
326461
{item.url && (
327462
<ExternalLink className="w-3 h-3 text-gray-400 flex-shrink-0" />
328463
)}
464+
{item.type === "pullRequest" && item.mergeable === "CONFLICTING" && (
465+
<span title="Has merge conflicts">
466+
<AlertCircle className="w-4 h-4 text-red-500 flex-shrink-0" />
467+
</span>
468+
)}
469+
{item.type === "pullRequest" && item.statusCheckRollup?.state === "SUCCESS" && (
470+
<span title="All checks passed">
471+
<CheckCircle2 className="w-4 h-4 text-green-500 flex-shrink-0" />
472+
</span>
473+
)}
474+
{item.type === "pullRequest" && item.statusCheckRollup?.state === "FAILURE" && (
475+
<span title="Checks failed">
476+
<XCircle className="w-4 h-4 text-red-500 flex-shrink-0" />
477+
</span>
478+
)}
479+
{item.type === "pullRequest" && item.statusCheckRollup?.state === "PENDING" && (
480+
<span title="Checks pending">
481+
<Loader2 className="w-4 h-4 text-yellow-500 flex-shrink-0 animate-spin" />
482+
</span>
483+
)}
329484
</div>
330485
<p className="text-sm text-gray-500 dark:text-gray-400 truncate">
331486
{item.repo}
@@ -386,6 +541,16 @@ function ActionRequiredContent() {
386541
{item.priority}
387542
</Badge>
388543
</TableCell>
544+
<TableCell>
545+
{item.type === "pullRequest" && (item.additions !== undefined || item.deletions !== undefined) ? (
546+
<div className="flex flex-col text-xs">
547+
<span className="text-green-600 dark:text-green-400">+{item.additions || 0}</span>
548+
<span className="text-red-600 dark:text-red-400">-{item.deletions || 0}</span>
549+
</div>
550+
) : (
551+
<span className="text-gray-400 text-xs">N/A</span>
552+
)}
553+
</TableCell>
389554
<TableCell>
390555
<div className="flex items-center gap-1 text-gray-600 dark:text-gray-300">
391556
<MessageSquare className="w-4 h-4" />
@@ -404,9 +569,10 @@ function ActionRequiredContent() {
404569
/>
405570
</TableCell>
406571
</TableRow>
407-
))}
408-
</TableBody>
409-
</Table>
572+
))}
573+
</TableBody>
574+
</Table>
575+
</div>
410576
</div>
411577
);
412578
};

src/app/globals.css

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,28 @@
224224
}
225225

226226
body {
227-
@apply bg-background text-foreground;
227+
@apply bg-background text-foreground font-sans;
228+
}
229+
230+
h1, h2, h3, h4, h5, h6 {
231+
@apply font-sans;
232+
}
233+
234+
code, pre, kbd, samp {
235+
@apply font-mono;
236+
}
237+
}
238+
239+
@layer utilities {
240+
.glass-card {
241+
@apply bg-background/80 backdrop-blur-md border border-border/20;
242+
}
243+
244+
.glass-modal {
245+
@apply bg-background/90 backdrop-blur-lg border border-border/30;
246+
}
247+
248+
.glass-header {
249+
@apply bg-background/80 backdrop-blur-md border-b border-border/50;
228250
}
229251
}

src/app/layout.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import { OAuthSessionSync } from "@/components/providers/OAuthSessionSync";
77

88
import { NotificationProvider } from "@/components/common/NotificationProvider";
99
import { DataInitializer } from "@/components/providers/DataInitializer";
10+
import { CommandPalette } from "@/components/command/CommandPalette";
11+
import { PageTracker } from "@/components/providers/PageTracker";
1012

1113
const geistSans = Geist({
1214
variable: "--font-geist-sans",
@@ -37,6 +39,8 @@ export default function RootLayout({
3739
<ThemeProvider defaultTheme="system" storageKey="githubmon-theme">
3840
<OAuthSessionSync />
3941
<DataInitializer />
42+
<PageTracker />
43+
<CommandPalette />
4044
{children}
4145
<NotificationProvider />
4246
</ThemeProvider>

0 commit comments

Comments
 (0)