Show when each Gitea repository was last updated

The API already returned Gitea's updated_at for every repo but the page
never displayed it. The Gitea table now has a sortable "Last updated"
column with a relative age ("3 d ago") over the exact date/time (in the
app's configured date format), and the CSV export includes it.

Gitea only exposes a single updated_at per repository (there is no
separate last-push time), so this is Gitea's own notion of "updated".

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-26 03:20:43 +02:00
co-authored by Claude Sonnet 5
parent 4118062405
commit 017014586f
2 changed files with 31 additions and 2 deletions
+16 -2
View File
@@ -6,6 +6,8 @@ import SortableTh from "../components/SortableTh";
import { usePagination } from "../hooks/usePagination";
import Pagination from "../components/Pagination";
import { downloadCsv } from "../utils/csv";
import { formatDateTime } from "../utils/date";
import { formatAgo } from "../utils/duration";
function runStatusBadge(repo: GiteaRepo) {
const run = repo.latestRun;
@@ -97,10 +99,11 @@ export default function Gitea({ user }: { user: CurrentUser }) {
if (!sorted) return;
downloadCsv(
"gitea-repos.csv",
["Repository", "Private", "Last run", "Branch", "Status"],
["Repository", "Private", "Last updated", "Last run", "Branch", "Status"],
sorted.map((r) => [
r.fullName,
r.private ? "yes" : "no",
r.updatedAt ? formatDateTime(new Date(r.updatedAt)) : "",
r.latestRun ? `#${r.latestRun.runNumber} ${r.latestRun.displayTitle}` : "",
r.latestRun?.headBranch ?? "",
r.latestRun ? r.latestRun.conclusion || r.latestRun.status : "",
@@ -167,6 +170,7 @@ export default function Gitea({ user }: { user: CurrentUser }) {
<thead>
<tr>
<SortableTh<GiteaRepo> label="Repository" sortKeyName="fullName" activeKey={sortKey} direction={sortDir} onSort={requestSort} />
<SortableTh<GiteaRepo> label="Last updated" sortKeyName="updatedAt" activeKey={sortKey} direction={sortDir} onSort={requestSort} />
<th>Last run</th>
<th>Branch</th>
<th>Status</th>
@@ -182,6 +186,16 @@ export default function Gitea({ user }: { user: CurrentUser }) {
</a>
{r.private && <span className="badge bg-secondary-lt ms-2">Private</span>}
</td>
<td>
{r.updatedAt ? (
<>
<div>{formatAgo(r.updatedAt)}</div>
<div className="text-secondary small">{formatDateTime(new Date(r.updatedAt))}</div>
</>
) : (
<span className="text-secondary">—</span>
)}
</td>
<td className="text-secondary">
{r.latestRun ? (
<a href={r.latestRun.htmlUrl} target="_blank" rel="noopener noreferrer">
@@ -212,7 +226,7 @@ export default function Gitea({ user }: { user: CurrentUser }) {
))}
{sorted?.length === 0 && (
<tr>
<td colSpan={canEdit ? 5 : 4} className="text-secondary text-center">
<td colSpan={canEdit ? 6 : 5} className="text-secondary text-center">
No repositories visible to this token.
</td>
</tr>
+15
View File
@@ -7,3 +7,18 @@ export function formatRemaining(endsAtIso: string, now: number = Date.now()): st
const days = Math.floor(hours / 24);
return hours % 24 ? `${days} d ${hours % 24} h` : `${days} d`;
}
/** "just now", "5 min ago", "3 h ago", "12 d ago", "5 mo ago", "2 y ago" for an ISO timestamp (a future one reads as "just now"). */
export function formatAgo(iso: string, now: number = Date.now()): string {
const t = new Date(iso).getTime();
if (!Number.isFinite(t)) return "";
const minutes = Math.floor(Math.max(0, now - t) / 60_000);
if (minutes < 1) return "just now";
if (minutes < 60) return `${minutes} min ago`;
const hours = Math.floor(minutes / 60);
if (hours < 24) return `${hours} h ago`;
const days = Math.floor(hours / 24);
if (days < 60) return `${days} d ago`;
if (days < 730) return `${Math.floor(days / 30)} mo ago`;
return `${Math.floor(days / 365)} y ago`;
}