Skip to content

refactor(dashboard): use shared url constructors for deploy routes#6393

Merged
ogzhanolguncu merged 15 commits into
mainfrom
ssot-deploy-links
Jun 12, 2026
Merged

refactor(dashboard): use shared url constructors for deploy routes#6393
ogzhanolguncu merged 15 commits into
mainfrom
ssot-deploy-links

Conversation

@ogzhanolguncu

@ogzhanolguncu ogzhanolguncu commented Jun 9, 2026

Copy link
Copy Markdown
Collaborator

NOTE

This PR enables type-safe routes so we can detect wrong routes during the build phase. This buys us type safety for router.push, redirect, etc. The downside is that it's not super smart by default, so it thinks some routes don't exist. There is a way to fix that, and we already do proper typechecking in routes/projects.ts. In the future, every URL in the codebase will adopt that structure, but for now we just assert with as Route to overcome it. Nothing changed behaviorally; we're just making triangle.js happy.

What we actually gained from this:
https://github.com/user-attachments/assets/b9c552c8-2d08-442b-b951-dc8fda6056ad

Motivation

This is the continuation of #6389. And this will make deploy routes type safe and robust

How to Test

  • mise exec -- pnpm --dir=web/apps/dashboard vitest run lib/navigation/routes => Should pass

Onboarding

  • Create a project → lands on the project.
  • App-creation wizard steps advance correctly (URL carries ?step= / ?appId=).
  • First deployment goes live and is reachable.

Project sidebar

project sidebar
  • Overview → /{ws}/projects/{projectId}
  • Deployments / Logs / Requests / Settings each load.

App sidebar

app sidebar
  • Each item → /{ws}/projects/{projectId}/apps/{appId}/... (deployments, settings, etc).

Top nav breadcrumbs

top nav
  • Projects → project → app crumbs each navigate to the right level.

Deployment links

  • Deployment list row → /{ws}/projects/{projectId}/apps/{appId}/deployments/{deploymentId}
  • Build / progress / "view build" links carry ?build=true.
  • Redeploy dialog, pending-redeploy banner, deployment-id-link → correct deployment.
  • Approval screen "cancel" (the bug this PR fixes) → app deployments list, not the old project-level /deployments (was 404ing).

Query-param links (highest risk — confirm the query survives)

  • Request detail → deployment: ?deploymentId=contains:{deploymentId}
request details
  • Project logs filtered by app: ?appId={appId}
  • Project requests: ?since= / ?appId=
  • OpenAPI diff: ?from={x}&to={y}

Lists

resource card
  • resource-card.tsx → project list and app list both navigate.

GitHub OAuth callback (touched, easy to miss)

  • After installing the GitHub app, the post-install redirect lands correctly on app setings, not a blank or wrong page.

@vercel

vercel Bot commented Jun 9, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
dashboard Ready Ready Preview, Comment Jun 12, 2026 3:59pm
design Ready Ready Preview, Comment Jun 12, 2026 3:59pm

Request Review

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ℹ️ No critical issues — one minor operator-label inconsistency inline.

Reviewed changes — a dashboard-wide refactor that routes deploy (and many adjacent) hrefs through a new typed route-builder layer and enables Next typedRoutes.

  • Add buildRoute + routes.projects.* builderslib/navigation/routes/{shared,projects,index}.ts substitute [segment] brackets from Next's generated ParamMap, validate the pattern against the AppRoutes literal union (typo'd/removed routes fail to compile), and append queries via withQuery.
  • Make withQuery genericlib/navigation/url.ts preserves the base's template-literal type so builder outputs stay typed.
  • Enable typedRoutesnext.config.js flips typedRoutes: true; package.json typecheck becomes next typegen && tsc.
  • Swap ~50 call sites — sidebar/leaves, breadcrumbs, crumbs, deployment rows/dialogs/banners, OAuth + Stripe + auth redirects move from hand-built template-literal urls to routes.* calls, with as Route casts added at sinks that accept arbitrary strings (behaviorally no-ops).

Behavior is preserved across the call-site swaps I traced. The one thing worth a glance is the logs deployment-filter operator flipping from is to contains (inline) — confirmed cosmetic, not a results regression.

Pullfrog  | Fix all ➔Fix 👍s ➔View workflow run | Using Claude Opus𝕏

@mcstepp mcstepp left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

future suggestion: lib/navigation/types.ts keeps href: string even though leaves.ts now returns typed Route values. tightening this to Route (or a union) would propagate type safety into the sidebar without needing as Route casts in nav-row.tsx.

@ogzhanolguncu

Copy link
Copy Markdown
Collaborator Author

future suggestion: lib/navigation/types.ts keeps href: string even though leaves.ts now returns typed Route values. tightening this to Route (or a union) would propagate type safety into the sidebar without needing as Route casts in nav-row.tsx.

yep i agree

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found.

Reviewed changes — the delta since the prior pullfrog review (ae5d7d0): a fix for the logs deployment-filter operator the last review flagged, plus a merge of main.

  • Unify the deployment-filter operator on is:containsFilter is renamed to isFilter in lib/navigation/routes/projects.ts, so both routes.projects.logs and routes.projects.requests now emit deploymentId=is:${id}. This restores the logs link to its pre-PR is: and aligns the requests link with the requests/logs deployment-filter UI, which itself emits operator: "is". The change is functionally inert — use-sentinel-logs-query.ts:50-52 and the runtime-logs query both discard the operator and match the id exactly — so only the filter-chip label is affected, and it now matches what manual UI filtering produces.
  • Drop the inert appId param from the logs table actiondeployment-list-table-action.popover.constants.tsx no longer forwards appId on the runtime-logs link (the page never parsed it).
  • Update projects.test.ts — the deployment-id assertion now expects is: for requests.
  • Merge mainb8dbffacc pulls in an unrelated openapi_specs unique-index migration and an audit-filter button style tweak; both sit outside this PR's refactor scope.

Prior review feedback is addressed cleanly and the merge introduced no conflicts in the touched route files.

Pullfrog  | View workflow run | Using Claude Opus𝕏

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found.

Reviewed changes — the delta since the prior pullfrog review (2730839): the final commit 9b122e5 swaps the last batch of hand-rolled URLs to the routes.* builders and adds one UX hardening to the create-project flow.

  • Swap remaining hand-rolled URLs to routes.*delete-project.tsx, delete-app.tsx, project-actions.tsx (3 links), project-card.tsx, and the custom-domains/add.ts CONFLICT error message all move from template-literal paths to typed builders. Each maps 1:1 to the prior string.
  • Harden the workspace lookup in delete dialogsdelete-project.tsx / delete-app.tsx switch from useWorkspace() + workspace?.slug to useWorkspaceNavigation() + workspace.slug; the new hook suspends/redirects on a null workspace, so this also removes a latent /undefined/... path during loading.
  • Keep the create-project dialog up during navigationcreate-project-dialog.tsx adds useTransition, drops the immediate onOpenChange(false), navigates inside startNavigation, and blocks dialog-close while submitting or navigating so the redirect isn't orphaned and the user isn't left on the old page.

The route swaps are behaviorally inert (or a small improvement in the delete dialogs), and the create-project dialog change is correct and intentional — the component unmounts once the new route mounts. The prior is:/contains: operator thread was already resolved and acknowledged.

Pullfrog  | View workflow run | Using Claude Opus𝕏

@ogzhanolguncu ogzhanolguncu added this pull request to the merge queue Jun 12, 2026
Merged via the queue into main with commit f44cb43 Jun 12, 2026
15 checks passed
@ogzhanolguncu ogzhanolguncu deleted the ssot-deploy-links branch June 12, 2026 16:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants