SEO route swap: / is now the landing page, dashboard moved to /dashboard

- app/landing/page.tsx → app/page.tsx (landing page at root)
- app/page.tsx → app/dashboard/page.tsx (dashboard at /dashboard)
- app/landing/ directory deleted
- next.config.ts: 301 redirect /landing → /
- app/layout.tsx: canonical, OG, JSON-LD metadata updated to /
- All internal router.push('/') → /dashboard in subpages
- All router.push('/landing') → / in ProtectedRoute and preview
- All href='/landing' → / in PublicSiteNavbar, roadmap, privacy, terms
- robots.ts: allow / instead of /landing, disallow /dashboard
- sitemap.ts: root URL updated to /
- Navbar.tsx: suppress on pathname === '/' instead of '/landing'
- msalConfig.ts + .env.local: post-logout redirect updated to /

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Oliver Kieselbach
2026-02-24 08:06:53 +01:00
parent ca39032ed4
commit 335f854ed8
25 changed files with 2351 additions and 2384 deletions
@@ -92,7 +92,7 @@ export default function AdminConfigurationPage() {
// Redirect if not in galactic admin mode
if (!galacticMode) {
router.push("/");
router.push("/dashboard");
}
}, [router]);
@@ -640,7 +640,7 @@ export default function AdminConfigurationPage() {
<div className="flex items-center justify-between">
<div>
<button
onClick={() => router.push("/")}
onClick={() => router.push("/dashboard")}
className="text-sm text-gray-600 hover:text-gray-900 mb-2 flex items-center"
>
&larr; Back to Dashboard
@@ -670,7 +670,7 @@ export default function AnalyzeRulesPage() {
<div className="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
<div className="flex items-center justify-between">
<div>
<button onClick={() => router.push("/")} className="text-sm text-gray-600 hover:text-gray-900 mb-2 flex items-center">
<button onClick={() => router.push("/dashboard")} className="text-sm text-gray-600 hover:text-gray-900 mb-2 flex items-center">
&larr; Back to Dashboard
</button>
<div>
@@ -151,7 +151,7 @@ export default function AuditPage() {
Retry
</button>
<button
onClick={() => router.push('/')}
onClick={() => router.push('/dashboard')}
className="w-full px-4 py-2 bg-gray-200 dark:bg-gray-700 text-gray-700 dark:text-gray-300 rounded-lg hover:bg-gray-300 dark:hover:bg-gray-600 transition-colors"
>
Back to Home
@@ -171,7 +171,7 @@ export default function AuditPage() {
<div className="flex items-center justify-between">
<div>
<button
onClick={() => router.push('/')}
onClick={() => router.push('/dashboard')}
className="text-sm text-gray-600 hover:text-gray-900 mb-2 flex items-center"
>
&larr; Back to Dashboard
File diff suppressed because it is too large Load Diff
@@ -354,7 +354,7 @@ export default function FleetHealthPage() {
<div className="flex items-center justify-between">
<div>
<button
onClick={() => router.push("/")}
onClick={() => router.push("/dashboard")}
className="text-sm text-gray-600 hover:text-gray-900 mb-2 flex items-center"
>
&larr; Back to Dashboard
@@ -666,7 +666,7 @@ export default function GatherRulesPage() {
<div className="flex items-center justify-between">
<div>
<button
onClick={() => router.push("/")}
onClick={() => router.push("/dashboard")}
className="text-sm text-gray-600 hover:text-gray-900 mb-2 flex items-center"
>
&larr; Back to Dashboard
@@ -1,72 +0,0 @@
import type { Metadata } from "next";
export const metadata: Metadata = {
title: {
absolute: "Autopilot Monitor – Real-Time Windows Autopilot Monitoring",
},
description:
"Real-time monitoring and troubleshooting for Windows Autopilot deployments. Track every enrollment phase, detect issues automatically with Analyze Rules, and resolve failures faster.",
keywords: [
"Windows Autopilot monitoring",
"Autopilot deployment visibility",
"Intune Autopilot analytics",
"Windows enrollment tracking",
"Autopilot troubleshooting",
"Autopilot real-time monitoring",
"Autopilot Monitor",
"Windows Autopilot dashboard",
"Autopilot failure detection",
"enrollment phase tracking",
],
openGraph: {
title: "Autopilot Monitor – Real-Time Windows Autopilot Monitoring",
description:
"Real-time monitoring and troubleshooting for Windows Autopilot deployments. Track every enrollment phase, detect issues automatically, and resolve failures faster.",
url: "https://autopilotmonitor.com/landing",
type: "website",
},
twitter: {
title: "Autopilot Monitor – Real-Time Windows Autopilot Monitoring",
description:
"Real-time monitoring and troubleshooting for Windows Autopilot deployments. Track every enrollment phase, detect issues automatically, and resolve failures faster.",
},
alternates: {
canonical: "https://autopilotmonitor.com/landing",
},
};
const jsonLd = {
"@context": "https://schema.org",
"@type": "SoftwareApplication",
name: "Autopilot Monitor",
description:
"Real-time monitoring and troubleshooting platform for Windows Autopilot deployments. Gives IT teams full visibility into enrollment phases, app progress, errors, and timelines.",
applicationCategory: "BusinessApplication",
operatingSystem: "Web",
offers: {
"@type": "Offer",
price: "0",
priceCurrency: "USD",
},
author: {
"@type": "Person",
name: "Oliver Kieselbach",
url: "https://www.linkedin.com/in/oliver-kieselbach/",
},
url: "https://autopilotmonitor.com/landing",
codeRepository: "https://github.com/okieselbach/Autopilot-Monitor",
keywords:
"Windows Autopilot, Intune, enrollment monitoring, autopilot troubleshooting, Windows deployment",
};
export default function LandingLayout({ children }: { children: React.ReactNode }) {
return (
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
{children}
</>
);
}
File diff suppressed because it is too large Load Diff
+44 -10
View File
@@ -13,23 +13,26 @@ const inter = Inter({ subsets: ["latin"] });
export const metadata: Metadata = {
metadataBase: new URL("https://autopilotmonitor.com"),
title: {
absolute: "Autopilot Monitor – Real-Time Windows Autopilot Monitoring",
default: "Autopilot Monitor",
template: "%s | Autopilot Monitor",
},
description:
"Real-time monitoring and troubleshooting for Windows Autopilot deployments. Track enrollment phases, detect issues automatically, and resolve failures faster.",
"Real-time monitoring and troubleshooting for Windows Autopilot deployments. Track every enrollment phase, detect issues automatically with Analyze Rules, and resolve failures faster.",
keywords: [
"Windows Autopilot",
"Autopilot monitoring",
"Windows enrollment monitoring",
"Intune Autopilot",
"Windows Autopilot monitoring",
"Autopilot deployment visibility",
"Intune Autopilot analytics",
"Windows enrollment tracking",
"Autopilot troubleshooting",
"Autopilot deployment",
"Autopilot real-time monitoring",
"Autopilot Monitor",
"Windows Autopilot dashboard",
"Autopilot failure detection",
"enrollment phase tracking",
"Windows device enrollment",
"Autopilot analytics",
"OOBE monitoring",
"Autopilot ESP",
"Windows Autopilot deployment",
"Intune enrollment monitoring",
],
authors: [{ name: "Oliver Kieselbach", url: "https://www.linkedin.com/in/oliver-kieselbach/" }],
@@ -41,13 +44,16 @@ export const metadata: Metadata = {
siteName: "Autopilot Monitor",
title: "Autopilot Monitor – Real-Time Windows Autopilot Monitoring",
description:
"Real-time monitoring and troubleshooting for Windows Autopilot deployments. Track enrollment phases, detect issues automatically, and resolve failures faster.",
"Real-time monitoring and troubleshooting for Windows Autopilot deployments. Track every enrollment phase, detect issues automatically, and resolve failures faster.",
},
twitter: {
card: "summary_large_image",
title: "Autopilot Monitor – Real-Time Windows Autopilot Monitoring",
description:
"Real-time monitoring and troubleshooting for Windows Autopilot deployments. Track enrollment phases, detect issues automatically, and resolve failures faster.",
"Real-time monitoring and troubleshooting for Windows Autopilot deployments. Track every enrollment phase, detect issues automatically, and resolve failures faster.",
},
alternates: {
canonical: "https://autopilotmonitor.com",
},
robots: {
index: true,
@@ -64,6 +70,30 @@ export const metadata: Metadata = {
},
};
const jsonLd = {
"@context": "https://schema.org",
"@type": "SoftwareApplication",
name: "Autopilot Monitor",
description:
"Real-time monitoring and troubleshooting platform for Windows Autopilot deployments. Gives IT teams full visibility into enrollment phases, app progress, errors, and timelines.",
applicationCategory: "BusinessApplication",
operatingSystem: "Web",
offers: {
"@type": "Offer",
price: "0",
priceCurrency: "USD",
},
author: {
"@type": "Person",
name: "Oliver Kieselbach",
url: "https://www.linkedin.com/in/oliver-kieselbach/",
},
url: "https://autopilotmonitor.com",
codeRepository: "https://github.com/okieselbach/Autopilot-Monitor",
keywords:
"Windows Autopilot, Intune, enrollment monitoring, autopilot troubleshooting, Windows deployment",
};
export default function RootLayout({
children,
}: Readonly<{
@@ -72,6 +102,10 @@ export default function RootLayout({
return (
<html lang="en">
<body className={inter.className}>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
<ThemeProvider>
<AuthProvider>
<NotificationProvider>
File diff suppressed because it is too large Load Diff
@@ -165,7 +165,7 @@ export default function PlatformUsageMetricsPage() {
Retry
</button>
<button
onClick={() => router.push('/')}
onClick={() => router.push('/dashboard')}
className="w-full px-4 py-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300 transition-colors"
>
Back to Home
@@ -189,7 +189,7 @@ export default function PlatformUsageMetricsPage() {
<div className="flex items-center justify-between">
<div>
<button
onClick={() => router.push('/')}
onClick={() => router.push('/dashboard')}
className="text-sm text-gray-600 hover:text-gray-900 mb-2 flex items-center"
>
&larr; Back to Dashboard
@@ -12,13 +12,13 @@ export default function PreviewPage() {
useEffect(() => {
if (!isLoading && isAuthenticated && user && !isPreviewBlocked) {
if (user.isTenantAdmin || user.isGalacticAdmin) {
router.push("/");
router.push("/dashboard");
} else {
router.push("/progress");
}
}
if (!isLoading && !isAuthenticated) {
router.push("/landing");
router.push("/");
}
}, [isAuthenticated, isLoading, user, isPreviewBlocked, router]);
@@ -61,7 +61,7 @@ export default function PrivacyPage() {
</div>
<div className="flex items-center gap-4 text-sm">
<Link href="/landing" className="text-blue-700 hover:text-blue-800 font-medium">Landing page</Link>
<Link href="/" className="text-blue-700 hover:text-blue-800 font-medium">Landing page</Link>
<Link href="/docs" className="text-blue-700 hover:text-blue-800 font-medium">
Documentation
</Link>
@@ -27,7 +27,7 @@ export default function RoadmapPage() {
<div className="mt-10">
<Link
href="/landing"
href="/"
className="inline-flex items-center text-sm font-medium text-blue-700 hover:text-blue-800"
>
Landing page
+2 -2
View File
@@ -5,9 +5,9 @@ export default function robots(): MetadataRoute.Robots {
rules: [
{
userAgent: "*",
allow: ["/landing", "/about", "/docs", "/roadmap", "/privacy", "/terms"],
allow: ["/", "/about", "/docs", "/roadmap", "/privacy", "/terms"],
disallow: [
"/",
"/dashboard",
"/fleet-health",
"/health-check",
"/usage-metrics",
@@ -528,7 +528,7 @@ export default function SessionDetailPage() {
<div className="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8 flex items-center justify-between">
<div>
<button
onClick={() => router.push('/')}
onClick={() => router.push('/dashboard')}
className="text-sm text-gray-600 hover:text-gray-900 mb-2 flex items-center"
>
← Back to Dashboard
@@ -560,7 +560,7 @@ export default function SettingsPage() {
<div className="flex items-center justify-between">
<div>
<button
onClick={() => router.push("/")}
onClick={() => router.push("/dashboard")}
className="text-sm text-gray-600 hover:text-gray-900 mb-2 flex items-center"
>
&larr; Back to Dashboard
+1 -1
View File
@@ -5,7 +5,7 @@ const BASE_URL = "https://autopilotmonitor.com";
export default function sitemap(): MetadataRoute.Sitemap {
return [
{
url: `${BASE_URL}/landing`,
url: `${BASE_URL}/`,
lastModified: new Date(),
changeFrequency: "monthly",
priority: 1,
@@ -48,7 +48,7 @@ export default function TermsPage() {
</div>
<div className="flex items-center gap-4 text-sm">
<Link href="/landing" className="text-blue-700 hover:text-blue-800 font-medium">Landing page</Link>
<Link href="/" className="text-blue-700 hover:text-blue-800 font-medium">Landing page</Link>
<Link href="/docs" className="text-blue-700 hover:text-blue-800 font-medium">
Documentation
</Link>
@@ -156,7 +156,7 @@ export default function UsageMetricsPage() {
Retry
</button>
<button
onClick={() => router.push('/')}
onClick={() => router.push('/dashboard')}
className="w-full px-4 py-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300 transition-colors"
>
Back to Home
@@ -180,7 +180,7 @@ export default function UsageMetricsPage() {
<div className="flex items-center justify-between">
<div>
<button
onClick={() => router.push('/')}
onClick={() => router.push('/dashboard')}
className="text-sm text-gray-600 hover:text-gray-900 mb-2 flex items-center"
>
&larr; Back to Dashboard
@@ -72,8 +72,8 @@ export default function Navbar() {
return () => document.removeEventListener('mousedown', handleClickOutside);
}, []);
// Don't render navbar on landing page
if (pathname === '/landing') {
// Don't render navbar on landing page (root route)
if (pathname === '/') {
return null;
}
@@ -20,7 +20,7 @@ export function ProtectedRoute({ children, requireGalacticAdmin = false }: Prote
useEffect(() => {
if (!isLoading && !isAuthenticated) {
// Not authenticated -> redirect to landing
router.push("/landing");
router.push("/");
}
// Note: We don't automatically redirect for galactic admin check
// Instead, we show the "Access Denied" dialog and let user click "Back to Home"
@@ -54,7 +54,7 @@ export function ProtectedRoute({ children, requireGalacticAdmin = false }: Prote
<h2 className="text-xl font-semibold text-gray-900 mb-2">Access Denied</h2>
<p className="text-gray-600 mb-6">You need Galactic Admin permissions to access this page.</p>
<button
onClick={() => router.push("/")}
onClick={() => router.push("/dashboard")}
className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
>
Back to Home
@@ -21,7 +21,7 @@ export function PublicSiteNavbar({ showSectionLinks }: { showSectionLinks: boole
}`}
>
<div className="max-w-7xl mx-auto px-4 sm:px-6 flex items-center justify-between h-14">
<Link href="/landing" className="flex items-center space-x-2.5 shrink-0">
<Link href="/" className="flex items-center space-x-2.5 shrink-0">
<div className="w-8 h-8 bg-gradient-to-br from-blue-600 to-indigo-600 rounded-lg flex items-center justify-center">
<svg className="w-5 h-5 text-white" viewBox="0 0 24 24" fill="none">
<rect x="5.0" y="12.2" width="2.8" height="7.8" rx="0.9" fill="currentColor" />
@@ -15,7 +15,7 @@ export const msalConfig: Configuration = {
clientId: process.env.NEXT_PUBLIC_ENTRA_CLIENT_ID || "YOUR_CLIENT_ID_HERE",
authority: "https://login.microsoftonline.com/organizations", // Multi-tenant
redirectUri: process.env.NEXT_PUBLIC_ENTRA_REDIRECT_URI || "http://localhost:3000",
postLogoutRedirectUri: process.env.NEXT_PUBLIC_ENTRA_POST_LOGOUT_REDIRECT_URI || "http://localhost:3000/landing",
postLogoutRedirectUri: process.env.NEXT_PUBLIC_ENTRA_POST_LOGOUT_REDIRECT_URI || "http://localhost:3000",
navigateToLoginRequestUrl: true,
},
cache: {
+6 -1
View File
@@ -1,8 +1,13 @@
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
/* config options here */
reactStrictMode: true,
async redirects() {
return [
// Permanent redirect for old /landing URL — all SEO equity flows to /
{ source: "/landing", destination: "/", permanent: true },
];
},
};
export default nextConfig;