Skip to content

Commit 4b775ff

Browse files
committed
fix: improve SEO
1 parent a37e37c commit 4b775ff

File tree

10 files changed

+155
-44
lines changed

10 files changed

+155
-44
lines changed

apps/start/public/robots.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
# https://www.robotstxt.org/robotstxt.html
22
User-agent: *
3-
Disallow:
3+
Allow: /share/
4+
Allow: /login
5+
Allow: /onboarding
6+
Disallow: /

apps/start/src/components/login-left-panel.tsx

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { LogoSquare } from '@/components/logo';
21
import {
32
Carousel,
43
CarouselContent,
@@ -64,18 +63,8 @@ const sellingPoints = [
6463
export function LoginLeftPanel() {
6564
return (
6665
<div className="relative h-screen overflow-hidden">
67-
<div className="row justify-between items-center p-8">
68-
<LogoSquare className="h-8 w-8" />
69-
<a
70-
href="https://openpanel.dev"
71-
className="text-sm text-muted-foreground"
72-
>
73-
Back to website →
74-
</a>
75-
</div>
76-
7766
{/* Carousel */}
78-
<div className="flex items-center justify-center h-full">
67+
<div className="flex items-center justify-center h-full mt-24">
7968
<Carousel
8069
className="w-full h-full [&>div]:h-full [&>div]:min-h-full"
8170
opts={{
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import { useAppContext } from '@/hooks/use-app-context';
2+
import { cn } from '@/utils/cn';
3+
import { MenuIcon, XIcon } from 'lucide-react';
4+
import { useState } from 'react';
5+
import { LogoSquare } from './logo';
6+
import { Button, LinkButton } from './ui/button';
7+
8+
export function LoginNavbar({ className }: { className?: string }) {
9+
const { isSelfHosted } = useAppContext();
10+
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
11+
12+
return (
13+
<div
14+
className={cn(
15+
'absolute top-0 left-0 w-full row justify-between items-center p-8 z-10',
16+
className,
17+
)}
18+
>
19+
<a href="https://openpanel.dev" className="row items-center gap-2">
20+
<LogoSquare className="size-8 shrink-0" />
21+
<span className="font-medium text-sm text-muted-foreground">
22+
{isSelfHosted ? 'Self-hosted analytics' : 'OpenPanel.dev'}
23+
</span>
24+
</a>
25+
<nav className="max-md:hidden">
26+
<ul className="row gap-4 items-center [&>li>a]:text-sm [&>li>a]:text-muted-foreground [&>li>a]:hover:underline">
27+
<li>
28+
<a href="https://openpanel.dev">OpenPanel Cloud</a>
29+
</li>
30+
<li>
31+
<a href="https://openpanel.dev/compare/mixpanel-alternative">
32+
Mixpanel alternative
33+
</a>
34+
</li>
35+
<li>
36+
<a href="https://openpanel.dev/compare/mixpanel-alternative">
37+
Posthog alternative
38+
</a>
39+
</li>
40+
<li>
41+
<a href="https://openpanel.dev/articles/open-source-web-analytics">
42+
Open source analytics
43+
</a>
44+
</li>
45+
</ul>
46+
</nav>
47+
<div className="md:hidden relative">
48+
<Button
49+
size="icon"
50+
variant="ghost"
51+
onClick={() => setMobileMenuOpen((prev) => !prev)}
52+
>
53+
{mobileMenuOpen ? <XIcon size={20} /> : <MenuIcon size={20} />}
54+
</Button>
55+
{mobileMenuOpen && (
56+
<>
57+
<button
58+
type="button"
59+
onClick={() => setMobileMenuOpen(false)}
60+
className="fixed inset-0 z-40 bg-black/20 backdrop-blur-sm"
61+
/>
62+
<nav className="absolute right-0 top-full mt-2 z-50 bg-card border border-border rounded-md shadow-lg min-w-48 py-2">
63+
<ul className="flex flex-col *:text-sm *:text-muted-foreground">
64+
<li>
65+
<a
66+
href="https://openpanel.dev"
67+
className="block px-4 py-2 hover:bg-accent hover:text-accent-foreground"
68+
onClick={() => setMobileMenuOpen(false)}
69+
>
70+
OpenPanel Cloud
71+
</a>
72+
</li>
73+
<li>
74+
<a
75+
href="https://openpanel.dev/compare/mixpanel-alternative"
76+
className="block px-4 py-2 hover:bg-accent hover:text-accent-foreground"
77+
onClick={() => setMobileMenuOpen(false)}
78+
>
79+
Posthog alternative
80+
</a>
81+
</li>
82+
<li>
83+
<a
84+
href="https://openpanel.dev/compare/mixpanel-alternative"
85+
className="block px-4 py-2 hover:bg-accent hover:text-accent-foreground"
86+
onClick={() => setMobileMenuOpen(false)}
87+
>
88+
Mixpanel alternative
89+
</a>
90+
</li>
91+
<li>
92+
<a
93+
href="https://openpanel.dev/articles/open-source-web-analytics"
94+
className="block px-4 py-2 hover:bg-accent hover:text-accent-foreground"
95+
onClick={() => setMobileMenuOpen(false)}
96+
>
97+
Open source analytics
98+
</a>
99+
</li>
100+
</ul>
101+
</nav>
102+
</>
103+
)}
104+
</div>
105+
</div>
106+
);
107+
}

apps/start/src/components/onboarding-left-panel.tsx

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -91,19 +91,8 @@ const onboardingSellingPoints = [
9191
export function OnboardingLeftPanel() {
9292
return (
9393
<div className="sticky top-0 h-screen overflow-hidden">
94-
<div className="row justify-between items-center p-8">
95-
<LogoSquare className="h-8 w-8" />
96-
97-
<div className="text-sm text-muted-foreground">
98-
Already have an account?{' '}
99-
<Link to="/login" className="text-sm text-muted-foreground underline">
100-
Sign in
101-
</Link>
102-
</div>
103-
</div>
104-
10594
{/* Carousel */}
106-
<div className="flex items-center justify-center h-full">
95+
<div className="flex items-center justify-center h-full mt-24">
10796
<Carousel
10897
className="w-full h-full [&>div]:h-full [&>div]:min-h-full"
10998
opts={{

apps/start/src/routes/_login.login.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ import { z } from 'zod';
1212
export const Route = createFileRoute('/_login/login')({
1313
component: LoginPage,
1414
head: () => ({
15-
meta: [{ title: createTitle(PAGE_TITLES.LOGIN) }],
15+
meta: [
16+
{ title: createTitle(PAGE_TITLES.LOGIN) },
17+
{ name: 'robots', content: 'noindex, follow' },
18+
],
1619
}),
1720
validateSearch: z.object({
1821
error: z.string().optional(),
@@ -26,11 +29,13 @@ function LoginPage() {
2629
return (
2730
<div className="col gap-8 w-full text-left">
2831
<div>
29-
<LogoSquare className="size-12 mb-8 md:hidden" />
3032
<h1 className="text-3xl font-bold text-foreground mb-2">Sign in</h1>
3133
<p className="text-muted-foreground">
3234
Don't have an account?{' '}
33-
<a href="/onboarding" className="underline">
35+
<a
36+
href="/onboarding"
37+
className="underline font-medium text-foreground"
38+
>
3439
Create one today
3540
</a>
3641
</p>

apps/start/src/routes/_login.reset-password.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ import { z } from 'zod';
66

77
export const Route = createFileRoute('/_login/reset-password')({
88
head: () => ({
9-
meta: [{ title: createTitle(PAGE_TITLES.RESET_PASSWORD) }],
9+
meta: [
10+
{ title: createTitle(PAGE_TITLES.RESET_PASSWORD) },
11+
{ name: 'robots', content: 'noindex, follow' },
12+
],
1013
}),
1114
component: Component,
1215
validateSearch: z.object({

apps/start/src/routes/_login.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { LoginLeftPanel } from '@/components/login-left-panel';
2+
import { LoginNavbar } from '@/components/login-navbar';
23
import { SkeletonDashboard } from '@/components/skeleton-dashboard';
34
import { Outlet, createFileRoute, redirect } from '@tanstack/react-router';
45

@@ -14,6 +15,7 @@ export const Route = createFileRoute('/_login')({
1415
function AuthLayout() {
1516
return (
1617
<div className="relative min-h-screen grid md:grid-cols-2">
18+
<LoginNavbar />
1719
<div className="hidden md:block">
1820
<LoginLeftPanel />
1921
</div>

apps/start/src/routes/_public.onboarding.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export const Route = createFileRoute('/_public/onboarding')({
1818
head: () => ({
1919
meta: [
2020
{ title: createEntityTitle('Create an account', PAGE_TITLES.ONBOARDING) },
21+
{ name: 'robots', content: 'noindex, follow' },
2122
],
2223
}),
2324
beforeLoad: async ({ context }) => {
@@ -56,7 +57,6 @@ function Component() {
5657
return (
5758
<div className="col gap-8 w-full text-left">
5859
<div>
59-
<LogoSquare className="size-12 mb-8 md:hidden" />
6060
<h1 className="text-3xl font-bold text-foreground mb-2">
6161
Create an account
6262
</h1>

apps/start/src/routes/_public.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { LoginNavbar } from '@/components/login-navbar';
12
import { OnboardingLeftPanel } from '@/components/onboarding-left-panel';
23
import { Outlet, createFileRoute, redirect } from '@tanstack/react-router';
34

@@ -8,6 +9,7 @@ export const Route = createFileRoute('/_public')({
89
function OnboardingLayout() {
910
return (
1011
<div className="relative min-h-screen grid md:grid-cols-2">
12+
<LoginNavbar />
1113
<div className="hidden md:block">
1214
<OnboardingLeftPanel />
1315
</div>

apps/start/src/routes/share.overview.$shareId.tsx

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { ShareEnterPassword } from '@/components/auth/share-enter-password';
22
import { FullPageEmptyState } from '@/components/full-page-empty-state';
33
import FullPageLoadingState from '@/components/full-page-loading-state';
4+
import { LoginNavbar } from '@/components/login-navbar';
45
import { OverviewFiltersButtons } from '@/components/overview/filters/overview-filters-buttons';
56
import { LiveCounter } from '@/components/overview/live-counter';
67
import OverviewMetrics from '@/components/overview/overview-metrics';
@@ -24,11 +25,32 @@ export const Route = createFileRoute('/share/overview/$shareId')({
2425
component: RouteComponent,
2526
validateSearch: shareSearchSchema,
2627
loader: async ({ context, params }) => {
27-
await context.queryClient.prefetchQuery(
28+
const share = await context.queryClient.ensureQueryData(
2829
context.trpc.share.overview.queryOptions({
2930
shareId: params.shareId,
3031
}),
3132
);
33+
34+
return { share };
35+
},
36+
head: ({ loaderData }) => {
37+
if (!loaderData || !loaderData.share) {
38+
return {
39+
meta: [
40+
{
41+
title: 'Share not found - OpenPanel.dev',
42+
},
43+
],
44+
};
45+
}
46+
47+
return {
48+
meta: [
49+
{
50+
title: `${loaderData.share.project?.name} - ${loaderData.share.organization?.name} - OpenPanel.dev`,
51+
},
52+
],
53+
};
3254
},
3355
pendingComponent: FullPageLoadingState,
3456
errorComponent: () => (
@@ -78,19 +100,8 @@ function RouteComponent() {
78100
return (
79101
<div>
80102
{isHeaderVisible && (
81-
<div className="mx-auto max-w-7xl justify-between row gap-4 p-4 pb-0">
82-
<div className="col gap-1">
83-
<span className="text-sm">{share.organization?.name}</span>
84-
<h1 className="text-xl font-medium">{share.project?.name}</h1>
85-
</div>
86-
<a
87-
href="https://openpanel.dev?utm_source=openpanel.dev&utm_medium=share"
88-
className="col gap-1 items-end"
89-
title="OpenPanel"
90-
>
91-
<span className="text-xs">POWERED BY</span>
92-
<span className="text-xl font-medium">openpanel.dev</span>
93-
</a>
103+
<div className="mx-auto max-w-7xl">
104+
<LoginNavbar className="relative p-4" />
94105
</div>
95106
)}
96107
<div className="">

0 commit comments

Comments
 (0)