From e54a29230dfb17391c4d3037ef3286475e599623 Mon Sep 17 00:00:00 2001 From: null Date: Mon, 25 May 2026 14:48:24 -0500 Subject: [PATCH] ticker --- frontend/src/app/git-projects/page.tsx | 5 +- frontend/src/app/globals.css | 2 +- .../git/ForgejoRepositoriesTable.tsx | 51 ++++++++++++++++--- frontend/src/lib/api-forgejo.ts | 22 ++++++++ 4 files changed, 71 insertions(+), 9 deletions(-) diff --git a/frontend/src/app/git-projects/page.tsx b/frontend/src/app/git-projects/page.tsx index b5c6b67..46dd73d 100644 --- a/frontend/src/app/git-projects/page.tsx +++ b/frontend/src/app/git-projects/page.tsx @@ -40,6 +40,7 @@ import { getApiBaseUrl } from "@/lib/api-base"; import { getForgejoRepositories, deleteForgejoRepository, + refreshRepositoryRecentIssues, syncRepository, validateRepository, type ForgejoRepository, @@ -531,10 +532,10 @@ export default function ForgejoRepositoriesPage() { const handleSync = async (repository: ForgejoRepository) => { try { - const result = await syncRepository(repository.id); + const result = await refreshRepositoryRecentIssues(repository.id); setNotice({ tone: "success", - message: `${repositoryName(repository)} synced: ${result.created} created, ${result.updated} updated, ${result.open} open, ${result.closed} closed.`, + message: `${repositoryName(repository)} refreshed: ${result.created} created, ${result.updated} updated, ${result.open} open, ${result.closed} closed.`, }); // Refetch to update last_sync_at const data = await getForgejoRepositories(); diff --git a/frontend/src/app/globals.css b/frontend/src/app/globals.css index 2b1b018..7c46d3d 100644 --- a/frontend/src/app/globals.css +++ b/frontend/src/app/globals.css @@ -272,7 +272,7 @@ textarea::placeholder { animation: progress-shimmer 1.8s linear infinite; } .animate-ticker { - animation: ticker-scroll 45s linear infinite; + animation: ticker-scroll 70s linear infinite; } .ticker-fade-mask { -webkit-mask-image: linear-gradient(to right, transparent 0px, black 48px, black calc(100% - 48px), transparent 100%); diff --git a/frontend/src/components/git/ForgejoRepositoriesTable.tsx b/frontend/src/components/git/ForgejoRepositoriesTable.tsx index 65a9cb9..3471246 100644 --- a/frontend/src/components/git/ForgejoRepositoriesTable.tsx +++ b/frontend/src/components/git/ForgejoRepositoriesTable.tsx @@ -22,6 +22,7 @@ import { Archive, CheckCircle2, CircleDot, + ExternalLink, Eye, GitBranch, GitCommitHorizontal, @@ -51,6 +52,25 @@ const formatConnectionUrl = (value?: string | null) => { return value.replace(/^https?:\/\//i, "").replace(/\/$/, ""); }; +const buildRepositoryUrl = (repo: ForgejoRepository) => { + const baseUrl = repo.connection?.base_url; + if (!baseUrl) return null; + const normalizedBaseUrl = /^https?:\/\//i.test(baseUrl) + ? baseUrl + : `https://${baseUrl}`; + try { + const base = normalizedBaseUrl.endsWith("/") + ? normalizedBaseUrl + : `${normalizedBaseUrl}/`; + return new URL( + `${encodeURIComponent(repo.owner)}/${encodeURIComponent(repo.repo)}`, + base, + ).toString(); + } catch { + return null; + } +}; + const repositoryTone = (repo: ForgejoRepository) => { if (repo.last_sync_error) return "danger"; if (!repo.active || repo.is_archived) return "muted"; @@ -300,12 +320,31 @@ const columns = ( accessorKey: "connection", header: "Connection", cell: ({ row }) => { - const connection = row.original.connection; + const repo = row.original; + const repositoryUrl = buildRepositoryUrl(repo); + const label = formatConnectionUrl(repo.connection?.base_url); + if (!repositoryUrl) { + return ( +
+ + {label} + +
+ ); + } + return (
- - {formatConnectionUrl(connection?.base_url)} - + + {label} + +
); }, @@ -503,7 +542,7 @@ function ActionsCell({ onClick={handleSync} disabled={isSyncLoading} className="h-7 w-7 rounded-lg p-0 text-muted hover:bg-[color:var(--surface-muted)] hover:text-strong" - aria-label={`Sync issues for ${repository.display_name || `${repository.owner}/${repository.repo}`}`} + aria-label={`Refresh recent issues for ${repository.display_name || `${repository.owner}/${repository.repo}`}`} > {isSyncLoading ? ( @@ -514,7 +553,7 @@ function ActionsCell({ )} - Sync issues + Refresh recent issues )} {onValidate && ( diff --git a/frontend/src/lib/api-forgejo.ts b/frontend/src/lib/api-forgejo.ts index d676b2f..5976907 100644 --- a/frontend/src/lib/api-forgejo.ts +++ b/frontend/src/lib/api-forgejo.ts @@ -258,6 +258,28 @@ export async function syncRepository(repositoryId: string): Promise<{ }); } +export async function refreshRepositoryRecentIssues( + repositoryId: string, + days = 7, +): Promise<{ + created: number; + updated: number; + open: number; + closed: number; + total: number; +}> { + const params = new URLSearchParams({ days: String(days) }); + return fetchJson<{ + created: number; + updated: number; + open: number; + closed: number; + total: number; + }>(`/api/v1/forgejo/repositories/${repositoryId}/sync/recent?${params}`, { + method: "POST", + }); +} + export interface ForgejoValidationStatus { ok: boolean; status: string;