Skip to content

Commit e1ba095

Browse files
feat(issues): add Slack and Telegram source icons to issue cards (#105)
* feat(issues): add Slack and Telegram source icons to issue cards Show inline brand icons (Slack/Telegram) on issue cards and detail page to indicate where each issue originated. The source is derived from existing slackChannelId/telegramChatId DB columns and exposed as a computed "source" field in both the list and detail API responses. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(issues): use colored brand icons for Slack and Telegram Slack icon now uses official 4-color brand palette (#E01E5A, #36C5F0, #2EB67D, #ECB22E) and Telegram uses blue circle (#26A5E4) with white paper plane, replacing the monochrome currentColor versions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 7a1c5bf commit e1ba095

File tree

4 files changed

+75
-1
lines changed

4 files changed

+75
-1
lines changed

src/app/(app)/issues/[id]/page.tsx

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,26 @@ function encodeProjectDir(fsPath: string): string {
3131
return fsPath.replace(/[/.]/g, "-");
3232
}
3333

34+
function SlackIcon({ className }: { className?: string }) {
35+
return (
36+
<svg viewBox="0 0 24 24" className={className} role="img" aria-label="Slack">
37+
<path d="M5.042 15.165a2.528 2.528 0 0 1-2.52 2.523A2.528 2.528 0 0 1 0 15.165a2.527 2.527 0 0 1 2.522-2.52h2.52v2.52zM6.313 15.165a2.527 2.527 0 0 1 2.521-2.52 2.527 2.527 0 0 1 2.521 2.52v6.313A2.528 2.528 0 0 1 8.834 24a2.528 2.528 0 0 1-2.521-2.522v-6.313z" fill="#E01E5A"/>
38+
<path d="M8.834 5.042a2.528 2.528 0 0 1-2.521-2.52A2.528 2.528 0 0 1 8.834 0a2.528 2.528 0 0 1 2.521 2.522v2.52H8.834zM8.834 6.313a2.528 2.528 0 0 1 2.521 2.521 2.528 2.528 0 0 1-2.521 2.521H2.522A2.528 2.528 0 0 1 0 8.834a2.528 2.528 0 0 1 2.522-2.521h6.312z" fill="#36C5F0"/>
39+
<path d="M18.956 8.834a2.528 2.528 0 0 1 2.522-2.521A2.528 2.528 0 0 1 24 8.834a2.528 2.528 0 0 1-2.522 2.521h-2.522V8.834zM17.688 8.834a2.528 2.528 0 0 1-2.523 2.521 2.527 2.527 0 0 1-2.52-2.521V2.522A2.527 2.527 0 0 1 15.165 0a2.528 2.528 0 0 1 2.523 2.522v6.312z" fill="#2EB67D"/>
40+
<path d="M15.165 18.956a2.528 2.528 0 0 1 2.523 2.522A2.528 2.528 0 0 1 15.165 24a2.527 2.527 0 0 1-2.52-2.522v-2.522h2.52zM15.165 17.688a2.527 2.527 0 0 1-2.52-2.523 2.526 2.526 0 0 1 2.52-2.52h6.313A2.527 2.527 0 0 1 24 15.165a2.528 2.528 0 0 1-2.522 2.523h-6.313z" fill="#ECB22E"/>
41+
</svg>
42+
);
43+
}
44+
45+
function TelegramIcon({ className }: { className?: string }) {
46+
return (
47+
<svg viewBox="0 0 24 24" className={className} role="img" aria-label="Telegram">
48+
<circle cx="12" cy="12" r="12" fill="#26A5E4"/>
49+
<path d="M16.906 7.224c.1-.002.321.023.465.14a.506.506 0 0 1 .171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.48.33-.913.49-1.302.48-.428-.008-1.252-.241-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.216.325-.437.893-.663 3.498-1.524 5.83-2.529 6.998-3.014 3.332-1.386 4.025-1.627 4.476-1.635z" fill="#FFFFFF"/>
50+
</svg>
51+
);
52+
}
53+
3454
interface IssueDetail {
3555
id: string;
3656
repositoryId: string;
@@ -40,6 +60,7 @@ interface IssueDetail {
4060
description: string;
4161
status: string;
4262
currentPhase: number;
63+
source: "slack" | "telegram" | "web";
4364
prUrl: string | null;
4465
prStatus: string | null;
4566
prSummary: string | null;
@@ -441,6 +462,18 @@ export default function IssueDetailPage({ params }: { params: Promise<{ id: stri
441462
</h1>
442463
</div>
443464
<div className="flex items-center gap-3 ml-6 text-[13px] font-mono text-muted-foreground">
465+
{issue.source === "slack" && (
466+
<>
467+
<SlackIcon className="h-3.5 w-3.5" />
468+
<span className="text-border">|</span>
469+
</>
470+
)}
471+
{issue.source === "telegram" && (
472+
<>
473+
<TelegramIcon className="h-3.5 w-3.5" />
474+
<span className="text-border">|</span>
475+
</>
476+
)}
444477
<span>{issue.repositoryName}</span>
445478
<span className="text-border">|</span>
446479
<span>{formatDistanceToNow(new Date(issue.createdAt), { addSuffix: true })}</span>

src/app/(app)/issues/page.tsx

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ interface Issue {
2121
title: string;
2222
status: string;
2323
currentPhase: number;
24+
source: "slack" | "telegram" | "web";
2425
prUrl: string | null;
2526
prStatus: string | null;
2627
error: string | null;
@@ -31,6 +32,26 @@ interface Issue {
3132
archivedAt: string | null;
3233
}
3334

35+
function SlackIcon({ className }: { className?: string }) {
36+
return (
37+
<svg viewBox="0 0 24 24" className={className} role="img" aria-label="Slack">
38+
<path d="M5.042 15.165a2.528 2.528 0 0 1-2.52 2.523A2.528 2.528 0 0 1 0 15.165a2.527 2.527 0 0 1 2.522-2.52h2.52v2.52zM6.313 15.165a2.527 2.527 0 0 1 2.521-2.52 2.527 2.527 0 0 1 2.521 2.52v6.313A2.528 2.528 0 0 1 8.834 24a2.528 2.528 0 0 1-2.521-2.522v-6.313z" fill="#E01E5A"/>
39+
<path d="M8.834 5.042a2.528 2.528 0 0 1-2.521-2.52A2.528 2.528 0 0 1 8.834 0a2.528 2.528 0 0 1 2.521 2.522v2.52H8.834zM8.834 6.313a2.528 2.528 0 0 1 2.521 2.521 2.528 2.528 0 0 1-2.521 2.521H2.522A2.528 2.528 0 0 1 0 8.834a2.528 2.528 0 0 1 2.522-2.521h6.312z" fill="#36C5F0"/>
40+
<path d="M18.956 8.834a2.528 2.528 0 0 1 2.522-2.521A2.528 2.528 0 0 1 24 8.834a2.528 2.528 0 0 1-2.522 2.521h-2.522V8.834zM17.688 8.834a2.528 2.528 0 0 1-2.523 2.521 2.527 2.527 0 0 1-2.52-2.521V2.522A2.527 2.527 0 0 1 15.165 0a2.528 2.528 0 0 1 2.523 2.522v6.312z" fill="#2EB67D"/>
41+
<path d="M15.165 18.956a2.528 2.528 0 0 1 2.523 2.522A2.528 2.528 0 0 1 15.165 24a2.527 2.527 0 0 1-2.52-2.522v-2.522h2.52zM15.165 17.688a2.527 2.527 0 0 1-2.52-2.523 2.526 2.526 0 0 1 2.52-2.52h6.313A2.527 2.527 0 0 1 24 15.165a2.528 2.528 0 0 1-2.522 2.523h-6.313z" fill="#ECB22E"/>
42+
</svg>
43+
);
44+
}
45+
46+
function TelegramIcon({ className }: { className?: string }) {
47+
return (
48+
<svg viewBox="0 0 24 24" className={className} role="img" aria-label="Telegram">
49+
<circle cx="12" cy="12" r="12" fill="#26A5E4"/>
50+
<path d="M16.906 7.224c.1-.002.321.023.465.14a.506.506 0 0 1 .171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.48.33-.913.49-1.302.48-.428-.008-1.252-.241-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.216.325-.437.893-.663 3.498-1.524 5.83-2.529 6.998-3.014 3.332-1.386 4.025-1.627 4.476-1.635z" fill="#FFFFFF"/>
51+
</svg>
52+
);
53+
}
54+
3455
const STATUS_STYLES: Record<string, { bg: string; text: string; dot?: string }> = {
3556
pending: { bg: "bg-muted/10", text: "text-muted-foreground" },
3657
planning: { bg: "bg-accent/10", text: "text-accent", dot: "bg-accent animate-pulse" },
@@ -114,7 +135,21 @@ function IssueCard({ issue, index, onArchive }: { issue: Issue; index: number; o
114135
</span>
115136
</div>
116137
<div className="flex items-center gap-2 ml-4">
117-
<span className="text-[12px] text-muted font-mono">{issue.repositoryName}</span>
138+
{issue.source === "slack" && (
139+
<>
140+
<SlackIcon className="h-3 w-3" />
141+
<span className="text-border">|</span>
142+
</>
143+
)}
144+
{issue.source === "telegram" && (
145+
<>
146+
<TelegramIcon className="h-3 w-3" />
147+
<span className="text-border">|</span>
148+
</>
149+
)}
150+
{issue.repositoryName && (
151+
<span className="text-[12px] text-muted font-mono">{issue.repositoryName}</span>
152+
)}
118153
<span className="text-border">|</span>
119154
<span className="text-[12px] text-muted font-mono">{created}</span>
120155
</div>

src/app/api/issues/[id]/route.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ export const GET = withErrorHandler(async (
5858
description: issue.description,
5959
status: issue.status,
6060
currentPhase: issue.currentPhase,
61+
// Slack takes precedence if both are somehow set
62+
source: issue.slackChannelId ? "slack" : issue.telegramChatId ? "telegram" : "web",
6163
prUrl: issue.prUrl,
6264
prStatus,
6365
prSummary: issue.prSummary,

src/app/api/issues/route.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ export const GET = withErrorHandler(async (request: Request) => {
4848
prStatus: issues.prStatus,
4949
error: issues.error,
5050
worktreePath: issues.worktreePath,
51+
slackChannelId: issues.slackChannelId,
52+
telegramChatId: issues.telegramChatId,
5153
createdAt: issues.createdAt,
5254
updatedAt: issues.updatedAt,
5355
completedAt: issues.completedAt,
@@ -79,6 +81,8 @@ export const GET = withErrorHandler(async (request: Request) => {
7981
prStatus: row.prStatus,
8082
error: row.error,
8183
hasWorktree: row.worktreePath != null,
84+
// Slack takes precedence if both are somehow set (no DB constraint prevents it)
85+
source: row.slackChannelId ? "slack" as const : row.telegramChatId ? "telegram" as const : "web" as const,
8286
createdAt: row.createdAt.toISOString(),
8387
updatedAt: row.updatedAt.toISOString(),
8488
completedAt: row.completedAt?.toISOString() || null,

0 commit comments

Comments
 (0)