Skip to content

Commit 130d3a4

Browse files
authored
Merge pull request #125 from ArjinAlbay/main
Add 'Add to Kanban' quick action for Quick Wins issues
2 parents b1cb5b4 + 56a8e41 commit 130d3a4

File tree

4 files changed

+138
-18
lines changed

4 files changed

+138
-18
lines changed

src/app/action-required/page.tsx

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,20 @@ function formatTimeAgo(dateString: string): string {
6666
return `${Math.floor(diffInSeconds / 2592000)}mo ago`;
6767
}
6868

69+
function getStaleRowClassName(daysOld: number | undefined): string {
70+
if (!daysOld) return "";
71+
72+
if (daysOld > 14) {
73+
return "bg-red-50 dark:bg-red-950/20 hover:bg-red-100 dark:hover:bg-red-950/30";
74+
}
75+
76+
if (daysOld > 7) {
77+
return "bg-yellow-50 dark:bg-yellow-950/20 hover:bg-yellow-100 dark:hover:bg-yellow-950/30";
78+
}
79+
80+
return "";
81+
}
82+
6983
function ActionRequiredContent() {
7084
const {
7185
assignedItems,
@@ -279,7 +293,10 @@ function ActionRequiredContent() {
279293
</TableHeader>
280294
<TableBody>
281295
{items.map((item: ActionItem) => (
282-
<TableRow key={item.id}>
296+
<TableRow
297+
key={item.id}
298+
className={type === "stale" ? getStaleRowClassName(item.daysOld) : ""}
299+
>
283300
<TableCell>
284301
<div className="flex items-center gap-2">
285302
<div className="min-w-0 flex-1">

src/components/quick-wins/QuickWinsTable.tsx

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"use client";
22

3-
import { useState, useMemo } from "react";
3+
import { useState, useMemo, useCallback } from "react";
44
import {
55
useReactTable,
66
getCoreRowModel,
@@ -45,6 +45,7 @@ import {
4545

4646
import { createColumns } from "./columns";
4747
import type { GitHubIssue } from "@/types/quickWins";
48+
import { useKanbanStore } from "@/stores/kanban";
4849

4950
interface QuickWinsTableProps {
5051
data: GitHubIssue[];
@@ -70,7 +71,21 @@ export function QuickWinsTable({
7071

7172
const [languageFilter, setLanguageFilter] = useState<string>("all");
7273

73-
const columns = useMemo(() => createColumns(), []);
74+
const addTask = useKanbanStore((state) => state.addTask);
75+
76+
const handleAddToKanban = useCallback((issue: GitHubIssue) => {
77+
addTask({
78+
title: issue.title,
79+
description: `From ${issue.repository}`,
80+
type: "github-issue",
81+
priority: issue.priority,
82+
githubUrl: issue.url,
83+
labels: issue.labels.map((label) => label.name),
84+
columnId: "todo",
85+
});
86+
}, [addTask]);
87+
88+
const columns = useMemo(() => createColumns({ onAddToKanban: handleAddToKanban }), [handleAddToKanban]);
7489

7590
const filteredData = useMemo(() => {
7691
let filtered = data;

src/components/quick-wins/columns.tsx

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@ import { ColumnDef } from "@tanstack/react-table";
22
import { Badge } from "@/components/ui/badge";
33
import { Button } from "@/components/ui/button";
44
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
5-
import { ExternalLink, Calendar } from "lucide-react";
5+
import { ExternalLink, Calendar, ListPlus } from "lucide-react";
66
import type { GitHubIssue } from "@/types/quickWins";
77

8-
export const createColumns = (): ColumnDef<GitHubIssue>[] => [
8+
interface CreateColumnsOptions {
9+
onAddToKanban?: (issue: GitHubIssue) => void;
10+
}
11+
12+
export const createColumns = (options?: CreateColumnsOptions): ColumnDef<GitHubIssue>[] => [
913
{
1014
accessorKey: "title",
1115
header: ({ column }) => (
@@ -157,4 +161,24 @@ export const createColumns = (): ColumnDef<GitHubIssue>[] => [
157161
},
158162
enableSorting: false,
159163
},
164+
{
165+
id: "actions",
166+
header: "Actions",
167+
cell: ({ row }) => {
168+
const issue = row.original;
169+
return (
170+
<Button
171+
size="sm"
172+
variant="outline"
173+
onClick={() => options?.onAddToKanban?.(issue)}
174+
disabled={!options?.onAddToKanban}
175+
className="gap-1"
176+
>
177+
<ListPlus className="w-4 h-4" />
178+
Add to Kanban
179+
</Button>
180+
);
181+
},
182+
enableSorting: false,
183+
},
160184
];

src/lib/api/github-graphql-client.ts

Lines changed: 77 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,8 @@ interface PullRequest {
148148
comments: {
149149
totalCount: number;
150150
};
151+
additions?: number;
152+
deletions?: number;
151153
__typename?: string;
152154
}
153155

@@ -163,6 +165,8 @@ interface PullRequestWithReviews extends PullRequest {
163165

164166
interface StalePullRequest extends Omit<PullRequest, "assignees"> {
165167
reviewDecision: string | null;
168+
additions?: number;
169+
deletions?: number;
166170
}
167171

168172
class GitHubGraphQLClient {
@@ -521,7 +525,7 @@ class GitHubGraphQLClient {
521525
}
522526
}
523527
}
524-
# Assigned Pull Requests
528+
# Assigned Pull Requests
525529
pullRequests(states: OPEN, first: 50) {
526530
nodes {
527531
__typename
@@ -552,6 +556,8 @@ class GitHubGraphQLClient {
552556
comments {
553557
totalCount
554558
}
559+
additions
560+
deletions
555561
}
556562
}
557563
}
@@ -587,6 +593,8 @@ class GitHubGraphQLClient {
587593
totalCount
588594
}
589595
reviewDecision
596+
additions
597+
deletions
590598
}
591599
}
592600
}
@@ -622,6 +630,8 @@ class GitHubGraphQLClient {
622630
totalCount
623631
}
624632
reviewDecision
633+
additions
634+
deletions
625635
}
626636
}
627637
}
@@ -690,6 +700,8 @@ class GitHubGraphQLClient {
690700
}
691701
}
692702
}
703+
additions
704+
deletions
693705
}
694706
}
695707
}
@@ -733,6 +745,8 @@ class GitHubGraphQLClient {
733745
}
734746
}
735747
}
748+
additions
749+
deletions
736750
}
737751
}
738752
}
@@ -857,6 +871,11 @@ class GitHubGraphQLClient {
857871
const labelNames = labels.map((l) => l.name);
858872

859873
const isPR = this.isPullRequest(item);
874+
const commentCount = item.comments?.totalCount || 0;
875+
876+
const prSize = isPR && "additions" in item && "deletions" in item
877+
? { additions: item.additions, deletions: item.deletions }
878+
: undefined;
860879

861880
return {
862881
id: item.id,
@@ -869,11 +888,17 @@ class GitHubGraphQLClient {
869888
avatarUrl: item.author?.avatarUrl || "",
870889
},
871890
labels,
872-
priority: this.calculateActionPriority(labelNames, daysOld, mentionType),
891+
priority: this.calculateActionPriority(
892+
labelNames,
893+
daysOld,
894+
mentionType,
895+
commentCount,
896+
prSize
897+
),
873898
daysOld,
874899
createdAt: item.createdAt,
875900
updatedAt: item.updatedAt,
876-
comments: item.comments?.totalCount || 0,
901+
comments: commentCount,
877902
stars: item.repository.stargazerCount || 0,
878903
...(mentionType && { mentionType }),
879904
};
@@ -882,10 +907,36 @@ class GitHubGraphQLClient {
882907
private calculateActionPriority(
883908
labels: string[],
884909
daysOld: number,
885-
mentionType?: "mention" | "review_request" | "comment"
910+
mentionType?: "mention" | "review_request" | "comment",
911+
commentCount = 0,
912+
prSize?: { additions?: number; deletions?: number }
886913
): "urgent" | "high" | "medium" | "low" {
887-
if (mentionType === "review_request") {
888-
return "urgent";
914+
let score = 0;
915+
916+
const statusScores = {
917+
review_request: 50,
918+
mention: 30,
919+
comment: 25,
920+
};
921+
if (mentionType && statusScores[mentionType]) {
922+
score += statusScores[mentionType];
923+
} else {
924+
score += 20;
925+
}
926+
927+
score += Math.min(commentCount * 2, 30);
928+
929+
if (prSize && (prSize.additions || prSize.deletions)) {
930+
const totalChanges = (prSize.additions || 0) + (prSize.deletions || 0);
931+
if (totalChanges > 1000) {
932+
score += 20;
933+
} else if (totalChanges > 500) {
934+
score += 15;
935+
} else if (totalChanges > 100) {
936+
score += 10;
937+
} else {
938+
score += 5;
939+
}
889940
}
890941

891942
const lowerLabels = labels.map((l) => l.toLowerCase());
@@ -896,20 +947,33 @@ class GitHubGraphQLClient {
896947
l.includes("critical") || l.includes("urgent") || l.includes("p0")
897948
)
898949
) {
899-
return "urgent";
900-
}
901-
if (
950+
score += 40;
951+
} else if (
902952
lowerLabels.some(
903953
(l) => l.includes("high") || l.includes("p1") || l.includes("bug")
904954
)
905955
) {
906-
return "high";
956+
score += 25;
957+
} else if (
958+
lowerLabels.some(
959+
(l) =>
960+
l.includes("low") || l.includes("p3") || l.includes("enhancement")
961+
)
962+
) {
963+
score -= 10;
907964
}
908965

909-
if (daysOld > 14) return "urgent";
910-
if (daysOld > 7) return "high";
911-
if (daysOld > 3) return "medium";
966+
if (daysOld > 14) {
967+
score *= 1.5;
968+
} else if (daysOld > 7) {
969+
score *= 1.3;
970+
} else if (daysOld > 3) {
971+
score *= 1.1;
972+
}
912973

974+
if (score >= 100) return "urgent";
975+
if (score >= 70) return "high";
976+
if (score >= 40) return "medium";
913977
return "low";
914978
}
915979
}

0 commit comments

Comments
 (0)