diff --git a/app/Http/Controllers/OverviewController.php b/app/Http/Controllers/OverviewController.php index 2b96876..f446f7c 100644 --- a/app/Http/Controllers/OverviewController.php +++ b/app/Http/Controllers/OverviewController.php @@ -3,19 +3,51 @@ namespace App\Http\Controllers; use App\Domain\Dashboard\Queries\GetOverviewDashboardQuery; +use App\Domain\GitHub\Queries\WorkItemsForUserQuery; +use Illuminate\Http\Request; use Inertia\Inertia; use Inertia\Response; /** - * Overview dashboard. Delegates the read entirely to - * `GetOverviewDashboardQuery` (roadmap §10.2). The query is responsible - * for distinguishing real DB-backed slices from clearly-marked mock - * placeholders. + * Overview dashboard. Mostly delegates the read to + * `GetOverviewDashboardQuery` (roadmap §10.2) — that query handles the + * single-tenant phase-1 slices that don't need a user (projects, + * hosts, deployments, top repositories, activity heatmap). + * + * The Issues & PRs widget is the one user-scoped slice on this page + * — `WorkItemsForUserQuery` requires a `User` so we run it here and + * merge the result rather than threading the user into + * `GetOverviewDashboardQuery`'s no-arg signature. */ class OverviewController extends Controller { - public function __invoke(GetOverviewDashboardQuery $query): Response - { - return Inertia::render('Overview', $query->handle()); + /** + * Cap for the Issues & PRs widget — keeps the card visually + * consistent with the other 4-row stubs around it. + */ + private const TOP_WORK_ITEMS_LIMIT = 4; + + public function __invoke( + Request $request, + GetOverviewDashboardQuery $query, + WorkItemsForUserQuery $workItemsQuery, + ): Response { + $payload = $query->handle(); + + // Spec 016 shipped the work-items query; this surfaces the top + // N open items on the Overview's Issues & PRs widget so the + // card reflects real activity instead of fixture data. + $topWorkItems = array_slice( + $workItemsQuery->execute($request->user(), [ + 'kind' => 'all', + 'state' => 'open', + ]), + 0, + self::TOP_WORK_ITEMS_LIMIT, + ); + + return Inertia::render('Overview', array_merge($payload, [ + 'topWorkItems' => $topWorkItems, + ])); } } diff --git a/resources/js/Pages/Overview.vue b/resources/js/Pages/Overview.vue index f175eae..6ce8e63 100644 --- a/resources/js/Pages/Overview.vue +++ b/resources/js/Pages/Overview.vue @@ -22,9 +22,22 @@ import { ShieldCheck, } from 'lucide-vue-next'; +interface TopWorkItem { + id: string; + kind: 'issue' | 'pull_request' | string; + number: number; + title: string; + state: string | null; + author_login: string | null; + updated_at_github: string | null; + html_url: string | null; + repository: { id: number; full_name: string; name: string } | null; +} + defineProps<{ dashboard: DashboardPayload; activityHeatmap: ActivityHeatmapPayload; + topWorkItems: TopWorkItem[]; }>(); // ----- Hardcoded mock data for the populated stub widgets ----- @@ -32,40 +45,6 @@ defineProps<{ // mock data here exists only so the page looks intentional, not skeletal — // each stub footer points at the phase that will replace it. -const stubIssues = [ - { - title: 'Login flow rejects valid 2FA codes intermittently', - repo: 'nexus-web', - time: '12 min', - priority: 'critical' as const, - }, - { - title: 'Migrate analytics events to BigQuery sink', - repo: 'nexus-api', - time: '3 h', - priority: 'high' as const, - }, - { - title: 'Add dark-mode tokens to email templates', - repo: 'nexus-mail', - time: '1 d', - priority: 'medium' as const, - }, - { - title: 'Expose feature-flag SDK in the JS bundle', - repo: 'nexus-flags', - time: '2 d', - priority: 'low' as const, - }, -]; - -const priorityToneMap = { - critical: 'danger', - high: 'warning', - medium: 'info', - low: 'muted', -} as const; - const stubHosts = [ { name: 'prod-web-01', region: 'us-east', cpu: 0.42, mem: 0.61, status: 'success' as const }, { name: 'prod-api-02', region: 'us-east', cpu: 0.78, mem: 0.83, status: 'warning' as const }, @@ -96,12 +75,16 @@ const stubServices = [ }, ]; +// Stubs only list widgets whose owning phase hasn't shipped yet. +// `Deployment timeline` graduated when Phase 4 (specs 020–022) shipped +// — its real surface is the `Deployments` sidebar entry / `/deployments` +// page. `Website performance` stays here until spec 025 lands the +// dedicated Overview widget on top of the spec-023 monitor data. const visualizationStubs = [ { label: 'World map', icon: Globe, phase: 'Phase 8' }, { label: 'Resource utilization', icon: Activity, phase: 'Phase 6' }, - { label: 'Website performance', icon: LineChart, phase: 'Phase 5' }, + { label: 'Website performance', icon: LineChart, phase: 'Phase 5 · spec 025' }, { label: 'System metrics', icon: Gauge, phase: 'Phase 8' }, - { label: 'Deployment timeline', icon: Rocket, phase: 'Phase 4' }, ] as const; @@ -224,7 +207,10 @@ const visualizationStubs = [
- {{ issue.title }} -
-
+
+ + #{{ item.number }} + + {{ item.title }} +
++ + {{ item.repository.full_name }} + + · @{{ item.author_login }} + + + + · Updated {{ item.updated_at_github }} + +
++ No open issues or pull requests yet — connect a + GitHub repository to start syncing them. +
@@ -454,7 +470,7 @@ const visualizationStubs = [