Skip to content

Commit

Permalink
Add meta tags (#632)
Browse files Browse the repository at this point in the history
* Add meta tags

* Compat with Next15

* Move util out of a hook
  • Loading branch information
neoxelox authored Nov 19, 2024
1 parent 02810ce commit 3f610b2
Show file tree
Hide file tree
Showing 17 changed files with 239 additions and 5 deletions.
5 changes: 5 additions & 0 deletions apps/web/src/app/(admin)/backoffice/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import { ReactNode } from 'react'

import { SessionProvider } from '@latitude-data/web-ui'
import buildMetatags from '$/app/_lib/buildMetatags'
import { getCurrentUser } from '$/services/auth/getCurrentUser'
import { getSession } from '$/services/auth/getSession'
import { ROUTES } from '$/services/routes'
import { redirect } from 'next/navigation'

import { BackofficeTabs } from './_components/BackofficeTabs'

export const metadata = buildMetatags({
title: 'Backoffice',
})

export default async function AdminLayout({
children,
}: {
Expand Down
5 changes: 5 additions & 0 deletions apps/web/src/app/(private)/dashboard/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
TableBlankSlate,
TableWithHeader,
} from '@latitude-data/web-ui'
import buildMetatags from '$/app/_lib/buildMetatags'
import { AppTabs } from '$/app/(private)/AppTabs'
import { getCurrentUser } from '$/services/auth/getCurrentUser'
import { ROUTES } from '$/services/routes'
Expand All @@ -13,6 +14,10 @@ import Link from 'next/link'
import { getActiveProjectsCached } from '../_data-access'
import { ProjectsTable } from './_components/ProjectsTable'

export const metadata = buildMetatags({
title: 'Dashboard',
})

export default async function DashboardLayout({
children,
}: Readonly<{
Expand Down
5 changes: 5 additions & 0 deletions apps/web/src/app/(private)/datasets/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,17 @@ import { ReactNode } from 'react'

import { DatasetsRepository } from '@latitude-data/core/repositories'
import { Container, TableWithHeader } from '@latitude-data/web-ui'
import buildMetatags from '$/app/_lib/buildMetatags'
import { AppTabs } from '$/app/(private)/AppTabs'
import { DatasetsTable } from '$/app/(private)/datasets/_components/DatasetsTable'
import { getCurrentUser } from '$/services/auth/getCurrentUser'
import { ROUTES } from '$/services/routes'
import Link from 'next/link'

export const metadata = buildMetatags({
title: 'Datasets',
})

export default async function DatasetsList({
children,
}: Readonly<{
Expand Down
27 changes: 27 additions & 0 deletions apps/web/src/app/(private)/datasets/preview/[datasetId]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,34 @@
import { NotFoundError } from '@latitude-data/core/lib/errors'
import buildMetatags from '$/app/_lib/buildMetatags'
import { getDatasetCached } from '$/app/(private)/_data-access'
import type { ResolvingMetadata } from 'next'
import { notFound } from 'next/navigation'

import PreviewDatasetModal from './_components/PreviewDatasetModal'

export async function generateMetadata(
{
params,
}: {
params: Promise<{ datasetId: string }>
},
parent: ResolvingMetadata,
) {
const { datasetId } = await params

try {
const dataset = await getDatasetCached(datasetId)

return buildMetatags({
title: `${dataset.name} (preview)`,
parent: await parent,
})
} catch (error) {
if (error instanceof NotFoundError) return notFound()
throw error
}
}

export default async function DatasetPreviewPage({
params,
}: {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { ReactNode } from 'react'

import { NotFoundError } from '@latitude-data/core/lib/errors'
import buildMetatags from '$/app/_lib/buildMetatags'
import { getEvaluationByUuidCached } from '$/app/(private)/_data-access'
import type { ResolvingMetadata } from 'next'
import { notFound } from 'next/navigation'

export async function generateMetadata(
{
params,
}: {
params: Promise<{ evaluationUuid: string }>
},
parent: ResolvingMetadata,
) {
const { evaluationUuid } = await params

try {
const evaluation = await getEvaluationByUuidCached(evaluationUuid)

return buildMetatags({
title: evaluation.name,
parent: await parent,
})
} catch (error) {
if (error instanceof NotFoundError) return notFound()
throw error
}
}

export default async function EvaluationLayout({
children,
}: Readonly<{
children: ReactNode
}>) {
return <>{children}</>
}
15 changes: 15 additions & 0 deletions apps/web/src/app/(private)/evaluations/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { ReactNode } from 'react'

import buildMetatags from '$/app/_lib/buildMetatags'

export const metadata = buildMetatags({
title: 'Evaluations',
})

export default async function EvaluationsLayout({
children,
}: Readonly<{
children: ReactNode
}>) {
return <>{children}</>
}
5 changes: 5 additions & 0 deletions apps/web/src/app/(private)/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ReactNode } from 'react'

import { SessionProvider } from '@latitude-data/web-ui/browser'
import buildMetatags from '$/app/_lib/buildMetatags'
import { createSupportUserIdentity } from '$/app/(private)/_lib/createSupportUserIdentity'
import { SupportChat } from '$/components/IntercomSupportChat'
import { AppLayout } from '$/components/layouts'
Expand All @@ -17,6 +18,10 @@ import { redirect } from 'next/navigation'
import { CSPostHogProvider, IdentifyUser } from '../providers'
import { NAV_LINKS } from './_lib/constants'

export const metadata = buildMetatags({
title: 'Home',
})

export default async function PrivateLayout({
children,
}: Readonly<{
Expand Down
43 changes: 43 additions & 0 deletions apps/web/src/app/(private)/projects/[projectId]/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { ReactNode } from 'react'

import { NotFoundError } from '@latitude-data/core/lib/errors'
import buildMetatags from '$/app/_lib/buildMetatags'
import { findProjectCached } from '$/app/(private)/_data-access'
import { getCurrentUser } from '$/services/auth/getCurrentUser'
import type { ResolvingMetadata } from 'next'
import { notFound } from 'next/navigation'

export async function generateMetadata(
{
params,
}: {
params: Promise<{ projectId: string }>
},
parent: ResolvingMetadata,
) {
const { projectId } = await params

try {
const session = await getCurrentUser()
const project = await findProjectCached({
projectId: Number(projectId),
workspaceId: session.workspace.id,
})

return buildMetatags({
title: project.name,
parent: await parent,
})
} catch (error) {
if (error instanceof NotFoundError) return notFound()
throw error
}
}

export default async function ProjectLayout({
children,
}: Readonly<{
children: ReactNode
}>) {
return <>{children}</>
}
15 changes: 15 additions & 0 deletions apps/web/src/app/(private)/projects/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { ReactNode } from 'react'

import buildMetatags from '$/app/_lib/buildMetatags'

export const metadata = buildMetatags({
title: 'Projects',
})

export default async function ProjectsLayout({
children,
}: Readonly<{
children: ReactNode
}>) {
return <>{children}</>
}
5 changes: 5 additions & 0 deletions apps/web/src/app/(private)/settings/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import { ReactNode } from 'react'

import { Container, TitleWithActions } from '@latitude-data/web-ui'
import buildMetatags from '$/app/_lib/buildMetatags'
import { AppTabs } from '$/app/(private)/AppTabs'

import Memberships from './_components/Memberships'
import ProviderApiKeys from './_components/ProviderApiKeys'
import WorkspaceApiKeys from './_components/WorkspaceApiKeys'
import WorkspaceName from './_components/WorkspaceName'

export const metadata = buildMetatags({
title: 'Settings',
})

export default async function SettingsLayout({
children,
}: Readonly<{
Expand Down
5 changes: 5 additions & 0 deletions apps/web/src/app/(public)/invitations/[token]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { NotFoundError } from '@latitude-data/core/lib/errors'
import { Card, CardContent, FocusHeader } from '@latitude-data/web-ui'
import buildMetatags from '$/app/_lib/buildMetatags'
import { FocusLayout } from '$/components/layouts'
import { ROUTES } from '$/services/routes'
import { redirect } from 'next/navigation'
Expand All @@ -14,6 +15,10 @@ import InvitationForm from './InvitationForm'

export const dynamic = 'force-dynamic'

export const metadata = buildMetatags({
title: 'You have been invited to join a workspace',
})

export default async function InvitationPage({
params,
}: {
Expand Down
5 changes: 5 additions & 0 deletions apps/web/src/app/(public)/login/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Card, CardContent, FocusHeader } from '@latitude-data/web-ui'
import buildMetatags from '$/app/_lib/buildMetatags'
import AuthFooter from '$/app/(public)/_components/Footer'
import LoginFooter from '$/app/(public)/login/_components/LoginFooter'
import { FocusLayout } from '$/components/layouts'
Expand All @@ -10,6 +11,10 @@ import LoginForm from './LoginForm'

export const dynamic = 'force-dynamic'

export const metadata = buildMetatags({
title: 'Login to your account',
})

export default async function LoginPage() {
const data = await getSession()
if (data.session) return redirect(ROUTES.dashboard.root)
Expand Down
5 changes: 5 additions & 0 deletions apps/web/src/app/(public)/magic-links/sent/page.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import { FocusHeader } from '@latitude-data/web-ui'
import buildMetatags from '$/app/_lib/buildMetatags'
import { FocusLayout } from '$/components/layouts'
import { ROUTES } from '$/services/routes'
import { redirect } from 'next/navigation'

export const metadata = buildMetatags({
title: 'Login to your account',
})

export default async function MagicLinkSent({
searchParams,
}: {
Expand Down
5 changes: 5 additions & 0 deletions apps/web/src/app/(public)/setup/page.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import { Card, CardContent, FocusHeader } from '@latitude-data/web-ui'
import buildMetatags from '$/app/_lib/buildMetatags'
import AuthFooter from '$/app/(public)/_components/Footer'
import { FocusLayout } from '$/components/layouts'

import SetupForm from './SetupForm'

export const dynamic = 'force-dynamic'

export const metadata = buildMetatags({
title: 'Create an account',
})

export default async function SetupPage({
searchParams,
}: {
Expand Down
47 changes: 47 additions & 0 deletions apps/web/src/app/_lib/buildMetatags.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { env } from '@latitude-data/env'
import type { Metadata, ResolvedMetadata } from 'next'

const DEFAULT_TITLE = 'The Open-Source LLM Development Platform'
const DEFAULT_DESCRIPTION =
'Latitude is an end-to-end platform for prompt engineering where domain experts can collaborate with engineers to ship and maintain production-grade LLM features.'

// This function is necessary to define default metadata correctly, because
// Nextjs metadata merging would overwrite the nested objects totally.
export default function buildMetatags({
title,
description,
parent,
}: {
title?: string
description?: string
parent?: ResolvedMetadata
}): Metadata {
let parentTitle = parent?.title?.absolute || ''
let metaTitle =
title && parentTitle
? `${title} - ${parentTitle}`
: title || parentTitle || DEFAULT_TITLE
if (!metaTitle.endsWith(' - Latitude')) metaTitle += ' - Latitude'

const metaDescription = description || DEFAULT_DESCRIPTION

return {
metadataBase: new URL(env.LATITUDE_URL),
title: metaTitle,
description: metaDescription,
openGraph: {
// Note, og:url is not set because there is no way to get
// the current url in server components, other than hacks
// like getting it from the HTTP headers...
type: 'website',
siteName: 'Latitude',
title: metaTitle,
description: metaDescription,
},
twitter: {
card: 'summary',
title: metaTitle,
description: metaDescription,
},
}
}
9 changes: 4 additions & 5 deletions apps/web/src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ReactNode } from 'react'

import type { Metadata } from 'next'
import buildMetatags from '$/app/_lib/buildMetatags'
import NextTopLoader from 'nextjs-toploader'

import '@latitude-data/web-ui/styles.css'
Expand All @@ -12,10 +12,9 @@ import {
} from '@latitude-data/web-ui'
import { fontMono, fontSans } from '$/helpers/fonts'

export const metadata: Metadata = {
title: 'Latitude App',
description: 'LLM - Latitude App',
}
export const metadata = buildMetatags({
title: 'The Open-Source LLM Development Platform',
})

export default function RootLayout({
children,
Expand Down
5 changes: 5 additions & 0 deletions apps/web/src/app/not-found.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import { Button } from '@latitude-data/web-ui'
import { ErrorComponent } from '@latitude-data/web-ui/browser'
import buildMetatags from '$/app/_lib/buildMetatags'
import { ROUTES } from '$/services/routes'
import Link from 'next/link'

export const metadata = buildMetatags({
title: 'Not found',
})

export default async function GlobalNoFound() {
return (
<ErrorComponent
Expand Down

0 comments on commit 3f610b2

Please sign in to comment.