From 83195d43f0492fc14cee6b16dbc227ff06c8728c Mon Sep 17 00:00:00 2001 From: Levi Zitting Date: Sun, 4 Oct 2026 07:17:28 -0500 Subject: [PATCH] chore: remove retired Next.js frontend --- .dockerignore | 3 +- .gitignore | 6 - README.md | 46 ++++- eslint.config.mjs | 9 - next.config.ts | 13 -- src/app/[conference]/[...slug]/layout.tsx | 37 ---- src/app/[conference]/[...slug]/page.tsx | 93 --------- src/app/[conference]/page.tsx | 76 ------- src/app/[conference]/register/layout.tsx | 3 - src/app/[conference]/register/page.tsx | 28 --- .../[conference]/register/success/page.tsx | 46 ----- src/app/[conference]/speak/route.ts | 19 -- src/app/[conference]/tickets/route.ts | 9 - src/app/api/newsletter/route.ts | 29 --- src/app/cms-media/[...path]/route.ts | 39 ---- src/app/globals.css | 44 ----- src/app/icon.png | Bin 6462 -> 0 bytes src/app/layout.tsx | 49 ----- src/app/mobile-app/page.tsx | 27 --- src/app/opengraph-image.jpg | Bin 129919 -> 0 bytes src/app/register/route.ts | 14 -- src/app/robots.ts | 13 -- src/app/route.ts | 12 -- src/app/speak/route.ts | 14 -- src/app/tickets/route.ts | 5 - src/app/umbraco/route.ts | 6 - src/components/Button.tsx | 104 ---------- src/components/ClientMobileAppRedirect.tsx | 33 ---- src/components/CustomAccordionItem.tsx | 64 ------ src/components/DefaultNav.tsx | 54 ----- src/components/Footer.tsx | 63 ------ src/components/HomeNav.tsx | 87 -------- src/components/Navigation.tsx | 21 -- src/components/NewsletterForm.tsx | 85 -------- src/components/OveItEmbed.tsx | 123 ------------ src/components/RichText.tsx | 15 -- .../ScheduleGrid/ScheduleGrid.module.css | 22 --- src/components/ScheduleGrid/ScheduleGrid.tsx | 145 -------------- .../ScheduleGrid/usePartialGridView.ts | 78 -------- src/components/SectionTitleBar.tsx | 16 -- src/components/SessionCard.module.css | 3 - src/components/SessionCard.tsx | 115 ----------- src/components/SponsorCard.tsx | 85 -------- src/components/SponsorTierItem.tsx | 50 ----- src/components/TextInput.tsx | 47 ----- .../IntroAndEmailSignupBlock.tsx | 79 -------- .../contentBlocks/LocationBlock.tsx | 84 -------- .../contentBlocks/RichTextBlock.tsx | 18 -- .../contentBlocks/ScheduleBlock.module.css | 12 -- .../contentBlocks/ScheduleBlock.tsx | 54 ----- .../contentBlocks/SponsorsBlock.tsx | 56 ------ .../contentBlocks/TextWithButtonsBlock.tsx | 27 --- src/components/pageTypes/GenericPage.tsx | 187 ------------------ .../pageTypes/SpeakerDetailPage.tsx | 154 --------------- tsconfig.json | 8 - 55 files changed, 39 insertions(+), 2490 deletions(-) delete mode 100644 next.config.ts delete mode 100644 src/app/[conference]/[...slug]/layout.tsx delete mode 100644 src/app/[conference]/[...slug]/page.tsx delete mode 100644 src/app/[conference]/page.tsx delete mode 100644 src/app/[conference]/register/layout.tsx delete mode 100644 src/app/[conference]/register/page.tsx delete mode 100644 src/app/[conference]/register/success/page.tsx delete mode 100644 src/app/[conference]/speak/route.ts delete mode 100644 src/app/[conference]/tickets/route.ts delete mode 100644 src/app/api/newsletter/route.ts delete mode 100644 src/app/cms-media/[...path]/route.ts delete mode 100644 src/app/globals.css delete mode 100644 src/app/icon.png delete mode 100644 src/app/layout.tsx delete mode 100644 src/app/mobile-app/page.tsx delete mode 100644 src/app/opengraph-image.jpg delete mode 100644 src/app/register/route.ts delete mode 100644 src/app/robots.ts delete mode 100644 src/app/route.ts delete mode 100644 src/app/speak/route.ts delete mode 100644 src/app/tickets/route.ts delete mode 100644 src/app/umbraco/route.ts delete mode 100644 src/components/Button.tsx delete mode 100644 src/components/ClientMobileAppRedirect.tsx delete mode 100644 src/components/CustomAccordionItem.tsx delete mode 100644 src/components/DefaultNav.tsx delete mode 100644 src/components/Footer.tsx delete mode 100644 src/components/HomeNav.tsx delete mode 100644 src/components/Navigation.tsx delete mode 100644 src/components/NewsletterForm.tsx delete mode 100644 src/components/OveItEmbed.tsx delete mode 100644 src/components/RichText.tsx delete mode 100644 src/components/ScheduleGrid/ScheduleGrid.module.css delete mode 100644 src/components/ScheduleGrid/ScheduleGrid.tsx delete mode 100644 src/components/ScheduleGrid/usePartialGridView.ts delete mode 100644 src/components/SectionTitleBar.tsx delete mode 100644 src/components/SessionCard.module.css delete mode 100644 src/components/SessionCard.tsx delete mode 100644 src/components/SponsorCard.tsx delete mode 100644 src/components/SponsorTierItem.tsx delete mode 100644 src/components/TextInput.tsx delete mode 100644 src/components/contentBlocks/IntroAndEmailSignupBlock.tsx delete mode 100644 src/components/contentBlocks/LocationBlock.tsx delete mode 100644 src/components/contentBlocks/RichTextBlock.tsx delete mode 100644 src/components/contentBlocks/ScheduleBlock.module.css delete mode 100644 src/components/contentBlocks/ScheduleBlock.tsx delete mode 100644 src/components/contentBlocks/SponsorsBlock.tsx delete mode 100644 src/components/contentBlocks/TextWithButtonsBlock.tsx delete mode 100644 src/components/pageTypes/GenericPage.tsx delete mode 100644 src/components/pageTypes/SpeakerDetailPage.tsx diff --git a/.dockerignore b/.dockerignore index 24e571e..0f3a30e 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,7 +1,6 @@ .git .github -.env -.next +.env* .svelte-kit backend build diff --git a/.gitignore b/.gitignore index f842bc9..b33fbb6 100644 --- a/.gitignore +++ b/.gitignore @@ -10,9 +10,7 @@ /coverage # framework output -/.next/ /.svelte-kit/ -/out/ # production /build @@ -30,12 +28,8 @@ yarn-error.log* .env*.local .env -# vercel -.vercel - # typescript *.tsbuildinfo -next-env.d.ts .idea /backend/ diff --git a/README.md b/README.md index fb145f2..1e949c2 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,60 @@ # MethodConf -The MethodConf frontend is being migrated to SvelteKit. This branch only contains the temporary SvelteKit shell and tooling base. The old Next.js source stays in the repo as reference until the page migration layers replace it. - -The Umbraco CMS lives in [`sgfdevs/cms.methodconf.com`](https://github.com/sgfdevs/cms.methodconf.com). +The MethodConf frontend uses Svelte 5 and SvelteKit 3. Server-rendered conference pages, schedules, speakers, sponsors, and registration content come from the [Umbraco CMS](https://github.com/sgfdevs/cms.methodconf.com). Static assets and self-hosted fonts live in `static/`; CMS media uses the streaming proxy and image optimizer. ## Local development -1. Copy `.env.example` to `.env` when working on routes that need CMS or newsletter configuration. -2. Install dependencies and start the development server: +Use the Node version in `.nvmrc`. Copy `.env.example` to `.env`, configure your local CMS, then run: ```bash npm ci npm run dev ``` -Useful checks for this foundation layer: +Vite prints the development URL. To check the app: ```bash npm run lint npm run check -npm run test +npm test +``` + +`npm test` runs unit tests, builds the app, and checks routes against local CMS and newsletter fixtures. `npm run test:unit` runs only unit tests. + +## Runtime configuration + +SvelteKit reads the private server variables declared in `src/env.ts` at runtime. Do not expose them through public environment variables. + +- `UMBRACO_BASE_URL` must point to the Umbraco API used by page loaders and the media proxy. +- `CMS_PUBLIC_URL` is the public CMS URL for the `/umbraco/` redirect. +- `SITE_URL` is the canonical frontend URL, including its scheme. +- `SEARCH_INDEXING_ENABLED=true` allows indexing. Any other value disables it. +- `NEWSLETTER_ENDPOINT` and `NEWSLETTER_LIST_ID` configure the server-side newsletter subscription request. Without both, subscriptions return a configuration error. For local work, use a fake local endpoint and list ID so form submissions cannot reach the live service. + +Keep credentials in local environment files or deployment configuration, not in Git. Production Node processes need these variables in their environment; `npm start` does not load `.env` automatically. + +## Production and Docker + +```bash npm run build +npm start ``` -The production smoke image can be built locally with: +The Node adapter writes the server and assets to `build/`. Set `HOST` and `PORT` for the listener and `ORIGIN` when needed behind a reverse proxy. These adapter settings are separate from the canonical `SITE_URL`. ```bash docker build -t methodconf.com . +docker run --rm --env-file .env -p 3000:3000 methodconf.com +``` + +The Dockerfile builds the app, installs production dependencies, and runs `node build` as a non-root user on port 3000. Environment files stay outside the image. The `frontend-prod` Compose profile loads `.env`; `APP_PORT` controls the host port. The `frontend-dev` profile mounts the project for development commands inside the container. + +## Umbraco schemas + +With `UMBRACO_BASE_URL` pointing to the intended CMS, run: + +```bash +npm run generate:umbraco-api-client ``` + +The script reads the delivery and default OpenAPI schemas and writes `src/lib/umbraco/deliveryApiSchema.d.ts` and `src/lib/umbraco/defaultApiSchema.d.ts`. Commit schema changes when the CMS contract changes. diff --git a/eslint.config.mjs b/eslint.config.mjs index e700832..11260e3 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -7,20 +7,11 @@ import tseslint from 'typescript-eslint'; export default tseslint.config( { ignores: [ - '.next/**', '.svelte-kit/**', 'build/**', 'coverage/**', 'node_modules/**', - 'next-env.d.ts', - 'next.config.ts', - 'src/app/**', - 'src/components/**', - 'src/config.ts', - 'src/data/**', 'src/lib/umbraco/*ApiSchema.d.ts', - 'src/serverConfig.ts', - 'src/util.ts', ], }, js.configs.recommended, diff --git a/next.config.ts b/next.config.ts deleted file mode 100644 index 0abfe32..0000000 --- a/next.config.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { NextConfig } from 'next'; - -const nextConfig: NextConfig = { - output: 'standalone', - trailingSlash: true, - images: { - localPatterns: [{ pathname: '/cms-media/**' }], - qualities: [75, 100], - }, - productionBrowserSourceMaps: true, -}; - -export default nextConfig; diff --git a/src/app/[conference]/[...slug]/layout.tsx b/src/app/[conference]/[...slug]/layout.tsx deleted file mode 100644 index 7eb57da..0000000 --- a/src/app/[conference]/[...slug]/layout.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import type { ReactNode } from 'react'; -import { DefaultNav } from '@/components/DefaultNav'; -import { getConference } from '@/data/getConference'; -import { notFound } from 'next/navigation'; -import { getSponsors } from '@/data/getSponsors'; -import { SponsorsBlock } from '@/components/contentBlocks/SponsorsBlock'; - -export interface PageLayoutProps { - children: ReactNode; - params: Promise<{ - conference: string; - }>; -} - -export default async function PageLayout({ - children, - params, -}: PageLayoutProps) { - const resolvedParams = await params; - const conference = await getConference(resolvedParams.conference); - - if (!conference) { - return notFound(); - } - - const sponsors = await getSponsors(conference.id); - - return ( - <> - -
- {children} - {sponsors ? : null} -
- - ); -} diff --git a/src/app/[conference]/[...slug]/page.tsx b/src/app/[conference]/[...slug]/page.tsx deleted file mode 100644 index cdc9f39..0000000 --- a/src/app/[conference]/[...slug]/page.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import type { Metadata } from 'next'; -import dynamic from 'next/dynamic'; -import { notFound } from 'next/navigation'; -import { getConference } from '@/data/getConference'; -import { getItemByPathOrDefault } from '@/data/umbraco/getItemByPath'; -import { generateMetadata as generateSpeakerMetadata } from '@/components/pageTypes/SpeakerDetailPage'; -import { generateMetadata as generateGenericMetadata } from '@/components/pageTypes/GenericPage'; - -export interface PageProps { - params: Promise<{ - conference: string; - slug: string[]; - }>; -} - -export async function generateMetadata({ - params, -}: PageProps): Promise { - const resolvedParams = await params; - const { conference, item } = await getData(resolvedParams); - - if (!conference || !item) { - return notFound(); - } - - switch (item.contentType) { - case 'speaker': - return await generateSpeakerMetadata({ - conference, - speaker: item, - }); - case 'page': - return await generateGenericMetadata({ - params: resolvedParams, - conference, - page: item, - }); - } - - return {}; -} - -const SpeakerDetailPage = dynamic(() => - import('@/components/pageTypes/SpeakerDetailPage').then( - (mod) => mod.SpeakerDetailPage, - ), -); - -const GenericPage = dynamic(() => - import('@/components/pageTypes/GenericPage').then((mod) => mod.GenericPage), -); - -export default async function Page({ params }: PageProps) { - const resolvedParams = await params; - const { conference, item } = await getData(resolvedParams); - - if (!conference || !item) { - return notFound(); - } - - switch (item.contentType) { - case 'speaker': - return ; - case 'page': - return ( - - ); - default: - return notFound(); - } -} - -async function getData(params: Awaited) { - const conference = await getConference(params.conference); - - if (!conference) { - return {}; - } - - const item = await getItemByPathOrDefault( - `${params.conference}/${params.slug.join('/')}`, - ); - - if (!item) { - return { conference }; - } - - return { conference, item }; -} diff --git a/src/app/[conference]/page.tsx b/src/app/[conference]/page.tsx deleted file mode 100644 index 9d02be3..0000000 --- a/src/app/[conference]/page.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import { HomeNav } from '@/components/HomeNav'; -import { getConference } from '@/data/getConference'; -import { notFound } from 'next/navigation'; -import { - generateMetadata as generateGenericMetadata, - GenericPage, -} from '@/components/pageTypes/GenericPage'; -import { getItemByPathOrDefault } from '@/data/umbraco/getItemByPath'; -import type { Metadata } from 'next'; - -export interface RootPageProps { - params: Promise<{ conference: string }>; -} - -export async function generateMetadata({ - params, -}: RootPageProps): Promise { - const { conference: conferenceSlug } = await params; - const { conference, homeContent } = await getHomePageData(conferenceSlug); - - if (!conference || !homeContent) { - return notFound(); - } - - return await generateGenericMetadata({ - params: { - conference: conferenceSlug, - slug: [], - }, - conference, - page: homeContent, - }); -} - -export default async function Home({ params }: RootPageProps) { - const resolvedParams = await params; - const { conference, homeContent } = await getHomePageData( - resolvedParams.conference, - ); - - if (!conference || !homeContent) { - return notFound(); - } - - return ( - <> - -
- -
- - ); -} - -async function getHomePageData(conferenceSlug: string) { - const conference = await getConference(conferenceSlug); - - if (!conference) { - return {}; - } - - const homeContent = await getItemByPathOrDefault(`${conferenceSlug}/home`); - - if (homeContent?.contentType !== 'home') { - return { conference }; - } - - return { conference, homeContent }; -} diff --git a/src/app/[conference]/register/layout.tsx b/src/app/[conference]/register/layout.tsx deleted file mode 100644 index 6143083..0000000 --- a/src/app/[conference]/register/layout.tsx +++ /dev/null @@ -1,3 +0,0 @@ -import PageLayout from '@/app/[conference]/[...slug]/layout'; - -export default PageLayout; diff --git a/src/app/[conference]/register/page.tsx b/src/app/[conference]/register/page.tsx deleted file mode 100644 index 0c49005..0000000 --- a/src/app/[conference]/register/page.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { notFound } from 'next/navigation'; -import { getConference } from '@/data/getConference'; -import { OveItEmbed } from '@/components/OveItEmbed'; - -export interface RegisterProps { - params: Promise<{ - conference: string; - }>; -} - -export default async function Register({ params }: RegisterProps) { - const { conference: conferenceSlug } = await params; - const conference = await getConference(conferenceSlug); - - const registerUrl = conference?.properties.registerUrl; - - if (!registerUrl) { - return notFound(); - } - - return ( -
-
- -
-
- ); -} diff --git a/src/app/[conference]/register/success/page.tsx b/src/app/[conference]/register/success/page.tsx deleted file mode 100644 index 223ad0c..0000000 --- a/src/app/[conference]/register/success/page.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import type { Metadata } from 'next'; -import Link from 'next/link'; - -export interface SuccessPageProps { - params: Promise<{ - conference: string; - }>; -} - -export const metadata: Metadata = { - robots: { index: false }, -}; - -export default async function SuccessPage({ params }: SuccessPageProps) { - const { conference } = await params; - - return ( -
-
-

- Thank You for Registering -

-

- We’ve sent a confirmation email to your inbox. -

-

- If you don{`'`}t receive that email or have any other issues - please email{' '} - - info@methodconf.com - -

-
- - Back Home - -
-
- ); -} diff --git a/src/app/[conference]/speak/route.ts b/src/app/[conference]/speak/route.ts deleted file mode 100644 index 6002dbd..0000000 --- a/src/app/[conference]/speak/route.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { notFound, redirect } from 'next/navigation'; -import { parseUrl } from '@/util'; -import { getConference } from '@/data/getConference'; - -export async function GET( - request: Request, - { params }: { params: Promise<{ conference: string }> }, -) { - const { conference: conferenceSlug } = await params; - const conference = await getConference(conferenceSlug); - - const url = parseUrl(conference?.properties?.callForSpeakersUrl); - - if (!url) { - return notFound(); - } - - redirect(url.toString()); -} diff --git a/src/app/[conference]/tickets/route.ts b/src/app/[conference]/tickets/route.ts deleted file mode 100644 index e397bd1..0000000 --- a/src/app/[conference]/tickets/route.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { permanentRedirect } from 'next/navigation'; - -export async function GET( - request: Request, - { params }: { params: Promise<{ conference: string }> }, -) { - const { conference } = await params; - permanentRedirect(`/${conference}/register/`); -} diff --git a/src/app/api/newsletter/route.ts b/src/app/api/newsletter/route.ts deleted file mode 100644 index ea876f8..0000000 --- a/src/app/api/newsletter/route.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { getNewsletterConfig } from '@/serverConfig'; - -export async function POST(request: Request): Promise { - const { endpoint, listId } = getNewsletterConfig(); - - if (!endpoint || !listId) { - return Response.json({ success: false }, { status: 500 }); - } - - const { name, email, nullCheck } = await request.json(); - - if (!nullCheck) { - try { - await fetch(endpoint, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ - name, - email, - list_uuids: [listId], - }), - }).then((res) => res.json()); - } catch { - return Response.json({ success: false }, { status: 500 }); - } - } - - return Response.json({ success: true }); -} diff --git a/src/app/cms-media/[...path]/route.ts b/src/app/cms-media/[...path]/route.ts deleted file mode 100644 index 08cdece..0000000 --- a/src/app/cms-media/[...path]/route.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { getUmbracoBaseUrl } from '@/serverConfig'; - -interface RouteContext { - params: Promise<{ path: string[] }>; -} - -async function proxyCmsMedia( - request: Request, - { params }: RouteContext, -): Promise { - const { path } = await params; - const upstreamUrl = new URL( - path.map(encodeURIComponent).join('/'), - `${getUmbracoBaseUrl().toString().replace(/\/$/, '')}/`, - ); - upstreamUrl.search = new URL(request.url).search; - - const headers = new Headers(); - const accept = request.headers.get('accept'); - const range = request.headers.get('range'); - - if (accept) headers.set('accept', accept); - if (range) headers.set('range', range); - - const response = await fetch(upstreamUrl, { - method: request.method, - headers, - next: { revalidate: 60 }, - }); - - return new Response(response.body, { - status: response.status, - statusText: response.statusText, - headers: response.headers, - }); -} - -export const GET = proxyCmsMedia; -export const HEAD = proxyCmsMedia; diff --git a/src/app/globals.css b/src/app/globals.css deleted file mode 100644 index 19bea03..0000000 --- a/src/app/globals.css +++ /dev/null @@ -1,44 +0,0 @@ -@import "tailwindcss"; -@plugin "@tailwindcss/typography"; - -@theme { - --color-primary: #D72F32; - --color-secondary: #1E1E1E; -} - -@layer components { - .button { - @apply text-white bg-black p-4 uppercase font-bold tracking-wider text-sm hover:-translate-y-1 transition-transform - } - - .button.secondary { - @apply bg-primary - } - - .content-container { - @apply container; - } - - .large-content-container { - max-width: 1900px; - } - - .small-content-container { - max-width: 800px; - } - - .content-container, - .large-content-container, - .small-content-container { - @apply mx-auto; - width: calc(100% - calc(var(--spacing) * 8)); - - @media (width >= theme(--breakpoint-md)) { - width: calc(100% - calc(var(--spacing) * 20)); - } - } - - .links a { - @apply text-primary underline - } -} diff --git a/src/app/icon.png b/src/app/icon.png deleted file mode 100644 index 3a90ecddf58c7af2f283c9e7c311afc66def84b8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6462 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 JR;fG0{|{mxdPe{N diff --git a/src/app/layout.tsx b/src/app/layout.tsx deleted file mode 100644 index de5b8da..0000000 --- a/src/app/layout.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import type { Metadata } from 'next'; -import { connection } from 'next/server'; -import type { ReactNode } from 'react'; -import { Source_Sans_3 } from 'next/font/google'; -import PlausibleProvider from 'next-plausible'; -import { config } from '@fortawesome/fontawesome-svg-core'; -import '@fortawesome/fontawesome-svg-core/styles.css'; -import './globals.css'; -import { Footer } from '@/components/Footer'; -import { getSiteUrl, isSearchIndexingEnabled } from '@/serverConfig'; - -config.autoAddCss = false; - -const sourceSans = Source_Sans_3({ subsets: ['latin'] }); - -export async function generateMetadata(): Promise { - await connection(); - - return { - title: { - template: 'Method Conference - %s', - default: 'Method Conference - October 12th 2024 - Springfield, MO', - }, - metadataBase: getSiteUrl(), - robots: isSearchIndexingEnabled() - ? undefined - : { index: false, follow: false }, - }; -} - -export default function RootLayout({ - children, -}: Readonly<{ - children: ReactNode; -}>) { - return ( - - - - {children} -