From 889adb09b34b70c871a39fab4e8da4d4a2966f90 Mon Sep 17 00:00:00 2001 From: Anthony Ettinger Date: Mon, 17 Aug 2026 06:40:10 +0000 Subject: [PATCH] feat(routing): move the signed-in app under /dashboard, add a public /ads page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The top level was doing two jobs: /pricing and /about were public, while /projects, /settings and /ads were the signed-in app. That left no room for a marketing page on any path the app had already claimed — /ads in particular, which is the one product here with two sides to sell and nowhere to sell them. Split it: every authenticated resource now lives under /dashboard/**, and the top level belongs entirely to public pages. - app/(app)/* moves wholesale into app/(app)/dashboard/*. The auth gate is the (app) layout, which still wraps all of it, so nothing changes about who can reach what. - next.config redirects every old top-level path to its /dashboard equivalent, so bookmarks, links in already-sent report emails and the URLs in past invoices keep working. 307 rather than 308: none of these were ever indexed (they sat behind a login), so there is no SEO to preserve, and a temporary redirect stays reversible instead of being cached in browsers forever. - /ads is the exception at both ends. The bare path is now the public marketing page, and /ads/house/* is artwork in public/ — redirects run before the filesystem, so a blanket /ads/:path* would have 404'd every house creative. The redirect excludes it explicitly. - The proxy's auth check drops the now-dead /projects, /audits and /settings prefixes. /dashboard is the whole contract. The marketing page sells both sides of the network, and every figure on it is imported from lib/ads/pricing rather than typed in — the publisher's per-click number comes from creditsToPayoutCents, the same function ad_charge_click() mirrors, so what a publisher reads is what actually accrues. Verified against a production build: /ads renders publicly, /ads/house artwork still serves 200, every legacy prefix 307s to its new home, and /dashboard/** 302s signed-out visitors to /login with the new path in ?redirect. --- .../email-broadcast/EmailBroadcastForm.tsx | 0 .../admin/email-broadcast/page.tsx | 2 +- app/(app)/{ => dashboard}/admin/form.tsx | 0 .../admin/github/setup/done/client.tsx | 2 +- .../admin/github/setup/done/page.tsx | 4 +- .../admin/github/setup/page.tsx | 2 +- .../admin/integrations-form.tsx | 0 .../{ => dashboard}/admin/leads/page.tsx | 2 +- app/(app)/{ => dashboard}/admin/page.tsx | 6 +- .../admin/provider-costs-panel.tsx | 0 .../ads/[id]/edit/edit-form.tsx | 0 .../{ => dashboard}/ads/[id]/edit/page.tsx | 2 +- app/(app)/{ => dashboard}/ads/[id]/page.tsx | 4 +- .../{ => dashboard}/ads/earnings/page.tsx | 6 +- app/(app)/{ => dashboard}/ads/new/form.tsx | 2 +- app/(app)/{ => dashboard}/ads/new/page.tsx | 4 +- app/(app)/{ => dashboard}/ads/page.tsx | 14 +- app/(app)/{ => dashboard}/ads/slots/page.tsx | 8 +- app/(app)/{ => dashboard}/analytics/page.tsx | 6 +- .../{ => dashboard}/audits/[id]/page.tsx | 2 +- app/(app)/{ => dashboard}/autoblog/page.tsx | 4 +- .../{ => dashboard}/github/dashboard/page.tsx | 2 +- app/(app)/dashboard/page.tsx | 8 +- .../projects/[id]/autoblog/actions.tsx | 0 .../autoblog/articles/[articleId]/page.tsx | 2 +- .../articles/[articleId]/publish-button.tsx | 0 .../projects/[id]/autoblog/auto-refresh.tsx | 0 .../[id]/autoblog/backlinks/chart.tsx | 0 .../projects/[id]/autoblog/backlinks/page.tsx | 10 +- .../projects/[id]/autoblog/countdown.tsx | 0 .../[id]/autoblog/delete-autoblog-button.tsx | 0 .../projects/[id]/autoblog/guest-posts.tsx | 2 +- .../projects/[id]/autoblog/history/page.tsx | 12 +- .../projects/[id]/autoblog/page.tsx | 14 +- .../projects/[id]/autoblog/plan/actions.ts | 4 +- .../projects/[id]/autoblog/plan/page.tsx | 10 +- .../projects/[id]/autoblog/setup/form.tsx | 2 +- .../projects/[id]/autoblog/setup/page.tsx | 6 +- .../projects/[id]/delete-project-button.tsx | 0 .../projects/[id]/edit-url-form.tsx | 0 .../projects/[id]/getting-started/page.tsx | 10 +- .../projects/[id]/layout-client.tsx | 0 .../{ => dashboard}/projects/[id]/layout.tsx | 0 .../projects/[id]/leads/page.tsx | 0 .../projects/[id]/members/members-client.tsx | 0 .../projects/[id]/members/page.tsx | 0 .../{ => dashboard}/projects/[id]/page.tsx | 8 +- .../projects/[id]/performance/page.tsx | 0 .../projects/[id]/repos/add-repo-modal.tsx | 0 .../projects/[id]/repos/page.tsx | 4 +- .../projects/[id]/repos/remove-button.tsx | 0 .../projects/[id]/runs/[runId]/page.tsx | 2 +- .../projects/[id]/scans/page.tsx | 4 +- .../projects/[id]/security/page.tsx | 0 .../[id]/security/request-scan-button.tsx | 2 +- .../projects/[id]/security/stream/route.ts | 0 .../projects/[id]/settings/page.tsx | 0 .../[id]/social/api-tokens/client.tsx | 0 .../projects/[id]/social/api-tokens/page.tsx | 2 +- .../projects/[id]/social/auto-refresh.tsx | 0 .../projects/[id]/social/feed-settings.tsx | 0 .../projects/[id]/social/page.tsx | 4 +- .../projects/[id]/social/post-now.tsx | 0 .../[id]/social/retry-feed-item-button.tsx | 0 .../[id]/social/retry-post-button.tsx | 0 .../[id]/social/setup/app-password-reveal.tsx | 0 .../projects/[id]/social/setup/form.tsx | 0 .../projects/[id]/social/setup/page.tsx | 4 +- .../projects/[id]/social/social-profile.tsx | 0 .../projects/[id]/stats/auto-install.tsx | 2 +- .../projects/[id]/stats/careers-toggle.tsx | 0 .../[id]/stats/careers/applications.tsx | 0 .../[id]/stats/careers/careers-install.tsx | 0 .../[id]/stats/careers/job-manager.tsx | 0 .../projects/[id]/stats/careers/page.tsx | 2 +- .../projects/[id]/stats/install-snippet.tsx | 0 .../stats/integrations/integration-form.tsx | 0 .../projects/[id]/stats/integrations/page.tsx | 0 .../projects/[id]/stats/live-chart.tsx | 0 .../projects/[id]/stats/live-visitors.tsx | 0 .../projects/[id]/stats/page.tsx | 4 +- .../projects/[id]/stats/stats-subnav.tsx | 6 +- .../projects/[id]/stats/tracker-toggle.tsx | 0 .../projects/[id]/stats/visitor-globe.tsx | 0 .../projects/[id]/uptime/add-monitor-form.tsx | 0 .../projects/[id]/uptime/monitor-actions.tsx | 0 .../projects/[id]/uptime/page.tsx | 0 .../{ => dashboard}/projects/new/form.tsx | 2 +- .../{ => dashboard}/projects/new/page.tsx | 0 .../{ => dashboard}/promote/[id]/page.tsx | 4 +- .../{ => dashboard}/promote/accounts/page.tsx | 4 +- .../{ => dashboard}/promote/new/page.tsx | 0 app/(app)/{ => dashboard}/promote/page.tsx | 12 +- .../settings/billing/buy-credits-button.tsx | 0 .../{ => dashboard}/settings/billing/page.tsx | 2 +- .../billing/purchase-status-banner.tsx | 0 app/(app)/{ => dashboard}/settings/form.tsx | 0 .../integrations/github/bind-button.tsx | 0 .../settings/integrations/github/page.tsx | 2 +- .../integrations/github/repos-filter.tsx | 0 app/(app)/{ => dashboard}/settings/page.tsx | 4 +- app/(app)/{ => dashboard}/social/page.tsx | 4 +- .../{ => dashboard}/social/setup/page.tsx | 4 +- app/(app)/layout.tsx | 16 +- app/(app)/site-picker.tsx | 4 +- app/(marketing)/ads/page.tsx | 277 ++++++++++++++++++ .../docs/autoblog-webhook/page.tsx | 4 +- app/(marketing)/page.tsx | 4 +- app/(marketing)/pricing/page.tsx | 2 +- app/(marketing)/recent/outreach-form.tsx | 2 +- app/actions/abortScanRun.ts | 4 +- app/actions/admin.ts | 4 +- app/actions/ads.ts | 26 +- app/actions/auditOps.ts | 8 +- app/actions/careers.ts | 6 +- app/actions/coinpay.ts | 4 +- app/actions/currentSite.ts | 2 +- app/actions/integrations.ts | 4 +- app/actions/leads.ts | 2 +- app/actions/linkExchange.ts | 30 +- app/actions/mailbox.ts | 4 +- app/actions/monitors.ts | 6 +- app/actions/orgs.ts | 2 +- app/actions/portScans.ts | 2 +- app/actions/project-members.ts | 8 +- app/actions/projects.ts | 8 +- app/actions/promote.ts | 22 +- app/actions/seedCredentials.ts | 6 +- app/actions/socialPosting.ts | 32 +- app/actions/tracker-integrations.ts | 4 +- app/api/credits/create-invoice/route.ts | 4 +- app/api/github/callback/route.ts | 2 +- app/api/github/setup-callback/route.ts | 6 +- app/api/sp/oauth/facebook/callback/route.ts | 4 +- app/api/sp/oauth/linkedin/callback/route.ts | 4 +- app/api/sp/oauth/mastodon/callback/route.ts | 4 +- app/api/sp/oauth/mastodon/start/route.ts | 2 +- app/api/sp/oauth/reddit/callback/route.ts | 4 +- app/api/sp/oauth/threads/callback/route.ts | 4 +- app/api/sp/oauth/x/callback/route.ts | 4 +- app/invite/[token]/page.tsx | 4 +- components/engines-panel.tsx | 2 +- components/project-tabs-nav.tsx | 54 ++-- components/promote/list-actions.tsx | 2 +- components/promote/promote-form.tsx | 4 +- components/report/apply-fix-button.tsx | 2 +- components/report/premium-engines.tsx | 2 +- components/run-now-button.tsx | 2 +- components/scan-run-poller.tsx | 2 +- components/scan-run-results.tsx | 2 +- components/site-header.tsx | 1 + lib/email.ts | 2 +- lib/lx/currentSite.ts | 2 +- lib/mcp/promote.ts | 4 +- lib/perfReport.ts | 4 +- next.config.ts | 40 +++ proxy.ts | 12 +- .../lx-article-embeds.integration.test.ts | 2 +- tests/e2e/autoblog-plan.spec.ts | 4 +- tests/leads-page-columns.test.ts | 2 +- worker/index.ts | 8 +- 161 files changed, 621 insertions(+), 301 deletions(-) rename app/(app)/{ => dashboard}/admin/email-broadcast/EmailBroadcastForm.tsx (100%) rename app/(app)/{ => dashboard}/admin/email-broadcast/page.tsx (97%) rename app/(app)/{ => dashboard}/admin/form.tsx (100%) rename app/(app)/{ => dashboard}/admin/github/setup/done/client.tsx (98%) rename app/(app)/{ => dashboard}/admin/github/setup/done/page.tsx (89%) rename app/(app)/{ => dashboard}/admin/github/setup/page.tsx (98%) rename app/(app)/{ => dashboard}/admin/integrations-form.tsx (100%) rename app/(app)/{ => dashboard}/admin/leads/page.tsx (99%) rename app/(app)/{ => dashboard}/admin/page.tsx (96%) rename app/(app)/{ => dashboard}/admin/provider-costs-panel.tsx (100%) rename app/(app)/{ => dashboard}/ads/[id]/edit/edit-form.tsx (100%) rename app/(app)/{ => dashboard}/ads/[id]/edit/page.tsx (96%) rename app/(app)/{ => dashboard}/ads/[id]/page.tsx (97%) rename app/(app)/{ => dashboard}/ads/earnings/page.tsx (97%) rename app/(app)/{ => dashboard}/ads/new/form.tsx (99%) rename app/(app)/{ => dashboard}/ads/new/page.tsx (93%) rename app/(app)/{ => dashboard}/ads/page.tsx (93%) rename app/(app)/{ => dashboard}/ads/slots/page.tsx (93%) rename app/(app)/{ => dashboard}/analytics/page.tsx (99%) rename app/(app)/{ => dashboard}/audits/[id]/page.tsx (99%) rename app/(app)/{ => dashboard}/autoblog/page.tsx (80%) rename app/(app)/{ => dashboard}/github/dashboard/page.tsx (73%) rename app/(app)/{ => dashboard}/projects/[id]/autoblog/actions.tsx (100%) rename app/(app)/{ => dashboard}/projects/[id]/autoblog/articles/[articleId]/page.tsx (99%) rename app/(app)/{ => dashboard}/projects/[id]/autoblog/articles/[articleId]/publish-button.tsx (100%) rename app/(app)/{ => dashboard}/projects/[id]/autoblog/auto-refresh.tsx (100%) rename app/(app)/{ => dashboard}/projects/[id]/autoblog/backlinks/chart.tsx (100%) rename app/(app)/{ => dashboard}/projects/[id]/autoblog/backlinks/page.tsx (97%) rename app/(app)/{ => dashboard}/projects/[id]/autoblog/countdown.tsx (100%) rename app/(app)/{ => dashboard}/projects/[id]/autoblog/delete-autoblog-button.tsx (100%) rename app/(app)/{ => dashboard}/projects/[id]/autoblog/guest-posts.tsx (99%) rename app/(app)/{ => dashboard}/projects/[id]/autoblog/history/page.tsx (90%) rename app/(app)/{ => dashboard}/projects/[id]/autoblog/page.tsx (96%) rename app/(app)/{ => dashboard}/projects/[id]/autoblog/plan/actions.ts (92%) rename app/(app)/{ => dashboard}/projects/[id]/autoblog/plan/page.tsx (95%) rename app/(app)/{ => dashboard}/projects/[id]/autoblog/setup/form.tsx (99%) rename app/(app)/{ => dashboard}/projects/[id]/autoblog/setup/page.tsx (93%) rename app/(app)/{ => dashboard}/projects/[id]/delete-project-button.tsx (100%) rename app/(app)/{ => dashboard}/projects/[id]/edit-url-form.tsx (100%) rename app/(app)/{ => dashboard}/projects/[id]/getting-started/page.tsx (95%) rename app/(app)/{ => dashboard}/projects/[id]/layout-client.tsx (100%) rename app/(app)/{ => dashboard}/projects/[id]/layout.tsx (100%) rename app/(app)/{ => dashboard}/projects/[id]/leads/page.tsx (100%) rename app/(app)/{ => dashboard}/projects/[id]/members/members-client.tsx (100%) rename app/(app)/{ => dashboard}/projects/[id]/members/page.tsx (100%) rename app/(app)/{ => dashboard}/projects/[id]/page.tsx (97%) rename app/(app)/{ => dashboard}/projects/[id]/performance/page.tsx (100%) rename app/(app)/{ => dashboard}/projects/[id]/repos/add-repo-modal.tsx (100%) rename app/(app)/{ => dashboard}/projects/[id]/repos/page.tsx (98%) rename app/(app)/{ => dashboard}/projects/[id]/repos/remove-button.tsx (100%) rename app/(app)/{ => dashboard}/projects/[id]/runs/[runId]/page.tsx (98%) rename app/(app)/{ => dashboard}/projects/[id]/scans/page.tsx (96%) rename app/(app)/{ => dashboard}/projects/[id]/security/page.tsx (100%) rename app/(app)/{ => dashboard}/projects/[id]/security/request-scan-button.tsx (96%) rename app/(app)/{ => dashboard}/projects/[id]/security/stream/route.ts (100%) rename app/(app)/{ => dashboard}/projects/[id]/settings/page.tsx (100%) rename app/(app)/{ => dashboard}/projects/[id]/social/api-tokens/client.tsx (100%) rename app/(app)/{ => dashboard}/projects/[id]/social/api-tokens/page.tsx (98%) rename app/(app)/{ => dashboard}/projects/[id]/social/auto-refresh.tsx (100%) rename app/(app)/{ => dashboard}/projects/[id]/social/feed-settings.tsx (100%) rename app/(app)/{ => dashboard}/projects/[id]/social/page.tsx (99%) rename app/(app)/{ => dashboard}/projects/[id]/social/post-now.tsx (100%) rename app/(app)/{ => dashboard}/projects/[id]/social/retry-feed-item-button.tsx (100%) rename app/(app)/{ => dashboard}/projects/[id]/social/retry-post-button.tsx (100%) rename app/(app)/{ => dashboard}/projects/[id]/social/setup/app-password-reveal.tsx (100%) rename app/(app)/{ => dashboard}/projects/[id]/social/setup/form.tsx (100%) rename app/(app)/{ => dashboard}/projects/[id]/social/setup/page.tsx (98%) rename app/(app)/{ => dashboard}/projects/[id]/social/social-profile.tsx (100%) rename app/(app)/{ => dashboard}/projects/[id]/stats/auto-install.tsx (99%) rename app/(app)/{ => dashboard}/projects/[id]/stats/careers-toggle.tsx (100%) rename app/(app)/{ => dashboard}/projects/[id]/stats/careers/applications.tsx (100%) rename app/(app)/{ => dashboard}/projects/[id]/stats/careers/careers-install.tsx (100%) rename app/(app)/{ => dashboard}/projects/[id]/stats/careers/job-manager.tsx (100%) rename app/(app)/{ => dashboard}/projects/[id]/stats/careers/page.tsx (98%) rename app/(app)/{ => dashboard}/projects/[id]/stats/install-snippet.tsx (100%) rename app/(app)/{ => dashboard}/projects/[id]/stats/integrations/integration-form.tsx (100%) rename app/(app)/{ => dashboard}/projects/[id]/stats/integrations/page.tsx (100%) rename app/(app)/{ => dashboard}/projects/[id]/stats/live-chart.tsx (100%) rename app/(app)/{ => dashboard}/projects/[id]/stats/live-visitors.tsx (100%) rename app/(app)/{ => dashboard}/projects/[id]/stats/page.tsx (99%) rename app/(app)/{ => dashboard}/projects/[id]/stats/stats-subnav.tsx (80%) rename app/(app)/{ => dashboard}/projects/[id]/stats/tracker-toggle.tsx (100%) rename app/(app)/{ => dashboard}/projects/[id]/stats/visitor-globe.tsx (100%) rename app/(app)/{ => dashboard}/projects/[id]/uptime/add-monitor-form.tsx (100%) rename app/(app)/{ => dashboard}/projects/[id]/uptime/monitor-actions.tsx (100%) rename app/(app)/{ => dashboard}/projects/[id]/uptime/page.tsx (100%) rename app/(app)/{ => dashboard}/projects/new/form.tsx (98%) rename app/(app)/{ => dashboard}/projects/new/page.tsx (100%) rename app/(app)/{ => dashboard}/promote/[id]/page.tsx (97%) rename app/(app)/{ => dashboard}/promote/accounts/page.tsx (98%) rename app/(app)/{ => dashboard}/promote/new/page.tsx (100%) rename app/(app)/{ => dashboard}/promote/page.tsx (93%) rename app/(app)/{ => dashboard}/settings/billing/buy-credits-button.tsx (100%) rename app/(app)/{ => dashboard}/settings/billing/page.tsx (98%) rename app/(app)/{ => dashboard}/settings/billing/purchase-status-banner.tsx (100%) rename app/(app)/{ => dashboard}/settings/form.tsx (100%) rename app/(app)/{ => dashboard}/settings/integrations/github/bind-button.tsx (100%) rename app/(app)/{ => dashboard}/settings/integrations/github/page.tsx (99%) rename app/(app)/{ => dashboard}/settings/integrations/github/repos-filter.tsx (100%) rename app/(app)/{ => dashboard}/settings/page.tsx (94%) rename app/(app)/{ => dashboard}/social/page.tsx (78%) rename app/(app)/{ => dashboard}/social/setup/page.tsx (86%) create mode 100644 app/(marketing)/ads/page.tsx diff --git a/app/(app)/admin/email-broadcast/EmailBroadcastForm.tsx b/app/(app)/dashboard/admin/email-broadcast/EmailBroadcastForm.tsx similarity index 100% rename from app/(app)/admin/email-broadcast/EmailBroadcastForm.tsx rename to app/(app)/dashboard/admin/email-broadcast/EmailBroadcastForm.tsx diff --git a/app/(app)/admin/email-broadcast/page.tsx b/app/(app)/dashboard/admin/email-broadcast/page.tsx similarity index 97% rename from app/(app)/admin/email-broadcast/page.tsx rename to app/(app)/dashboard/admin/email-broadcast/page.tsx index da14809c..652f8698 100644 --- a/app/(app)/admin/email-broadcast/page.tsx +++ b/app/(app)/dashboard/admin/email-broadcast/page.tsx @@ -27,7 +27,7 @@ export default async function EmailBroadcastPage() {
← Admin diff --git a/app/(app)/admin/form.tsx b/app/(app)/dashboard/admin/form.tsx similarity index 100% rename from app/(app)/admin/form.tsx rename to app/(app)/dashboard/admin/form.tsx diff --git a/app/(app)/admin/github/setup/done/client.tsx b/app/(app)/dashboard/admin/github/setup/done/client.tsx similarity index 98% rename from app/(app)/admin/github/setup/done/client.tsx rename to app/(app)/dashboard/admin/github/setup/done/client.tsx index 257b4314..0b720ddb 100644 --- a/app/(app)/admin/github/setup/done/client.tsx +++ b/app/(app)/dashboard/admin/github/setup/done/client.tsx @@ -111,7 +111,7 @@ export function SetupDoneClient() { Install App → . End users do the same from their{" "} - + personal GitHub settings page . diff --git a/app/(app)/admin/github/setup/done/page.tsx b/app/(app)/dashboard/admin/github/setup/done/page.tsx similarity index 89% rename from app/(app)/admin/github/setup/done/page.tsx rename to app/(app)/dashboard/admin/github/setup/done/page.tsx index ed4f66cb..c66b0010 100644 --- a/app/(app)/admin/github/setup/done/page.tsx +++ b/app/(app)/dashboard/admin/github/setup/done/page.tsx @@ -30,7 +30,7 @@ export default async function AdminGithubSetupDonePage() { return (

- + ← Admin

@@ -39,7 +39,7 @@ export default async function AdminGithubSetupDonePage() { Copy each value into Railway, set them on both the Next.js app and the worker service, then redeploy. After deploy, end users can install the App from their own{" "} - + GitHub settings page . diff --git a/app/(app)/admin/github/setup/page.tsx b/app/(app)/dashboard/admin/github/setup/page.tsx similarity index 98% rename from app/(app)/admin/github/setup/page.tsx rename to app/(app)/dashboard/admin/github/setup/page.tsx index 0ade5b7e..886b08fc 100644 --- a/app/(app)/admin/github/setup/page.tsx +++ b/app/(app)/dashboard/admin/github/setup/page.tsx @@ -86,7 +86,7 @@ export default async function AdminGithubSetupPage({ return (

- + ← Admin

diff --git a/app/(app)/admin/integrations-form.tsx b/app/(app)/dashboard/admin/integrations-form.tsx similarity index 100% rename from app/(app)/admin/integrations-form.tsx rename to app/(app)/dashboard/admin/integrations-form.tsx diff --git a/app/(app)/admin/leads/page.tsx b/app/(app)/dashboard/admin/leads/page.tsx similarity index 99% rename from app/(app)/admin/leads/page.tsx rename to app/(app)/dashboard/admin/leads/page.tsx index 8fa8ae5f..0112c18c 100644 --- a/app/(app)/admin/leads/page.tsx +++ b/app/(app)/dashboard/admin/leads/page.tsx @@ -125,7 +125,7 @@ export default async function AdminLeadsPage() {

Leads

- + ← Admin
diff --git a/app/(app)/admin/page.tsx b/app/(app)/dashboard/admin/page.tsx similarity index 96% rename from app/(app)/admin/page.tsx rename to app/(app)/dashboard/admin/page.tsx index 3cdba2f3..1ea9dfde 100644 --- a/app/(app)/admin/page.tsx +++ b/app/(app)/dashboard/admin/page.tsx @@ -95,7 +95,7 @@ export default async function AdminPage() { URLs, and live audience counts per segment.

- + Open leads dashboard →
@@ -117,7 +117,7 @@ export default async function AdminPage() { the App from their own settings page after the env is set.

- + {env.githubAppId ? "Re-register App" : "Register App"} @@ -135,7 +135,7 @@ export default async function AdminPage() {

Email broadcast

Send a mass email to all registered users.

- + Compose & send →
diff --git a/app/(app)/admin/provider-costs-panel.tsx b/app/(app)/dashboard/admin/provider-costs-panel.tsx similarity index 100% rename from app/(app)/admin/provider-costs-panel.tsx rename to app/(app)/dashboard/admin/provider-costs-panel.tsx diff --git a/app/(app)/ads/[id]/edit/edit-form.tsx b/app/(app)/dashboard/ads/[id]/edit/edit-form.tsx similarity index 100% rename from app/(app)/ads/[id]/edit/edit-form.tsx rename to app/(app)/dashboard/ads/[id]/edit/edit-form.tsx diff --git a/app/(app)/ads/[id]/edit/page.tsx b/app/(app)/dashboard/ads/[id]/edit/page.tsx similarity index 96% rename from app/(app)/ads/[id]/edit/page.tsx rename to app/(app)/dashboard/ads/[id]/edit/page.tsx index f66679f2..3090cf4f 100644 --- a/app/(app)/ads/[id]/edit/page.tsx +++ b/app/(app)/dashboard/ads/[id]/edit/page.tsx @@ -64,7 +64,7 @@ export default async function EditCampaignPage({ return (
- + ← Campaign
diff --git a/app/(app)/ads/[id]/page.tsx b/app/(app)/dashboard/ads/[id]/page.tsx similarity index 97% rename from app/(app)/ads/[id]/page.tsx rename to app/(app)/dashboard/ads/[id]/page.tsx index 52ba772f..964e61cf 100644 --- a/app/(app)/ads/[id]/page.tsx +++ b/app/(app)/dashboard/ads/[id]/page.tsx @@ -103,7 +103,7 @@ export default async function CampaignDetailPage({ return (
- + ← Ad campaigns @@ -129,7 +129,7 @@ export default async function CampaignDetailPage({ {display.label} - + Edit diff --git a/app/(app)/ads/earnings/page.tsx b/app/(app)/dashboard/ads/earnings/page.tsx similarity index 97% rename from app/(app)/ads/earnings/page.tsx rename to app/(app)/dashboard/ads/earnings/page.tsx index bdc6e5d6..6e4ff4d2 100644 --- a/app/(app)/ads/earnings/page.tsx +++ b/app/(app)/dashboard/ads/earnings/page.tsx @@ -47,7 +47,7 @@ export default async function EarningsPage() { return (
- + ← Ad campaigns
@@ -83,7 +83,7 @@ export default async function EarningsPage() { {model.slots.length === 0 ? (
No monetized sites yet.{" "} - + Monetize a site . @@ -126,7 +126,7 @@ export default async function EarningsPage() { {model.campaigns.length === 0 ? (
No ad campaigns yet.{" "} - + Create an ad . diff --git a/app/(app)/ads/new/form.tsx b/app/(app)/dashboard/ads/new/form.tsx similarity index 99% rename from app/(app)/ads/new/form.tsx rename to app/(app)/dashboard/ads/new/form.tsx index 53af5d7b..2f4153f4 100644 --- a/app/(app)/ads/new/form.tsx +++ b/app/(app)/dashboard/ads/new/form.tsx @@ -88,7 +88,7 @@ export function NewAdForm() { setError(res.error); return; } - router.push(`/ads?created=${res.refSlug}`); + router.push(`/dashboard/ads?created=${res.refSlug}`); }); } diff --git a/app/(app)/ads/new/page.tsx b/app/(app)/dashboard/ads/new/page.tsx similarity index 93% rename from app/(app)/ads/new/page.tsx rename to app/(app)/dashboard/ads/new/page.tsx index 1170ee44..938a34d9 100644 --- a/app/(app)/ads/new/page.tsx +++ b/app/(app)/dashboard/ads/new/page.tsx @@ -29,7 +29,7 @@ export default async function NewAdPage() { return (
- + ← Ad campaigns

Create an ad

@@ -42,7 +42,7 @@ export default async function NewAdPage() {
🎁 Launch offer: your first deposit is matched 100% in bonus ad credits (up to $100 free).{" "} - + Add credits . diff --git a/app/(app)/ads/page.tsx b/app/(app)/dashboard/ads/page.tsx similarity index 93% rename from app/(app)/ads/page.tsx rename to app/(app)/dashboard/ads/page.tsx index 765092d2..3be82d35 100644 --- a/app/(app)/ads/page.tsx +++ b/app/(app)/dashboard/ads/page.tsx @@ -96,13 +96,13 @@ export default async function AdsPage({

Ad campaigns

- + Earnings & reports - + Monetize a site - + Create an ad
@@ -168,7 +168,7 @@ export default async function AdsPage({ {campaigns.length === 0 ? (
No campaigns yet.{" "} - + Create your first ad . @@ -185,7 +185,7 @@ export default async function AdsPage({
  • - + {c.name}
    @@ -194,13 +194,13 @@ export default async function AdsPage({
    - + {display.label} - + Edit diff --git a/app/(app)/ads/slots/page.tsx b/app/(app)/dashboard/ads/slots/page.tsx similarity index 93% rename from app/(app)/ads/slots/page.tsx rename to app/(app)/dashboard/ads/slots/page.tsx index 62fe1523..14ee88b4 100644 --- a/app/(app)/ads/slots/page.tsx +++ b/app/(app)/dashboard/ads/slots/page.tsx @@ -92,19 +92,19 @@ export default async function SlotsPage() { return (
    - + ← Ad campaigns

    Monetize your site

    - + Earnings & reports {/* Always-visible way to add a site to monetize — a site is a project, so this starts the project-creation flow. Previously the only link to it was hidden inside the empty state. */} - + Monetize a new site
    @@ -118,7 +118,7 @@ export default async function SlotsPage() { {projects.length === 0 ? (
    No sites yet — a site is a CrawlProof project.{" "} - + Add one {" "} to enable a slot. diff --git a/app/(app)/analytics/page.tsx b/app/(app)/dashboard/analytics/page.tsx similarity index 99% rename from app/(app)/analytics/page.tsx rename to app/(app)/dashboard/analytics/page.tsx index 2882d33a..efd59c0d 100644 --- a/app/(app)/analytics/page.tsx +++ b/app/(app)/dashboard/analytics/page.tsx @@ -139,7 +139,7 @@ export default async function PortfolioAnalyticsPage({

    No projects yet.{" "} - + Add your first property {" "} to start seeing portfolio-wide analytics. @@ -578,7 +578,7 @@ function ProjectTrendTable({ rows }: { rows: ProjectRow[] }) { > - + ← Back to report

    Dashboard

    - + New project
    @@ -191,7 +191,7 @@ export default async function DashboardPage({
      {projects.map((p) => (
    • - +
      No projects yet.{" "} - + Create one . @@ -298,7 +298,7 @@ export default async function DashboardPage({ {audits.map((a) => (
    • - + {a.target_url}
      diff --git a/app/(app)/projects/[id]/autoblog/actions.tsx b/app/(app)/dashboard/projects/[id]/autoblog/actions.tsx similarity index 100% rename from app/(app)/projects/[id]/autoblog/actions.tsx rename to app/(app)/dashboard/projects/[id]/autoblog/actions.tsx diff --git a/app/(app)/projects/[id]/autoblog/articles/[articleId]/page.tsx b/app/(app)/dashboard/projects/[id]/autoblog/articles/[articleId]/page.tsx similarity index 99% rename from app/(app)/projects/[id]/autoblog/articles/[articleId]/page.tsx rename to app/(app)/dashboard/projects/[id]/autoblog/articles/[articleId]/page.tsx index b5c84c9e..db75b330 100644 --- a/app/(app)/projects/[id]/autoblog/articles/[articleId]/page.tsx +++ b/app/(app)/dashboard/projects/[id]/autoblog/articles/[articleId]/page.tsx @@ -54,7 +54,7 @@ export default async function AutoblogArticlePage({
      ← Article history diff --git a/app/(app)/projects/[id]/autoblog/articles/[articleId]/publish-button.tsx b/app/(app)/dashboard/projects/[id]/autoblog/articles/[articleId]/publish-button.tsx similarity index 100% rename from app/(app)/projects/[id]/autoblog/articles/[articleId]/publish-button.tsx rename to app/(app)/dashboard/projects/[id]/autoblog/articles/[articleId]/publish-button.tsx diff --git a/app/(app)/projects/[id]/autoblog/auto-refresh.tsx b/app/(app)/dashboard/projects/[id]/autoblog/auto-refresh.tsx similarity index 100% rename from app/(app)/projects/[id]/autoblog/auto-refresh.tsx rename to app/(app)/dashboard/projects/[id]/autoblog/auto-refresh.tsx diff --git a/app/(app)/projects/[id]/autoblog/backlinks/chart.tsx b/app/(app)/dashboard/projects/[id]/autoblog/backlinks/chart.tsx similarity index 100% rename from app/(app)/projects/[id]/autoblog/backlinks/chart.tsx rename to app/(app)/dashboard/projects/[id]/autoblog/backlinks/chart.tsx diff --git a/app/(app)/projects/[id]/autoblog/backlinks/page.tsx b/app/(app)/dashboard/projects/[id]/autoblog/backlinks/page.tsx similarity index 97% rename from app/(app)/projects/[id]/autoblog/backlinks/page.tsx rename to app/(app)/dashboard/projects/[id]/autoblog/backlinks/page.tsx index cf709c91..445be8d4 100644 --- a/app/(app)/projects/[id]/autoblog/backlinks/page.tsx +++ b/app/(app)/dashboard/projects/[id]/autoblog/backlinks/page.tsx @@ -136,7 +136,7 @@ export default async function AutoblogBacklinksPage({ domain: string; blog_root_url: string | null; } | null; - if (!site) redirect(`/projects/${projectId}/autoblog/setup`); + if (!site) redirect(`/dashboard/projects/${projectId}/autoblog/setup`); const currentSitePublic = { blog_root_url: site.blog_root_url }; const svc = serviceClient(); @@ -197,7 +197,7 @@ export default async function AutoblogBacklinksPage({
      ← Autoblog @@ -209,13 +209,13 @@ export default async function AutoblogBacklinksPage({
      diff --git a/app/(app)/projects/[id]/autoblog/countdown.tsx b/app/(app)/dashboard/projects/[id]/autoblog/countdown.tsx similarity index 100% rename from app/(app)/projects/[id]/autoblog/countdown.tsx rename to app/(app)/dashboard/projects/[id]/autoblog/countdown.tsx diff --git a/app/(app)/projects/[id]/autoblog/delete-autoblog-button.tsx b/app/(app)/dashboard/projects/[id]/autoblog/delete-autoblog-button.tsx similarity index 100% rename from app/(app)/projects/[id]/autoblog/delete-autoblog-button.tsx rename to app/(app)/dashboard/projects/[id]/autoblog/delete-autoblog-button.tsx diff --git a/app/(app)/projects/[id]/autoblog/guest-posts.tsx b/app/(app)/dashboard/projects/[id]/autoblog/guest-posts.tsx similarity index 99% rename from app/(app)/projects/[id]/autoblog/guest-posts.tsx rename to app/(app)/dashboard/projects/[id]/autoblog/guest-posts.tsx index 989abd7d..cbebcf67 100644 --- a/app/(app)/projects/[id]/autoblog/guest-posts.tsx +++ b/app/(app)/dashboard/projects/[id]/autoblog/guest-posts.tsx @@ -266,7 +266,7 @@ function TopicAction({ generated {req.article_id && ( View diff --git a/app/(app)/projects/[id]/autoblog/history/page.tsx b/app/(app)/dashboard/projects/[id]/autoblog/history/page.tsx similarity index 90% rename from app/(app)/projects/[id]/autoblog/history/page.tsx rename to app/(app)/dashboard/projects/[id]/autoblog/history/page.tsx index 093c2d02..3d2860d6 100644 --- a/app/(app)/projects/[id]/autoblog/history/page.tsx +++ b/app/(app)/dashboard/projects/[id]/autoblog/history/page.tsx @@ -33,7 +33,7 @@ export default async function AutoblogHistoryPage({ }); if (!project) notFound(); const site = project.lx_site as { id: string; domain: string } | null; - if (!site) redirect(`/projects/${projectId}/autoblog/setup`); + if (!site) redirect(`/dashboard/projects/${projectId}/autoblog/setup`); const { data: articles } = await supabase .from("lx_article") @@ -48,7 +48,7 @@ export default async function AutoblogHistoryPage({
      ← Autoblog @@ -60,13 +60,13 @@ export default async function AutoblogHistoryPage({
      @@ -93,7 +93,7 @@ export default async function AutoblogHistoryPage({ {a.title} diff --git a/app/(app)/projects/[id]/autoblog/page.tsx b/app/(app)/dashboard/projects/[id]/autoblog/page.tsx similarity index 96% rename from app/(app)/projects/[id]/autoblog/page.tsx rename to app/(app)/dashboard/projects/[id]/autoblog/page.tsx index 69bd4621..fcbc490c 100644 --- a/app/(app)/projects/[id]/autoblog/page.tsx +++ b/app/(app)/dashboard/projects/[id]/autoblog/page.tsx @@ -76,7 +76,7 @@ export default async function AutoblogDashboardPage({ You haven't set up Autoblog yet for this project.

      Get started @@ -197,16 +197,16 @@ export default async function AutoblogDashboardPage({

      - + Plan - + Settings - + History - + Backlinks
      @@ -262,7 +262,7 @@ export default async function AutoblogDashboardPage({ {previews!.map((p: any) => (
    • {p.title} @@ -427,7 +427,7 @@ export default async function AutoblogDashboardPage({
    • {a.title} diff --git a/app/(app)/projects/[id]/autoblog/plan/actions.ts b/app/(app)/dashboard/projects/[id]/autoblog/plan/actions.ts similarity index 92% rename from app/(app)/projects/[id]/autoblog/plan/actions.ts rename to app/(app)/dashboard/projects/[id]/autoblog/plan/actions.ts index 19b600f9..aedad0a8 100644 --- a/app/(app)/projects/[id]/autoblog/plan/actions.ts +++ b/app/(app)/dashboard/projects/[id]/autoblog/plan/actions.ts @@ -56,7 +56,7 @@ export async function updateKeywordPlan( .in("status", ["queued", "failed"]); if (error) throw new Error(error.message); - revalidatePath(`/projects/${projectId}/autoblog/plan`); - revalidatePath(`/projects/${projectId}/autoblog`); + revalidatePath(`/dashboard/projects/${projectId}/autoblog/plan`); + revalidatePath(`/dashboard/projects/${projectId}/autoblog`); } diff --git a/app/(app)/projects/[id]/autoblog/plan/page.tsx b/app/(app)/dashboard/projects/[id]/autoblog/plan/page.tsx similarity index 95% rename from app/(app)/projects/[id]/autoblog/plan/page.tsx rename to app/(app)/dashboard/projects/[id]/autoblog/plan/page.tsx index 25a469d0..76f28885 100644 --- a/app/(app)/projects/[id]/autoblog/plan/page.tsx +++ b/app/(app)/dashboard/projects/[id]/autoblog/plan/page.tsx @@ -69,7 +69,7 @@ export default async function AutoblogPlanPage({ const site = project.lx_site as | { id: string; domain: string; status: string } | null; - if (!site) redirect(`/projects/${projectId}/autoblog/setup`); + if (!site) redirect(`/dashboard/projects/${projectId}/autoblog/setup`); const { start, end } = monthWindow(); const { data } = await supabase @@ -90,7 +90,7 @@ export default async function AutoblogPlanPage({
      ← Autoblog @@ -101,10 +101,10 @@ export default async function AutoblogPlanPage({

      - + Dashboard - + Articles
      @@ -156,7 +156,7 @@ export default async function AutoblogPlanPage({
      {row.keyword}
      {row.article_id && ( Open article diff --git a/app/(app)/projects/[id]/autoblog/setup/form.tsx b/app/(app)/dashboard/projects/[id]/autoblog/setup/form.tsx similarity index 99% rename from app/(app)/projects/[id]/autoblog/setup/form.tsx rename to app/(app)/dashboard/projects/[id]/autoblog/setup/form.tsx index 945a785b..e21e7329 100644 --- a/app/(app)/projects/[id]/autoblog/setup/form.tsx +++ b/app/(app)/dashboard/projects/[id]/autoblog/setup/form.tsx @@ -593,7 +593,7 @@ export function SetupForm({ } setNotice("Settings saved."); if (!initial) { - router.push(`/projects/${projectId}/autoblog`); + router.push(`/dashboard/projects/${projectId}/autoblog`); } else { router.refresh(); } diff --git a/app/(app)/projects/[id]/autoblog/setup/page.tsx b/app/(app)/dashboard/projects/[id]/autoblog/setup/page.tsx similarity index 93% rename from app/(app)/projects/[id]/autoblog/setup/page.tsx rename to app/(app)/dashboard/projects/[id]/autoblog/setup/page.tsx index 627d5d57..fa990710 100644 --- a/app/(app)/projects/[id]/autoblog/setup/page.tsx +++ b/app/(app)/dashboard/projects/[id]/autoblog/setup/page.tsx @@ -71,13 +71,13 @@ export default async function AutoblogSetupPage({ {site ? "Autoblog settings" : "Set up Autoblog"}
      - + Dashboard - + History - + Backlinks
      diff --git a/app/(app)/projects/[id]/delete-project-button.tsx b/app/(app)/dashboard/projects/[id]/delete-project-button.tsx similarity index 100% rename from app/(app)/projects/[id]/delete-project-button.tsx rename to app/(app)/dashboard/projects/[id]/delete-project-button.tsx diff --git a/app/(app)/projects/[id]/edit-url-form.tsx b/app/(app)/dashboard/projects/[id]/edit-url-form.tsx similarity index 100% rename from app/(app)/projects/[id]/edit-url-form.tsx rename to app/(app)/dashboard/projects/[id]/edit-url-form.tsx diff --git a/app/(app)/projects/[id]/getting-started/page.tsx b/app/(app)/dashboard/projects/[id]/getting-started/page.tsx similarity index 95% rename from app/(app)/projects/[id]/getting-started/page.tsx rename to app/(app)/dashboard/projects/[id]/getting-started/page.tsx index e9a25d41..b2ff96df 100644 --- a/app/(app)/projects/[id]/getting-started/page.tsx +++ b/app/(app)/dashboard/projects/[id]/getting-started/page.tsx @@ -69,15 +69,15 @@ export default async function GettingStartedPage({ body: "Scan your site with one or more AI engines to produce your first audit. Each engine reports what it can find — content, schema, robots, AI-bot rules — and gives you concrete fixes.", done: hasFirstScan, cta: hasFirstScan - ? { href: `/projects/${id}/scans`, label: "View scans" } - : { href: `/projects/${id}`, label: "Run a scan" }, + ? { href: `/dashboard/projects/${id}/scans`, label: "View scans" } + : { href: `/dashboard/projects/${id}`, label: "Run a scan" }, }, { id: "stats-tracker", title: "Enable the stats tracker", body: "Add one