feat(forgejo): add create issue dialog and integrate with metric cards

This commit is contained in:
null 2026-05-22 22:14:51 -05:00
parent 463a75fdb7
commit 1076ca27bb
3 changed files with 40 additions and 11 deletions

View File

@ -19,6 +19,7 @@ import { DashboardSidebar } from "@/components/organisms/DashboardSidebar";
import { DashboardShell } from "@/components/templates/DashboardShell"; import { DashboardShell } from "@/components/templates/DashboardShell";
import { SignedOutPanel } from "@/components/auth/SignedOutPanel"; import { SignedOutPanel } from "@/components/auth/SignedOutPanel";
import { ForgejoIssueMetricCards } from "@/components/git/ForgejoIssueMetricCards"; import { ForgejoIssueMetricCards } from "@/components/git/ForgejoIssueMetricCards";
import { CreateForgejoIssueDialog } from "@/components/git/CreateForgejoIssueDialog";
import { ForgejoHeatmap } from "@/components/git/ForgejoHeatmap"; import { ForgejoHeatmap } from "@/components/git/ForgejoHeatmap";
import { import {
DashboardMetricCard, DashboardMetricCard,
@ -440,6 +441,7 @@ export default function DashboardPage() {
const [selectedForgejoRepositoryId, setSelectedForgejoRepositoryId] = const [selectedForgejoRepositoryId, setSelectedForgejoRepositoryId] =
useState(ALL_FORGEJO_REPOSITORIES); useState(ALL_FORGEJO_REPOSITORIES);
const [isRefreshingForgejoSync, setIsRefreshingForgejoSync] = useState(false); const [isRefreshingForgejoSync, setIsRefreshingForgejoSync] = useState(false);
const [createForgejoIssueOpen, setCreateForgejoIssueOpen] = useState(false);
const boardsQuery = useListBoardsApiV1BoardsGet< const boardsQuery = useListBoardsApiV1BoardsGet<
listBoardsApiV1BoardsGetResponse, listBoardsApiV1BoardsGetResponse,
@ -1325,10 +1327,30 @@ export default function DashboardPage() {
selectedRepositoryId={selectedForgejoRepositoryId} selectedRepositoryId={selectedForgejoRepositoryId}
onSelectedRepositoryChange={setSelectedForgejoRepositoryId} onSelectedRepositoryChange={setSelectedForgejoRepositoryId}
onRefreshLastSync={handleRefreshForgejoLastSync} onRefreshLastSync={handleRefreshForgejoLastSync}
onCreateIssue={() => setCreateForgejoIssueOpen(true)}
isRefreshingLastSync={isRefreshingForgejoSync} isRefreshingLastSync={isRefreshingForgejoSync}
isLoading={forgejoIssueMetricsLoading} isLoading={forgejoIssueMetricsLoading}
error={forgejoIssueMetricsError} error={forgejoIssueMetricsError}
/> />
<CreateForgejoIssueDialog
repositories={forgejoRepositories}
open={createForgejoIssueOpen}
onOpenChange={setCreateForgejoIssueOpen}
defaultRepositoryId={
selectedForgejoRepositoryId === ALL_FORGEJO_REPOSITORIES
? undefined
: selectedForgejoRepositoryId
}
onSuccess={() => {
setCreateForgejoIssueOpen(false);
void Promise.all([
forgejoRepositoriesQuery.refetch(),
forgejoMetricsQuery.refetch(),
forgejoHeatmapQuery.refetch(),
forgejoLastPushQuery.refetch(),
]);
}}
/>
</div> </div>
<div className="mt-4"> <div className="mt-4">

View File

@ -1,6 +1,6 @@
"use client"; "use client";
import { useState } from "react"; import { useEffect, useState } from "react";
import { import {
Dialog, Dialog,
@ -43,6 +43,12 @@ export function CreateForgejoIssueDialog({
const [isSubmitting, setIsSubmitting] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
useEffect(() => {
if (open && !isSubmitting) {
setRepositoryId(defaultRepositoryId ?? repositories[0]?.id ?? "");
}
}, [defaultRepositoryId, isSubmitting, open, repositories]);
const reset = () => { const reset = () => {
setTitle(""); setTitle("");
setBody(ISSUE_TEMPLATE); setBody(ISSUE_TEMPLATE);

View File

@ -8,6 +8,7 @@ import {
ArrowUpRight, ArrowUpRight,
CheckCircle2, CheckCircle2,
Clock3, Clock3,
PlusCircle,
RefreshCw, RefreshCw,
ShieldAlert, ShieldAlert,
ShieldCheck, ShieldCheck,
@ -30,6 +31,7 @@ type ForgejoIssueMetricCardsProps = {
selectedRepositoryId: string; selectedRepositoryId: string;
onSelectedRepositoryChange: (repositoryId: string) => void; onSelectedRepositoryChange: (repositoryId: string) => void;
onRefreshLastSync: () => void; onRefreshLastSync: () => void;
onCreateIssue: () => void;
isRefreshingLastSync?: boolean; isRefreshingLastSync?: boolean;
isLoading?: boolean; isLoading?: boolean;
error?: string | null; error?: string | null;
@ -309,6 +311,7 @@ export function ForgejoIssueMetricCards({
selectedRepositoryId, selectedRepositoryId,
onSelectedRepositoryChange, onSelectedRepositoryChange,
onRefreshLastSync, onRefreshLastSync,
onCreateIssue,
isRefreshingLastSync = false, isRefreshingLastSync = false,
isLoading = false, isLoading = false,
error, error,
@ -412,17 +415,15 @@ export function ForgejoIssueMetricCards({
))} ))}
</SelectContent> </SelectContent>
</Select> </Select>
<Link <button
href={ type="button"
selectedRepositoryId === ALL_REPOSITORIES_VALUE onClick={onCreateIssue}
? "/git-projects/issues" disabled={repositories.length === 0}
: `/git-projects/issues?repository_id=${encodeURIComponent(selectedRepositoryId)}` className="inline-flex w-fit items-center gap-1 text-sm font-medium text-[color:var(--accent)] transition hover:text-[color:var(--accent-strong)] disabled:cursor-not-allowed disabled:opacity-50"
}
className="inline-flex w-fit items-center gap-1 text-sm font-medium text-[color:var(--accent)] transition hover:text-[color:var(--accent-strong)]"
> >
Open issues Create issue
<ArrowUpRight className="h-4 w-4" /> <PlusCircle className="h-4 w-4" />
</Link> </button>
</div> </div>
</div> </div>