diff
May 25, 02:00 AMdiff-artifact/v1{
"artifact": {
"files": [
{
"path": "apps/web/components/dashboard/billing-view.tsx",
"hunks": [
{
"lines": [
{
"type": "context",
"content": "import React from \"react\";",
"newLineNumber": 1,
"oldLineNumber": 1
},
{
"type": "context",
"content": "import type { WorkspaceBillingResponse } from \"@firmcode/shared\";",
"newLineNumber": 2,
"oldLineNumber": 2
},
{
"type": "addition",
"content": "import { isAllowedExternalDashboardUrl } from \"../../lib/dashboard-route-readiness\";",
"newLineNumber": 3,
"oldLineNumber": null
},
{
"type": "context",
"content": "import type { ViewState } from \"../../lib/view-state\";",
"newLineNumber": 4,
"oldLineNumber": 3
},
{
"type": "context",
"content": "",
"newLineNumber": 5,
"oldLineNumber": 4
},
{
"type": "context",
"content": "interface BillingViewProps {",
"newLineNumber": 6,
"oldLineNumber": 5
}
],
"newStart": 1,
"oldStart": 1,
"newLineCount": 6,
"oldLineCount": 5,
"sectionHeader": ""
},
{
"lines": [
{
"type": "context",
"content": " <section className=\"rounded-lg border border-red-200 bg-red-50 p-4\">",
"newLineNumber": 15,
"oldLineNumber": 14
},
{
"type": "context",
"content": " <h2 className=\"text-sm font-semibold text-red-800\">Billing could not be loaded</h2>",
"newLineNumber": 16,
"oldLineNumber": 15
},
{
"type": "context",
"content": " <p className=\"mt-2 text-sm leading-6 text-red-700\">{state.message}</p>",
"newLineNumber": 17,
"oldLineNumber": 16
},
{
"type": "deletion",
"content": " <button className=\"mt-4 rounded-md bg-slate-200 px-3 py-2 text-sm font-medium text-secondary\" type=\"button\" disabled>",
"newLineNumber": null,
"oldLineNumber": 17
},
{
"type": "addition",
"content": " <button",
"newLineNumber": 18,
"oldLineNumber": null
},
{
"type": "addition",
"content": " className=\"mt-4 rounded-md bg-slate-200 px-3 py-2 text-sm font-medium text-secondary\"",
"newLineNumber": 19,
"oldLineNumber": null
},
{
"type": "addition",
"content": " type=\"button\"",
"newLineNumber": 20,
"oldLineNumber": null
},
{
"type": "addition",
"content": " disabled",
"newLineNumber": 21,
"oldLineNumber": null
},
{
"type": "addition",
"content": " title=\"Billing management is unavailable until billing status loads.\"",
"newLineNumber": 22,
"oldLineNumber": null
},
{
"type": "addition",
"content": " >",
"newLineNumber": 23,
"oldLineNumber": null
},
{
"type": "context",
"content": " Manage subscription",
"newLineNumber": 24,
"oldLineNumber": 18
},
{
"type": "context",
"content": " </button>",
"newLineNumber": 25,
"oldLineNumber": 19
},
{
"type": "context",
"content": " </section>",
"newLineNumber": 26,
"oldLineNumber": 20
}
],
"newStart": 15,
"oldStart": 14,
"newLineCount": 12,
"oldLineCount": 7,
"sectionHeader": "export function BillingView({ state, billingPortalUrl }: BillingViewProps) {"
},
{
"lines": [
{
"type": "context",
"content": " <BillingMetric label=\"Usage\" value={formatNullableMetric(data?.usage.reviewRunsThisMonth, \"runs\")} />",
"newLineNumber": 45,
"oldLineNumber": 39
},
{
"type": "context",
"content": " <BillingMetric label=\"Seats\" value={formatNullableMetric(data?.usage.seats, \"seats\")} />",
"newLineNumber": 46,
"oldLineNumber": 40
},
{
"type": "context",
"content": " </dl>",
"newLineNumber": 47,
"oldLineNumber": 41
},
{
"type": "deletion",
"content": " {data?.workspace.canManageBilling === true && billingPortalUrl !== null ? (",
"newLineNumber": null,
"oldLineNumber": 42
},
{
"type": "deletion",
"content": " <a className=\"mt-6 inline-flex h-10 items-center rounded-md bg-accent px-4 text-sm font-medium text-white\" href={billingPortalUrl}>",
"newLineNumber": null,
"oldLineNumber": 43
},
{
"type": "addition",
"content": " {data?.workspace.canManageBilling === true &&",
"newLineNumber": 48,
"oldLineNumber": null
},
{
"type": "addition",
"content": " billingPortalUrl !== null &&",
"newLineNumber": 49,
"oldLineNumber": null
},
{
"type": "addition",
"content": " isAllowedExternalDashboardUrl(billingPortalUrl, \"clerk\") ? (",
"newLineNumber": 50,
"oldLineNumber": null
},
{
"type": "addition",
"content": " <a",
"newLineNumber": 51,
"oldLineNumber": null
},
{
"type": "addition",
"content": " className=\"mt-6 inline-flex h-10 items-center rounded-md bg-accent px-4 text-sm font-medium text-white\"",
"newLineNumber": 52,
"oldLineNumber": null
},
{
"type": "addition",
"content": " data-dashboard-destination=\"external\"",
"newLineNumber": 53,
"oldLineNumber": null
},
{
"type": "addition",
"content": " data-dashboard-provider=\"clerk\"",
"newLineNumber": 54,
"oldLineNumber": null
},
{
"type": "addition",
"content": " href={billingPortalUrl}",
"newLineNumber": 55,
"oldLineNumber": null
},
{
"type": "addition",
"content": " rel=\"noreferrer\"",
"newLineNumber": 56,
"oldLineNumber": null
},
{
"type": "addition",
"content": " >",
"newLineNumber": 57,
"oldLineNumber": null
},
{
"type": "context",
"content": " Manage subscription",
"newLineNumber": 58,
"oldLineNumber": 44
},
{
"type": "context",
"content": " </a>",
"newLineNumber": 59,
"oldLineNumber": 45
},
{
"type": "context",
"content": " ) : (",
"newLineNumber": 60,
"oldLineNumber": 46
},
{
"type": "deletion",
"content": " <button className=\"mt-6 inline-flex h-10 items-center rounded-md bg-slate-200 px-4 text-sm font-medium text-secondary\" type=\"button\" disabled>",
"newLineNumber": null,
"oldLineNumber": 47
},
{
"type": "addition",
"content": " <button",
"newLineNumber": 61,
"oldLineNumber": null
},
{
"type": "addition",
"content": " className=\"mt-6 inline-flex h-10 items-center rounded-md bg-slate-200 px-4 text-sm font-medium text-secondary\"",
"newLineNumber": 62,
"oldLineNumber": null
},
{
"type": "addition",
"content": " type=\"button\"",
"newLineNumber": 63,
"oldLineNumber": null
},
{
"type": "addition",
"content": " disabled",
"newLineNumber": 64,
"oldLineNumber": null
},
{
"type": "addition",
"content": " title={",
"newLineNumber": 65,
"oldLineNumber": null
},
{
"type": "addition",
"content": " data?.workspace.canManageBilling === true",
"newLineNumber": 66,
"oldLineNumber": null
},
{
"type": "addition",
"content": " ? \"Clerk billing portal URL is not configured.\"",
"newLineNumber": 67,
"oldLineNumber": null
},
{
"type": "addition",
"content": " : \"Owner billing permission is required to manage subscriptions.\"",
"newLineNumber": 68,
"oldLineNumber": null
},
{
"type": "addition",
"content": " }",
"newLineNumber": 69,
"oldLineNumber": null
},
{
"type": "addition",
"content": " >",
"newLineNumber": 70,
"oldLineNumber": null
},
{
"type": "context",
"content": " Manage subscription",
"newLineNumber": 71,
"oldLineNumber": 48
},
{
"type": "context",
"content": " </button>",
"newLineNumber": 72,
"oldLineNumber": 49
},
{
"type": "context",
"content": " )}",
"newLineNumber": 73,
"oldLineNumber": 50
}
],
"newStart": 45,
"oldStart": 39,
"newLineCount": 29,
"oldLineCount": 12,
"sectionHeader": "export function BillingView({ state, billingPortalUrl }: BillingViewProps) {"
}
],
"patch": "@@ -1,5 +1,6 @@\n import React from \"react\";\n import type { WorkspaceBillingResponse } from \"@firmcode/shared\";\n+import { isAllowedExternalDashboardUrl } from \"../../lib/dashboard-route-readiness\";\n import type { ViewState } from \"../../lib/view-state\";\n \n interface BillingViewProps {\n@@ -14,7 +15,12 @@ export function BillingView({ state, billingPortalUrl }: BillingViewProps) {\n <section className=\"rounded-lg border border-red-200 bg-red-50 p-4\">\n <h2 className=\"text-sm font-semibold text-red-800\">Billing could not be loaded</h2>\n <p className=\"mt-2 text-sm leading-6 text-red-700\">{state.message}</p>\n- <button className=\"mt-4 rounded-md bg-slate-200 px-3 py-2 text-sm font-medium text-secondary\" type=\"button\" disabled>\n+ <button\n+ className=\"mt-4 rounded-md bg-slate-200 px-3 py-2 text-sm font-medium text-secondary\"\n+ type=\"button\"\n+ disabled\n+ title=\"Billing management is unavailable until billing status loads.\"\n+ >\n Manage subscription\n </button>\n </section>\n@@ -39,12 +45,29 @@ export function BillingView({ state, billingPortalUrl }: BillingViewProps) {\n <BillingMetric label=\"Usage\" value={formatNullableMetric(data?.usage.reviewRunsThisMonth, \"runs\")} />\n <BillingMetric label=\"Seats\" value={formatNullableMetric(data?.usage.seats, \"seats\")} />\n </dl>\n- {data?.workspace.canManageBilling === true && billingPortalUrl !== null ? (\n- <a className=\"mt-6 inline-flex h-10 items-center rounded-md bg-accent px-4 text-sm font-medium text-white\" href={billingPortalUrl}>\n+ {data?.workspace.canManageBilling === true &&\n+ billingPortalUrl !== null &&\n+ isAllowedExternalDashboardUrl(billingPortalUrl, \"clerk\") ? (\n+ <a\n+ className=\"mt-6 inline-flex h-10 items-center rounded-md bg-accent px-4 text-sm font-medium text-white\"\n+ data-dashboard-destination=\"external\"\n+ data-dashboard-provider=\"clerk\"\n+ href={billingPortalUrl}\n+ rel=\"noreferrer\"\n+ >\n Manage subscription\n </a>\n ) : (\n- <button className=\"mt-6 inline-flex h-10 items-center rounded-md bg-slate-200 px-4 text-sm font-medium text-secondary\" type=\"button\" disabled>\n+ <button\n+ className=\"mt-6 inline-flex h-10 items-center rounded-md bg-slate-200 px-4 text-sm font-medium text-secondary\"\n+ type=\"button\"\n+ disabled\n+ title={\n+ data?.workspace.canManageBilling === true\n+ ? \"Clerk billing portal URL is not configured.\"\n+ : \"Owner billing permission is required to manage subscriptions.\"\n+ }\n+ >\n Manage subscription\n </button>\n )}",
"status": "modified",
"language": "typescript",
"additions": 27,
"deletions": 4,
"sizeBytes": 3631,
"previousPath": null,
"changedNewLines": [
3,
18,
19,
20,
21,
22,
23,
48,
49,
50,
51,
52,
53,
54,
55,
56,
57,
61,
62,
63,
64,
65,
66,
67,
68,
69,
70
],
"headContentSha256": "5a7bed1af3fb2f2a41f21d34c120f0da2210837e7eeecef38c5ed9125578eae8"
},
{
"path": "apps/web/components/dashboard/dashboard-shell.tsx",
"hunks": [
{
"lines": [
{
"type": "context",
"content": "import React from \"react\";",
"newLineNumber": 1,
"oldLineNumber": 1
},
{
"type": "addition",
"content": "import { DASHBOARD_NAV_ITEMS, type DashboardActiveItem } from \"../../lib/dashboard-navigation\";",
"newLineNumber": 2,
"oldLineNumber": null
},
{
"type": "context",
"content": "",
"newLineNumber": 3,
"oldLineNumber": 2
},
{
"type": "context",
"content": "interface DashboardShellProps {",
"newLineNumber": 4,
"oldLineNumber": 3
},
{
"type": "deletion",
"content": " activeItem:",
"newLineNumber": null,
"oldLineNumber": 4
},
{
"type": "deletion",
"content": " | \"Overview\"",
"newLineNumber": null,
"oldLineNumber": 5
},
{
"type": "deletion",
"content": " | \"PR Review\"",
"newLineNumber": null,
"oldLineNumber": 6
},
{
"type": "deletion",
"content": " | \"Repositories\"",
"newLineNumber": null,
"oldLineNumber": 7
},
{
"type": "deletion",
"content": " | \"Pull Requests\"",
"newLineNumber": null,
"oldLineNumber": 8
},
{
"type": "deletion",
"content": " | \"Review Runs\"",
"newLineNumber": null,
"oldLineNumber": 9
},
{
"type": "deletion",
"content": " | \"Findings\"",
"newLineNumber": null,
"oldLineNumber": 10
},
{
"type": "deletion",
"content": " | \"CI Failures\"",
"newLineNumber": null,
"oldLineNumber": 11
},
{
"type": "deletion",
"content": " | \"Rules\"",
"newLineNumber": null,
"oldLineNumber": 12
},
{
"type": "deletion",
"content": " | \"Settings\"",
"newLineNumber": null,
"oldLineNumber": 13
},
{
"type": "deletion",
"content": " | \"Billing\";",
"newLineNumber": null,
"oldLineNumber": 14
},
{
"type": "addition",
"content": " activeItem: DashboardActiveItem;",
"newLineNumber": 5,
"oldLineNumber": null
},
{
"type": "context",
"content": " children: React.ReactNode;",
"newLineNumber": 6,
"oldLineNumber": 15
},
{
"type": "context",
"content": "}",
"newLineNumber": 7,
"oldLineNumber": 16
},
{
"type": "context",
"content": "",
"newLineNumber": 8,
"oldLineNumber": 17
},
{
"type": "deletion",
"content": "const navItems = [",
"newLineNumber": null,
"oldLineNumber": 18
},
{
"type": "deletion",
"content": " { label: \"Overview\", href: \"/\", enabled: true },",
"newLineNumber": null,
"oldLineNumber": 19
},
{
"type": "deletion",
"content": " { label: \"PR Review\", href: \"/github/installations\", enabled: true },",
"newLineNumber": null,
"oldLineNumber": 20
},
{
"type": "deletion",
"content": " { label: \"Repositories\", href: \"/repositories\", enabled: true },",
"newLineNumber": null,
"oldLineNumber": 21
},
{
"type": "deletion",
"content": " { label: \"Pull Requests\", href: \"/pull-requests\", enabled: true },",
"newLineNumber": null,
"oldLineNumber": 22
},
{
"type": "deletion",
"content": " { label: \"Review Runs\", href: \"/review-runs\", enabled: true },",
"newLineNumber": null,
"oldLineNumber": 23
},
{
"type": "deletion",
"content": " { label: \"Findings\", href: \"/findings\", enabled: true },",
"newLineNumber": null,
"oldLineNumber": 24
},
{
"type": "deletion",
"content": " { label: \"CI Failures\", href: \"/ci-failures\", enabled: true },",
"newLineNumber": null,
"oldLineNumber": 25
},
{
"type": "deletion",
"content": " { label: \"Rules / Policies\", href: \"/rules\", enabled: true },",
"newLineNumber": null,
"oldLineNumber": 26
},
{
"type": "deletion",
"content": " { label: \"Settings\", href: \"/settings\", enabled: true },",
"newLineNumber": null,
"oldLineNumber": 27
},
{
"type": "deletion",
"content": " { label: \"Billing\", href: \"/billing\", enabled: true }",
"newLineNumber": null,
"oldLineNumber": 28
},
{
"type": "deletion",
"content": "];",
"newLineNumber": null,
"oldLineNumber": 29
},
{
"type": "deletion",
"content": "",
"newLineNumber": null,
"oldLineNumber": 30
},
{
"type": "context",
"content": "export function DashboardShell({ activeItem, children }: DashboardShellProps) {",
"newLineNumber": 9,
"oldLineNumber": 31
},
{
"type": "context",
"content": " return (",
"newLineNumber": 10,
"oldLineNumber": 32
},
{
"type": "context",
"content": " <div className=\"min-h-screen bg-shell text-primary\" data-clerk-authenticated=\"required\">",
"newLineNumber": 11,
"oldLineNumber": 33
}
],
"newStart": 1,
"oldStart": 1,
"newLineCount": 11,
"oldLineCount": 33,
"sectionHeader": ""
},
{
"lines": [
{
"type": "context",
"content": " <details className=\"border-t border-border px-4 py-2 lg:hidden\">",
"newLineNumber": 46,
"oldLineNumber": 68
},
{
"type": "context",
"content": " <summary className=\"cursor-pointer text-sm font-medium text-primary\">Navigation</summary>",
"newLineNumber": 47,
"oldLineNumber": 69
},
{
"type": "context",
"content": " <nav className=\"mt-2 grid gap-1\" aria-label=\"Mobile dashboard\">",
"newLineNumber": 48,
"oldLineNumber": 70
},
{
"type": "deletion",
"content": " {navItems.map((item) => {",
"newLineNumber": null,
"oldLineNumber": 71
},
{
"type": "deletion",
"content": " const normalizedLabel = item.label === \"Rules / Policies\" ? \"Rules\" : item.label;",
"newLineNumber": null,
"oldLineNumber": 72
},
{
"type": "deletion",
"content": " const active = normalizedLabel === activeItem;",
"newLineNumber": null,
"oldLineNumber": 73
},
{
"type": "addition",
"content": " {DASHBOARD_NAV_ITEMS.map((item) => {",
"newLineNumber": 49,
"oldLineNumber": null
},
{
"type": "addition",
"content": " const active = item.activeItem === activeItem;",
"newLineNumber": 50,
"oldLineNumber": null
},
{
"type": "context",
"content": "",
"newLineNumber": 51,
"oldLineNumber": 74
},
{
"type": "context",
"content": " return item.enabled ? (",
"newLineNumber": 52,
"oldLineNumber": 75
},
{
"type": "context",
"content": " <a",
"newLineNumber": 53,
"oldLineNumber": 76
}
],
"newStart": 46,
"oldStart": 68,
"newLineCount": 8,
"oldLineCount": 9,
"sectionHeader": "export function DashboardShell({ activeItem, children }: DashboardShellProps) {"
},
{
"lines": [
{
"type": "context",
"content": " key={item.label}",
"newLineNumber": 65,
"oldLineNumber": 88
},
{
"type": "context",
"content": " className=\"cursor-not-allowed rounded-md px-3 py-2 text-sm font-medium text-secondary opacity-55\"",
"newLineNumber": 66,
"oldLineNumber": 89
},
{
"type": "context",
"content": " aria-disabled=\"true\"",
"newLineNumber": 67,
"oldLineNumber": 90
},
{
"type": "deletion",
"content": " title=\"Planned dashboard section\"",
"newLineNumber": null,
"oldLineNumber": 91
},
{
"type": "addition",
"content": " title={item.disabledTitle ?? \"Planned dashboard section\"}",
"newLineNumber": 68,
"oldLineNumber": null
},
{
"type": "context",
"content": " >",
"newLineNumber": 69,
"oldLineNumber": 92
},
{
"type": "context",
"content": " {item.label}",
"newLineNumber": 70,
"oldLineNumber": 93
},
{
"type": "context",
"content": " </span>",
"newLineNumber": 71,
"oldLineNumber": 94
}
],
"newStart": 65,
"oldStart": 88,
"newLineCount": 7,
"oldLineCount": 7,
"sectionHeader": "export function DashboardShell({ activeItem, children }: DashboardShellProps) {"
},
{
"lines": [
{
"type": "context",
"content": " </div>",
"newLineNumber": 86,
"oldLineNumber": 109
},
{
"type": "context",
"content": " </div>",
"newLineNumber": 87,
"oldLineNumber": 110
},
{
"type": "context",
"content": " <nav className=\"flex flex-col gap-1\" aria-label=\"Dashboard\">",
"newLineNumber": 88,
"oldLineNumber": 111
},
{
"type": "deletion",
"content": " {navItems.map((item) => {",
"newLineNumber": null,
"oldLineNumber": 112
},
{
"type": "deletion",
"content": " const normalizedLabel = item.label === \"Rules / Policies\" ? \"Rules\" : item.label;",
"newLineNumber": null,
"oldLineNumber": 113
},
{
"type": "deletion",
"content": " const active = normalizedLabel === activeItem;",
"newLineNumber": null,
"oldLineNumber": 114
},
{
"type": "addition",
"content": " {DASHBOARD_NAV_ITEMS.map((item) => {",
"newLineNumber": 89,
"oldLineNumber": null
},
{
"type": "addition",
"content": " const active = item.activeItem === activeItem;",
"newLineNumber": 90,
"oldLineNumber": null
},
{
"type": "context",
"content": "",
"newLineNumber": 91,
"oldLineNumber": 115
},
{
"type": "context",
"content": " return item.enabled ? (",
"newLineNumber": 92,
"oldLineNumber": 116
},
{
"type": "context",
"content": " <a",
"newLineNumber": 93,
"oldLineNumber": 117
}
],
"newStart": 86,
"oldStart": 109,
"newLineCount": 8,
"oldLineCount": 9,
"sectionHeader": "export function DashboardShell({ activeItem, children }: DashboardShellProps) {"
},
{
"lines": [
{
"type": "context",
"content": " key={item.label}",
"newLineNumber": 107,
"oldLineNumber": 131
},
{
"type": "context",
"content": " className=\"cursor-not-allowed rounded-md border border-transparent px-3 py-2 text-sm font-medium text-secondary opacity-55\"",
"newLineNumber": 108,
"oldLineNumber": 132
},
{
"type": "context",
"content": " aria-disabled=\"true\"",
"newLineNumber": 109,
"oldLineNumber": 133
},
{
"type": "deletion",
"content": " title=\"Planned dashboard section\"",
"newLineNumber": null,
"oldLineNumber": 134
},
{
"type": "addition",
"content": " title={item.disabledTitle ?? \"Planned dashboard section\"}",
"newLineNumber": 110,
"oldLineNumber": null
},
{
"type": "context",
"content": " >",
"newLineNumber": 111,
"oldLineNumber": 135
},
{
"type": "context",
"content": " {item.label}",
"newLineNumber": 112,
"oldLineNumber": 136
},
{
"type": "context",
"content": " </span>",
"newLineNumber": 113,
"oldLineNumber": 137
}
],
"newStart": 107,
"oldStart": 131,
"newLineCount": 7,
"oldLineCount": 7,
"sectionHeader": "export function DashboardShell({ activeItem, children }: DashboardShellProps) {"
}
],
"patch": "@@ -1,33 +1,11 @@\n import React from \"react\";\n+import { DASHBOARD_NAV_ITEMS, type DashboardActiveItem } from \"../../lib/dashboard-navigation\";\n \n interface DashboardShellProps {\n- activeItem:\n- | \"Overview\"\n- | \"PR Review\"\n- | \"Repositories\"\n- | \"Pull Requests\"\n- | \"Review Runs\"\n- | \"Findings\"\n- | \"CI Failures\"\n- | \"Rules\"\n- | \"Settings\"\n- | \"Billing\";\n+ activeItem: DashboardActiveItem;\n children: React.ReactNode;\n }\n \n-const navItems = [\n- { label: \"Overview\", href: \"/\", enabled: true },\n- { label: \"PR Review\", href: \"/github/installations\", enabled: true },\n- { label: \"Repositories\", href: \"/repositories\", enabled: true },\n- { label: \"Pull Requests\", href: \"/pull-requests\", enabled: true },\n- { label: \"Review Runs\", href: \"/review-runs\", enabled: true },\n- { label: \"Findings\", href: \"/findings\", enabled: true },\n- { label: \"CI Failures\", href: \"/ci-failures\", enabled: true },\n- { label: \"Rules / Policies\", href: \"/rules\", enabled: true },\n- { label: \"Settings\", href: \"/settings\", enabled: true },\n- { label: \"Billing\", href: \"/billing\", enabled: true }\n-];\n-\n export function DashboardShell({ activeItem, children }: DashboardShellProps) {\n return (\n <div className=\"min-h-screen bg-shell text-primary\" data-clerk-authenticated=\"required\">\n@@ -68,9 +46,8 @@ export function DashboardShell({ activeItem, children }: DashboardShellProps) {\n <details className=\"border-t border-border px-4 py-2 lg:hidden\">\n <summary className=\"cursor-pointer text-sm font-medium text-primary\">Navigation</summary>\n <nav className=\"mt-2 grid gap-1\" aria-label=\"Mobile dashboard\">\n- {navItems.map((item) => {\n- const normalizedLabel = item.label === \"Rules / Policies\" ? \"Rules\" : item.label;\n- const active = normalizedLabel === activeItem;\n+ {DASHBOARD_NAV_ITEMS.map((item) => {\n+ const active = item.activeItem === activeItem;\n \n return item.enabled ? (\n <a\n@@ -88,7 +65,7 @@ export function DashboardShell({ activeItem, children }: DashboardShellProps) {\n key={item.label}\n className=\"cursor-not-allowed rounded-md px-3 py-2 text-sm font-medium text-secondary opacity-55\"\n aria-disabled=\"true\"\n- title=\"Planned dashboard section\"\n+ title={item.disabledTitle ?? \"Planned dashboard section\"}\n >\n {item.label}\n </span>\n@@ -109,9 +86,8 @@ export function DashboardShell({ activeItem, children }: DashboardShellProps) {\n </div>\n </div>\n <nav className=\"flex flex-col gap-1\" aria-label=\"Dashboard\">\n- {navItems.map((item) => {\n- const normalizedLabel = item.label === \"Rules / Policies\" ? \"Rules\" : item.label;\n- const active = normalizedLabel === activeItem;\n+ {DASHBOARD_NAV_ITEMS.map((item) => {\n+ const active = item.activeItem === activeItem;\n \n return item.enabled ? (\n <a\n@@ -131,7 +107,7 @@ export function DashboardShell({ activeItem, children }: DashboardShellProps) {\n key={item.label}\n className=\"cursor-not-allowed rounded-md border border-transparent px-3 py-2 text-sm font-medium text-secondary opacity-55\"\n aria-disabled=\"true\"\n- title=\"Planned dashboard section\"\n+ title={item.disabledTitle ?? \"Planned dashboard section\"}\n >\n {item.label}\n </span>",
"status": "modified",
"language": "typescript",
"additions": 8,
"deletions": 32,
"sizeBytes": 5786,
"previousPath": null,
"changedNewLines": [
2,
5,
49,
50,
68,
89,
90,
110
],
"headContentSha256": "855b0900298643a6aa4ec764a7acf02057f6d84d3fcbf572c835bae22d4c845d"
},
{
"path": "apps/web/components/dashboard/github-installations-view.tsx",
"hunks": [
{
"lines": [
{
"type": "context",
"content": "} from \"@firmcode/shared\";",
"newLineNumber": 7,
"oldLineNumber": 7
},
{
"type": "context",
"content": "import type { GitHubAppInstallConfig } from \"../../config/github-app-installation\";",
"newLineNumber": 8,
"oldLineNumber": 8
},
{
"type": "context",
"content": "import type { GitHubInstallationsState, GitHubSyncDashboardData } from \"../../lib/dashboard-data\";",
"newLineNumber": 9,
"oldLineNumber": 9
},
{
"type": "addition",
"content": "import { isAllowedExternalDashboardUrl } from \"../../lib/dashboard-route-readiness\";",
"newLineNumber": 10,
"oldLineNumber": null
},
{
"type": "context",
"content": "import { formatDateTime, shortSha } from \"./format\";",
"newLineNumber": 11,
"oldLineNumber": 10
},
{
"type": "context",
"content": "import { GitHubInstallationSyncButton, GitHubRepositorySyncButton } from \"./github-sync-controls\";",
"newLineNumber": 12,
"oldLineNumber": 11
},
{
"type": "context",
"content": "import { RepositoryAutomationToggle } from \"./repository-automation-toggle\";",
"newLineNumber": 13,
"oldLineNumber": 12
}
],
"newStart": 7,
"oldStart": 7,
"newLineCount": 7,
"oldLineCount": 6,
"sectionHeader": "import {"
},
{
"lines": [
{
"type": "context",
"content": " disabledReason={syncDisabledReason(state.data)}",
"newLineNumber": 37,
"oldLineNumber": 36
},
{
"type": "context",
"content": " />",
"newLineNumber": 38,
"oldLineNumber": 37
},
{
"type": "context",
"content": " ) : (",
"newLineNumber": 39,
"oldLineNumber": 38
},
{
"type": "deletion",
"content": " <button className=\"rounded-md border border-border bg-subtle px-3 py-2 text-sm font-medium text-secondary\" type=\"button\" disabled>",
"newLineNumber": null,
"oldLineNumber": 39
},
{
"type": "addition",
"content": " <button",
"newLineNumber": 40,
"oldLineNumber": null
},
{
"type": "addition",
"content": " className=\"rounded-md border border-border bg-subtle px-3 py-2 text-sm font-medium text-secondary\"",
"newLineNumber": 41,
"oldLineNumber": null
},
{
"type": "addition",
"content": " type=\"button\"",
"newLineNumber": 42,
"oldLineNumber": null
},
{
"type": "addition",
"content": " disabled",
"newLineNumber": 43,
"oldLineNumber": null
},
{
"type": "addition",
"content": " title=\"GitHub connection status must load before syncing.\"",
"newLineNumber": 44,
"oldLineNumber": null
},
{
"type": "addition",
"content": " >",
"newLineNumber": 45,
"oldLineNumber": null
},
{
"type": "context",
"content": " Sync GitHub",
"newLineNumber": 46,
"oldLineNumber": 40
},
{
"type": "context",
"content": " </button>",
"newLineNumber": 47,
"oldLineNumber": 41
},
{
"type": "context",
"content": " )}",
"newLineNumber": 48,
"oldLineNumber": 42
}
],
"newStart": 37,
"oldStart": 36,
"newLineCount": 12,
"oldLineCount": 7,
"sectionHeader": "export function GitHubInstallationsView({ state, installConfig }: GitHubInstalla"
},
{
"lines": [
{
"type": "context",
"content": " <p className=\"mt-2 text-sm leading-6 text-amber-800\">",
"newLineNumber": 106,
"oldLineNumber": 100
},
{
"type": "context",
"content": " Firmcode needs a Clerk workspace session before it can connect a GitHub account or map a GitHub App installation.",
"newLineNumber": 107,
"oldLineNumber": 101
},
{
"type": "context",
"content": " </p>",
"newLineNumber": 108,
"oldLineNumber": 102
},
{
"type": "deletion",
"content": " <button className=\"mt-4 rounded-md bg-slate-200 px-3 py-2 text-sm font-medium text-secondary\" type=\"button\" disabled>",
"newLineNumber": null,
"oldLineNumber": 103
},
{
"type": "addition",
"content": " <button",
"newLineNumber": 109,
"oldLineNumber": null
},
{
"type": "addition",
"content": " className=\"mt-4 rounded-md bg-slate-200 px-3 py-2 text-sm font-medium text-secondary\"",
"newLineNumber": 110,
"oldLineNumber": null
},
{
"type": "addition",
"content": " type=\"button\"",
"newLineNumber": 111,
"oldLineNumber": null
},
{
"type": "addition",
"content": " disabled",
"newLineNumber": 112,
"oldLineNumber": null
},
{
"type": "addition",
"content": " title=\"Sign in before connecting GitHub.\"",
"newLineNumber": 113,
"oldLineNumber": null
},
{
"type": "addition",
"content": " >",
"newLineNumber": 114,
"oldLineNumber": null
},
{
"type": "context",
"content": " Connect GitHub",
"newLineNumber": 115,
"oldLineNumber": 104
},
{
"type": "context",
"content": " </button>",
"newLineNumber": 116,
"oldLineNumber": 105
},
{
"type": "context",
"content": " </section>",
"newLineNumber": 117,
"oldLineNumber": 106
}
],
"newStart": 106,
"oldStart": 100,
"newLineCount": 12,
"oldLineCount": 7,
"sectionHeader": "function SignedOutState({ installConfig }: { installConfig: GitHubAppInstallConf"
},
{
"lines": [
{
"type": "context",
"content": "}) {",
"newLineNumber": 285,
"oldLineNumber": 274
},
{
"type": "context",
"content": " if (!hasOAuth) {",
"newLineNumber": 286,
"oldLineNumber": 275
},
{
"type": "context",
"content": " return (",
"newLineNumber": 287,
"oldLineNumber": 276
},
{
"type": "deletion",
"content": " <button className=\"rounded-md bg-slate-200 px-3 py-2 text-sm font-medium text-secondary\" type=\"button\" disabled>",
"newLineNumber": null,
"oldLineNumber": 277
},
{
"type": "addition",
"content": " <button",
"newLineNumber": 288,
"oldLineNumber": null
},
{
"type": "addition",
"content": " className=\"rounded-md bg-slate-200 px-3 py-2 text-sm font-medium text-secondary\"",
"newLineNumber": 289,
"oldLineNumber": null
},
{
"type": "addition",
"content": " type=\"button\"",
"newLineNumber": 290,
"oldLineNumber": null
},
{
"type": "addition",
"content": " disabled",
"newLineNumber": 291,
"oldLineNumber": null
},
{
"type": "addition",
"content": " title=\"Connect GitHub OAuth before installing the GitHub App.\"",
"newLineNumber": 292,
"oldLineNumber": null
},
{
"type": "addition",
"content": " >",
"newLineNumber": 293,
"oldLineNumber": null
},
{
"type": "context",
"content": " Connect GitHub first",
"newLineNumber": 294,
"oldLineNumber": 278
},
{
"type": "context",
"content": " </button>",
"newLineNumber": 295,
"oldLineNumber": 279
},
{
"type": "context",
"content": " );",
"newLineNumber": 296,
"oldLineNumber": 280
},
{
"type": "context",
"content": " }",
"newLineNumber": 297,
"oldLineNumber": 281
},
{
"type": "context",
"content": "",
"newLineNumber": 298,
"oldLineNumber": 282
},
{
"type": "context",
"content": " if (!canManage) {",
"newLineNumber": 299,
"oldLineNumber": 283
},
{
"type": "context",
"content": " return (",
"newLineNumber": 300,
"oldLineNumber": 284
},
{
"type": "deletion",
"content": " <button className=\"rounded-md bg-slate-200 px-3 py-2 text-sm font-medium text-secondary\" type=\"button\" disabled>",
"newLineNumber": null,
"oldLineNumber": 285
},
{
"type": "addition",
"content": " <button",
"newLineNumber": 301,
"oldLineNumber": null
},
{
"type": "addition",
"content": " className=\"rounded-md bg-slate-200 px-3 py-2 text-sm font-medium text-secondary\"",
"newLineNumber": 302,
"oldLineNumber": null
},
{
"type": "addition",
"content": " type=\"button\"",
"newLineNumber": 303,
"oldLineNumber": null
},
{
"type": "addition",
"content": " disabled",
"newLineNumber": 304,
"oldLineNumber": null
},
{
"type": "addition",
"content": " title=\"Owner or Admin required to install the GitHub App.\"",
"newLineNumber": 305,
"oldLineNumber": null
},
{
"type": "addition",
"content": " >",
"newLineNumber": 306,
"oldLineNumber": null
},
{
"type": "context",
"content": " Owner or Admin required",
"newLineNumber": 307,
"oldLineNumber": 286
},
{
"type": "context",
"content": " </button>",
"newLineNumber": 308,
"oldLineNumber": 287
},
{
"type": "context",
"content": " );",
"newLineNumber": 309,
"oldLineNumber": 288
},
{
"type": "context",
"content": " }",
"newLineNumber": 310,
"oldLineNumber": 289
},
{
"type": "context",
"content": "",
"newLineNumber": 311,
"oldLineNumber": 290
},
{
"type": "context",
"content": " if (installConfig.status !== \"configured\") {",
"newLineNumber": 312,
"oldLineNumber": 291
},
{
"type": "context",
"content": " return (",
"newLineNumber": 313,
"oldLineNumber": 292
},
{
"type": "deletion",
"content": " <button className=\"rounded-md bg-slate-200 px-3 py-2 text-sm font-medium text-secondary\" type=\"button\" disabled>",
"newLineNumber": null,
"oldLineNumber": 293
},
{
"type": "addition",
"content": " <button",
"newLineNumber": 314,
"oldLineNumber": null
},
{
"type": "addition",
"content": " className=\"rounded-md bg-slate-200 px-3 py-2 text-sm font-medium text-secondary\"",
"newLineNumber": 315,
"oldLineNumber": null
},
{
"type": "addition",
"content": " type=\"button\"",
"newLineNumber": 316,
"oldLineNumber": null
},
{
"type": "addition",
"content": " disabled",
"newLineNumber": 317,
"oldLineNumber": null
},
{
"type": "addition",
"content": " title=\"Set GITHUB_APP_INSTALL_URL or GITHUB_APP_SLUG before installing the GitHub App.\"",
"newLineNumber": 318,
"oldLineNumber": null
},
{
"type": "addition",
"content": " >",
"newLineNumber": 319,
"oldLineNumber": null
},
{
"type": "context",
"content": " Install URL not configured",
"newLineNumber": 320,
"oldLineNumber": 294
},
{
"type": "context",
"content": " </button>",
"newLineNumber": 321,
"oldLineNumber": 295
},
{
"type": "context",
"content": " );",
"newLineNumber": 322,
"oldLineNumber": 296
},
{
"type": "context",
"content": " }",
"newLineNumber": 323,
"oldLineNumber": 297
},
{
"type": "context",
"content": "",
"newLineNumber": 324,
"oldLineNumber": 298
},
{
"type": "addition",
"content": " if (isAllowedExternalDashboardUrl(installConfig.installUrl, \"github\")) {",
"newLineNumber": 325,
"oldLineNumber": null
},
{
"type": "addition",
"content": " return (",
"newLineNumber": 326,
"oldLineNumber": null
},
{
"type": "addition",
"content": " <a",
"newLineNumber": 327,
"oldLineNumber": null
},
{
"type": "addition",
"content": " className=\"rounded-md bg-accent px-3 py-2 text-sm font-medium text-white\"",
"newLineNumber": 328,
"oldLineNumber": null
},
{
"type": "addition",
"content": " data-dashboard-destination=\"external\"",
"newLineNumber": 329,
"oldLineNumber": null
},
{
"type": "addition",
"content": " data-dashboard-provider=\"github\"",
"newLineNumber": 330,
"oldLineNumber": null
},
{
"type": "addition",
"content": " href={installConfig.installUrl}",
"newLineNumber": 331,
"oldLineNumber": null
},
{
"type": "addition",
"content": " rel=\"noreferrer\"",
"newLineNumber": 332,
"oldLineNumber": null
},
{
"type": "addition",
"content": " >",
"newLineNumber": 333,
"oldLineNumber": null
},
{
"type": "addition",
"content": " Install GitHub App",
"newLineNumber": 334,
"oldLineNumber": null
},
{
"type": "addition",
"content": " </a>",
"newLineNumber": 335,
"oldLineNumber": null
},
{
"type": "addition",
"content": " );",
"newLineNumber": 336,
"oldLineNumber": null
},
{
"type": "addition",
"content": " }",
"newLineNumber": 337,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 338,
"oldLineNumber": null
},
{
"type": "context",
"content": " return (",
"newLineNumber": 339,
"oldLineNumber": 299
},
{
"type": "deletion",
"content": " <a className=\"rounded-md bg-accent px-3 py-2 text-sm font-medium text-white\" href={installConfig.installUrl} rel=\"noreferrer\">",
"newLineNumber": null,
"oldLineNumber": 300
},
{
"type": "addition",
"content": " <button",
"newLineNumber": 340,
"oldLineNumber": null
},
{
"type": "addition",
"content": " className=\"rounded-md bg-slate-200 px-3 py-2 text-sm font-medium text-secondary\"",
"newLineNumber": 341,
"oldLineNumber": null
},
{
"type": "addition",
"content": " type=\"button\"",
"newLineNumber": 342,
"oldLineNumber": null
},
{
"type": "addition",
"content": " disabled",
"newLineNumber": 343,
"oldLineNumber": null
},
{
"type": "addition",
"content": " title=\"GitHub App install URL must be an external GitHub URL.\"",
"newLineNumber": 344,
"oldLineNumber": null
},
{
"type": "addition",
"content": " >",
"newLineNumber": 345,
"oldLineNumber": null
},
{
"type": "context",
"content": " Install GitHub App",
"newLineNumber": 346,
"oldLineNumber": 301
},
{
"type": "deletion",
"content": " </a>",
"newLineNumber": null,
"oldLineNumber": 302
},
{
"type": "addition",
"content": " </button>",
"newLineNumber": 347,
"oldLineNumber": null
},
{
"type": "context",
"content": " );",
"newLineNumber": 348,
"oldLineNumber": 303
},
{
"type": "context",
"content": "}",
"newLineNumber": 349,
"oldLineNumber": 304
},
{
"type": "context",
"content": "",
"newLineNumber": 350,
"oldLineNumber": 305
}
],
"newStart": 285,
"oldStart": 274,
"newLineCount": 66,
"oldLineCount": 32,
"sectionHeader": "function InstallAction({"
},
{
"lines": [
{
"type": "context",
"content": " className=\"rounded-md border border-border bg-subtle px-2 py-1 text-xs font-medium text-secondary\"",
"newLineNumber": 383,
"oldLineNumber": 338
},
{
"type": "context",
"content": " disabled",
"newLineNumber": 384,
"oldLineNumber": 339
},
{
"type": "context",
"content": " type=\"button\"",
"newLineNumber": 385,
"oldLineNumber": 340
},
{
"type": "addition",
"content": " title={rowSyncDisabledReason({ hasOAuth, canManageRepositories, hasInstallations }) ?? \"Repository automation is unavailable.\"}",
"newLineNumber": 386,
"oldLineNumber": null
},
{
"type": "context",
"content": " >",
"newLineNumber": 387,
"oldLineNumber": 341
},
{
"type": "context",
"content": " {repository.enabled ? \"Enabled\" : \"Disabled\"}",
"newLineNumber": 388,
"oldLineNumber": 342
},
{
"type": "context",
"content": " </button>",
"newLineNumber": 389,
"oldLineNumber": 343
}
],
"newStart": 383,
"oldStart": 338,
"newLineCount": 7,
"oldLineCount": 6,
"sectionHeader": "function RepositoryAutomationRow({"
}
],
"patch": "@@ -7,6 +7,7 @@ import {\n } from \"@firmcode/shared\";\n import type { GitHubAppInstallConfig } from \"../../config/github-app-installation\";\n import type { GitHubInstallationsState, GitHubSyncDashboardData } from \"../../lib/dashboard-data\";\n+import { isAllowedExternalDashboardUrl } from \"../../lib/dashboard-route-readiness\";\n import { formatDateTime, shortSha } from \"./format\";\n import { GitHubInstallationSyncButton, GitHubRepositorySyncButton } from \"./github-sync-controls\";\n import { RepositoryAutomationToggle } from \"./repository-automation-toggle\";\n@@ -36,7 +37,12 @@ export function GitHubInstallationsView({ state, installConfig }: GitHubInstalla\n disabledReason={syncDisabledReason(state.data)}\n />\n ) : (\n- <button className=\"rounded-md border border-border bg-subtle px-3 py-2 text-sm font-medium text-secondary\" type=\"button\" disabled>\n+ <button\n+ className=\"rounded-md border border-border bg-subtle px-3 py-2 text-sm font-medium text-secondary\"\n+ type=\"button\"\n+ disabled\n+ title=\"GitHub connection status must load before syncing.\"\n+ >\n Sync GitHub\n </button>\n )}\n@@ -100,7 +106,12 @@ function SignedOutState({ installConfig }: { installConfig: GitHubAppInstallConf\n <p className=\"mt-2 text-sm leading-6 text-amber-800\">\n Firmcode needs a Clerk workspace session before it can connect a GitHub account or map a GitHub App installation.\n </p>\n- <button className=\"mt-4 rounded-md bg-slate-200 px-3 py-2 text-sm font-medium text-secondary\" type=\"button\" disabled>\n+ <button\n+ className=\"mt-4 rounded-md bg-slate-200 px-3 py-2 text-sm font-medium text-secondary\"\n+ type=\"button\"\n+ disabled\n+ title=\"Sign in before connecting GitHub.\"\n+ >\n Connect GitHub\n </button>\n </section>\n@@ -274,32 +285,66 @@ function InstallAction({\n }) {\n if (!hasOAuth) {\n return (\n- <button className=\"rounded-md bg-slate-200 px-3 py-2 text-sm font-medium text-secondary\" type=\"button\" disabled>\n+ <button\n+ className=\"rounded-md bg-slate-200 px-3 py-2 text-sm font-medium text-secondary\"\n+ type=\"button\"\n+ disabled\n+ title=\"Connect GitHub OAuth before installing the GitHub App.\"\n+ >\n Connect GitHub first\n </button>\n );\n }\n \n if (!canManage) {\n return (\n- <button className=\"rounded-md bg-slate-200 px-3 py-2 text-sm font-medium text-secondary\" type=\"button\" disabled>\n+ <button\n+ className=\"rounded-md bg-slate-200 px-3 py-2 text-sm font-medium text-secondary\"\n+ type=\"button\"\n+ disabled\n+ title=\"Owner or Admin required to install the GitHub App.\"\n+ >\n Owner or Admin required\n </button>\n );\n }\n \n if (installConfig.status !== \"configured\") {\n return (\n- <button className=\"rounded-md bg-slate-200 px-3 py-2 text-sm font-medium text-secondary\" type=\"button\" disabled>\n+ <button\n+ className=\"rounded-md bg-slate-200 px-3 py-2 text-sm font-medium text-secondary\"\n+ type=\"button\"\n+ disabled\n+ title=\"Set GITHUB_APP_INSTALL_URL or GITHUB_APP_SLUG before installing the GitHub App.\"\n+ >\n Install URL not configured\n </button>\n );\n }\n \n+ if (isAllowedExternalDashboardUrl(installConfig.installUrl, \"github\")) {\n+ return (\n+ <a\n+ className=\"rounded-md bg-accent px-3 py-2 text-sm font-medium text-white\"\n+ data-dashboard-destination=\"external\"\n+ data-dashboard-provider=\"github\"\n+ href={installConfig.installUrl}\n+ rel=\"noreferrer\"\n+ >\n+ Install GitHub App\n+ </a>\n+ );\n+ }\n+\n return (\n- <a className=\"rounded-md bg-accent px-3 py-2 text-sm font-medium text-white\" href={installConfig.installUrl} rel=\"noreferrer\">\n+ <button\n+ className=\"rounded-md bg-slate-200 px-3 py-2 text-sm font-medium text-secondary\"\n+ type=\"button\"\n+ disabled\n+ title=\"GitHub App install URL must be an external GitHub URL.\"\n+ >\n Install GitHub App\n- </a>\n+ </button>\n );\n }\n \n@@ -338,6 +383,7 @@ function RepositoryAutomationRow({\n className=\"rounded-md border border-border bg-subtle px-2 py-1 text-xs font-medium text-secondary\"\n disabled\n type=\"button\"\n+ title={rowSyncDisabledReason({ hasOAuth, canManageRepositories, hasInstallations }) ?? \"Repository automation is unavailable.\"}\n >\n {repository.enabled ? \"Enabled\" : \"Disabled\"}\n </button>",
"status": "modified",
"language": "typescript",
"additions": 53,
"deletions": 7,
"sizeBytes": 21814,
"previousPath": null,
"changedNewLines": [
10,
40,
41,
42,
43,
44,
45,
109,
110,
111,
112,
113,
114,
288,
289,
290,
291,
292,
293,
301,
302,
303,
304,
305,
306,
314,
315,
316,
317,
318,
319,
325,
326,
327,
328,
329,
330,
331,
332,
333,
334,
335,
336,
337,
338,
340,
341,
342,
343,
344,
345,
347,
386
],
"headContentSha256": "ac03a614d93400b964b24c909d2244076c584ec72c0d9eee7d3ddf3b7cbe4c4e"
},
{
"path": "apps/web/components/dashboard/repositories-view.tsx",
"hunks": [
{
"lines": [
{
"type": "context",
"content": " }",
"newLineNumber": 235,
"oldLineNumber": 235
},
{
"type": "context",
"content": "",
"newLineNumber": 236,
"oldLineNumber": 236
},
{
"type": "context",
"content": " return (",
"newLineNumber": 237,
"oldLineNumber": 237
},
{
"type": "deletion",
"content": " <button className=\"rounded-md bg-subtle px-3 py-2 text-sm font-medium text-secondary\" type=\"button\" disabled>",
"newLineNumber": null,
"oldLineNumber": 238
},
{
"type": "addition",
"content": " <button",
"newLineNumber": 238,
"oldLineNumber": null
},
{
"type": "addition",
"content": " className=\"rounded-md bg-subtle px-3 py-2 text-sm font-medium text-secondary\"",
"newLineNumber": 239,
"oldLineNumber": null
},
{
"type": "addition",
"content": " type=\"button\"",
"newLineNumber": 240,
"oldLineNumber": null
},
{
"type": "addition",
"content": " disabled",
"newLineNumber": 241,
"oldLineNumber": null
},
{
"type": "addition",
"content": " title={controls.syncDisabledReason ?? \"GitHub App connection is unavailable.\"}",
"newLineNumber": 242,
"oldLineNumber": null
},
{
"type": "addition",
"content": " >",
"newLineNumber": 243,
"oldLineNumber": null
},
{
"type": "context",
"content": " Connect GitHub App",
"newLineNumber": 244,
"oldLineNumber": 239
},
{
"type": "context",
"content": " </button>",
"newLineNumber": 245,
"oldLineNumber": 240
},
{
"type": "context",
"content": " );",
"newLineNumber": 246,
"oldLineNumber": 241
}
],
"newStart": 235,
"oldStart": 235,
"newLineCount": 12,
"oldLineCount": 7,
"sectionHeader": "function GitHubConnectionAction({ controls }: { controls: RepositoryControls })"
}
],
"patch": "@@ -235,7 +235,12 @@ function GitHubConnectionAction({ controls }: { controls: RepositoryControls })\n }\n \n return (\n- <button className=\"rounded-md bg-subtle px-3 py-2 text-sm font-medium text-secondary\" type=\"button\" disabled>\n+ <button\n+ className=\"rounded-md bg-subtle px-3 py-2 text-sm font-medium text-secondary\"\n+ type=\"button\"\n+ disabled\n+ title={controls.syncDisabledReason ?? \"GitHub App connection is unavailable.\"}\n+ >\n Connect GitHub App\n </button>\n );",
"status": "modified",
"language": "typescript",
"additions": 6,
"deletions": 1,
"sizeBytes": 12744,
"previousPath": null,
"changedNewLines": [
238,
239,
240,
241,
242,
243
],
"headContentSha256": "439cb271cfc675a299301ab069e28dae8a3377d5798a4021f6a8c16ffb7941e7"
},
{
"path": "apps/web/components/dashboard/settings-view.tsx",
"hunks": [
{
"lines": [
{
"type": "context",
"content": " type DashboardWorkspaceRole,",
"newLineNumber": 4,
"oldLineNumber": 4
},
{
"type": "context",
"content": " type WorkspaceSettingsResponse",
"newLineNumber": 5,
"oldLineNumber": 5
},
{
"type": "context",
"content": "} from \"@firmcode/shared\";",
"newLineNumber": 6,
"oldLineNumber": 6
},
{
"type": "addition",
"content": "import {",
"newLineNumber": 7,
"oldLineNumber": null
},
{
"type": "addition",
"content": " isAllowedExternalDashboardUrl,",
"newLineNumber": 8,
"oldLineNumber": null
},
{
"type": "addition",
"content": " isExternalDashboardUrl,",
"newLineNumber": 9,
"oldLineNumber": null
},
{
"type": "addition",
"content": " isImplementedDashboardRoute,",
"newLineNumber": 10,
"oldLineNumber": null
},
{
"type": "addition",
"content": " type DashboardExternalProvider",
"newLineNumber": 11,
"oldLineNumber": null
},
{
"type": "addition",
"content": "} from \"../../lib/dashboard-route-readiness\";",
"newLineNumber": 12,
"oldLineNumber": null
},
{
"type": "context",
"content": "import type { ViewState } from \"../../lib/view-state\";",
"newLineNumber": 13,
"oldLineNumber": 7
},
{
"type": "context",
"content": "import { formatDateTime } from \"./format\";",
"newLineNumber": 14,
"oldLineNumber": 8
},
{
"type": "context",
"content": "",
"newLineNumber": 15,
"oldLineNumber": 9
}
],
"newStart": 4,
"oldStart": 4,
"newLineCount": 12,
"oldLineCount": 6,
"sectionHeader": "import {"
},
{
"lines": [
{
"type": "context",
"content": " <MetadataCard label=\"Clerk organization\" value={data.workspace.clerkOrgId ?? \"Personal workspace\"} monospace />",
"newLineNumber": 179,
"oldLineNumber": 173
},
{
"type": "context",
"content": " </dl>",
"newLineNumber": 180,
"oldLineNumber": 174
},
{
"type": "context",
"content": " <div className=\"mt-4 flex flex-wrap gap-2\">",
"newLineNumber": 181,
"oldLineNumber": 175
},
{
"type": "deletion",
"content": " <a className=\"rounded-md border border-border px-3 py-2 text-sm font-medium text-primary\" href={data.clerk.userProfileUrl}>",
"newLineNumber": null,
"oldLineNumber": 176
},
{
"type": "deletion",
"content": " Open Clerk profile",
"newLineNumber": null,
"oldLineNumber": 177
},
{
"type": "deletion",
"content": " </a>",
"newLineNumber": null,
"oldLineNumber": 178
},
{
"type": "deletion",
"content": " <a className=\"rounded-md border border-border px-3 py-2 text-sm font-medium text-primary\" href={data.clerk.organizationProfileUrl}>",
"newLineNumber": null,
"oldLineNumber": 179
},
{
"type": "deletion",
"content": " Open Clerk organization",
"newLineNumber": null,
"oldLineNumber": 180
},
{
"type": "deletion",
"content": " </a>",
"newLineNumber": null,
"oldLineNumber": 181
},
{
"type": "addition",
"content": " <RouteReadyAction href={data.clerk.userProfileUrl} label=\"Open Clerk profile\" provider=\"clerk\" />",
"newLineNumber": 182,
"oldLineNumber": null
},
{
"type": "addition",
"content": " <RouteReadyAction href={data.clerk.organizationProfileUrl} label=\"Open Clerk organization\" provider=\"clerk\" />",
"newLineNumber": 183,
"oldLineNumber": null
},
{
"type": "context",
"content": " </div>",
"newLineNumber": 184,
"oldLineNumber": 182
},
{
"type": "context",
"content": " </SettingsPanel>",
"newLineNumber": 185,
"oldLineNumber": 183
},
{
"type": "context",
"content": " );",
"newLineNumber": 186,
"oldLineNumber": 184
}
],
"newStart": 179,
"oldStart": 173,
"newLineCount": 8,
"oldLineCount": 12,
"sectionHeader": "function GeneralPanel({ data }: { data: WorkspaceSettingsResponse }) {"
},
{
"lines": [
{
"type": "context",
"content": " >",
"newLineNumber": 196,
"oldLineNumber": 194
},
{
"type": "context",
"content": " <div className=\"flex flex-wrap gap-2\">",
"newLineNumber": 197,
"oldLineNumber": 195
},
{
"type": "context",
"content": " {canManage ? (",
"newLineNumber": 198,
"oldLineNumber": 196
},
{
"type": "deletion",
"content": " <a",
"newLineNumber": null,
"oldLineNumber": 197
},
{
"type": "deletion",
"content": " className=\"rounded-md bg-accent px-3 py-2 text-sm font-medium text-white\"",
"newLineNumber": null,
"oldLineNumber": 198
},
{
"type": "deletion",
"content": " href={data.githubApp.installUrl}",
"newLineNumber": null,
"oldLineNumber": 199
},
{
"type": "deletion",
"content": " >",
"newLineNumber": null,
"oldLineNumber": 200
},
{
"type": "deletion",
"content": " Connect GitHub App",
"newLineNumber": null,
"oldLineNumber": 201
},
{
"type": "deletion",
"content": " </a>",
"newLineNumber": null,
"oldLineNumber": 202
},
{
"type": "addition",
"content": " <RouteReadyAction href={data.githubApp.installUrl} label=\"Connect GitHub App\" primary provider=\"github\" />",
"newLineNumber": 199,
"oldLineNumber": null
},
{
"type": "context",
"content": " ) : (",
"newLineNumber": 200,
"oldLineNumber": 203
},
{
"type": "deletion",
"content": " <button className=\"rounded-md bg-mist px-3 py-2 text-sm font-medium text-secondary\" type=\"button\" disabled>",
"newLineNumber": null,
"oldLineNumber": 204
},
{
"type": "addition",
"content": " <button",
"newLineNumber": 201,
"oldLineNumber": null
},
{
"type": "addition",
"content": " className=\"rounded-md bg-mist px-3 py-2 text-sm font-medium text-secondary\"",
"newLineNumber": 202,
"oldLineNumber": null
},
{
"type": "addition",
"content": " type=\"button\"",
"newLineNumber": 203,
"oldLineNumber": null
},
{
"type": "addition",
"content": " disabled",
"newLineNumber": 204,
"oldLineNumber": null
},
{
"type": "addition",
"content": " title=\"Owner or Admin required to connect the GitHub App.\"",
"newLineNumber": 205,
"oldLineNumber": null
},
{
"type": "addition",
"content": " >",
"newLineNumber": 206,
"oldLineNumber": null
},
{
"type": "context",
"content": " Connect GitHub App",
"newLineNumber": 207,
"oldLineNumber": 205
},
{
"type": "context",
"content": " </button>",
"newLineNumber": 208,
"oldLineNumber": 206
},
{
"type": "context",
"content": " )}",
"newLineNumber": 209,
"oldLineNumber": 207
},
{
"type": "deletion",
"content": " <a className=\"rounded-md border border-border px-3 py-2 text-sm font-medium text-primary\" href={data.githubApp.repositoryConfigurationUrl}>",
"newLineNumber": null,
"oldLineNumber": 208
},
{
"type": "deletion",
"content": " Repository configuration",
"newLineNumber": null,
"oldLineNumber": 209
},
{
"type": "deletion",
"content": " </a>",
"newLineNumber": null,
"oldLineNumber": 210
},
{
"type": "addition",
"content": " <RouteReadyAction href={data.githubApp.repositoryConfigurationUrl} label=\"Repository configuration\" provider=\"github\" />",
"newLineNumber": 210,
"oldLineNumber": null
},
{
"type": "context",
"content": " </div>",
"newLineNumber": 211,
"oldLineNumber": 211
},
{
"type": "context",
"content": " <div className=\"mt-4 grid gap-3\">",
"newLineNumber": 212,
"oldLineNumber": 212
},
{
"type": "context",
"content": " {data.githubApp.installations.length === 0 ? (",
"newLineNumber": 213,
"oldLineNumber": 213
}
],
"newStart": 196,
"oldStart": 194,
"newLineCount": 18,
"oldLineCount": 20,
"sectionHeader": "function GitHubAppPanel({ data }: { data: WorkspaceSettingsResponse }) {"
},
{
"lines": [
{
"type": "context",
"content": " <SettingsPanel title=\"Members\" description=\"Clerk owns member invitations, removals, profile details, and organization roles.\">",
"newLineNumber": 247,
"oldLineNumber": 247
},
{
"type": "context",
"content": " <div className=\"flex flex-wrap gap-2\">",
"newLineNumber": 248,
"oldLineNumber": 248
},
{
"type": "context",
"content": " {canManage ? (",
"newLineNumber": 249,
"oldLineNumber": 249
},
{
"type": "deletion",
"content": " <a className=\"rounded-md bg-accent px-3 py-2 text-sm font-medium text-white\" href={data.clerk.memberManagementUrl}>",
"newLineNumber": null,
"oldLineNumber": 250
},
{
"type": "deletion",
"content": " Open Clerk members",
"newLineNumber": null,
"oldLineNumber": 251
},
{
"type": "deletion",
"content": " </a>",
"newLineNumber": null,
"oldLineNumber": 252
},
{
"type": "addition",
"content": " <RouteReadyAction href={data.clerk.memberManagementUrl} label=\"Open Clerk members\" primary provider=\"clerk\" />",
"newLineNumber": 250,
"oldLineNumber": null
},
{
"type": "context",
"content": " ) : (",
"newLineNumber": 251,
"oldLineNumber": 253
},
{
"type": "deletion",
"content": " <button className=\"rounded-md bg-slate-200 px-3 py-2 text-sm font-medium text-secondary\" type=\"button\" disabled>",
"newLineNumber": null,
"oldLineNumber": 254
},
{
"type": "addition",
"content": " <button",
"newLineNumber": 252,
"oldLineNumber": null
},
{
"type": "addition",
"content": " className=\"rounded-md bg-slate-200 px-3 py-2 text-sm font-medium text-secondary\"",
"newLineNumber": 253,
"oldLineNumber": null
},
{
"type": "addition",
"content": " type=\"button\"",
"newLineNumber": 254,
"oldLineNumber": null
},
{
"type": "addition",
"content": " disabled",
"newLineNumber": 255,
"oldLineNumber": null
},
{
"type": "addition",
"content": " title=\"Owner or Admin required to manage Clerk members.\"",
"newLineNumber": 256,
"oldLineNumber": null
},
{
"type": "addition",
"content": " >",
"newLineNumber": 257,
"oldLineNumber": null
},
{
"type": "context",
"content": " Open Clerk members",
"newLineNumber": 258,
"oldLineNumber": 255
},
{
"type": "context",
"content": " </button>",
"newLineNumber": 259,
"oldLineNumber": 256
},
{
"type": "context",
"content": " )}",
"newLineNumber": 260,
"oldLineNumber": 257
},
{
"type": "deletion",
"content": " <a className=\"rounded-md border border-border px-3 py-2 text-sm font-medium text-primary\" href={data.clerk.organizationProfileUrl}>",
"newLineNumber": null,
"oldLineNumber": 258
},
{
"type": "deletion",
"content": " View organization",
"newLineNumber": null,
"oldLineNumber": 259
},
{
"type": "deletion",
"content": " </a>",
"newLineNumber": null,
"oldLineNumber": 260
},
{
"type": "addition",
"content": " <RouteReadyAction href={data.clerk.organizationProfileUrl} label=\"View organization\" provider=\"clerk\" />",
"newLineNumber": 261,
"oldLineNumber": null
},
{
"type": "context",
"content": " </div>",
"newLineNumber": 262,
"oldLineNumber": 261
},
{
"type": "context",
"content": " <p className=\"mt-4 rounded-md border border-border bg-subtle p-3 text-sm leading-6 text-secondary\">",
"newLineNumber": 263,
"oldLineNumber": 262
},
{
"type": "context",
"content": " Firmcode reads the active workspace membership for authorization and leaves member lifecycle workflows in Clerk.",
"newLineNumber": 264,
"oldLineNumber": 263
}
],
"newStart": 247,
"oldStart": 247,
"newLineCount": 18,
"oldLineCount": 17,
"sectionHeader": "function MembersPanel({ data }: { data: WorkspaceSettingsResponse }) {"
},
{
"lines": [
{
"type": "context",
"content": " className=\"mt-4 rounded-md bg-slate-200 px-3 py-2 text-sm font-medium text-secondary disabled:cursor-not-allowed disabled:opacity-60\"",
"newLineNumber": 277,
"oldLineNumber": 276
},
{
"type": "context",
"content": " type=\"button\"",
"newLineNumber": 278,
"oldLineNumber": 277
},
{
"type": "context",
"content": " disabled={!canManage || !data.apiKeys.enabled}",
"newLineNumber": 279,
"oldLineNumber": 278
},
{
"type": "addition",
"content": " title={",
"newLineNumber": 280,
"oldLineNumber": null
},
{
"type": "addition",
"content": " canManage",
"newLineNumber": 281,
"oldLineNumber": null
},
{
"type": "addition",
"content": " ? \"Workspace API keys are planned and not enabled in the MVP.\"",
"newLineNumber": 282,
"oldLineNumber": null
},
{
"type": "addition",
"content": " : \"Owner or Admin required to create workspace API keys.\"",
"newLineNumber": 283,
"oldLineNumber": null
},
{
"type": "addition",
"content": " }",
"newLineNumber": 284,
"oldLineNumber": null
},
{
"type": "context",
"content": " >",
"newLineNumber": 285,
"oldLineNumber": 279
},
{
"type": "context",
"content": " Create API key",
"newLineNumber": 286,
"oldLineNumber": 280
},
{
"type": "context",
"content": " </button>",
"newLineNumber": 287,
"oldLineNumber": 281
}
],
"newStart": 277,
"oldStart": 276,
"newLineCount": 11,
"oldLineCount": 6,
"sectionHeader": "function ApiKeysPanel({ data }: { data: WorkspaceSettingsResponse }) {"
},
{
"lines": [
{
"type": "context",
"content": " return (",
"newLineNumber": 348,
"oldLineNumber": 342
},
{
"type": "context",
"content": " <label className=\"flex items-center justify-between gap-3 rounded-md border border-border bg-surface p-3 text-sm font-medium text-primary\">",
"newLineNumber": 349,
"oldLineNumber": 343
},
{
"type": "context",
"content": " {label}",
"newLineNumber": 350,
"oldLineNumber": 344
},
{
"type": "deletion",
"content": " <input className=\"h-4 w-4\" type=\"checkbox\" disabled={disabled} />",
"newLineNumber": null,
"oldLineNumber": 345
},
{
"type": "addition",
"content": " <input",
"newLineNumber": 351,
"oldLineNumber": null
},
{
"type": "addition",
"content": " className=\"h-4 w-4\"",
"newLineNumber": 352,
"oldLineNumber": null
},
{
"type": "addition",
"content": " type=\"checkbox\"",
"newLineNumber": 353,
"oldLineNumber": null
},
{
"type": "addition",
"content": " disabled={disabled}",
"newLineNumber": 354,
"oldLineNumber": null
},
{
"type": "addition",
"content": " title={disabled ? `${label} are planned and not enabled in the MVP.` : label}",
"newLineNumber": 355,
"oldLineNumber": null
},
{
"type": "addition",
"content": " />",
"newLineNumber": 356,
"oldLineNumber": null
},
{
"type": "context",
"content": " </label>",
"newLineNumber": 357,
"oldLineNumber": 346
},
{
"type": "context",
"content": " );",
"newLineNumber": 358,
"oldLineNumber": 347
},
{
"type": "context",
"content": "}",
"newLineNumber": 359,
"oldLineNumber": 348
},
{
"type": "context",
"content": "",
"newLineNumber": 360,
"oldLineNumber": 349
},
{
"type": "addition",
"content": "function RouteReadyAction({",
"newLineNumber": 361,
"oldLineNumber": null
},
{
"type": "addition",
"content": " href,",
"newLineNumber": 362,
"oldLineNumber": null
},
{
"type": "addition",
"content": " label,",
"newLineNumber": 363,
"oldLineNumber": null
},
{
"type": "addition",
"content": " primary = false,",
"newLineNumber": 364,
"oldLineNumber": null
},
{
"type": "addition",
"content": " provider",
"newLineNumber": 365,
"oldLineNumber": null
},
{
"type": "addition",
"content": "}: {",
"newLineNumber": 366,
"oldLineNumber": null
},
{
"type": "addition",
"content": " href: string;",
"newLineNumber": 367,
"oldLineNumber": null
},
{
"type": "addition",
"content": " label: string;",
"newLineNumber": 368,
"oldLineNumber": null
},
{
"type": "addition",
"content": " primary?: boolean;",
"newLineNumber": 369,
"oldLineNumber": null
},
{
"type": "addition",
"content": " provider: DashboardExternalProvider;",
"newLineNumber": 370,
"oldLineNumber": null
},
{
"type": "addition",
"content": "}) {",
"newLineNumber": 371,
"oldLineNumber": null
},
{
"type": "addition",
"content": " const className = primary",
"newLineNumber": 372,
"oldLineNumber": null
},
{
"type": "addition",
"content": " ? \"rounded-md bg-accent px-3 py-2 text-sm font-medium text-white\"",
"newLineNumber": 373,
"oldLineNumber": null
},
{
"type": "addition",
"content": " : \"rounded-md border border-border px-3 py-2 text-sm font-medium text-primary\";",
"newLineNumber": 374,
"oldLineNumber": null
},
{
"type": "addition",
"content": " const disabledClassName = primary",
"newLineNumber": 375,
"oldLineNumber": null
},
{
"type": "addition",
"content": " ? \"rounded-md bg-slate-200 px-3 py-2 text-sm font-medium text-secondary\"",
"newLineNumber": 376,
"oldLineNumber": null
},
{
"type": "addition",
"content": " : \"rounded-md border border-border bg-subtle px-3 py-2 text-sm font-medium text-secondary\";",
"newLineNumber": 377,
"oldLineNumber": null
},
{
"type": "addition",
"content": " const external = isExternalDashboardUrl(href);",
"newLineNumber": 378,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 379,
"oldLineNumber": null
},
{
"type": "addition",
"content": " if (external && isAllowedExternalDashboardUrl(href, provider)) {",
"newLineNumber": 380,
"oldLineNumber": null
},
{
"type": "addition",
"content": " return (",
"newLineNumber": 381,
"oldLineNumber": null
},
{
"type": "addition",
"content": " <a",
"newLineNumber": 382,
"oldLineNumber": null
},
{
"type": "addition",
"content": " className={className}",
"newLineNumber": 383,
"oldLineNumber": null
},
{
"type": "addition",
"content": " data-dashboard-destination=\"external\"",
"newLineNumber": 384,
"oldLineNumber": null
},
{
"type": "addition",
"content": " data-dashboard-provider={provider}",
"newLineNumber": 385,
"oldLineNumber": null
},
{
"type": "addition",
"content": " href={href}",
"newLineNumber": 386,
"oldLineNumber": null
},
{
"type": "addition",
"content": " rel=\"noreferrer\"",
"newLineNumber": 387,
"oldLineNumber": null
},
{
"type": "addition",
"content": " >",
"newLineNumber": 388,
"oldLineNumber": null
},
{
"type": "addition",
"content": " {label}",
"newLineNumber": 389,
"oldLineNumber": null
},
{
"type": "addition",
"content": " </a>",
"newLineNumber": 390,
"oldLineNumber": null
},
{
"type": "addition",
"content": " );",
"newLineNumber": 391,
"oldLineNumber": null
},
{
"type": "addition",
"content": " }",
"newLineNumber": 392,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 393,
"oldLineNumber": null
},
{
"type": "addition",
"content": " if (!external && isImplementedDashboardRoute(href)) {",
"newLineNumber": 394,
"oldLineNumber": null
},
{
"type": "addition",
"content": " return (",
"newLineNumber": 395,
"oldLineNumber": null
},
{
"type": "addition",
"content": " <a className={className} data-dashboard-destination=\"internal\" href={href}>",
"newLineNumber": 396,
"oldLineNumber": null
},
{
"type": "addition",
"content": " {label}",
"newLineNumber": 397,
"oldLineNumber": null
},
{
"type": "addition",
"content": " </a>",
"newLineNumber": 398,
"oldLineNumber": null
},
{
"type": "addition",
"content": " );",
"newLineNumber": 399,
"oldLineNumber": null
},
{
"type": "addition",
"content": " }",
"newLineNumber": 400,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 401,
"oldLineNumber": null
},
{
"type": "addition",
"content": " return (",
"newLineNumber": 402,
"oldLineNumber": null
},
{
"type": "addition",
"content": " <button",
"newLineNumber": 403,
"oldLineNumber": null
},
{
"type": "addition",
"content": " className={disabledClassName}",
"newLineNumber": 404,
"oldLineNumber": null
},
{
"type": "addition",
"content": " type=\"button\"",
"newLineNumber": 405,
"oldLineNumber": null
},
{
"type": "addition",
"content": " disabled",
"newLineNumber": 406,
"oldLineNumber": null
},
{
"type": "addition",
"content": " title={`${label} is planned until its ${external ? \"external\" : \"internal\"} destination is route-ready.`}",
"newLineNumber": 407,
"oldLineNumber": null
},
{
"type": "addition",
"content": " >",
"newLineNumber": 408,
"oldLineNumber": null
},
{
"type": "addition",
"content": " {label}",
"newLineNumber": 409,
"oldLineNumber": null
},
{
"type": "addition",
"content": " </button>",
"newLineNumber": 410,
"oldLineNumber": null
},
{
"type": "addition",
"content": " );",
"newLineNumber": 411,
"oldLineNumber": null
},
{
"type": "addition",
"content": "}",
"newLineNumber": 412,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 413,
"oldLineNumber": null
},
{
"type": "context",
"content": "function SettingsPanel({ title, description, children }: { title: string; description: string; children: React.ReactNode }) {",
"newLineNumber": 414,
"oldLineNumber": 350
},
{
"type": "context",
"content": " return (",
"newLineNumber": 415,
"oldLineNumber": 351
},
{
"type": "context",
"content": " <section className=\"rounded-lg border border-border bg-surface p-4\">",
"newLineNumber": 416,
"oldLineNumber": 352
}
],
"newStart": 348,
"oldStart": 342,
"newLineCount": 69,
"oldLineCount": 11,
"sectionHeader": "function TogglePlaceholder({ label, disabled }: { label: string; disabled: boole"
}
],
"patch": "@@ -4,6 +4,12 @@ import {\n type DashboardWorkspaceRole,\n type WorkspaceSettingsResponse\n } from \"@firmcode/shared\";\n+import {\n+ isAllowedExternalDashboardUrl,\n+ isExternalDashboardUrl,\n+ isImplementedDashboardRoute,\n+ type DashboardExternalProvider\n+} from \"../../lib/dashboard-route-readiness\";\n import type { ViewState } from \"../../lib/view-state\";\n import { formatDateTime } from \"./format\";\n \n@@ -173,12 +179,8 @@ function GeneralPanel({ data }: { data: WorkspaceSettingsResponse }) {\n <MetadataCard label=\"Clerk organization\" value={data.workspace.clerkOrgId ?? \"Personal workspace\"} monospace />\n </dl>\n <div className=\"mt-4 flex flex-wrap gap-2\">\n- <a className=\"rounded-md border border-border px-3 py-2 text-sm font-medium text-primary\" href={data.clerk.userProfileUrl}>\n- Open Clerk profile\n- </a>\n- <a className=\"rounded-md border border-border px-3 py-2 text-sm font-medium text-primary\" href={data.clerk.organizationProfileUrl}>\n- Open Clerk organization\n- </a>\n+ <RouteReadyAction href={data.clerk.userProfileUrl} label=\"Open Clerk profile\" provider=\"clerk\" />\n+ <RouteReadyAction href={data.clerk.organizationProfileUrl} label=\"Open Clerk organization\" provider=\"clerk\" />\n </div>\n </SettingsPanel>\n );\n@@ -194,20 +196,18 @@ function GitHubAppPanel({ data }: { data: WorkspaceSettingsResponse }) {\n >\n <div className=\"flex flex-wrap gap-2\">\n {canManage ? (\n- <a\n- className=\"rounded-md bg-accent px-3 py-2 text-sm font-medium text-white\"\n- href={data.githubApp.installUrl}\n- >\n- Connect GitHub App\n- </a>\n+ <RouteReadyAction href={data.githubApp.installUrl} label=\"Connect GitHub App\" primary provider=\"github\" />\n ) : (\n- <button className=\"rounded-md bg-mist px-3 py-2 text-sm font-medium text-secondary\" type=\"button\" disabled>\n+ <button\n+ className=\"rounded-md bg-mist px-3 py-2 text-sm font-medium text-secondary\"\n+ type=\"button\"\n+ disabled\n+ title=\"Owner or Admin required to connect the GitHub App.\"\n+ >\n Connect GitHub App\n </button>\n )}\n- <a className=\"rounded-md border border-border px-3 py-2 text-sm font-medium text-primary\" href={data.githubApp.repositoryConfigurationUrl}>\n- Repository configuration\n- </a>\n+ <RouteReadyAction href={data.githubApp.repositoryConfigurationUrl} label=\"Repository configuration\" provider=\"github\" />\n </div>\n <div className=\"mt-4 grid gap-3\">\n {data.githubApp.installations.length === 0 ? (\n@@ -247,17 +247,18 @@ function MembersPanel({ data }: { data: WorkspaceSettingsResponse }) {\n <SettingsPanel title=\"Members\" description=\"Clerk owns member invitations, removals, profile details, and organization roles.\">\n <div className=\"flex flex-wrap gap-2\">\n {canManage ? (\n- <a className=\"rounded-md bg-accent px-3 py-2 text-sm font-medium text-white\" href={data.clerk.memberManagementUrl}>\n- Open Clerk members\n- </a>\n+ <RouteReadyAction href={data.clerk.memberManagementUrl} label=\"Open Clerk members\" primary provider=\"clerk\" />\n ) : (\n- <button className=\"rounded-md bg-slate-200 px-3 py-2 text-sm font-medium text-secondary\" type=\"button\" disabled>\n+ <button\n+ className=\"rounded-md bg-slate-200 px-3 py-2 text-sm font-medium text-secondary\"\n+ type=\"button\"\n+ disabled\n+ title=\"Owner or Admin required to manage Clerk members.\"\n+ >\n Open Clerk members\n </button>\n )}\n- <a className=\"rounded-md border border-border px-3 py-2 text-sm font-medium text-primary\" href={data.clerk.organizationProfileUrl}>\n- View organization\n- </a>\n+ <RouteReadyAction href={data.clerk.organizationProfileUrl} label=\"View organization\" provider=\"clerk\" />\n </div>\n <p className=\"mt-4 rounded-md border border-border bg-subtle p-3 text-sm leading-6 text-secondary\">\n Firmcode reads the active workspace membership for authorization and leaves member lifecycle workflows in Clerk.\n@@ -276,6 +277,11 @@ function ApiKeysPanel({ data }: { data: WorkspaceSettingsResponse }) {\n className=\"mt-4 rounded-md bg-slate-200 px-3 py-2 text-sm font-medium text-secondary disabled:cursor-not-allowed disabled:opacity-60\"\n type=\"button\"\n disabled={!canManage || !data.apiKeys.enabled}\n+ title={\n+ canManage\n+ ? \"Workspace API keys are planned and not enabled in the MVP.\"\n+ : \"Owner or Admin required to create workspace API keys.\"\n+ }\n >\n Create API key\n </button>\n@@ -342,11 +348,69 @@ function TogglePlaceholder({ label, disabled }: { label: string; disabled: boole\n return (\n <label className=\"flex items-center justify-between gap-3 rounded-md border border-border bg-surface p-3 text-sm font-medium text-primary\">\n {label}\n- <input className=\"h-4 w-4\" type=\"checkbox\" disabled={disabled} />\n+ <input\n+ className=\"h-4 w-4\"\n+ type=\"checkbox\"\n+ disabled={disabled}\n+ title={disabled ? `${label} are planned and not enabled in the MVP.` : label}\n+ />\n </label>\n );\n }\n \n+function RouteReadyAction({\n+ href,\n+ label,\n+ primary = false,\n+ provider\n+}: {\n+ href: string;\n+ label: string;\n+ primary?: boolean;\n+ provider: DashboardExternalProvider;\n+}) {\n+ const className = primary\n+ ? \"rounded-md bg-accent px-3 py-2 text-sm font-medium text-white\"\n+ : \"rounded-md border border-border px-3 py-2 text-sm font-medium text-primary\";\n+ const disabledClassName = primary\n+ ? \"rounded-md bg-slate-200 px-3 py-2 text-sm font-medium text-secondary\"\n+ : \"rounded-md border border-border bg-subtle px-3 py-2 text-sm font-medium text-secondary\";\n+ const external = isExternalDashboardUrl(href);\n+\n+ if (external && isAllowedExternalDashboardUrl(href, provider)) {\n+ return (\n+ <a\n+ className={className}\n+ data-dashboard-destination=\"external\"\n+ data-dashboard-provider={provider}\n+ href={href}\n+ rel=\"noreferrer\"\n+ >\n+ {label}\n+ </a>\n+ );\n+ }\n+\n+ if (!external && isImplementedDashboardRoute(href)) {\n+ return (\n+ <a className={className} data-dashboard-destination=\"internal\" href={href}>\n+ {label}\n+ </a>\n+ );\n+ }\n+\n+ return (\n+ <button\n+ className={disabledClassName}\n+ type=\"button\"\n+ disabled\n+ title={`${label} is planned until its ${external ? \"external\" : \"internal\"} destination is route-ready.`}\n+ >\n+ {label}\n+ </button>\n+ );\n+}\n+\n function SettingsPanel({ title, description, children }: { title: string; description: string; children: React.ReactNode }) {\n return (\n <section className=\"rounded-lg border border-border bg-surface p-4\">",
"status": "modified",
"language": "typescript",
"additions": 88,
"deletions": 24,
"sizeBytes": 17496,
"previousPath": null,
"changedNewLines": [
7,
8,
9,
10,
11,
12,
182,
183,
199,
201,
202,
203,
204,
205,
206,
210,
250,
252,
253,
254,
255,
256,
257,
261,
280,
281,
282,
283,
284,
351,
352,
353,
354,
355,
356,
361,
362,
363,
364,
365,
366,
367,
368,
369,
370,
371,
372,
373,
374,
375,
376,
377,
378,
379,
380,
381,
382,
383,
384,
385,
386,
387,
388,
389,
390,
391,
392,
393,
394,
395,
396,
397,
398,
399,
400,
401,
402,
403,
404,
405,
406,
407,
408,
409,
410,
411,
412,
413
],
"headContentSha256": "d60588c0b9e9655100452145f6c2db34f96be94558bc4378d87d634bf95d8b2c"
},
{
"path": "apps/web/lib/dashboard-action-manifest.ts",
"hunks": [
{
"lines": [
{
"type": "addition",
"content": "import { DASHBOARD_NAV_ITEMS } from \"./dashboard-navigation\";",
"newLineNumber": 1,
"oldLineNumber": null
},
{
"type": "addition",
"content": "import type { DashboardExternalProvider } from \"./dashboard-route-readiness\";",
"newLineNumber": 2,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 3,
"oldLineNumber": null
},
{
"type": "addition",
"content": "export type DashboardActionStatus = \"active\" | \"planned-disabled\";",
"newLineNumber": 4,
"oldLineNumber": null
},
{
"type": "addition",
"content": "export type DashboardActionDestination = \"internal\" | \"external\" | \"none\";",
"newLineNumber": 5,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 6,
"oldLineNumber": null
},
{
"type": "addition",
"content": "export interface DashboardRouteAction {",
"newLineNumber": 7,
"oldLineNumber": null
},
{
"type": "addition",
"content": " readonly surface:",
"newLineNumber": 8,
"oldLineNumber": null
},
{
"type": "addition",
"content": " | \"sidebar\"",
"newLineNumber": 9,
"oldLineNumber": null
},
{
"type": "addition",
"content": " | \"topbar\"",
"newLineNumber": 10,
"oldLineNumber": null
},
{
"type": "addition",
"content": " | \"overview\"",
"newLineNumber": 11,
"oldLineNumber": null
},
{
"type": "addition",
"content": " | \"repositories\"",
"newLineNumber": 12,
"oldLineNumber": null
},
{
"type": "addition",
"content": " | \"settings\"",
"newLineNumber": 13,
"oldLineNumber": null
},
{
"type": "addition",
"content": " | \"billing\"",
"newLineNumber": 14,
"oldLineNumber": null
},
{
"type": "addition",
"content": " | \"github-installations\";",
"newLineNumber": 15,
"oldLineNumber": null
},
{
"type": "addition",
"content": " readonly label: string;",
"newLineNumber": 16,
"oldLineNumber": null
},
{
"type": "addition",
"content": " readonly status: DashboardActionStatus;",
"newLineNumber": 17,
"oldLineNumber": null
},
{
"type": "addition",
"content": " readonly destination: DashboardActionDestination;",
"newLineNumber": 18,
"oldLineNumber": null
},
{
"type": "addition",
"content": " readonly href?: string;",
"newLineNumber": 19,
"oldLineNumber": null
},
{
"type": "addition",
"content": " readonly provider?: DashboardExternalProvider;",
"newLineNumber": 20,
"oldLineNumber": null
},
{
"type": "addition",
"content": " readonly title?: string;",
"newLineNumber": 21,
"oldLineNumber": null
},
{
"type": "addition",
"content": "}",
"newLineNumber": 22,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 23,
"oldLineNumber": null
},
{
"type": "addition",
"content": "export const DASHBOARD_ROUTE_ACTIONS: readonly DashboardRouteAction[] = [",
"newLineNumber": 24,
"oldLineNumber": null
},
{
"type": "addition",
"content": " ...DASHBOARD_NAV_ITEMS.map((item) => ({",
"newLineNumber": 25,
"oldLineNumber": null
},
{
"type": "addition",
"content": " surface: \"sidebar\" as const,",
"newLineNumber": 26,
"oldLineNumber": null
},
{
"type": "addition",
"content": " label: item.label,",
"newLineNumber": 27,
"oldLineNumber": null
},
{
"type": "addition",
"content": " status: item.enabled ? (\"active\" as const) : (\"planned-disabled\" as const),",
"newLineNumber": 28,
"oldLineNumber": null
},
{
"type": "addition",
"content": " destination: item.enabled ? (\"internal\" as const) : (\"none\" as const),",
"newLineNumber": 29,
"oldLineNumber": null
},
{
"type": "addition",
"content": " href: item.enabled ? item.href : undefined,",
"newLineNumber": 30,
"oldLineNumber": null
},
{
"type": "addition",
"content": " title: item.disabledTitle",
"newLineNumber": 31,
"oldLineNumber": null
},
{
"type": "addition",
"content": " })),",
"newLineNumber": 32,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { surface: \"topbar\", label: \"Connect GitHub\", status: \"active\", destination: \"internal\", href: \"/github/installations\" },",
"newLineNumber": 33,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { surface: \"overview\", label: \"Pull requests\", status: \"active\", destination: \"internal\", href: \"/pull-requests\" },",
"newLineNumber": 34,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { surface: \"overview\", label: \"View all review runs\", status: \"active\", destination: \"internal\", href: \"/review-runs\" },",
"newLineNumber": 35,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { surface: \"overview\", label: \"High severity findings\", status: \"active\", destination: \"internal\", href: \"/findings?severity=high\" },",
"newLineNumber": 36,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { surface: \"overview\", label: \"CI failures\", status: \"active\", destination: \"internal\", href: \"/ci-failures\" },",
"newLineNumber": 37,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { surface: \"overview\", label: \"Incomplete repository configuration\", status: \"active\", destination: \"internal\", href: \"/repositories\" },",
"newLineNumber": 38,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { surface: \"repositories\", label: \"Connect GitHub OAuth\", status: \"active\", destination: \"internal\", href: \"/auth/github\" },",
"newLineNumber": 39,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { surface: \"repositories\", label: \"Connect GitHub App\", status: \"active\", destination: \"internal\", href: \"/github/installations\" },",
"newLineNumber": 40,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { surface: \"repositories\", label: \"Manage GitHub App\", status: \"active\", destination: \"internal\", href: \"/github/installations\" },",
"newLineNumber": 41,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { surface: \"repositories\", label: \"Configure\", status: \"active\", destination: \"internal\", href: \"/repositories/[id]?tab=configuration\" },",
"newLineNumber": 42,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { surface: \"repositories\", label: \"View runs\", status: \"active\", destination: \"internal\", href: \"/review-runs?repositoryId=[id]\" },",
"newLineNumber": 43,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { surface: \"settings\", label: \"Connect GitHub App\", status: \"active\", destination: \"internal\", href: \"/github/installations\" },",
"newLineNumber": 44,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { surface: \"settings\", label: \"Repository configuration\", status: \"active\", destination: \"internal\", href: \"/repositories\" },",
"newLineNumber": 45,
"oldLineNumber": null
},
{
"type": "addition",
"content": " {",
"newLineNumber": 46,
"oldLineNumber": null
},
{
"type": "addition",
"content": " surface: \"settings\",",
"newLineNumber": 47,
"oldLineNumber": null
},
{
"type": "addition",
"content": " label: \"Open Clerk profile\",",
"newLineNumber": 48,
"oldLineNumber": null
},
{
"type": "addition",
"content": " status: \"active\",",
"newLineNumber": 49,
"oldLineNumber": null
},
{
"type": "addition",
"content": " destination: \"external\",",
"newLineNumber": 50,
"oldLineNumber": null
},
{
"type": "addition",
"content": " provider: \"clerk\",",
"newLineNumber": 51,
"oldLineNumber": null
},
{
"type": "addition",
"content": " href: \"https://accounts.clerk.example/user\"",
"newLineNumber": 52,
"oldLineNumber": null
},
{
"type": "addition",
"content": " },",
"newLineNumber": 53,
"oldLineNumber": null
},
{
"type": "addition",
"content": " {",
"newLineNumber": 54,
"oldLineNumber": null
},
{
"type": "addition",
"content": " surface: \"settings\",",
"newLineNumber": 55,
"oldLineNumber": null
},
{
"type": "addition",
"content": " label: \"Open Clerk organization\",",
"newLineNumber": 56,
"oldLineNumber": null
},
{
"type": "addition",
"content": " status: \"active\",",
"newLineNumber": 57,
"oldLineNumber": null
},
{
"type": "addition",
"content": " destination: \"external\",",
"newLineNumber": 58,
"oldLineNumber": null
},
{
"type": "addition",
"content": " provider: \"clerk\",",
"newLineNumber": 59,
"oldLineNumber": null
},
{
"type": "addition",
"content": " href: \"https://accounts.clerk.example/organization\"",
"newLineNumber": 60,
"oldLineNumber": null
},
{
"type": "addition",
"content": " },",
"newLineNumber": 61,
"oldLineNumber": null
},
{
"type": "addition",
"content": " {",
"newLineNumber": 62,
"oldLineNumber": null
},
{
"type": "addition",
"content": " surface: \"settings\",",
"newLineNumber": 63,
"oldLineNumber": null
},
{
"type": "addition",
"content": " label: \"Open Clerk members\",",
"newLineNumber": 64,
"oldLineNumber": null
},
{
"type": "addition",
"content": " status: \"active\",",
"newLineNumber": 65,
"oldLineNumber": null
},
{
"type": "addition",
"content": " destination: \"external\",",
"newLineNumber": 66,
"oldLineNumber": null
},
{
"type": "addition",
"content": " provider: \"clerk\",",
"newLineNumber": 67,
"oldLineNumber": null
},
{
"type": "addition",
"content": " href: \"https://accounts.clerk.example/members\"",
"newLineNumber": 68,
"oldLineNumber": null
},
{
"type": "addition",
"content": " },",
"newLineNumber": 69,
"oldLineNumber": null
},
{
"type": "addition",
"content": " {",
"newLineNumber": 70,
"oldLineNumber": null
},
{
"type": "addition",
"content": " surface: \"settings\",",
"newLineNumber": 71,
"oldLineNumber": null
},
{
"type": "addition",
"content": " label: \"Create API key\",",
"newLineNumber": 72,
"oldLineNumber": null
},
{
"type": "addition",
"content": " status: \"planned-disabled\",",
"newLineNumber": 73,
"oldLineNumber": null
},
{
"type": "addition",
"content": " destination: \"none\",",
"newLineNumber": 74,
"oldLineNumber": null
},
{
"type": "addition",
"content": " title: \"Workspace API keys are planned and not enabled in the MVP.\"",
"newLineNumber": 75,
"oldLineNumber": null
},
{
"type": "addition",
"content": " },",
"newLineNumber": 76,
"oldLineNumber": null
},
{
"type": "addition",
"content": " {",
"newLineNumber": 77,
"oldLineNumber": null
},
{
"type": "addition",
"content": " surface: \"settings\",",
"newLineNumber": 78,
"oldLineNumber": null
},
{
"type": "addition",
"content": " label: \"Slack notifications\",",
"newLineNumber": 79,
"oldLineNumber": null
},
{
"type": "addition",
"content": " status: \"planned-disabled\",",
"newLineNumber": 80,
"oldLineNumber": null
},
{
"type": "addition",
"content": " destination: \"none\",",
"newLineNumber": 81,
"oldLineNumber": null
},
{
"type": "addition",
"content": " title: \"Slack notifications are planned and not enabled in the MVP.\"",
"newLineNumber": 82,
"oldLineNumber": null
},
{
"type": "addition",
"content": " },",
"newLineNumber": 83,
"oldLineNumber": null
},
{
"type": "addition",
"content": " {",
"newLineNumber": 84,
"oldLineNumber": null
},
{
"type": "addition",
"content": " surface: \"billing\",",
"newLineNumber": 85,
"oldLineNumber": null
},
{
"type": "addition",
"content": " label: \"Manage subscription\",",
"newLineNumber": 86,
"oldLineNumber": null
},
{
"type": "addition",
"content": " status: \"active\",",
"newLineNumber": 87,
"oldLineNumber": null
},
{
"type": "addition",
"content": " destination: \"external\",",
"newLineNumber": 88,
"oldLineNumber": null
},
{
"type": "addition",
"content": " provider: \"clerk\",",
"newLineNumber": 89,
"oldLineNumber": null
},
{
"type": "addition",
"content": " href: \"https://accounts.clerk.example/billing\"",
"newLineNumber": 90,
"oldLineNumber": null
},
{
"type": "addition",
"content": " },",
"newLineNumber": 91,
"oldLineNumber": null
},
{
"type": "addition",
"content": " {",
"newLineNumber": 92,
"oldLineNumber": null
},
{
"type": "addition",
"content": " surface: \"github-installations\",",
"newLineNumber": 93,
"oldLineNumber": null
},
{
"type": "addition",
"content": " label: \"Install GitHub App\",",
"newLineNumber": 94,
"oldLineNumber": null
},
{
"type": "addition",
"content": " status: \"active\",",
"newLineNumber": 95,
"oldLineNumber": null
},
{
"type": "addition",
"content": " destination: \"external\",",
"newLineNumber": 96,
"oldLineNumber": null
},
{
"type": "addition",
"content": " provider: \"github\",",
"newLineNumber": 97,
"oldLineNumber": null
},
{
"type": "addition",
"content": " href: \"https://github.com/apps/firmcode/installations/new\"",
"newLineNumber": 98,
"oldLineNumber": null
},
{
"type": "addition",
"content": " },",
"newLineNumber": 99,
"oldLineNumber": null
},
{
"type": "addition",
"content": " {",
"newLineNumber": 100,
"oldLineNumber": null
},
{
"type": "addition",
"content": " surface: \"github-installations\",",
"newLineNumber": 101,
"oldLineNumber": null
},
{
"type": "addition",
"content": " label: \"Configure\",",
"newLineNumber": 102,
"oldLineNumber": null
},
{
"type": "addition",
"content": " status: \"planned-disabled\",",
"newLineNumber": 103,
"oldLineNumber": null
},
{
"type": "addition",
"content": " destination: \"none\",",
"newLineNumber": 104,
"oldLineNumber": null
},
{
"type": "addition",
"content": " title: \"Repository detail configuration is planned\"",
"newLineNumber": 105,
"oldLineNumber": null
},
{
"type": "addition",
"content": " },",
"newLineNumber": 106,
"oldLineNumber": null
},
{
"type": "addition",
"content": " {",
"newLineNumber": 107,
"oldLineNumber": null
},
{
"type": "addition",
"content": " surface: \"github-installations\",",
"newLineNumber": 108,
"oldLineNumber": null
},
{
"type": "addition",
"content": " label: \"Run\",",
"newLineNumber": 109,
"oldLineNumber": null
},
{
"type": "addition",
"content": " status: \"planned-disabled\",",
"newLineNumber": 110,
"oldLineNumber": null
},
{
"type": "addition",
"content": " destination: \"none\",",
"newLineNumber": 111,
"oldLineNumber": null
},
{
"type": "addition",
"content": " title: \"Manual review runs are planned\"",
"newLineNumber": 112,
"oldLineNumber": null
},
{
"type": "addition",
"content": " }",
"newLineNumber": 113,
"oldLineNumber": null
},
{
"type": "addition",
"content": "] as const;",
"newLineNumber": 114,
"oldLineNumber": null
}
],
"newStart": 1,
"oldStart": 0,
"newLineCount": 114,
"oldLineCount": 0,
"sectionHeader": ""
}
],
"patch": "@@ -0,0 +1,114 @@\n+import { DASHBOARD_NAV_ITEMS } from \"./dashboard-navigation\";\n+import type { DashboardExternalProvider } from \"./dashboard-route-readiness\";\n+\n+export type DashboardActionStatus = \"active\" | \"planned-disabled\";\n+export type DashboardActionDestination = \"internal\" | \"external\" | \"none\";\n+\n+export interface DashboardRouteAction {\n+ readonly surface:\n+ | \"sidebar\"\n+ | \"topbar\"\n+ | \"overview\"\n+ | \"repositories\"\n+ | \"settings\"\n+ | \"billing\"\n+ | \"github-installations\";\n+ readonly label: string;\n+ readonly status: DashboardActionStatus;\n+ readonly destination: DashboardActionDestination;\n+ readonly href?: string;\n+ readonly provider?: DashboardExternalProvider;\n+ readonly title?: string;\n+}\n+\n+export const DASHBOARD_ROUTE_ACTIONS: readonly DashboardRouteAction[] = [\n+ ...DASHBOARD_NAV_ITEMS.map((item) => ({\n+ surface: \"sidebar\" as const,\n+ label: item.label,\n+ status: item.enabled ? (\"active\" as const) : (\"planned-disabled\" as const),\n+ destination: item.enabled ? (\"internal\" as const) : (\"none\" as const),\n+ href: item.enabled ? item.href : undefined,\n+ title: item.disabledTitle\n+ })),\n+ { surface: \"topbar\", label: \"Connect GitHub\", status: \"active\", destination: \"internal\", href: \"/github/installations\" },\n+ { surface: \"overview\", label: \"Pull requests\", status: \"active\", destination: \"internal\", href: \"/pull-requests\" },\n+ { surface: \"overview\", label: \"View all review runs\", status: \"active\", destination: \"internal\", href: \"/review-runs\" },\n+ { surface: \"overview\", label: \"High severity findings\", status: \"active\", destination: \"internal\", href: \"/findings?severity=high\" },\n+ { surface: \"overview\", label: \"CI failures\", status: \"active\", destination: \"internal\", href: \"/ci-failures\" },\n+ { surface: \"overview\", label: \"Incomplete repository configuration\", status: \"active\", destination: \"internal\", href: \"/repositories\" },\n+ { surface: \"repositories\", label: \"Connect GitHub OAuth\", status: \"active\", destination: \"internal\", href: \"/auth/github\" },\n+ { surface: \"repositories\", label: \"Connect GitHub App\", status: \"active\", destination: \"internal\", href: \"/github/installations\" },\n+ { surface: \"repositories\", label: \"Manage GitHub App\", status: \"active\", destination: \"internal\", href: \"/github/installations\" },\n+ { surface: \"repositories\", label: \"Configure\", status: \"active\", destination: \"internal\", href: \"/repositories/[id]?tab=configuration\" },\n+ { surface: \"repositories\", label: \"View runs\", status: \"active\", destination: \"internal\", href: \"/review-runs?repositoryId=[id]\" },\n+ { surface: \"settings\", label: \"Connect GitHub App\", status: \"active\", destination: \"internal\", href: \"/github/installations\" },\n+ { surface: \"settings\", label: \"Repository configuration\", status: \"active\", destination: \"internal\", href: \"/repositories\" },\n+ {\n+ surface: \"settings\",\n+ label: \"Open Clerk profile\",\n+ status: \"active\",\n+ destination: \"external\",\n+ provider: \"clerk\",\n+ href: \"https://accounts.clerk.example/user\"\n+ },\n+ {\n+ surface: \"settings\",\n+ label: \"Open Clerk organization\",\n+ status: \"active\",\n+ destination: \"external\",\n+ provider: \"clerk\",\n+ href: \"https://accounts.clerk.example/organization\"\n+ },\n+ {\n+ surface: \"settings\",\n+ label: \"Open Clerk members\",\n+ status: \"active\",\n+ destination: \"external\",\n+ provider: \"clerk\",\n+ href: \"https://accounts.clerk.example/members\"\n+ },\n+ {\n+ surface: \"settings\",\n+ label: \"Create API key\",\n+ status: \"planned-disabled\",\n+ destination: \"none\",\n+ title: \"Workspace API keys are planned and not enabled in the MVP.\"\n+ },\n+ {\n+ surface: \"settings\",\n+ label: \"Slack notifications\",\n+ status: \"planned-disabled\",\n+ destination: \"none\",\n+ title: \"Slack notifications are planned and not enabled in the MVP.\"\n+ },\n+ {\n+ surface: \"billing\",\n+ label: \"Manage subscription\",\n+ status: \"active\",\n+ destination: \"external\",\n+ provider: \"clerk\",\n+ href: \"https://accounts.clerk.example/billing\"\n+ },\n+ {\n+ surface: \"github-installations\",\n+ label: \"Install GitHub App\",\n+ status: \"active\",\n+ destination: \"external\",\n+ provider: \"github\",\n+ href: \"https://github.com/apps/firmcode/installations/new\"\n+ },\n+ {\n+ surface: \"github-installations\",\n+ label: \"Configure\",\n+ status: \"planned-disabled\",\n+ destination: \"none\",\n+ title: \"Repository detail configuration is planned\"\n+ },\n+ {\n+ surface: \"github-installations\",\n+ label: \"Run\",\n+ status: \"planned-disabled\",\n+ destination: \"none\",\n+ title: \"Manual review runs are planned\"\n+ }\n+] as const;",
"status": "added",
"language": "typescript",
"additions": 114,
"deletions": 0,
"sizeBytes": 4534,
"previousPath": null,
"changedNewLines": [
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26,
27,
28,
29,
30,
31,
32,
33,
34,
35,
36,
37,
38,
39,
40,
41,
42,
43,
44,
45,
46,
47,
48,
49,
50,
51,
52,
53,
54,
55,
56,
57,
58,
59,
60,
61,
62,
63,
64,
65,
66,
67,
68,
69,
70,
71,
72,
73,
74,
75,
76,
77,
78,
79,
80,
81,
82,
83,
84,
85,
86,
87,
88,
89,
90,
91,
92,
93,
94,
95,
96,
97,
98,
99,
100,
101,
102,
103,
104,
105,
106,
107,
108,
109,
110,
111,
112,
113,
114
],
"headContentSha256": "4bfe0e93177095228f68a2d20d289cc78b0ce427c00de874807692647ee6d83f"
},
{
"path": "apps/web/lib/dashboard-navigation.ts",
"hunks": [
{
"lines": [
{
"type": "addition",
"content": "export type DashboardActiveItem =",
"newLineNumber": 1,
"oldLineNumber": null
},
{
"type": "addition",
"content": " | \"Overview\"",
"newLineNumber": 2,
"oldLineNumber": null
},
{
"type": "addition",
"content": " | \"PR Review\"",
"newLineNumber": 3,
"oldLineNumber": null
},
{
"type": "addition",
"content": " | \"Repositories\"",
"newLineNumber": 4,
"oldLineNumber": null
},
{
"type": "addition",
"content": " | \"Pull Requests\"",
"newLineNumber": 5,
"oldLineNumber": null
},
{
"type": "addition",
"content": " | \"Review Runs\"",
"newLineNumber": 6,
"oldLineNumber": null
},
{
"type": "addition",
"content": " | \"Findings\"",
"newLineNumber": 7,
"oldLineNumber": null
},
{
"type": "addition",
"content": " | \"CI Failures\"",
"newLineNumber": 8,
"oldLineNumber": null
},
{
"type": "addition",
"content": " | \"Rules\"",
"newLineNumber": 9,
"oldLineNumber": null
},
{
"type": "addition",
"content": " | \"Settings\"",
"newLineNumber": 10,
"oldLineNumber": null
},
{
"type": "addition",
"content": " | \"Billing\";",
"newLineNumber": 11,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 12,
"oldLineNumber": null
},
{
"type": "addition",
"content": "export interface DashboardNavItem {",
"newLineNumber": 13,
"oldLineNumber": null
},
{
"type": "addition",
"content": " readonly label: string;",
"newLineNumber": 14,
"oldLineNumber": null
},
{
"type": "addition",
"content": " readonly href: string;",
"newLineNumber": 15,
"oldLineNumber": null
},
{
"type": "addition",
"content": " readonly enabled: boolean;",
"newLineNumber": 16,
"oldLineNumber": null
},
{
"type": "addition",
"content": " readonly activeItem: DashboardActiveItem;",
"newLineNumber": 17,
"oldLineNumber": null
},
{
"type": "addition",
"content": " readonly disabledTitle?: string;",
"newLineNumber": 18,
"oldLineNumber": null
},
{
"type": "addition",
"content": "}",
"newLineNumber": 19,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 20,
"oldLineNumber": null
},
{
"type": "addition",
"content": "export const DASHBOARD_NAV_ITEMS: readonly DashboardNavItem[] = [",
"newLineNumber": 21,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { label: \"Overview\", href: \"/\", enabled: true, activeItem: \"Overview\" },",
"newLineNumber": 22,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { label: \"PR Review\", href: \"/github/installations\", enabled: true, activeItem: \"PR Review\" },",
"newLineNumber": 23,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { label: \"Repositories\", href: \"/repositories\", enabled: true, activeItem: \"Repositories\" },",
"newLineNumber": 24,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { label: \"Pull Requests\", href: \"/pull-requests\", enabled: true, activeItem: \"Pull Requests\" },",
"newLineNumber": 25,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { label: \"Review Runs\", href: \"/review-runs\", enabled: true, activeItem: \"Review Runs\" },",
"newLineNumber": 26,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { label: \"Findings\", href: \"/findings\", enabled: true, activeItem: \"Findings\" },",
"newLineNumber": 27,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { label: \"CI Failures\", href: \"/ci-failures\", enabled: true, activeItem: \"CI Failures\" },",
"newLineNumber": 28,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { label: \"Rules / Policies\", href: \"/rules\", enabled: true, activeItem: \"Rules\" },",
"newLineNumber": 29,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { label: \"Settings\", href: \"/settings\", enabled: true, activeItem: \"Settings\" },",
"newLineNumber": 30,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { label: \"Billing\", href: \"/billing\", enabled: true, activeItem: \"Billing\" }",
"newLineNumber": 31,
"oldLineNumber": null
},
{
"type": "addition",
"content": "] as const;",
"newLineNumber": 32,
"oldLineNumber": null
}
],
"newStart": 1,
"oldStart": 0,
"newLineCount": 32,
"oldLineCount": 0,
"sectionHeader": ""
}
],
"patch": "@@ -0,0 +1,32 @@\n+export type DashboardActiveItem =\n+ | \"Overview\"\n+ | \"PR Review\"\n+ | \"Repositories\"\n+ | \"Pull Requests\"\n+ | \"Review Runs\"\n+ | \"Findings\"\n+ | \"CI Failures\"\n+ | \"Rules\"\n+ | \"Settings\"\n+ | \"Billing\";\n+\n+export interface DashboardNavItem {\n+ readonly label: string;\n+ readonly href: string;\n+ readonly enabled: boolean;\n+ readonly activeItem: DashboardActiveItem;\n+ readonly disabledTitle?: string;\n+}\n+\n+export const DASHBOARD_NAV_ITEMS: readonly DashboardNavItem[] = [\n+ { label: \"Overview\", href: \"/\", enabled: true, activeItem: \"Overview\" },\n+ { label: \"PR Review\", href: \"/github/installations\", enabled: true, activeItem: \"PR Review\" },\n+ { label: \"Repositories\", href: \"/repositories\", enabled: true, activeItem: \"Repositories\" },\n+ { label: \"Pull Requests\", href: \"/pull-requests\", enabled: true, activeItem: \"Pull Requests\" },\n+ { label: \"Review Runs\", href: \"/review-runs\", enabled: true, activeItem: \"Review Runs\" },\n+ { label: \"Findings\", href: \"/findings\", enabled: true, activeItem: \"Findings\" },\n+ { label: \"CI Failures\", href: \"/ci-failures\", enabled: true, activeItem: \"CI Failures\" },\n+ { label: \"Rules / Policies\", href: \"/rules\", enabled: true, activeItem: \"Rules\" },\n+ { label: \"Settings\", href: \"/settings\", enabled: true, activeItem: \"Settings\" },\n+ { label: \"Billing\", href: \"/billing\", enabled: true, activeItem: \"Billing\" }\n+] as const;",
"status": "added",
"language": "typescript",
"additions": 32,
"deletions": 0,
"sizeBytes": 1353,
"previousPath": null,
"changedNewLines": [
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26,
27,
28,
29,
30,
31,
32
],
"headContentSha256": "09d653be8693f3671a3ed369c00db85675b10230e9c79e9d29dc306b7ad8c43d"
},
{
"path": "apps/web/lib/dashboard-route-readiness.ts",
"hunks": [
{
"lines": [
{
"type": "addition",
"content": "export type DashboardDestinationKind = \"internal\" | \"external\";",
"newLineNumber": 1,
"oldLineNumber": null
},
{
"type": "addition",
"content": "export type DashboardExternalProvider = \"clerk\" | \"github\";",
"newLineNumber": 2,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 3,
"oldLineNumber": null
},
{
"type": "addition",
"content": "export interface DashboardRoutePattern {",
"newLineNumber": 4,
"oldLineNumber": null
},
{
"type": "addition",
"content": " readonly pattern: string;",
"newLineNumber": 5,
"oldLineNumber": null
},
{
"type": "addition",
"content": " readonly kind: \"page\" | \"route-handler\";",
"newLineNumber": 6,
"oldLineNumber": null
},
{
"type": "addition",
"content": "}",
"newLineNumber": 7,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 8,
"oldLineNumber": null
},
{
"type": "addition",
"content": "export interface DashboardDestinationReadiness {",
"newLineNumber": 9,
"oldLineNumber": null
},
{
"type": "addition",
"content": " readonly kind: DashboardDestinationKind;",
"newLineNumber": 10,
"oldLineNumber": null
},
{
"type": "addition",
"content": " readonly routeReady: boolean;",
"newLineNumber": 11,
"oldLineNumber": null
},
{
"type": "addition",
"content": " readonly pathname: string;",
"newLineNumber": 12,
"oldLineNumber": null
},
{
"type": "addition",
"content": "}",
"newLineNumber": 13,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 14,
"oldLineNumber": null
},
{
"type": "addition",
"content": "export const DASHBOARD_IMPLEMENTED_ROUTE_PATTERNS: readonly DashboardRoutePattern[] = [",
"newLineNumber": 15,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { pattern: \"/\", kind: \"page\" },",
"newLineNumber": 16,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { pattern: \"/auth/github\", kind: \"route-handler\" },",
"newLineNumber": 17,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { pattern: \"/billing\", kind: \"page\" },",
"newLineNumber": 18,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { pattern: \"/ci-failures\", kind: \"page\" },",
"newLineNumber": 19,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { pattern: \"/ci-failures/[id]\", kind: \"page\" },",
"newLineNumber": 20,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { pattern: \"/findings\", kind: \"page\" },",
"newLineNumber": 21,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { pattern: \"/github/installations\", kind: \"page\" },",
"newLineNumber": 22,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { pattern: \"/pull-requests\", kind: \"page\" },",
"newLineNumber": 23,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { pattern: \"/pull-requests/[id]\", kind: \"page\" },",
"newLineNumber": 24,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { pattern: \"/repositories\", kind: \"page\" },",
"newLineNumber": 25,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { pattern: \"/repositories/[id]\", kind: \"page\" },",
"newLineNumber": 26,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { pattern: \"/review-runs\", kind: \"page\" },",
"newLineNumber": 27,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { pattern: \"/review-runs/[id]\", kind: \"page\" },",
"newLineNumber": 28,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { pattern: \"/rules\", kind: \"page\" },",
"newLineNumber": 29,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { pattern: \"/settings\", kind: \"page\" },",
"newLineNumber": 30,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { pattern: \"/api/github/installations/sync\", kind: \"route-handler\" },",
"newLineNumber": 31,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { pattern: \"/api/github/oauth/status\", kind: \"route-handler\" },",
"newLineNumber": 32,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { pattern: \"/api/repositories/[id]\", kind: \"route-handler\" },",
"newLineNumber": 33,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { pattern: \"/api/repositories/[id]/activity\", kind: \"route-handler\" },",
"newLineNumber": 34,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { pattern: \"/api/repositories/[id]/configuration\", kind: \"route-handler\" },",
"newLineNumber": 35,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { pattern: \"/api/repositories/[id]/sync\", kind: \"route-handler\" },",
"newLineNumber": 36,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { pattern: \"/api/review-runs/[id]/artifacts/[artifactId]/raw\", kind: \"route-handler\" },",
"newLineNumber": 37,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { pattern: \"/api/review-runs/[id]/retry\", kind: \"route-handler\" },",
"newLineNumber": 38,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { pattern: \"/api/rules\", kind: \"route-handler\" },",
"newLineNumber": 39,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { pattern: \"/api/pull-requests\", kind: \"route-handler\" },",
"newLineNumber": 40,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { pattern: \"/api/pull-requests/[id]\", kind: \"route-handler\" },",
"newLineNumber": 41,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { pattern: \"/api/ci-failures\", kind: \"route-handler\" },",
"newLineNumber": 42,
"oldLineNumber": null
},
{
"type": "addition",
"content": " { pattern: \"/api/ci-failures/[id]\", kind: \"route-handler\" }",
"newLineNumber": 43,
"oldLineNumber": null
},
{
"type": "addition",
"content": "] as const;",
"newLineNumber": 44,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 45,
"oldLineNumber": null
},
{
"type": "addition",
"content": "const routeMatchers = DASHBOARD_IMPLEMENTED_ROUTE_PATTERNS.map((route) => ({",
"newLineNumber": 46,
"oldLineNumber": null
},
{
"type": "addition",
"content": " ...route,",
"newLineNumber": 47,
"oldLineNumber": null
},
{
"type": "addition",
"content": " matcher: routePatternToRegExp(route.pattern)",
"newLineNumber": 48,
"oldLineNumber": null
},
{
"type": "addition",
"content": "}));",
"newLineNumber": 49,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 50,
"oldLineNumber": null
},
{
"type": "addition",
"content": "export function isExternalDashboardUrl(href: string): boolean {",
"newLineNumber": 51,
"oldLineNumber": null
},
{
"type": "addition",
"content": " return href.startsWith(\"https://\") || href.startsWith(\"http://\");",
"newLineNumber": 52,
"oldLineNumber": null
},
{
"type": "addition",
"content": "}",
"newLineNumber": 53,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 54,
"oldLineNumber": null
},
{
"type": "addition",
"content": "export function isImplementedDashboardRoute(href: string): boolean {",
"newLineNumber": 55,
"oldLineNumber": null
},
{
"type": "addition",
"content": " const destination = classifyDashboardDestination(href);",
"newLineNumber": 56,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 57,
"oldLineNumber": null
},
{
"type": "addition",
"content": " return destination.kind === \"external\" || destination.routeReady;",
"newLineNumber": 58,
"oldLineNumber": null
},
{
"type": "addition",
"content": "}",
"newLineNumber": 59,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 60,
"oldLineNumber": null
},
{
"type": "addition",
"content": "export function classifyDashboardDestination(href: string): DashboardDestinationReadiness {",
"newLineNumber": 61,
"oldLineNumber": null
},
{
"type": "addition",
"content": " if (isExternalDashboardUrl(href)) {",
"newLineNumber": 62,
"oldLineNumber": null
},
{
"type": "addition",
"content": " return {",
"newLineNumber": 63,
"oldLineNumber": null
},
{
"type": "addition",
"content": " kind: \"external\",",
"newLineNumber": 64,
"oldLineNumber": null
},
{
"type": "addition",
"content": " routeReady: true,",
"newLineNumber": 65,
"oldLineNumber": null
},
{
"type": "addition",
"content": " pathname: href",
"newLineNumber": 66,
"oldLineNumber": null
},
{
"type": "addition",
"content": " };",
"newLineNumber": 67,
"oldLineNumber": null
},
{
"type": "addition",
"content": " }",
"newLineNumber": 68,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 69,
"oldLineNumber": null
},
{
"type": "addition",
"content": " const pathname = normalizeInternalPathname(href);",
"newLineNumber": 70,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 71,
"oldLineNumber": null
},
{
"type": "addition",
"content": " return {",
"newLineNumber": 72,
"oldLineNumber": null
},
{
"type": "addition",
"content": " kind: \"internal\",",
"newLineNumber": 73,
"oldLineNumber": null
},
{
"type": "addition",
"content": " routeReady: routeMatchers.some((route) => route.matcher.test(pathname)),",
"newLineNumber": 74,
"oldLineNumber": null
},
{
"type": "addition",
"content": " pathname",
"newLineNumber": 75,
"oldLineNumber": null
},
{
"type": "addition",
"content": " };",
"newLineNumber": 76,
"oldLineNumber": null
},
{
"type": "addition",
"content": "}",
"newLineNumber": 77,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 78,
"oldLineNumber": null
},
{
"type": "addition",
"content": "export function isAllowedExternalDashboardUrl(href: string, provider: DashboardExternalProvider): boolean {",
"newLineNumber": 79,
"oldLineNumber": null
},
{
"type": "addition",
"content": " if (!isExternalDashboardUrl(href)) {",
"newLineNumber": 80,
"oldLineNumber": null
},
{
"type": "addition",
"content": " return false;",
"newLineNumber": 81,
"oldLineNumber": null
},
{
"type": "addition",
"content": " }",
"newLineNumber": 82,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 83,
"oldLineNumber": null
},
{
"type": "addition",
"content": " const hostname = new URL(href).hostname.toLowerCase();",
"newLineNumber": 84,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 85,
"oldLineNumber": null
},
{
"type": "addition",
"content": " if (provider === \"github\") {",
"newLineNumber": 86,
"oldLineNumber": null
},
{
"type": "addition",
"content": " return hostname === \"github.com\" || hostname.endsWith(\".github.com\");",
"newLineNumber": 87,
"oldLineNumber": null
},
{
"type": "addition",
"content": " }",
"newLineNumber": 88,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 89,
"oldLineNumber": null
},
{
"type": "addition",
"content": " return hostname.includes(\"clerk\") || hostname.includes(\"accounts\") || hostname.includes(\"billing\");",
"newLineNumber": 90,
"oldLineNumber": null
},
{
"type": "addition",
"content": "}",
"newLineNumber": 91,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 92,
"oldLineNumber": null
},
{
"type": "addition",
"content": "function normalizeInternalPathname(href: string): string {",
"newLineNumber": 93,
"oldLineNumber": null
},
{
"type": "addition",
"content": " const withoutHash = href.split(\"#\", 1)[0] ?? href;",
"newLineNumber": 94,
"oldLineNumber": null
},
{
"type": "addition",
"content": " const withoutQuery = withoutHash.split(\"?\", 1)[0] ?? withoutHash;",
"newLineNumber": 95,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 96,
"oldLineNumber": null
},
{
"type": "addition",
"content": " return withoutQuery === \"\" ? \"/\" : withoutQuery;",
"newLineNumber": 97,
"oldLineNumber": null
},
{
"type": "addition",
"content": "}",
"newLineNumber": 98,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 99,
"oldLineNumber": null
},
{
"type": "addition",
"content": "function routePatternToRegExp(pattern: string): RegExp {",
"newLineNumber": 100,
"oldLineNumber": null
},
{
"type": "addition",
"content": " if (pattern === \"/\") {",
"newLineNumber": 101,
"oldLineNumber": null
},
{
"type": "addition",
"content": " return /^\\/$/;",
"newLineNumber": 102,
"oldLineNumber": null
},
{
"type": "addition",
"content": " }",
"newLineNumber": 103,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 104,
"oldLineNumber": null
},
{
"type": "addition",
"content": " const segments = pattern",
"newLineNumber": 105,
"oldLineNumber": null
},
{
"type": "addition",
"content": " .split(\"/\")",
"newLineNumber": 106,
"oldLineNumber": null
},
{
"type": "addition",
"content": " .filter(Boolean)",
"newLineNumber": 107,
"oldLineNumber": null
},
{
"type": "addition",
"content": " .map((segment) => (segment.startsWith(\"[\") && segment.endsWith(\"]\") ? \"[^/]+\" : escapeRegExp(segment)));",
"newLineNumber": 108,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 109,
"oldLineNumber": null
},
{
"type": "addition",
"content": " return new RegExp(`^/${segments.join(\"/\")}$`);",
"newLineNumber": 110,
"oldLineNumber": null
},
{
"type": "addition",
"content": "}",
"newLineNumber": 111,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 112,
"oldLineNumber": null
},
{
"type": "addition",
"content": "function escapeRegExp(value: string): string {",
"newLineNumber": 113,
"oldLineNumber": null
},
{
"type": "addition",
"content": " return value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");",
"newLineNumber": 114,
"oldLineNumber": null
},
{
"type": "addition",
"content": "}",
"newLineNumber": 115,
"oldLineNumber": null
}
],
"newStart": 1,
"oldStart": 0,
"newLineCount": 115,
"oldLineCount": 0,
"sectionHeader": ""
}
],
"patch": "@@ -0,0 +1,115 @@\n+export type DashboardDestinationKind = \"internal\" | \"external\";\n+export type DashboardExternalProvider = \"clerk\" | \"github\";\n+\n+export interface DashboardRoutePattern {\n+ readonly pattern: string;\n+ readonly kind: \"page\" | \"route-handler\";\n+}\n+\n+export interface DashboardDestinationReadiness {\n+ readonly kind: DashboardDestinationKind;\n+ readonly routeReady: boolean;\n+ readonly pathname: string;\n+}\n+\n+export const DASHBOARD_IMPLEMENTED_ROUTE_PATTERNS: readonly DashboardRoutePattern[] = [\n+ { pattern: \"/\", kind: \"page\" },\n+ { pattern: \"/auth/github\", kind: \"route-handler\" },\n+ { pattern: \"/billing\", kind: \"page\" },\n+ { pattern: \"/ci-failures\", kind: \"page\" },\n+ { pattern: \"/ci-failures/[id]\", kind: \"page\" },\n+ { pattern: \"/findings\", kind: \"page\" },\n+ { pattern: \"/github/installations\", kind: \"page\" },\n+ { pattern: \"/pull-requests\", kind: \"page\" },\n+ { pattern: \"/pull-requests/[id]\", kind: \"page\" },\n+ { pattern: \"/repositories\", kind: \"page\" },\n+ { pattern: \"/repositories/[id]\", kind: \"page\" },\n+ { pattern: \"/review-runs\", kind: \"page\" },\n+ { pattern: \"/review-runs/[id]\", kind: \"page\" },\n+ { pattern: \"/rules\", kind: \"page\" },\n+ { pattern: \"/settings\", kind: \"page\" },\n+ { pattern: \"/api/github/installations/sync\", kind: \"route-handler\" },\n+ { pattern: \"/api/github/oauth/status\", kind: \"route-handler\" },\n+ { pattern: \"/api/repositories/[id]\", kind: \"route-handler\" },\n+ { pattern: \"/api/repositories/[id]/activity\", kind: \"route-handler\" },\n+ { pattern: \"/api/repositories/[id]/configuration\", kind: \"route-handler\" },\n+ { pattern: \"/api/repositories/[id]/sync\", kind: \"route-handler\" },\n+ { pattern: \"/api/review-runs/[id]/artifacts/[artifactId]/raw\", kind: \"route-handler\" },\n+ { pattern: \"/api/review-runs/[id]/retry\", kind: \"route-handler\" },\n+ { pattern: \"/api/rules\", kind: \"route-handler\" },\n+ { pattern: \"/api/pull-requests\", kind: \"route-handler\" },\n+ { pattern: \"/api/pull-requests/[id]\", kind: \"route-handler\" },\n+ { pattern: \"/api/ci-failures\", kind: \"route-handler\" },\n+ { pattern: \"/api/ci-failures/[id]\", kind: \"route-handler\" }\n+] as const;\n+\n+const routeMatchers = DASHBOARD_IMPLEMENTED_ROUTE_PATTERNS.map((route) => ({\n+ ...route,\n+ matcher: routePatternToRegExp(route.pattern)\n+}));\n+\n+export function isExternalDashboardUrl(href: string): boolean {\n+ return href.startsWith(\"https://\") || href.startsWith(\"http://\");\n+}\n+\n+export function isImplementedDashboardRoute(href: string): boolean {\n+ const destination = classifyDashboardDestination(href);\n+\n+ return destination.kind === \"external\" || destination.routeReady;\n+}\n+\n+export function classifyDashboardDestination(href: string): DashboardDestinationReadiness {\n+ if (isExternalDashboardUrl(href)) {\n+ return {\n+ kind: \"external\",\n+ routeReady: true,\n+ pathname: href\n+ };\n+ }\n+\n+ const pathname = normalizeInternalPathname(href);\n+\n+ return {\n+ kind: \"internal\",\n+ routeReady: routeMatchers.some((route) => route.matcher.test(pathname)),\n+ pathname\n+ };\n+}\n+\n+export function isAllowedExternalDashboardUrl(href: string, provider: DashboardExternalProvider): boolean {\n+ if (!isExternalDashboardUrl(href)) {\n+ return false;\n+ }\n+\n+ const hostname = new URL(href).hostname.toLowerCase();\n+\n+ if (provider === \"github\") {\n+ return hostname === \"github.com\" || hostname.endsWith(\".github.com\");\n+ }\n+\n+ return hostname.includes(\"clerk\") || hostname.includes(\"accounts\") || hostname.includes(\"billing\");\n+}\n+\n+function normalizeInternalPathname(href: string): string {\n+ const withoutHash = href.split(\"#\", 1)[0] ?? href;\n+ const withoutQuery = withoutHash.split(\"?\", 1)[0] ?? withoutHash;\n+\n+ return withoutQuery === \"\" ? \"/\" : withoutQuery;\n+}\n+\n+function routePatternToRegExp(pattern: string): RegExp {\n+ if (pattern === \"/\") {\n+ return /^\\/$/;\n+ }\n+\n+ const segments = pattern\n+ .split(\"/\")\n+ .filter(Boolean)\n+ .map((segment) => (segment.startsWith(\"[\") && segment.endsWith(\"]\") ? \"[^/]+\" : escapeRegExp(segment)));\n+\n+ return new RegExp(`^/${segments.join(\"/\")}$`);\n+}\n+\n+function escapeRegExp(value: string): string {\n+ return value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n+}",
"status": "added",
"language": "typescript",
"additions": 115,
"deletions": 0,
"sizeBytes": 4065,
"previousPath": null,
"changedNewLines": [
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26,
27,
28,
29,
30,
31,
32,
33,
34,
35,
36,
37,
38,
39,
40,
41,
42,
43,
44,
45,
46,
47,
48,
49,
50,
51,
52,
53,
54,
55,
56,
57,
58,
59,
60,
61,
62,
63,
64,
65,
66,
67,
68,
69,
70,
71,
72,
73,
74,
75,
76,
77,
78,
79,
80,
81,
82,
83,
84,
85,
86,
87,
88,
89,
90,
91,
92,
93,
94,
95,
96,
97,
98,
99,
100,
101,
102,
103,
104,
105,
106,
107,
108,
109,
110,
111,
112,
113,
114,
115
],
"headContentSha256": "10ce5df0a94b850c182a4b73c9b681d9d75933e118c47ca10f1e20dafbe92ce4"
},
{
"path": "apps/web/tests/dashboard-route-readiness.spec.tsx",
"hunks": [
{
"lines": [
{
"type": "addition",
"content": "import React from \"react\";",
"newLineNumber": 1,
"oldLineNumber": null
},
{
"type": "addition",
"content": "import { readdirSync } from \"node:fs\";",
"newLineNumber": 2,
"oldLineNumber": null
},
{
"type": "addition",
"content": "import { relative, join } from \"node:path\";",
"newLineNumber": 3,
"oldLineNumber": null
},
{
"type": "addition",
"content": "import { fileURLToPath } from \"node:url\";",
"newLineNumber": 4,
"oldLineNumber": null
},
{
"type": "addition",
"content": "import { renderToString } from \"react-dom/server\";",
"newLineNumber": 5,
"oldLineNumber": null
},
{
"type": "addition",
"content": "import type {",
"newLineNumber": 6,
"oldLineNumber": null
},
{
"type": "addition",
"content": " OverviewSupplementData,",
"newLineNumber": 7,
"oldLineNumber": null
},
{
"type": "addition",
"content": " RepositoryListResponse,",
"newLineNumber": 8,
"oldLineNumber": null
},
{
"type": "addition",
"content": " ReviewRunListItem,",
"newLineNumber": 9,
"oldLineNumber": null
},
{
"type": "addition",
"content": " WorkspaceBillingResponse,",
"newLineNumber": 10,
"oldLineNumber": null
},
{
"type": "addition",
"content": " WorkspaceSettingsResponse",
"newLineNumber": 11,
"oldLineNumber": null
},
{
"type": "addition",
"content": "} from \"@firmcode/shared\";",
"newLineNumber": 12,
"oldLineNumber": null
},
{
"type": "addition",
"content": "import { BillingView } from \"../components/dashboard/billing-view\";",
"newLineNumber": 13,
"oldLineNumber": null
},
{
"type": "addition",
"content": "import { DashboardShell } from \"../components/dashboard/dashboard-shell\";",
"newLineNumber": 14,
"oldLineNumber": null
},
{
"type": "addition",
"content": "import { GitHubInstallationsView } from \"../components/dashboard/github-installations-view\";",
"newLineNumber": 15,
"oldLineNumber": null
},
{
"type": "addition",
"content": "import { OverviewView } from \"../components/dashboard/overview-view\";",
"newLineNumber": 16,
"oldLineNumber": null
},
{
"type": "addition",
"content": "import { RepositoriesView } from \"../components/dashboard/repositories-view\";",
"newLineNumber": 17,
"oldLineNumber": null
},
{
"type": "addition",
"content": "import { SettingsView } from \"../components/dashboard/settings-view\";",
"newLineNumber": 18,
"oldLineNumber": null
},
{
"type": "addition",
"content": "import { DASHBOARD_ROUTE_ACTIONS } from \"../lib/dashboard-action-manifest\";",
"newLineNumber": 19,
"oldLineNumber": null
},
{
"type": "addition",
"content": "import { buildOverviewDashboardData } from \"../lib/overview-data\";",
"newLineNumber": 20,
"oldLineNumber": null
},
{
"type": "addition",
"content": "import {",
"newLineNumber": 21,
"oldLineNumber": null
},
{
"type": "addition",
"content": " DASHBOARD_IMPLEMENTED_ROUTE_PATTERNS,",
"newLineNumber": 22,
"oldLineNumber": null
},
{
"type": "addition",
"content": " classifyDashboardDestination,",
"newLineNumber": 23,
"oldLineNumber": null
},
{
"type": "addition",
"content": " isAllowedExternalDashboardUrl,",
"newLineNumber": 24,
"oldLineNumber": null
},
{
"type": "addition",
"content": " isImplementedDashboardRoute",
"newLineNumber": 25,
"oldLineNumber": null
},
{
"type": "addition",
"content": "} from \"../lib/dashboard-route-readiness\";",
"newLineNumber": 26,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 27,
"oldLineNumber": null
},
{
"type": "addition",
"content": "describe(\"dashboard route readiness guard\", () => {",
"newLineNumber": 28,
"oldLineNumber": null
},
{
"type": "addition",
"content": " it(\"keeps the implemented route manifest in sync with the Next app route tree\", () => {",
"newLineNumber": 29,
"oldLineNumber": null
},
{
"type": "addition",
"content": " const actualRoutes = discoverNextRoutes();",
"newLineNumber": 30,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 31,
"oldLineNumber": null
},
{
"type": "addition",
"content": " for (const route of DASHBOARD_IMPLEMENTED_ROUTE_PATTERNS) {",
"newLineNumber": 32,
"oldLineNumber": null
},
{
"type": "addition",
"content": " expect(actualRoutes).toContain(route.pattern);",
"newLineNumber": 33,
"oldLineNumber": null
},
{
"type": "addition",
"content": " }",
"newLineNumber": 34,
"oldLineNumber": null
},
{
"type": "addition",
"content": " });",
"newLineNumber": 35,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 36,
"oldLineNumber": null
},
{
"type": "addition",
"content": " it(\"requires every active internal navigation or action definition to target an implemented route\", () => {",
"newLineNumber": 37,
"oldLineNumber": null
},
{
"type": "addition",
"content": " const activeInternalActions = DASHBOARD_ROUTE_ACTIONS.filter(",
"newLineNumber": 38,
"oldLineNumber": null
},
{
"type": "addition",
"content": " (action) => action.status === \"active\" && action.destination === \"internal\"",
"newLineNumber": 39,
"oldLineNumber": null
},
{
"type": "addition",
"content": " );",
"newLineNumber": 40,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 41,
"oldLineNumber": null
},
{
"type": "addition",
"content": " expect(activeInternalActions.length).toBeGreaterThan(0);",
"newLineNumber": 42,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 43,
"oldLineNumber": null
},
{
"type": "addition",
"content": " for (const action of activeInternalActions) {",
"newLineNumber": 44,
"oldLineNumber": null
},
{
"type": "addition",
"content": " expect(action.href, `${action.surface}:${action.label}`).toBeDefined();",
"newLineNumber": 45,
"oldLineNumber": null
},
{
"type": "addition",
"content": " expect(isImplementedDashboardRoute(action.href!), `${action.surface}:${action.label} -> ${action.href}`).toBe(true);",
"newLineNumber": 46,
"oldLineNumber": null
},
{
"type": "addition",
"content": " }",
"newLineNumber": 47,
"oldLineNumber": null
},
{
"type": "addition",
"content": " });",
"newLineNumber": 48,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 49,
"oldLineNumber": null
},
{
"type": "addition",
"content": " it(\"keeps external Clerk and GitHub actions explicit and validates them separately\", () => {",
"newLineNumber": 50,
"oldLineNumber": null
},
{
"type": "addition",
"content": " const externalActions = DASHBOARD_ROUTE_ACTIONS.filter((action) => action.destination === \"external\");",
"newLineNumber": 51,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 52,
"oldLineNumber": null
},
{
"type": "addition",
"content": " expect(externalActions.length).toBeGreaterThan(0);",
"newLineNumber": 53,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 54,
"oldLineNumber": null
},
{
"type": "addition",
"content": " for (const action of externalActions) {",
"newLineNumber": 55,
"oldLineNumber": null
},
{
"type": "addition",
"content": " expect(action.provider, `${action.surface}:${action.label}`).toBeDefined();",
"newLineNumber": 56,
"oldLineNumber": null
},
{
"type": "addition",
"content": " expect(action.href, `${action.surface}:${action.label}`).toBeDefined();",
"newLineNumber": 57,
"oldLineNumber": null
},
{
"type": "addition",
"content": " expect(",
"newLineNumber": 58,
"oldLineNumber": null
},
{
"type": "addition",
"content": " isAllowedExternalDashboardUrl(action.href!, action.provider!),",
"newLineNumber": 59,
"oldLineNumber": null
},
{
"type": "addition",
"content": " `${action.surface}:${action.label} -> ${action.href}`",
"newLineNumber": 60,
"oldLineNumber": null
},
{
"type": "addition",
"content": " ).toBe(true);",
"newLineNumber": 61,
"oldLineNumber": null
},
{
"type": "addition",
"content": " }",
"newLineNumber": 62,
"oldLineNumber": null
},
{
"type": "addition",
"content": " });",
"newLineNumber": 63,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 64,
"oldLineNumber": null
},
{
"type": "addition",
"content": " it(\"requires planned dashboard actions to stay disabled and accessible\", () => {",
"newLineNumber": 65,
"oldLineNumber": null
},
{
"type": "addition",
"content": " const plannedActions = DASHBOARD_ROUTE_ACTIONS.filter((action) => action.status === \"planned-disabled\");",
"newLineNumber": 66,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 67,
"oldLineNumber": null
},
{
"type": "addition",
"content": " expect(plannedActions.length).toBeGreaterThan(0);",
"newLineNumber": 68,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 69,
"oldLineNumber": null
},
{
"type": "addition",
"content": " for (const action of plannedActions) {",
"newLineNumber": 70,
"oldLineNumber": null
},
{
"type": "addition",
"content": " expect(action.href, `${action.surface}:${action.label}`).toBeUndefined();",
"newLineNumber": 71,
"oldLineNumber": null
},
{
"type": "addition",
"content": " expect(action.title, `${action.surface}:${action.label}`).toEqual(expect.any(String));",
"newLineNumber": 72,
"oldLineNumber": null
},
{
"type": "addition",
"content": " }",
"newLineNumber": 73,
"oldLineNumber": null
},
{
"type": "addition",
"content": " });",
"newLineNumber": 74,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 75,
"oldLineNumber": null
},
{
"type": "addition",
"content": " it(\"validates rendered sidebar, topbar, overview, repository, settings, billing, and GitHub setup links\", () => {",
"newLineNumber": 76,
"oldLineNumber": null
},
{
"type": "addition",
"content": " const html = [",
"newLineNumber": 77,
"oldLineNumber": null
},
{
"type": "addition",
"content": " renderToString(",
"newLineNumber": 78,
"oldLineNumber": null
},
{
"type": "addition",
"content": " <DashboardShell activeItem=\"Overview\">",
"newLineNumber": 79,
"oldLineNumber": null
},
{
"type": "addition",
"content": " <main>Dashboard body</main>",
"newLineNumber": 80,
"oldLineNumber": null
},
{
"type": "addition",
"content": " </DashboardShell>",
"newLineNumber": 81,
"oldLineNumber": null
},
{
"type": "addition",
"content": " ),",
"newLineNumber": 82,
"oldLineNumber": null
},
{
"type": "addition",
"content": " renderToString(<OverviewView state={{ status: \"populated\", data: overviewData }} />),",
"newLineNumber": 83,
"oldLineNumber": null
},
{
"type": "addition",
"content": " renderToString(",
"newLineNumber": 84,
"oldLineNumber": null
},
{
"type": "addition",
"content": " <RepositoriesView",
"newLineNumber": 85,
"oldLineNumber": null
},
{
"type": "addition",
"content": " state={{ status: \"populated\", data: repositoryList }}",
"newLineNumber": 86,
"oldLineNumber": null
},
{
"type": "addition",
"content": " controlsState={{",
"newLineNumber": 87,
"oldLineNumber": null
},
{
"type": "addition",
"content": " status: \"ready\",",
"newLineNumber": 88,
"oldLineNumber": null
},
{
"type": "addition",
"content": " data: {",
"newLineNumber": 89,
"oldLineNumber": null
},
{
"type": "addition",
"content": " oauth: connectedOAuth,",
"newLineNumber": 90,
"oldLineNumber": null
},
{
"type": "addition",
"content": " settings: externalSettings",
"newLineNumber": 91,
"oldLineNumber": null
},
{
"type": "addition",
"content": " }",
"newLineNumber": 92,
"oldLineNumber": null
},
{
"type": "addition",
"content": " }}",
"newLineNumber": 93,
"oldLineNumber": null
},
{
"type": "addition",
"content": " />",
"newLineNumber": 94,
"oldLineNumber": null
},
{
"type": "addition",
"content": " ),",
"newLineNumber": 95,
"oldLineNumber": null
},
{
"type": "addition",
"content": " renderToString(<SettingsView state={{ status: \"populated\", data: externalSettings }} activeTab=\"general\" />),",
"newLineNumber": 96,
"oldLineNumber": null
},
{
"type": "addition",
"content": " renderToString(<SettingsView state={{ status: \"populated\", data: externalSettings }} activeTab=\"github-app\" />),",
"newLineNumber": 97,
"oldLineNumber": null
},
{
"type": "addition",
"content": " renderToString(<SettingsView state={{ status: \"populated\", data: externalSettings }} activeTab=\"members\" />),",
"newLineNumber": 98,
"oldLineNumber": null
},
{
"type": "addition",
"content": " renderToString(",
"newLineNumber": 99,
"oldLineNumber": null
},
{
"type": "addition",
"content": " <BillingView",
"newLineNumber": 100,
"oldLineNumber": null
},
{
"type": "addition",
"content": " state={{ status: \"populated\", data: billing }}",
"newLineNumber": 101,
"oldLineNumber": null
},
{
"type": "addition",
"content": " billingPortalUrl=\"https://accounts.clerk.example/billing\"",
"newLineNumber": 102,
"oldLineNumber": null
},
{
"type": "addition",
"content": " />",
"newLineNumber": 103,
"oldLineNumber": null
},
{
"type": "addition",
"content": " ),",
"newLineNumber": 104,
"oldLineNumber": null
},
{
"type": "addition",
"content": " renderToString(",
"newLineNumber": 105,
"oldLineNumber": null
},
{
"type": "addition",
"content": " <GitHubInstallationsView",
"newLineNumber": 106,
"oldLineNumber": null
},
{
"type": "addition",
"content": " state={{ status: \"populated\", data: syncData(externalSettings, true) }}",
"newLineNumber": 107,
"oldLineNumber": null
},
{
"type": "addition",
"content": " installConfig={{",
"newLineNumber": 108,
"oldLineNumber": null
},
{
"type": "addition",
"content": " status: \"configured\",",
"newLineNumber": 109,
"oldLineNumber": null
},
{
"type": "addition",
"content": " installUrl: \"https://github.com/apps/firmcode/installations/new\",",
"newLineNumber": 110,
"oldLineNumber": null
},
{
"type": "addition",
"content": " source: \"GITHUB_APP_INSTALL_URL\"",
"newLineNumber": 111,
"oldLineNumber": null
},
{
"type": "addition",
"content": " }}",
"newLineNumber": 112,
"oldLineNumber": null
},
{
"type": "addition",
"content": " />",
"newLineNumber": 113,
"oldLineNumber": null
},
{
"type": "addition",
"content": " )",
"newLineNumber": 114,
"oldLineNumber": null
},
{
"type": "addition",
"content": " ].join(\"\\n\");",
"newLineNumber": 115,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 116,
"oldLineNumber": null
},
{
"type": "addition",
"content": " for (const formAction of extractFormActions(html)) {",
"newLineNumber": 117,
"oldLineNumber": null
},
{
"type": "addition",
"content": " expect(isImplementedDashboardRoute(formAction), `form action -> ${formAction}`).toBe(true);",
"newLineNumber": 118,
"oldLineNumber": null
},
{
"type": "addition",
"content": " }",
"newLineNumber": 119,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 120,
"oldLineNumber": null
},
{
"type": "addition",
"content": " for (const anchor of extractAnchors(html)) {",
"newLineNumber": 121,
"oldLineNumber": null
},
{
"type": "addition",
"content": " const destination = classifyDashboardDestination(anchor.href);",
"newLineNumber": 122,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 123,
"oldLineNumber": null
},
{
"type": "addition",
"content": " if (destination.kind === \"internal\") {",
"newLineNumber": 124,
"oldLineNumber": null
},
{
"type": "addition",
"content": " expect(destination.routeReady, `${anchor.href} is not implemented`).toBe(true);",
"newLineNumber": 125,
"oldLineNumber": null
},
{
"type": "addition",
"content": " expect(anchor.tag).not.toContain('data-dashboard-destination=\"external\"');",
"newLineNumber": 126,
"oldLineNumber": null
},
{
"type": "addition",
"content": " } else {",
"newLineNumber": 127,
"oldLineNumber": null
},
{
"type": "addition",
"content": " expect(anchor.tag, `${anchor.href} must be explicitly marked external`).toContain('data-dashboard-destination=\"external\"');",
"newLineNumber": 128,
"oldLineNumber": null
},
{
"type": "addition",
"content": " expect(anchor.tag, `${anchor.href} must identify Clerk or GitHub`).toMatch(/data-dashboard-provider=\"(clerk|github)\"/);",
"newLineNumber": 129,
"oldLineNumber": null
},
{
"type": "addition",
"content": " }",
"newLineNumber": 130,
"oldLineNumber": null
},
{
"type": "addition",
"content": " }",
"newLineNumber": 131,
"oldLineNumber": null
},
{
"type": "addition",
"content": " });",
"newLineNumber": 132,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 133,
"oldLineNumber": null
},
{
"type": "addition",
"content": " it(\"disables planned Clerk settings actions when Clerk URLs are not external route-ready destinations\", () => {",
"newLineNumber": 134,
"oldLineNumber": null
},
{
"type": "addition",
"content": " const generalHtml = renderToString(<SettingsView state={{ status: \"populated\", data: internalClerkSettings }} activeTab=\"general\" />);",
"newLineNumber": 135,
"oldLineNumber": null
},
{
"type": "addition",
"content": " const membersHtml = renderToString(<SettingsView state={{ status: \"populated\", data: internalClerkSettings }} activeTab=\"members\" />);",
"newLineNumber": 136,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 137,
"oldLineNumber": null
},
{
"type": "addition",
"content": " expect(generalHtml).not.toContain('href=\"/user-profile\"');",
"newLineNumber": 138,
"oldLineNumber": null
},
{
"type": "addition",
"content": " expect(generalHtml).not.toContain('href=\"/organization-profile\"');",
"newLineNumber": 139,
"oldLineNumber": null
},
{
"type": "addition",
"content": " expect(generalHtml).toContain(\"Open Clerk profile is planned until its internal destination is route-ready.\");",
"newLineNumber": 140,
"oldLineNumber": null
},
{
"type": "addition",
"content": " expect(generalHtml).toContain(\"Open Clerk organization is planned until its internal destination is route-ready.\");",
"newLineNumber": 141,
"oldLineNumber": null
},
{
"type": "addition",
"content": " expect(membersHtml).not.toContain('href=\"/organization-profile/members\"');",
"newLineNumber": 142,
"oldLineNumber": null
},
{
"type": "addition",
"content": " expect(membersHtml).toContain(\"Open Clerk members is planned until its internal destination is route-ready.\");",
"newLineNumber": 143,
"oldLineNumber": null
},
{
"type": "addition",
"content": " });",
"newLineNumber": 144,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 145,
"oldLineNumber": null
},
{
"type": "addition",
"content": " it(\"keeps disabled planned controls rendered as disabled buttons with titles\", () => {",
"newLineNumber": 146,
"oldLineNumber": null
},
{
"type": "addition",
"content": " const settingsHtml = renderToString(<SettingsView state={{ status: \"populated\", data: externalSettings }} activeTab=\"api-keys\" />);",
"newLineNumber": 147,
"oldLineNumber": null
},
{
"type": "addition",
"content": " const githubHtml = renderToString(",
"newLineNumber": 148,
"oldLineNumber": null
},
{
"type": "addition",
"content": " <GitHubInstallationsView",
"newLineNumber": 149,
"oldLineNumber": null
},
{
"type": "addition",
"content": " state={{ status: \"populated\", data: syncData(externalSettings, true) }}",
"newLineNumber": 150,
"oldLineNumber": null
},
{
"type": "addition",
"content": " installConfig={{",
"newLineNumber": 151,
"oldLineNumber": null
},
{
"type": "addition",
"content": " status: \"configured\",",
"newLineNumber": 152,
"oldLineNumber": null
},
{
"type": "addition",
"content": " installUrl: \"https://github.com/apps/firmcode/installations/new\",",
"newLineNumber": 153,
"oldLineNumber": null
},
{
"type": "addition",
"content": " source: \"GITHUB_APP_INSTALL_URL\"",
"newLineNumber": 154,
"oldLineNumber": null
},
{
"type": "addition",
"content": " }}",
"newLineNumber": 155,
"oldLineNumber": null
},
{
"type": "addition",
"content": " />",
"newLineNumber": 156,
"oldLineNumber": null
},
{
"type": "addition",
"content": " );",
"newLineNumber": 157,
"oldLineNumber": null
},
{
"type": "addition",
"content": " const billingHtml = renderToString(<BillingView state={{ status: \"populated\", data: viewerBilling }} billingPortalUrl={null} />);",
"newLineNumber": 158,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 159,
"oldLineNumber": null
},
{
"type": "addition",
"content": " expect(settingsHtml).toContain(\"Workspace API keys are planned and not enabled in the MVP.\");",
"newLineNumber": 160,
"oldLineNumber": null
},
{
"type": "addition",
"content": " expect(settingsHtml).toContain(\"disabled=\\\"\\\"\");",
"newLineNumber": 161,
"oldLineNumber": null
},
{
"type": "addition",
"content": " expect(githubHtml).toContain(\"Repository detail configuration is planned\");",
"newLineNumber": 162,
"oldLineNumber": null
},
{
"type": "addition",
"content": " expect(githubHtml).toContain(\"Manual review runs are planned\");",
"newLineNumber": 163,
"oldLineNumber": null
},
{
"type": "addition",
"content": " expect(billingHtml).toContain(\"Owner billing permission is required to manage subscriptions.\");",
"newLineNumber": 164,
"oldLineNumber": null
},
{
"type": "addition",
"content": " expect(billingHtml).toContain(\"disabled=\\\"\\\"\");",
"newLineNumber": 165,
"oldLineNumber": null
},
{
"type": "addition",
"content": " });",
"newLineNumber": 166,
"oldLineNumber": null
},
{
"type": "addition",
"content": "});",
"newLineNumber": 167,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 168,
"oldLineNumber": null
},
{
"type": "addition",
"content": "function extractAnchors(html: string): Array<{ href: string; tag: string }> {",
"newLineNumber": 169,
"oldLineNumber": null
},
{
"type": "addition",
"content": " return [...html.matchAll(/<a\\b[^>]*href=\"([^\"]+)\"[^>]*>/g)].map((match) => ({",
"newLineNumber": 170,
"oldLineNumber": null
},
{
"type": "addition",
"content": " href: decodeHtmlAttribute(match[1]),",
"newLineNumber": 171,
"oldLineNumber": null
},
{
"type": "addition",
"content": " tag: match[0]",
"newLineNumber": 172,
"oldLineNumber": null
},
{
"type": "addition",
"content": " }));",
"newLineNumber": 173,
"oldLineNumber": null
},
{
"type": "addition",
"content": "}",
"newLineNumber": 174,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 175,
"oldLineNumber": null
},
{
"type": "addition",
"content": "function extractFormActions(html: string): string[] {",
"newLineNumber": 176,
"oldLineNumber": null
},
{
"type": "addition",
"content": " return [...html.matchAll(/<form\\b[^>]*action=\"([^\"]+)\"[^>]*>/g)].map((match) => decodeHtmlAttribute(match[1]));",
"newLineNumber": 177,
"oldLineNumber": null
},
{
"type": "addition",
"content": "}",
"newLineNumber": 178,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 179,
"oldLineNumber": null
},
{
"type": "addition",
"content": "function decodeHtmlAttribute(value: string): string {",
"newLineNumber": 180,
"oldLineNumber": null
},
{
"type": "addition",
"content": " return value.replaceAll(\"&\", \"&\");",
"newLineNumber": 181,
"oldLineNumber": null
},
{
"type": "addition",
"content": "}",
"newLineNumber": 182,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 183,
"oldLineNumber": null
},
{
"type": "addition",
"content": "function discoverNextRoutes(): string[] {",
"newLineNumber": 184,
"oldLineNumber": null
},
{
"type": "addition",
"content": " const appDir = fileURLToPath(new URL(\"../app\", import.meta.url));",
"newLineNumber": 185,
"oldLineNumber": null
},
{
"type": "addition",
"content": " const routes: string[] = [];",
"newLineNumber": 186,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 187,
"oldLineNumber": null
},
{
"type": "addition",
"content": " walk(appDir);",
"newLineNumber": 188,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 189,
"oldLineNumber": null
},
{
"type": "addition",
"content": " return routes.sort();",
"newLineNumber": 190,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 191,
"oldLineNumber": null
},
{
"type": "addition",
"content": " function walk(directory: string): void {",
"newLineNumber": 192,
"oldLineNumber": null
},
{
"type": "addition",
"content": " for (const entry of readdirSync(directory, { withFileTypes: true })) {",
"newLineNumber": 193,
"oldLineNumber": null
},
{
"type": "addition",
"content": " const fullPath = join(directory, entry.name);",
"newLineNumber": 194,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 195,
"oldLineNumber": null
},
{
"type": "addition",
"content": " if (entry.isDirectory()) {",
"newLineNumber": 196,
"oldLineNumber": null
},
{
"type": "addition",
"content": " walk(fullPath);",
"newLineNumber": 197,
"oldLineNumber": null
},
{
"type": "addition",
"content": " continue;",
"newLineNumber": 198,
"oldLineNumber": null
},
{
"type": "addition",
"content": " }",
"newLineNumber": 199,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 200,
"oldLineNumber": null
},
{
"type": "addition",
"content": " if (entry.name !== \"page.tsx\" && entry.name !== \"route.ts\") {",
"newLineNumber": 201,
"oldLineNumber": null
},
{
"type": "addition",
"content": " continue;",
"newLineNumber": 202,
"oldLineNumber": null
},
{
"type": "addition",
"content": " }",
"newLineNumber": 203,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 204,
"oldLineNumber": null
},
{
"type": "addition",
"content": " const routeDirectory = relative(appDir, directory);",
"newLineNumber": 205,
"oldLineNumber": null
},
{
"type": "addition",
"content": " routes.push(routeDirectory === \"\" ? \"/\" : `/${routeDirectory}`);",
"newLineNumber": 206,
"oldLineNumber": null
},
{
"type": "addition",
"content": " }",
"newLineNumber": 207,
"oldLineNumber": null
},
{
"type": "addition",
"content": " }",
"newLineNumber": 208,
"oldLineNumber": null
},
{
"type": "addition",
"content": "}",
"newLineNumber": 209,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 210,
"oldLineNumber": null
},
{
"type": "addition",
"content": "const repositoryList: RepositoryListResponse = {",
"newLineNumber": 211,
"oldLineNumber": null
},
{
"type": "addition",
"content": " filters: {},",
"newLineNumber": 212,
"oldLineNumber": null
},
{
"type": "addition",
"content": " repositories: [",
"newLineNumber": 213,
"oldLineNumber": null
},
{
"type": "addition",
"content": " {",
"newLineNumber": 214,
"oldLineNumber": null
},
{
"type": "addition",
"content": " id: \"repo-1\",",
"newLineNumber": 215,
"oldLineNumber": null
},
{
"type": "addition",
"content": " owner: \"openclaw\",",
"newLineNumber": 216,
"oldLineNumber": null
},
{
"type": "addition",
"content": " name: \"firmcode\",",
"newLineNumber": 217,
"oldLineNumber": null
},
{
"type": "addition",
"content": " fullName: \"openclaw/firmcode\",",
"newLineNumber": 218,
"oldLineNumber": null
},
{
"type": "addition",
"content": " private: false,",
"newLineNumber": 219,
"oldLineNumber": null
},
{
"type": "addition",
"content": " defaultBranch: \"main\",",
"newLineNumber": 220,
"oldLineNumber": null
},
{
"type": "addition",
"content": " enabled: true,",
"newLineNumber": 221,
"oldLineNumber": null
},
{
"type": "addition",
"content": " primaryLanguage: \"TypeScript\",",
"newLineNumber": 222,
"oldLineNumber": null
},
{
"type": "addition",
"content": " openFindingsCount: 2,",
"newLineNumber": 223,
"oldLineNumber": null
},
{
"type": "addition",
"content": " updatedAt: \"2026-05-22T10:00:00.000Z\",",
"newLineNumber": 224,
"oldLineNumber": null
},
{
"type": "addition",
"content": " lastReview: {",
"newLineNumber": 225,
"oldLineNumber": null
},
{
"type": "addition",
"content": " reviewRunId: \"run-1\",",
"newLineNumber": 226,
"oldLineNumber": null
},
{
"type": "addition",
"content": " pullRequestNumber: 7,",
"newLineNumber": 227,
"oldLineNumber": null
},
{
"type": "addition",
"content": " pullRequestTitle: \"Add route readiness\",",
"newLineNumber": 228,
"oldLineNumber": null
},
{
"type": "addition",
"content": " status: \"succeeded\",",
"newLineNumber": 229,
"oldLineNumber": null
},
{
"type": "addition",
"content": " headSha: \"abc123def456\",",
"newLineNumber": 230,
"oldLineNumber": null
},
{
"type": "addition",
"content": " createdAt: \"2026-05-22T10:00:00.000Z\",",
"newLineNumber": 231,
"oldLineNumber": null
},
{
"type": "addition",
"content": " finishedAt: \"2026-05-22T10:02:00.000Z\"",
"newLineNumber": 232,
"oldLineNumber": null
},
{
"type": "addition",
"content": " }",
"newLineNumber": 233,
"oldLineNumber": null
},
{
"type": "addition",
"content": " }",
"newLineNumber": 234,
"oldLineNumber": null
},
{
"type": "addition",
"content": " ]",
"newLineNumber": 235,
"oldLineNumber": null
},
{
"type": "addition",
"content": "};",
"newLineNumber": 236,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 237,
"oldLineNumber": null
},
{
"type": "addition",
"content": "const reviewRuns: ReviewRunListItem[] = [",
"newLineNumber": 238,
"oldLineNumber": null
},
{
"type": "addition",
"content": " {",
"newLineNumber": 239,
"oldLineNumber": null
},
{
"type": "addition",
"content": " id: \"run-1\",",
"newLineNumber": 240,
"oldLineNumber": null
},
{
"type": "addition",
"content": " repositoryId: \"repo-1\",",
"newLineNumber": 241,
"oldLineNumber": null
},
{
"type": "addition",
"content": " pullRequestId: \"pr-1\",",
"newLineNumber": 242,
"oldLineNumber": null
},
{
"type": "addition",
"content": " repositoryFullName: \"openclaw/firmcode\",",
"newLineNumber": 243,
"oldLineNumber": null
},
{
"type": "addition",
"content": " pullRequestNumber: 7,",
"newLineNumber": 244,
"oldLineNumber": null
},
{
"type": "addition",
"content": " pullRequestTitle: \"Add route readiness\",",
"newLineNumber": 245,
"oldLineNumber": null
},
{
"type": "addition",
"content": " pullRequestAuthor: \"kelly\",",
"newLineNumber": 246,
"oldLineNumber": null
},
{
"type": "addition",
"content": " triggerEvent: \"pull_request.opened\",",
"newLineNumber": 247,
"oldLineNumber": null
},
{
"type": "addition",
"content": " currentStage: \"Comments Published\",",
"newLineNumber": 248,
"oldLineNumber": null
},
{
"type": "addition",
"content": " durationMs: 120000,",
"newLineNumber": 249,
"oldLineNumber": null
},
{
"type": "addition",
"content": " commentsPostedCount: 2,",
"newLineNumber": 250,
"oldLineNumber": null
},
{
"type": "addition",
"content": " filesAnalyzedCount: 4,",
"newLineNumber": 251,
"oldLineNumber": null
},
{
"type": "addition",
"content": " riskLevel: \"high\",",
"newLineNumber": 252,
"oldLineNumber": null
},
{
"type": "addition",
"content": " headSha: \"abc123def456\",",
"newLineNumber": 253,
"oldLineNumber": null
},
{
"type": "addition",
"content": " status: \"failed\",",
"newLineNumber": 254,
"oldLineNumber": null
},
{
"type": "addition",
"content": " findingsCount: 4,",
"newLineNumber": 255,
"oldLineNumber": null
},
{
"type": "addition",
"content": " startedAt: \"2026-05-22T10:00:00.000Z\",",
"newLineNumber": 256,
"oldLineNumber": null
},
{
"type": "addition",
"content": " finishedAt: null,",
"newLineNumber": 257,
"oldLineNumber": null
},
{
"type": "addition",
"content": " createdAt: \"2026-05-22T10:00:00.000Z\",",
"newLineNumber": 258,
"oldLineNumber": null
},
{
"type": "addition",
"content": " updatedAt: \"2026-05-22T10:02:00.000Z\"",
"newLineNumber": 259,
"oldLineNumber": null
},
{
"type": "addition",
"content": " }",
"newLineNumber": 260,
"oldLineNumber": null
},
{
"type": "addition",
"content": "];",
"newLineNumber": 261,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 262,
"oldLineNumber": null
},
{
"type": "addition",
"content": "const supplement: OverviewSupplementData = {",
"newLineNumber": 263,
"oldLineNumber": null
},
{
"type": "addition",
"content": " securityFindingsCount: 5,",
"newLineNumber": 264,
"oldLineNumber": null
},
{
"type": "addition",
"content": " ciFailuresExplainedCount: 2,",
"newLineNumber": 265,
"oldLineNumber": null
},
{
"type": "addition",
"content": " highSeverityFindings: [",
"newLineNumber": 266,
"oldLineNumber": null
},
{
"type": "addition",
"content": " {",
"newLineNumber": 267,
"oldLineNumber": null
},
{
"type": "addition",
"content": " id: \"finding-1\",",
"newLineNumber": 268,
"oldLineNumber": null
},
{
"type": "addition",
"content": " kind: \"high_severity_finding\",",
"newLineNumber": 269,
"oldLineNumber": null
},
{
"type": "addition",
"content": " title: \"High severity finding\",",
"newLineNumber": 270,
"oldLineNumber": null
},
{
"type": "addition",
"content": " detail: \"openclaw/firmcode has an authentication-path finding awaiting triage.\",",
"newLineNumber": 271,
"oldLineNumber": null
},
{
"type": "addition",
"content": " href: \"/findings?severity=high\",",
"newLineNumber": 272,
"oldLineNumber": null
},
{
"type": "addition",
"content": " severity: \"high\",",
"newLineNumber": 273,
"oldLineNumber": null
},
{
"type": "addition",
"content": " updatedAt: \"2026-05-22T09:30:00.000Z\"",
"newLineNumber": 274,
"oldLineNumber": null
},
{
"type": "addition",
"content": " }",
"newLineNumber": 275,
"oldLineNumber": null
},
{
"type": "addition",
"content": " ],",
"newLineNumber": 276,
"oldLineNumber": null
},
{
"type": "addition",
"content": " ciFailures: [",
"newLineNumber": 277,
"oldLineNumber": null
},
{
"type": "addition",
"content": " {",
"newLineNumber": 278,
"oldLineNumber": null
},
{
"type": "addition",
"content": " id: \"ci-1\",",
"newLineNumber": 279,
"oldLineNumber": null
},
{
"type": "addition",
"content": " kind: \"ci_failure\",",
"newLineNumber": 280,
"oldLineNumber": null
},
{
"type": "addition",
"content": " title: \"CI failure\",",
"newLineNumber": 281,
"oldLineNumber": null
},
{
"type": "addition",
"content": " detail: \"firmcode dashboard tests need a failure explanation review.\",",
"newLineNumber": 282,
"oldLineNumber": null
},
{
"type": "addition",
"content": " href: \"/ci-failures\",",
"newLineNumber": 283,
"oldLineNumber": null
},
{
"type": "addition",
"content": " severity: \"medium\",",
"newLineNumber": 284,
"oldLineNumber": null
},
{
"type": "addition",
"content": " updatedAt: \"2026-05-22T08:45:00.000Z\"",
"newLineNumber": 285,
"oldLineNumber": null
},
{
"type": "addition",
"content": " }",
"newLineNumber": 286,
"oldLineNumber": null
},
{
"type": "addition",
"content": " ],",
"newLineNumber": 287,
"oldLineNumber": null
},
{
"type": "addition",
"content": " incompleteRepositoryConfigurationRepositoryIds: [\"repo-1\"],",
"newLineNumber": 288,
"oldLineNumber": null
},
{
"type": "addition",
"content": " qualityMetrics: [",
"newLineNumber": 289,
"oldLineNumber": null
},
{
"type": "addition",
"content": " {",
"newLineNumber": 290,
"oldLineNumber": null
},
{
"type": "addition",
"content": " label: \"Inline comment rate\",",
"newLineNumber": 291,
"oldLineNumber": null
},
{
"type": "addition",
"content": " value: \"42%\",",
"newLineNumber": 292,
"oldLineNumber": null
},
{
"type": "addition",
"content": " helper: \"Findings posted inline\",",
"newLineNumber": 293,
"oldLineNumber": null
},
{
"type": "addition",
"content": " tone: \"info\"",
"newLineNumber": 294,
"oldLineNumber": null
},
{
"type": "addition",
"content": " }",
"newLineNumber": 295,
"oldLineNumber": null
},
{
"type": "addition",
"content": " ]",
"newLineNumber": 296,
"oldLineNumber": null
},
{
"type": "addition",
"content": "};",
"newLineNumber": 297,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 298,
"oldLineNumber": null
},
{
"type": "addition",
"content": "const overviewData = buildOverviewDashboardData({",
"newLineNumber": 299,
"oldLineNumber": null
},
{
"type": "addition",
"content": " repositories: repositoryList.repositories,",
"newLineNumber": 300,
"oldLineNumber": null
},
{
"type": "addition",
"content": " reviewRuns,",
"newLineNumber": 301,
"oldLineNumber": null
},
{
"type": "addition",
"content": " supplement,",
"newLineNumber": 302,
"oldLineNumber": null
},
{
"type": "addition",
"content": " now: new Date(\"2026-05-24T12:00:00.000Z\")",
"newLineNumber": 303,
"oldLineNumber": null
},
{
"type": "addition",
"content": "});",
"newLineNumber": 304,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 305,
"oldLineNumber": null
},
{
"type": "addition",
"content": "const connectedOAuth = {",
"newLineNumber": 306,
"oldLineNumber": null
},
{
"type": "addition",
"content": " connected: true,",
"newLineNumber": 307,
"oldLineNumber": null
},
{
"type": "addition",
"content": " user: {",
"newLineNumber": 308,
"oldLineNumber": null
},
{
"type": "addition",
"content": " githubUserId: 42,",
"newLineNumber": 309,
"oldLineNumber": null
},
{
"type": "addition",
"content": " login: \"kelly\",",
"newLineNumber": 310,
"oldLineNumber": null
},
{
"type": "addition",
"content": " name: \"Kelly\",",
"newLineNumber": 311,
"oldLineNumber": null
},
{
"type": "addition",
"content": " avatarUrl: null,",
"newLineNumber": 312,
"oldLineNumber": null
},
{
"type": "addition",
"content": " connectedAt: \"2026-05-22T09:00:00.000Z\",",
"newLineNumber": 313,
"oldLineNumber": null
},
{
"type": "addition",
"content": " updatedAt: \"2026-05-22T09:00:00.000Z\"",
"newLineNumber": 314,
"oldLineNumber": null
},
{
"type": "addition",
"content": " }",
"newLineNumber": 315,
"oldLineNumber": null
},
{
"type": "addition",
"content": "};",
"newLineNumber": 316,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 317,
"oldLineNumber": null
},
{
"type": "addition",
"content": "const externalSettings: WorkspaceSettingsResponse = {",
"newLineNumber": 318,
"oldLineNumber": null
},
{
"type": "addition",
"content": " workspace: {",
"newLineNumber": 319,
"oldLineNumber": null
},
{
"type": "addition",
"content": " id: \"workspace-1\",",
"newLineNumber": 320,
"oldLineNumber": null
},
{
"type": "addition",
"content": " name: \"Firmcode\",",
"newLineNumber": 321,
"oldLineNumber": null
},
{
"type": "addition",
"content": " clerkOrgId: \"org_firmcode\",",
"newLineNumber": 322,
"oldLineNumber": null
},
{
"type": "addition",
"content": " role: \"owner\",",
"newLineNumber": 323,
"oldLineNumber": null
},
{
"type": "addition",
"content": " canManageSensitiveSettings: true",
"newLineNumber": 324,
"oldLineNumber": null
},
{
"type": "addition",
"content": " },",
"newLineNumber": 325,
"oldLineNumber": null
},
{
"type": "addition",
"content": " clerk: {",
"newLineNumber": 326,
"oldLineNumber": null
},
{
"type": "addition",
"content": " userProfileUrl: \"https://accounts.clerk.example/user\",",
"newLineNumber": 327,
"oldLineNumber": null
},
{
"type": "addition",
"content": " organizationProfileUrl: \"https://accounts.clerk.example/organization\",",
"newLineNumber": 328,
"oldLineNumber": null
},
{
"type": "addition",
"content": " memberManagementUrl: \"https://accounts.clerk.example/members\"",
"newLineNumber": 329,
"oldLineNumber": null
},
{
"type": "addition",
"content": " },",
"newLineNumber": 330,
"oldLineNumber": null
},
{
"type": "addition",
"content": " githubApp: {",
"newLineNumber": 331,
"oldLineNumber": null
},
{
"type": "addition",
"content": " installUrl: \"/github/installations\",",
"newLineNumber": 332,
"oldLineNumber": null
},
{
"type": "addition",
"content": " repositoryConfigurationUrl: \"/repositories\",",
"newLineNumber": 333,
"oldLineNumber": null
},
{
"type": "addition",
"content": " installations: [",
"newLineNumber": 334,
"oldLineNumber": null
},
{
"type": "addition",
"content": " {",
"newLineNumber": 335,
"oldLineNumber": null
},
{
"type": "addition",
"content": " id: \"install-1\",",
"newLineNumber": 336,
"oldLineNumber": null
},
{
"type": "addition",
"content": " installationId: 301,",
"newLineNumber": 337,
"oldLineNumber": null
},
{
"type": "addition",
"content": " accountLogin: \"openclaw\",",
"newLineNumber": 338,
"oldLineNumber": null
},
{
"type": "addition",
"content": " accountType: \"Organization\",",
"newLineNumber": 339,
"oldLineNumber": null
},
{
"type": "addition",
"content": " repositoryCount: 1,",
"newLineNumber": 340,
"oldLineNumber": null
},
{
"type": "addition",
"content": " enabledRepositoryCount: 1,",
"newLineNumber": 341,
"oldLineNumber": null
},
{
"type": "addition",
"content": " updatedAt: \"2026-05-22T10:00:00.000Z\"",
"newLineNumber": 342,
"oldLineNumber": null
},
{
"type": "addition",
"content": " }",
"newLineNumber": 343,
"oldLineNumber": null
},
{
"type": "addition",
"content": " ]",
"newLineNumber": 344,
"oldLineNumber": null
},
{
"type": "addition",
"content": " },",
"newLineNumber": 345,
"oldLineNumber": null
},
{
"type": "addition",
"content": " retention: {",
"newLineNumber": 346,
"oldLineNumber": null
},
{
"type": "addition",
"content": " artifactRetentionDays: 30,",
"newLineNumber": 347,
"oldLineNumber": null
},
{
"type": "addition",
"content": " changedFilePatchDays: 30,",
"newLineNumber": 348,
"oldLineNumber": null
},
{
"type": "addition",
"content": " fullSnapshotDays: 14,",
"newLineNumber": 349,
"oldLineNumber": null
},
{
"type": "addition",
"content": " ciLogDays: 14,",
"newLineNumber": 350,
"oldLineNumber": null
},
{
"type": "addition",
"content": " llmArtifactDays: 14,",
"newLineNumber": 351,
"oldLineNumber": null
},
{
"type": "addition",
"content": " semgrepArtifactDays: 30,",
"newLineNumber": 352,
"oldLineNumber": null
},
{
"type": "addition",
"content": " treeSitterArtifactDays: 30,",
"newLineNumber": 353,
"oldLineNumber": null
},
{
"type": "addition",
"content": " findingMetadataDays: 180,",
"newLineNumber": 354,
"oldLineNumber": null
},
{
"type": "addition",
"content": " aggregatedMetricDays: 365",
"newLineNumber": 355,
"oldLineNumber": null
},
{
"type": "addition",
"content": " },",
"newLineNumber": 356,
"oldLineNumber": null
},
{
"type": "addition",
"content": " apiKeys: {",
"newLineNumber": 357,
"oldLineNumber": null
},
{
"type": "addition",
"content": " enabled: false,",
"newLineNumber": 358,
"oldLineNumber": null
},
{
"type": "addition",
"content": " message: \"Workspace API key creation is not enabled in the MVP.\"",
"newLineNumber": 359,
"oldLineNumber": null
},
{
"type": "addition",
"content": " },",
"newLineNumber": 360,
"oldLineNumber": null
},
{
"type": "addition",
"content": " notifications: {",
"newLineNumber": 361,
"oldLineNumber": null
},
{
"type": "addition",
"content": " enabled: false,",
"newLineNumber": 362,
"oldLineNumber": null
},
{
"type": "addition",
"content": " message: \"Email and Slack notification routing is planned after review delivery stabilizes.\"",
"newLineNumber": 363,
"oldLineNumber": null
},
{
"type": "addition",
"content": " }",
"newLineNumber": 364,
"oldLineNumber": null
},
{
"type": "addition",
"content": "};",
"newLineNumber": 365,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 366,
"oldLineNumber": null
},
{
"type": "addition",
"content": "const internalClerkSettings: WorkspaceSettingsResponse = {",
"newLineNumber": 367,
"oldLineNumber": null
},
{
"type": "addition",
"content": " ...externalSettings,",
"newLineNumber": 368,
"oldLineNumber": null
},
{
"type": "addition",
"content": " clerk: {",
"newLineNumber": 369,
"oldLineNumber": null
},
{
"type": "addition",
"content": " userProfileUrl: \"/user-profile\",",
"newLineNumber": 370,
"oldLineNumber": null
},
{
"type": "addition",
"content": " organizationProfileUrl: \"/organization-profile\",",
"newLineNumber": 371,
"oldLineNumber": null
},
{
"type": "addition",
"content": " memberManagementUrl: \"/organization-profile/members\"",
"newLineNumber": 372,
"oldLineNumber": null
},
{
"type": "addition",
"content": " }",
"newLineNumber": 373,
"oldLineNumber": null
},
{
"type": "addition",
"content": "};",
"newLineNumber": 374,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 375,
"oldLineNumber": null
},
{
"type": "addition",
"content": "const billing: WorkspaceBillingResponse = {",
"newLineNumber": 376,
"oldLineNumber": null
},
{
"type": "addition",
"content": " workspace: {",
"newLineNumber": 377,
"oldLineNumber": null
},
{
"type": "addition",
"content": " id: \"workspace-1\",",
"newLineNumber": 378,
"oldLineNumber": null
},
{
"type": "addition",
"content": " role: \"owner\",",
"newLineNumber": 379,
"oldLineNumber": null
},
{
"type": "addition",
"content": " canManageBilling: true,",
"newLineNumber": 380,
"oldLineNumber": null
},
{
"type": "addition",
"content": " source: \"clerk\"",
"newLineNumber": 381,
"oldLineNumber": null
},
{
"type": "addition",
"content": " },",
"newLineNumber": 382,
"oldLineNumber": null
},
{
"type": "addition",
"content": " plan: {",
"newLineNumber": 383,
"oldLineNumber": null
},
{
"type": "addition",
"content": " name: \"Clerk managed\",",
"newLineNumber": 384,
"oldLineNumber": null
},
{
"type": "addition",
"content": " status: \"managed_by_clerk\"",
"newLineNumber": 385,
"oldLineNumber": null
},
{
"type": "addition",
"content": " },",
"newLineNumber": 386,
"oldLineNumber": null
},
{
"type": "addition",
"content": " usage: {",
"newLineNumber": 387,
"oldLineNumber": null
},
{
"type": "addition",
"content": " reviewRunsThisMonth: null,",
"newLineNumber": 388,
"oldLineNumber": null
},
{
"type": "addition",
"content": " aiTokensThisMonth: null,",
"newLineNumber": 389,
"oldLineNumber": null
},
{
"type": "addition",
"content": " repositoriesMonitored: null,",
"newLineNumber": 390,
"oldLineNumber": null
},
{
"type": "addition",
"content": " seats: null",
"newLineNumber": 391,
"oldLineNumber": null
},
{
"type": "addition",
"content": " }",
"newLineNumber": 392,
"oldLineNumber": null
},
{
"type": "addition",
"content": "};",
"newLineNumber": 393,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 394,
"oldLineNumber": null
},
{
"type": "addition",
"content": "const viewerBilling: WorkspaceBillingResponse = {",
"newLineNumber": 395,
"oldLineNumber": null
},
{
"type": "addition",
"content": " ...billing,",
"newLineNumber": 396,
"oldLineNumber": null
},
{
"type": "addition",
"content": " workspace: {",
"newLineNumber": 397,
"oldLineNumber": null
},
{
"type": "addition",
"content": " ...billing.workspace,",
"newLineNumber": 398,
"oldLineNumber": null
},
{
"type": "addition",
"content": " role: \"viewer\",",
"newLineNumber": 399,
"oldLineNumber": null
},
{
"type": "addition",
"content": " canManageBilling: false",
"newLineNumber": 400,
"oldLineNumber": null
},
{
"type": "addition",
"content": " }",
"newLineNumber": 401,
"oldLineNumber": null
},
{
"type": "addition",
"content": "};",
"newLineNumber": 402,
"oldLineNumber": null
},
{
"type": "addition",
"content": "",
"newLineNumber": 403,
"oldLineNumber": null
},
{
"type": "addition",
"content": "function syncData(settings: WorkspaceSettingsResponse, oauthConnected: boolean) {",
"newLineNumber": 404,
"oldLineNumber": null
},
{
"type": "addition",
"content": " return {",
"newLineNumber": 405,
"oldLineNumber": null
},
{
"type": "addition",
"content": " settings,",
"newLineNumber": 406,
"oldLineNumber": null
},
{
"type": "addition",
"content": " oauth: oauthConnected ? connectedOAuth : { connected: false, user: null },",
"newLineNumber": 407,
"oldLineNumber": null
},
{
"type": "addition",
"content": " repositories: repositoryList",
"newLineNumber": 408,
"oldLineNumber": null
},
{
"type": "addition",
"content": " };",
"newLineNumber": 409,
"oldLineNumber": null
},
{
"type": "addition",
"content": "}",
"newLineNumber": 410,
"oldLineNumber": null
}
],
"newStart": 1,
"oldStart": 0,
"newLineCount": 410,
"oldLineCount": 0,
"sectionHeader": ""
}
],
"patch": "@@ -0,0 +1,410 @@\n+import React from \"react\";\n+import { readdirSync } from \"node:fs\";\n+import { relative, join } from \"node:path\";\n+import { fileURLToPath } from \"node:url\";\n+import { renderToString } from \"react-dom/server\";\n+import type {\n+ OverviewSupplementData,\n+ RepositoryListResponse,\n+ ReviewRunListItem,\n+ WorkspaceBillingResponse,\n+ WorkspaceSettingsResponse\n+} from \"@firmcode/shared\";\n+import { BillingView } from \"../components/dashboard/billing-view\";\n+import { DashboardShell } from \"../components/dashboard/dashboard-shell\";\n+import { GitHubInstallationsView } from \"../components/dashboard/github-installations-view\";\n+import { OverviewView } from \"../components/dashboard/overview-view\";\n+import { RepositoriesView } from \"../components/dashboard/repositories-view\";\n+import { SettingsView } from \"../components/dashboard/settings-view\";\n+import { DASHBOARD_ROUTE_ACTIONS } from \"../lib/dashboard-action-manifest\";\n+import { buildOverviewDashboardData } from \"../lib/overview-data\";\n+import {\n+ DASHBOARD_IMPLEMENTED_ROUTE_PATTERNS,\n+ classifyDashboardDestination,\n+ isAllowedExternalDashboardUrl,\n+ isImplementedDashboardRoute\n+} from \"../lib/dashboard-route-readiness\";\n+\n+describe(\"dashboard route readiness guard\", () => {\n+ it(\"keeps the implemented route manifest in sync with the Next app route tree\", () => {\n+ const actualRoutes = discoverNextRoutes();\n+\n+ for (const route of DASHBOARD_IMPLEMENTED_ROUTE_PATTERNS) {\n+ expect(actualRoutes).toContain(route.pattern);\n+ }\n+ });\n+\n+ it(\"requires every active internal navigation or action definition to target an implemented route\", () => {\n+ const activeInternalActions = DASHBOARD_ROUTE_ACTIONS.filter(\n+ (action) => action.status === \"active\" && action.destination === \"internal\"\n+ );\n+\n+ expect(activeInternalActions.length).toBeGreaterThan(0);\n+\n+ for (const action of activeInternalActions) {\n+ expect(action.href, `${action.surface}:${action.label}`).toBeDefined();\n+ expect(isImplementedDashboardRoute(action.href!), `${action.surface}:${action.label} -> ${action.href}`).toBe(true);\n+ }\n+ });\n+\n+ it(\"keeps external Clerk and GitHub actions explicit and validates them separately\", () => {\n+ const externalActions = DASHBOARD_ROUTE_ACTIONS.filter((action) => action.destination === \"external\");\n+\n+ expect(externalActions.length).toBeGreaterThan(0);\n+\n+ for (const action of externalActions) {\n+ expect(action.provider, `${action.surface}:${action.label}`).toBeDefined();\n+ expect(action.href, `${action.surface}:${action.label}`).toBeDefined();\n+ expect(\n+ isAllowedExternalDashboardUrl(action.href!, action.provider!),\n+ `${action.surface}:${action.label} -> ${action.href}`\n+ ).toBe(true);\n+ }\n+ });\n+\n+ it(\"requires planned dashboard actions to stay disabled and accessible\", () => {\n+ const plannedActions = DASHBOARD_ROUTE_ACTIONS.filter((action) => action.status === \"planned-disabled\");\n+\n+ expect(plannedActions.length).toBeGreaterThan(0);\n+\n+ for (const action of plannedActions) {\n+ expect(action.href, `${action.surface}:${action.label}`).toBeUndefined();\n+ expect(action.title, `${action.surface}:${action.label}`).toEqual(expect.any(String));\n+ }\n+ });\n+\n+ it(\"validates rendered sidebar, topbar, overview, repository, settings, billing, and GitHub setup links\", () => {\n+ const html = [\n+ renderToString(\n+ <DashboardShell activeItem=\"Overview\">\n+ <main>Dashboard body</main>\n+ </DashboardShell>\n+ ),\n+ renderToString(<OverviewView state={{ status: \"populated\", data: overviewData }} />),\n+ renderToString(\n+ <RepositoriesView\n+ state={{ status: \"populated\", data: repositoryList }}\n+ controlsState={{\n+ status: \"ready\",\n+ data: {\n+ oauth: connectedOAuth,\n+ settings: externalSettings\n+ }\n+ }}\n+ />\n+ ),\n+ renderToString(<SettingsView state={{ status: \"populated\", data: externalSettings }} activeTab=\"general\" />),\n+ renderToString(<SettingsView state={{ status: \"populated\", data: externalSettings }} activeTab=\"github-app\" />),\n+ renderToString(<SettingsView state={{ status: \"populated\", data: externalSettings }} activeTab=\"members\" />),\n+ renderToString(\n+ <BillingView\n+ state={{ status: \"populated\", data: billing }}\n+ billingPortalUrl=\"https://accounts.clerk.example/billing\"\n+ />\n+ ),\n+ renderToString(\n+ <GitHubInstallationsView\n+ state={{ status: \"populated\", data: syncData(externalSettings, true) }}\n+ installConfig={{\n+ status: \"configured\",\n+ installUrl: \"https://github.com/apps/firmcode/installations/new\",\n+ source: \"GITHUB_APP_INSTALL_URL\"\n+ }}\n+ />\n+ )\n+ ].join(\"\\n\");\n+\n+ for (const formAction of extractFormActions(html)) {\n+ expect(isImplementedDashboardRoute(formAction), `form action -> ${formAction}`).toBe(true);\n+ }\n+\n+ for (const anchor of extractAnchors(html)) {\n+ const destination = classifyDashboardDestination(anchor.href);\n+\n+ if (destination.kind === \"internal\") {\n+ expect(destination.routeReady, `${anchor.href} is not implemented`).toBe(true);\n+ expect(anchor.tag).not.toContain('data-dashboard-destination=\"external\"');\n+ } else {\n+ expect(anchor.tag, `${anchor.href} must be explicitly marked external`).toContain('data-dashboard-destination=\"external\"');\n+ expect(anchor.tag, `${anchor.href} must identify Clerk or GitHub`).toMatch(/data-dashboard-provider=\"(clerk|github)\"/);\n+ }\n+ }\n+ });\n+\n+ it(\"disables planned Clerk settings actions when Clerk URLs are not external route-ready destinations\", () => {\n+ const generalHtml = renderToString(<SettingsView state={{ status: \"populated\", data: internalClerkSettings }} activeTab=\"general\" />);\n+ const membersHtml = renderToString(<SettingsView state={{ status: \"populated\", data: internalClerkSettings }} activeTab=\"members\" />);\n+\n+ expect(generalHtml).not.toContain('href=\"/user-profile\"');\n+ expect(generalHtml).not.toContain('href=\"/organization-profile\"');\n+ expect(generalHtml).toContain(\"Open Clerk profile is planned until its internal destination is route-ready.\");\n+ expect(generalHtml).toContain(\"Open Clerk organization is planned until its internal destination is route-ready.\");\n+ expect(membersHtml).not.toContain('href=\"/organization-profile/members\"');\n+ expect(membersHtml).toContain(\"Open Clerk members is planned until its internal destination is route-ready.\");\n+ });\n+\n+ it(\"keeps disabled planned controls rendered as disabled buttons with titles\", () => {\n+ const settingsHtml = renderToString(<SettingsView state={{ status: \"populated\", data: externalSettings }} activeTab=\"api-keys\" />);\n+ const githubHtml = renderToString(\n+ <GitHubInstallationsView\n+ state={{ status: \"populated\", data: syncData(externalSettings, true) }}\n+ installConfig={{\n+ status: \"configured\",\n+ installUrl: \"https://github.com/apps/firmcode/installations/new\",\n+ source: \"GITHUB_APP_INSTALL_URL\"\n+ }}\n+ />\n+ );\n+ const billingHtml = renderToString(<BillingView state={{ status: \"populated\", data: viewerBilling }} billingPortalUrl={null} />);\n+\n+ expect(settingsHtml).toContain(\"Workspace API keys are planned and not enabled in the MVP.\");\n+ expect(settingsHtml).toContain(\"disabled=\\\"\\\"\");\n+ expect(githubHtml).toContain(\"Repository detail configuration is planned\");\n+ expect(githubHtml).toContain(\"Manual review runs are planned\");\n+ expect(billingHtml).toContain(\"Owner billing permission is required to manage subscriptions.\");\n+ expect(billingHtml).toContain(\"disabled=\\\"\\\"\");\n+ });\n+});\n+\n+function extractAnchors(html: string): Array<{ href: string; tag: string }> {\n+ return [...html.matchAll(/<a\\b[^>]*href=\"([^\"]+)\"[^>]*>/g)].map((match) => ({\n+ href: decodeHtmlAttribute(match[1]),\n+ tag: match[0]\n+ }));\n+}\n+\n+function extractFormActions(html: string): string[] {\n+ return [...html.matchAll(/<form\\b[^>]*action=\"([^\"]+)\"[^>]*>/g)].map((match) => decodeHtmlAttribute(match[1]));\n+}\n+\n+function decodeHtmlAttribute(value: string): string {\n+ return value.replaceAll(\"&\", \"&\");\n+}\n+\n+function discoverNextRoutes(): string[] {\n+ const appDir = fileURLToPath(new URL(\"../app\", import.meta.url));\n+ const routes: string[] = [];\n+\n+ walk(appDir);\n+\n+ return routes.sort();\n+\n+ function walk(directory: string): void {\n+ for (const entry of readdirSync(directory, { withFileTypes: true })) {\n+ const fullPath = join(directory, entry.name);\n+\n+ if (entry.isDirectory()) {\n+ walk(fullPath);\n+ continue;\n+ }\n+\n+ if (entry.name !== \"page.tsx\" && entry.name !== \"route.ts\") {\n+ continue;\n+ }\n+\n+ const routeDirectory = relative(appDir, directory);\n+ routes.push(routeDirectory === \"\" ? \"/\" : `/${routeDirectory}`);\n+ }\n+ }\n+}\n+\n+const repositoryList: RepositoryListResponse = {\n+ filters: {},\n+ repositories: [\n+ {\n+ id: \"repo-1\",\n+ owner: \"openclaw\",\n+ name: \"firmcode\",\n+ fullName: \"openclaw/firmcode\",\n+ private: false,\n+ defaultBranch: \"main\",\n+ enabled: true,\n+ primaryLanguage: \"TypeScript\",\n+ openFindingsCount: 2,\n+ updatedAt: \"2026-05-22T10:00:00.000Z\",\n+ lastReview: {\n+ reviewRunId: \"run-1\",\n+ pullRequestNumber: 7,\n+ pullRequestTitle: \"Add route readiness\",\n+ status: \"succeeded\",\n+ headSha: \"abc123def456\",\n+ createdAt: \"2026-05-22T10:00:00.000Z\",\n+ finishedAt: \"2026-05-22T10:02:00.000Z\"\n+ }\n+ }\n+ ]\n+};\n+\n+const reviewRuns: ReviewRunListItem[] = [\n+ {\n+ id: \"run-1\",\n+ repositoryId: \"repo-1\",\n+ pullRequestId: \"pr-1\",\n+ repositoryFullName: \"openclaw/firmcode\",\n+ pullRequestNumber: 7,\n+ pullRequestTitle: \"Add route readiness\",\n+ pullRequestAuthor: \"kelly\",\n+ triggerEvent: \"pull_request.opened\",\n+ currentStage: \"Comments Published\",\n+ durationMs: 120000,\n+ commentsPostedCount: 2,\n+ filesAnalyzedCount: 4,\n+ riskLevel: \"high\",\n+ headSha: \"abc123def456\",\n+ status: \"failed\",\n+ findingsCount: 4,\n+ startedAt: \"2026-05-22T10:00:00.000Z\",\n+ finishedAt: null,\n+ createdAt: \"2026-05-22T10:00:00.000Z\",\n+ updatedAt: \"2026-05-22T10:02:00.000Z\"\n+ }\n+];\n+\n+const supplement: OverviewSupplementData = {\n+ securityFindingsCount: 5,\n+ ciFailuresExplainedCount: 2,\n+ highSeverityFindings: [\n+ {\n+ id: \"finding-1\",\n+ kind: \"high_severity_finding\",\n+ title: \"High severity finding\",\n+ detail: \"openclaw/firmcode has an authentication-path finding awaiting triage.\",\n+ href: \"/findings?severity=high\",\n+ severity: \"high\",\n+ updatedAt: \"2026-05-22T09:30:00.000Z\"\n+ }\n+ ],\n+ ciFailures: [\n+ {\n+ id: \"ci-1\",\n+ kind: \"ci_failure\",\n+ title: \"CI failure\",\n+ detail: \"firmcode dashboard tests need a failure explanation review.\",\n+ href: \"/ci-failures\",\n+ severity: \"medium\",\n+ updatedAt: \"2026-05-22T08:45:00.000Z\"\n+ }\n+ ],\n+ incompleteRepositoryConfigurationRepositoryIds: [\"repo-1\"],\n+ qualityMetrics: [\n+ {\n+ label: \"Inline comment rate\",\n+ value: \"42%\",\n+ helper: \"Findings posted inline\",\n+ tone: \"info\"\n+ }\n+ ]\n+};\n+\n+const overviewData = buildOverviewDashboardData({\n+ repositories: repositoryList.repositories,\n+ reviewRuns,\n+ supplement,\n+ now: new Date(\"2026-05-24T12:00:00.000Z\")\n+});\n+\n+const connectedOAuth = {\n+ connected: true,\n+ user: {\n+ githubUserId: 42,\n+ login: \"kelly\",\n+ name: \"Kelly\",\n+ avatarUrl: null,\n+ connectedAt: \"2026-05-22T09:00:00.000Z\",\n+ updatedAt: \"2026-05-22T09:00:00.000Z\"\n+ }\n+};\n+\n+const externalSettings: WorkspaceSettingsResponse = {\n+ workspace: {\n+ id: \"workspace-1\",\n+ name: \"Firmcode\",\n+ clerkOrgId: \"org_firmcode\",\n+ role: \"owner\",\n+ canManageSensitiveSettings: true\n+ },\n+ clerk: {\n+ userProfileUrl: \"https://accounts.clerk.example/user\",\n+ organizationProfileUrl: \"https://accounts.clerk.example/organization\",\n+ memberManagementUrl: \"https://accounts.clerk.example/members\"\n+ },\n+ githubApp: {\n+ installUrl: \"/github/installations\",\n+ repositoryConfigurationUrl: \"/repositories\",\n+ installations: [\n+ {\n+ id: \"install-1\",\n+ installationId: 301,\n+ accountLogin: \"openclaw\",\n+ accountType: \"Organization\",\n+ repositoryCount: 1,\n+ enabledRepositoryCount: 1,\n+ updatedAt: \"2026-05-22T10:00:00.000Z\"\n+ }\n+ ]\n+ },\n+ retention: {\n+ artifactRetentionDays: 30,\n+ changedFilePatchDays: 30,\n+ fullSnapshotDays: 14,\n+ ciLogDays: 14,\n+ llmArtifactDays: 14,\n+ semgrepArtifactDays: 30,\n+ treeSitterArtifactDays: 30,\n+ findingMetadataDays: 180,\n+ aggregatedMetricDays: 365\n+ },\n+ apiKeys: {\n+ enabled: false,\n+ message: \"Workspace API key creation is not enabled in the MVP.\"\n+ },\n+ notifications: {\n+ enabled: false,\n+ message: \"Email and Slack notification routing is planned after review delivery stabilizes.\"\n+ }\n+};\n+\n+const internalClerkSettings: WorkspaceSettingsResponse = {\n+ ...externalSettings,\n+ clerk: {\n+ userProfileUrl: \"/user-profile\",\n+ organizationProfileUrl: \"/organization-profile\",\n+ memberManagementUrl: \"/organization-profile/members\"\n+ }\n+};\n+\n+const billing: WorkspaceBillingResponse = {\n+ workspace: {\n+ id: \"workspace-1\",\n+ role: \"owner\",\n+ canManageBilling: true,\n+ source: \"clerk\"\n+ },\n+ plan: {\n+ name: \"Clerk managed\",\n+ status: \"managed_by_clerk\"\n+ },\n+ usage: {\n+ reviewRunsThisMonth: null,\n+ aiTokensThisMonth: null,\n+ repositoriesMonitored: null,\n+ seats: null\n+ }\n+};\n+\n+const viewerBilling: WorkspaceBillingResponse = {\n+ ...billing,\n+ workspace: {\n+ ...billing.workspace,\n+ role: \"viewer\",\n+ canManageBilling: false\n+ }\n+};\n+\n+function syncData(settings: WorkspaceSettingsResponse, oauthConnected: boolean) {\n+ return {\n+ settings,\n+ oauth: oauthConnected ? connectedOAuth : { connected: false, user: null },\n+ repositories: repositoryList\n+ };\n+}",
"status": "added",
"language": "typescript",
"additions": 410,
"deletions": 0,
"sizeBytes": 13909,
"previousPath": null,
"changedNewLines": [
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26,
27,
28,
29,
30,
31,
32,
33,
34,
35,
36,
37,
38,
39,
40,
41,
42,
43,
44,
45,
46,
47,
48,
49,
50,
51,
52,
53,
54,
55,
56,
57,
58,
59,
60,
61,
62,
63,
64,
65,
66,
67,
68,
69,
70,
71,
72,
73,
74,
75,
76,
77,
78,
79,
80,
81,
82,
83,
84,
85,
86,
87,
88,
89,
90,
91,
92,
93,
94,
95,
96,
97,
98,
99,
100,
101,
102,
103,
104,
105,
106,
107,
108,
109,
110,
111,
112,
113,
114,
115,
116,
117,
118,
119,
120,
121,
122,
123,
124,
125,
126,
127,
128,
129,
130,
131,
132,
133,
134,
135,
136,
137,
138,
139,
140,
141,
142,
143,
144,
145,
146,
147,
148,
149,
150,
151,
152,
153,
154,
155,
156,
157,
158,
159,
160,
161,
162,
163,
164,
165,
166,
167,
168,
169,
170,
171,
172,
173,
174,
175,
176,
177,
178,
179,
180,
181,
182,
183,
184,
185,
186,
187,
188,
189,
190,
191,
192,
193,
194,
195,
196,
197,
198,
199,
200,
201,
202,
203,
204,
205,
206,
207,
208,
209,
210,
211,
212,
213,
214,
215,
216,
217,
218,
219,
220,
221,
222,
223,
224,
225,
226,
227,
228,
229,
230,
231,
232,
233,
234,
235,
236,
237,
238,
239,
240,
241,
242,
243,
244,
245,
246,
247,
248,
249,
250,
251,
252,
253,
254,
255,
256,
257,
258,
259,
260,
261,
262,
263,
264,
265,
266,
267,
268,
269,
270,
271,
272,
273,
274,
275,
276,
277,
278,
279,
280,
281,
282,
283,
284,
285,
286,
287,
288,
289,
290,
291,
292,
293,
294,
295,
296,
297,
298,
299,
300,
301,
302,
303,
304,
305,
306,
307,
308,
309,
310,
311,
312,
313,
314,
315,
316,
317,
318,
319,
320,
321,
322,
323,
324,
325,
326,
327,
328,
329,
330,
331,
332,
333,
334,
335,
336,
337,
338,
339,
340,
341,
342,
343,
344,
345,
346,
347,
348,
349,
350,
351,
352,
353,
354,
355,
356,
357,
358,
359,
360,
361,
362,
363,
364,
365,
366,
367,
368,
369,
370,
371,
372,
373,
374,
375,
376,
377,
378,
379,
380,
381,
382,
383,
384,
385,
386,
387,
388,
389,
390,
391,
392,
393,
394,
395,
396,
397,
398,
399,
400,
401,
402,
403,
404,
405,
406,
407,
408,
409,
410
],
"headContentSha256": "3051bb5495594f7e2b0b6c0d1a92a1b397ae0ab38d69d60cc0d8cde548294e4e"
}
],
"baseSha": "af83772e38a722375fb0c5c51c533de6e7d85676",
"headSha": "aa2ecc89066ce95b10973e4fa10012fb47b60fe6",
"reviewRunId": "51c17aba-9971-4ea8-9761-afb651f9b8a4",
"skippedFiles": [],
"schemaVersion": "diff-artifact/v1",
"pullRequestNumber": 84,
"repositoryFullName": "firmcloudapps/firmcode-tester"
}
}