Add SEO (including better tab titles)

This commit is contained in:
MartinBraquet
2025-10-21 20:29:10 +02:00
parent d9fba6ce6b
commit 3fcef24cc9
24 changed files with 148 additions and 42 deletions

View File

@@ -3,6 +3,8 @@ import {Col} from "web/components/layout/col";
import ReactMarkdown from "react-markdown";
import React from "react";
import Link from "next/link";
import {SEO} from "web/components/SEO";
import {capitalize} from "lodash";
type Props = {
content: string;
@@ -26,8 +28,14 @@ const MarkdownLink = ({href, children}: { href?: string; children: React.ReactNo
}
export default function MarkdownPage({content, filename}: Props) {
const title = /[A-Z]/.test(filename) ? filename : capitalize(filename)
return (
<PageBase trackPageView={filename} className={'col-span-8'}>
<SEO
title={title}
description={title}
url={`/` + filename}
/>
<Col className="items-center">
<Col className='w-full rounded px-3 py-4 sm:px-6 space-y-4 custom-link'>
<ReactMarkdown

View File

@@ -6,10 +6,17 @@ import { Col } from 'web/components/layout/col'
import { Title } from 'web/components/widgets/title'
import { ExternalLinkIcon } from '@heroicons/react/outline'
import {discordLink, formLink, githubIssues} from "common/constants";
import {SEO} from "web/components/SEO";
import React from "react";
export default function Custom404(props: { customText?: string }) {
return (
<PageBase trackPageView={'404'}>
<SEO
title={'Not Found'}
description={'Not Found'}
url={`/404`}
/>
<Custom404Content customText={props.customText} />
</PageBase>
)

View File

@@ -1,7 +1,8 @@
import {PageBase} from 'web/components/page-base'
import {ReactNode} from "react";
import React, {ReactNode} from "react";
import Link from "next/link";
import {discordLink, formLink, githubRepo} from "common/constants";
import {SEO} from "web/components/SEO";
export const AboutBlock = (props: {
@@ -18,6 +19,11 @@ export const AboutBlock = (props: {
export default function About() {
return (
<PageBase trackPageView={'about'}>
<SEO
title={'About'}
description={'About Compass'}
url={`/about`}
/>
<div className="text-gray-600 dark:text-white min-h-screen p-6">
<div className="w-full">
<div className="relative py-8 mb-8 overflow-hidden">

View File

@@ -1,9 +1,16 @@
import {PageBase} from 'web/components/page-base'
import {Col} from 'web/components/layout/col'
import {SEO} from "web/components/SEO";
import React from "react";
export default function ConfirmEmail() {
return (
<PageBase trackPageView={'private messages page'}>
<SEO
title={'Confirm Email'}
description={'Confirm your email'}
url={`/confirm-email`}
/>
<Col className="items-center justify-center h-full">
<div className="text-xl font-semibold text-center mt-8">
Thank you for confirming your email!

View File

@@ -8,12 +8,12 @@ const FILENAME = __filename.split('/').pop()?.split('.').shift();
export async function getStaticProps() {
const filePath = path.join(process.cwd(), 'public', 'md', FILENAME + '.md');
const content = fs.readFileSync(filePath, 'utf8');
return {props: {content}};
return {props: {content, filename: FILENAME}};
}
type Props = { content: string };
type Props = { content: string, filename: string };
export default function Faq({content}: Props) {
if (!FILENAME) throw new Error('Could not determine filename');
return <MarkdownPage content={content} filename={FILENAME}></MarkdownPage>
export default function Page({content, filename}: Props) {
if (!filename) throw new Error('Could not determine filename');
return <MarkdownPage content={content} filename={filename}></MarkdownPage>
}

View File

@@ -6,7 +6,7 @@ import {ContactComponent} from "web/components/contact";
export default function ContactPage() {
return (
<PageBase
trackPageView={'vote page'}
trackPageView={'contact page'}
className={'relative p-2 sm:pt-0'}
>
<SEO

View File

@@ -7,12 +7,12 @@ const FILENAME = __filename.split('/').pop()?.split('.').shift();
export async function getStaticProps() {
const filePath = path.join(process.cwd(), 'public', 'md', FILENAME + '.md');
const content = fs.readFileSync(filePath, 'utf8');
return {props: {content}};
return {props: {content, filename: FILENAME}};
}
type Props = { content: string };
type Props = { content: string, filename: string };
export default function Faq({content}: Props) {
if (!FILENAME) throw new Error('Could not determine filename');
return <MarkdownPage content={content} filename={FILENAME}></MarkdownPage>
}
export default function Page({content, filename}: Props) {
if (!filename) throw new Error('Could not determine filename');
return <MarkdownPage content={content} filename={filename.toUpperCase()}></MarkdownPage>
}

View File

@@ -8,12 +8,12 @@ const FILENAME = __filename.split('/').pop()?.split('.').shift();
export async function getStaticProps() {
const filePath = path.join(process.cwd(), 'public', 'md', FILENAME + '.md');
const content = fs.readFileSync(filePath, 'utf8');
return {props: {content}};
return {props: {content, filename: FILENAME}};
}
type Props = { content: string };
type Props = { content: string, filename: string };
export default function Faq({content}: Props) {
if (!FILENAME) throw new Error('Could not determine filename');
return <MarkdownPage content={content} filename={FILENAME}></MarkdownPage>
export default function Page({content, filename}: Props) {
if (!filename) throw new Error('Could not determine filename');
return <MarkdownPage content={content} filename={filename}></MarkdownPage>
}

View File

@@ -3,10 +3,16 @@ import {SEO} from 'web/components/SEO'
import Link from 'next/link'
import {Col} from 'web/components/layout/col'
import {Row} from 'web/components/layout/row'
import React from "react";
export default function HelpPage() {
return (
<PageBase trackPageView={'help'} className={'relative p-2 sm:pt-0'}>
<SEO
title={'Help'}
description={'Help and support for Compass'}
url={`/help`}
/>
<SEO title={`Help`} description={'Get help with Compass'} url={`/help`}/>
<Col className="max-w-3xl w-full mx-auto gap-6 custom-link">
<h1 className="text-3xl font-semibold">Help & Support</h1>

View File

@@ -3,6 +3,7 @@ import {Col} from 'web/components/layout/col'
import {useUser} from 'web/hooks/use-user'
import {LoggedOutHome} from "web/components/home/home";
import {ProfilesHome} from "web/components/profiles/profiles-home";
import React from "react";
export default function ProfilesPage() {

View File

@@ -8,12 +8,12 @@ const FILENAME = __filename.split('/').pop()?.split('.').shift();
export async function getStaticProps() {
const filePath = path.join(process.cwd(), 'public', 'md', FILENAME + '.md');
const content = fs.readFileSync(filePath, 'utf8');
return {props: {content}};
return {props: {content, filename: FILENAME}};
}
type Props = { content: string };
type Props = { content: string, filename: string };
export default function Faq({content}: Props) {
if (!FILENAME) throw new Error('Could not determine filename');
return <MarkdownPage content={content} filename={FILENAME}></MarkdownPage>
export default function Page({content, filename}: Props) {
if (!filename) throw new Error('Could not determine filename');
return <MarkdownPage content={content} filename={filename}></MarkdownPage>
}

View File

@@ -3,7 +3,7 @@ import {useRouter} from 'next/router'
import {usePrivateMessages, useSortedPrivateMessageMemberships,} from 'web/hooks/use-private-messages'
import {Col} from 'web/components/layout/col'
import {User} from 'common/user'
import {useEffect, useState} from 'react'
import React, {useEffect, useState} from 'react'
import {track} from 'web/lib/service/analytics'
import {firebaseLogin} from 'web/lib/firebase/users'
import {uniq} from 'lodash'
@@ -32,6 +32,7 @@ import {useGroupedMessages, usePaginatedScrollingMessages,} from 'web/lib/supaba
import {PrivateMessageChannel} from 'common/supabase/private-messages'
import {ChatMessage} from 'common/chat-message'
import {BackButton} from 'web/components/back-button'
import {SEO} from "web/components/SEO";
export default function PrivateMessagesPage() {
const router = useRouter()
@@ -44,6 +45,11 @@ export default function PrivateMessagesPage() {
}
return (
<PageBase trackPageView={'private messages page'}>
<SEO
title={'Messages'}
description={'Messages'}
url={`/messages/${channelIdString}`}
/>
{router.isReady && channelId && user ? (
<PrivateMessagesContent user={user} channelId={channelId}/>
) : (

View File

@@ -19,6 +19,8 @@ import { useRedirectIfSignedOut } from 'web/hooks/use-redirect-if-signed-out'
import { MultipleOrSingleAvatars } from 'web/components/multiple-or-single-avatars'
import { BannedBadge } from 'web/components/widgets/user-link'
import { PrivateMessageChannel } from 'common/supabase/private-messages'
import {SEO} from "web/components/SEO";
import React from "react";
export default function MessagesPage() {
useRedirectIfSignedOut()
@@ -26,6 +28,11 @@ export default function MessagesPage() {
const currentUser = useUser()
return (
<PageBase trackPageView={'messages page'} className={'p-2'}>
<SEO
title={'Messages'}
description={'Your Messages'}
url={`/messages`}
/>
{currentUser && <MessagesContent currentUser={currentUser} />}
</PageBase>
)

View File

@@ -2,11 +2,18 @@ import {PageBase} from 'web/components/page-base'
import {GeneralButton} from "web/components/buttons/general-button";
import clsx from "clsx";
import {Col} from "web/components/layout/col";
import {SEO} from "web/components/SEO";
import React from "react";
export default function Organization() {
return (
<PageBase trackPageView={'social'}>
<SEO
title={'Organization'}
description={'Organization'}
url={`/organization`}
/>
<h3 className="text-4xl font-bold text-center mt-8 mb-8">Organization</h3>
<Col
className={clsx(

View File

@@ -1,5 +1,7 @@
import {PageBase} from "web/components/page-base";
import {supportEmail} from "common/constants";
import {SEO} from "web/components/SEO";
import React from "react";
// TODO: convert to MarkDown for better readability during modifications?
export default function PrivacyPage() {
@@ -8,6 +10,11 @@ export default function PrivacyPage() {
trackPageView={'terms'}
className="max-w-4xl mx-auto p-8 col-span-8 bg-canvas-0"
>
<SEO
title={'Privacy'}
description={'Privacy Policy for Compass'}
url={`/privacy`}
/>
<h1 className="text-3xl font-semibold text-center mb-6">Privacy Policy</h1>
<p className="text-center mb-12">

View File

@@ -5,11 +5,12 @@ import {OptionalProfileUserForm} from 'web/components/optional-profile-form'
import {RequiredProfileUserForm} from 'web/components/required-profile-form'
import {useProfileByUser} from 'web/hooks/use-profile'
import Router from 'next/router'
import {useEffect, useState} from 'react'
import React, {useEffect, useState} from 'react'
import {Col} from 'web/components/layout/col'
import {useUser} from 'web/hooks/use-user'
import {api} from 'web/lib/api'
import {PageBase} from "web/components/page-base";
import {SEO} from "web/components/SEO";
export default function ProfilePage() {
const user = useUser()
@@ -41,6 +42,11 @@ function ProfilePageInner(props: { user: User; profile: Profile }) {
return (
<PageBase trackPageView={'profile'}>
<SEO
title={'Profile'}
description={'Your Profile'}
url={`/profile`}
/>
<Col className="items-center">
<Col className={'w-full px-6 py-4'}>
<RequiredProfileUserForm

View File

@@ -1,6 +1,6 @@
"use client";
import {Suspense, useEffect, useState} from "react";
import React, {Suspense, useEffect, useState} from "react";
import Link from "next/link";
import {useSearchParams} from "next/navigation";
import {signupThenMaybeRedirectToSignup} from "web/lib/util/signup";
@@ -14,6 +14,7 @@ import {db} from "web/lib/supabase/db";
import Router from "next/router";
import {useUser} from "web/hooks/use-user";
import {GoogleButton} from "web/components/buttons/sign-up-button";
import {SEO} from "web/components/SEO";
export default function RegisterPage() {
@@ -111,6 +112,11 @@ function RegisterComponent() {
return (
<PageBase trackPageView={'register'}>
<SEO
title={'Register'}
description={'Register for a new account'}
url={`/register`}
/>
<div className="min-h-screen flex items-center justify-center py-12 px-4 sm:px-6 lg:px-8">
<div className="max-w-md w-full space-y-8">
{registrationSuccess ? (

View File

@@ -1,7 +1,7 @@
"use client";
import {useSearchParams} from "next/navigation";
import {Suspense, useEffect, useState} from "react";
import React, {Suspense, useEffect, useState} from "react";
import Link from "next/link";
import {auth, firebaseLogin} from "web/lib/firebase/users";
import FavIcon from "web/public/FavIcon";
@@ -13,6 +13,7 @@ import Router from "next/router";
import {PageBase} from "web/components/page-base";
import {useUser} from "web/hooks/use-user";
import {GoogleButton} from "web/components/buttons/sign-up-button";
import {SEO} from "web/components/SEO";
export default function LoginPage() {
return (
@@ -118,6 +119,11 @@ function RegisterComponent() {
console.debug('Form rendering');
return (
<PageBase trackPageView={'signin'}>
<SEO
title={'Sign In'}
description={'Sign in to your account'}
url={`/signin`}
/>
<div className="min-h-screen flex items-center justify-center py-12 px-4 sm:px-6 lg:px-8">
<div className="max-w-md w-full space-y-8">
<div>

View File

@@ -1,4 +1,4 @@
import {useEffect, useRef, useState} from 'react'
import React, {useEffect, useRef, useState} from 'react'
import {Col} from 'web/components/layout/col'
import {initialRequiredState, RequiredProfileUserForm,} from 'web/components/required-profile-form'
import {OptionalProfileUserForm} from 'web/components/optional-profile-form'
@@ -14,6 +14,7 @@ import {removeNullOrUndefinedProps} from 'common/util/object'
import {useProfileByUserId} from 'web/hooks/use-profile'
import {ProfileRow} from 'common/profiles/profile'
import {PageBase} from "web/components/page-base";
import {SEO} from "web/components/SEO";
export default function SignupPage() {
const [step, setStep] = useState(0)
@@ -72,6 +73,11 @@ export default function SignupPage() {
if (step === 1 && user) {
return <PageBase trackPageView={'register'}>
<SEO
title={'Sign Up'}
description={'Sign Up to Compass'}
url={`/signup`}
/>
<Col className={'w-full px-6 py-4'}>
<OptionalProfileUserForm
setProfile={setProfileState}

View File

@@ -3,11 +3,18 @@ import {discordLink, githubRepo, redditLink, stoatLink, supportEmail, xLink} fro
import {GeneralButton} from "web/components/buttons/general-button";
import clsx from "clsx";
import {Col} from "web/components/layout/col";
import {SEO} from "web/components/SEO";
import React from "react";
export default function Social() {
return (
<PageBase trackPageView={'social'}>
<SEO
title={'Socials'}
description={'Socials'}
url={`/social`}
/>
<h3 className="text-4xl font-bold text-center mt-8 mb-8">Socials</h3>
<Col
className={clsx(

View File

@@ -1,10 +1,11 @@
import {PageBase} from "web/components/page-base";
import ChartMembers from "web/components/widgets/charts";
import {getCount} from "web/lib/supabase/users";
import {useEffect, useState} from "react";
import React, {useEffect, useState} from "react";
import StatBox from "web/components/widgets/stat-box";
import clsx from "clsx";
import {Col} from "web/components/layout/col";
import {SEO} from "web/components/SEO";
export default function Stats() {
const [data, setData] = useState<Record<string, number | null>>({})
@@ -42,7 +43,12 @@ export default function Stats() {
}, [])
return (
<PageBase trackPageView={'charts'}>
<PageBase trackPageView={'stats'}>
<SEO
title={'Stats'}
description={'Stats'}
url={`/stats`}
/>
<h1 className="text-3xl font-semibold text-center mb-6">Growth & Stats</h1>
<Col className={'sm:mx-4 mx-1 mb-8'}>
<ChartMembers/>

View File

@@ -8,12 +8,12 @@ const FILENAME = __filename.split('/').pop()?.split('.').shift();
export async function getStaticProps() {
const filePath = path.join(process.cwd(), 'public', 'md', FILENAME + '.md');
const content = fs.readFileSync(filePath, 'utf8');
return {props: {content}};
return {props: {content, filename: FILENAME}};
}
type Props = { content: string };
type Props = { content: string, filename: string };
export default function Faq({content}: Props) {
if (!FILENAME) throw new Error('Could not determine filename');
return <MarkdownPage content={content} filename={FILENAME}></MarkdownPage>
}
export default function Page({content, filename}: Props) {
if (!filename) throw new Error('Could not determine filename');
return <MarkdownPage content={content} filename={filename}></MarkdownPage>
}

View File

@@ -1,5 +1,7 @@
import {PageBase} from "web/components/page-base";
import {supportEmail} from "common/constants";
import {SEO} from "web/components/SEO";
import React from "react";
// TODO: convert to MarkDown for better readability during modifications?
export default function TermsPage() {
@@ -8,6 +10,11 @@ export default function TermsPage() {
trackPageView={'terms'}
className="max-w-4xl mx-auto p-8 text-gray-800 dark:text-white col-span-8 bg-canvas-0"
>
<SEO
title={'Terms & Conditions'}
description={'Terms & Conditions for Compass'}
url={`/terms`}
/>
<h1 className="text-3xl font-semibold text-center mb-6">Terms & Conditions</h1>
<p className="text-center text-gray-500 dark:text-white mb-12">

View File

@@ -8,12 +8,12 @@ const FILENAME = __filename.split('/').pop()?.split('.').shift();
export async function getStaticProps() {
const filePath = path.join(process.cwd(), 'public', 'md', FILENAME + '.md');
const content = fs.readFileSync(filePath, 'utf8');
return {props: {content}};
return {props: {content, filename: FILENAME}};
}
type Props = { content: string };
type Props = { content: string, filename: string };
export default function Faq({content}: Props) {
if (!FILENAME) throw new Error('Could not determine filename');
return <MarkdownPage content={content} filename={FILENAME}></MarkdownPage>
}
export default function Page({content, filename}: Props) {
if (!filename) throw new Error('Could not determine filename');
return <MarkdownPage content={content} filename={filename}></MarkdownPage>
}