Scaffold Next.js MVP foundation with pnpm

This commit is contained in:
Matthias
2026-06-03 21:46:52 +02:00
parent 762571cb43
commit 20615e12a1
13 changed files with 8055 additions and 11741 deletions

27
.env.example Normal file
View File

@@ -0,0 +1,27 @@
# App / Coolify
APP_ENV=development
NEXT_PUBLIC_APP_URL=http://localhost:3000
# Convex
NEXT_PUBLIC_CONVEX_URL=
CONVEX_DEPLOYMENT=
# Google APIs
GOOGLE_GEOCODING_API_KEY=
GOOGLE_PLACES_API_KEY=
PAGESPEED_API_KEY=
# OpenRouter
OPENROUTER_API_KEY=
# SMTP / Stalwart
SMTP_HOST=
SMTP_PORT=465
SMTP_USER=
SMTP_PASSWORD=
SMTP_FROM=
# Rybbit
RYBBIT_API_URL=
RYBBIT_API_KEY=
NEXT_PUBLIC_RYBBIT_SITE_ID=

2
.gitignore vendored
View File

@@ -2,6 +2,7 @@
# dependencies
/node_modules
/.pnpm-store
/.pnp
.pnp.*
.yarn/*
@@ -32,6 +33,7 @@ yarn-error.log*
# env files (can opt-in for committing if needed)
.env*
!.env.example
# vercel
.vercel

View File

@@ -1,36 +1,42 @@
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
# WebDev Pipeline
Interner Akquise-Agent fuer lokale Webdesign-Leads. Das MVP startet mit Next.js App Router, TypeScript, Tailwind CSS, shadcn/ui und Platzhalter-Routen fuer Dashboard, Login und oeffentliche Audit-Seiten.
## Getting Started
First, run the development server:
```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
## Scripts
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
- `pnpm dev` starts the local development server.
- `pnpm lint` runs ESLint.
- `pnpm build` creates a production build.
- `pnpm start` starts the production server after a build.
## Learn More
## Environment Variables
To learn more about Next.js, take a look at the following resources:
Copy `.env.example` to `.env.local` for local development. Keep real secrets out of the repository and configure production values in Coolify and provider dashboards.
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
- **App / Coolify:** `APP_ENV`, `NEXT_PUBLIC_APP_URL`
- **Convex:** `NEXT_PUBLIC_CONVEX_URL`, `CONVEX_DEPLOYMENT`
- **Google:** `GOOGLE_GEOCODING_API_KEY`, `GOOGLE_PLACES_API_KEY`, `PAGESPEED_API_KEY`
- **OpenRouter:** `OPENROUTER_API_KEY`
- **SMTP / Stalwart:** `SMTP_HOST`, `SMTP_PORT`, `SMTP_USER`, `SMTP_PASSWORD`, `SMTP_FROM`
- **Rybbit:** `RYBBIT_API_URL`, `RYBBIT_API_KEY`, `NEXT_PUBLIC_RYBBIT_SITE_ID`
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
Only variables prefixed with `NEXT_PUBLIC_` are intended for browser exposure. All API keys, SMTP credentials, and server-only URLs must stay server-side.
## Deploy on Vercel
## Routes
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
- `/` MVP entry page.
- `/dashboard` internal dashboard placeholder.
- `/login` admin login placeholder.
- `/audit/[slug]` public audit placeholder.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
## Deployment Notes
Coolify should run `pnpm install`, `pnpm build`, and `pnpm start`. The current font setup uses `next/font/google`, so production builds need outbound access to Google Fonts unless fonts are later self-hosted.

27
app/audit/[slug]/page.tsx Normal file
View File

@@ -0,0 +1,27 @@
import { FileText } from "lucide-react";
export default async function PublicAuditPage({
params,
}: {
params: Promise<{ slug: string }>;
}) {
const { slug } = await params;
return (
<main className="flex min-h-dvh items-center justify-center bg-background px-6 py-12">
<section className="w-full max-w-2xl rounded-lg border bg-card p-6 text-card-foreground">
<FileText className="mb-5 size-6 text-muted-foreground" />
<p className="text-sm font-medium text-muted-foreground">
Audit: {slug}
</p>
<h1 className="mt-3 text-3xl font-semibold tracking-normal">
Dieser Audit ist noch nicht freigegeben
</h1>
<p className="mt-4 text-sm leading-6 text-muted-foreground">
Sobald der Bericht manuell geprueft und veroeffentlicht wurde,
erscheinen hier die freigegebenen Beobachtungen und Empfehlungen.
</p>
</section>
</main>
);
}

64
app/dashboard/page.tsx Normal file
View File

@@ -0,0 +1,64 @@
import { FileSearch, MailCheck, MapPinned, ShieldCheck } from "lucide-react";
const pipelineSteps = [
{
title: "Kampagnen",
description: "Kategorie, PLZ, Radius und Lauf-Limits vorbereiten.",
icon: MapPinned,
},
{
title: "Lead-Audit",
description: "Website-Potenzial, Screenshots und Quellen buendeln.",
icon: FileSearch,
},
{
title: "Freigabe",
description: "Audit-Seite, E-Mail und Telefon-Skript manuell pruefen.",
icon: ShieldCheck,
},
{
title: "Outreach",
description: "Freigegebene Kontakte senden und Antworten nachhalten.",
icon: MailCheck,
},
];
export default function DashboardPage() {
return (
<main className="min-h-dvh bg-background px-6 py-8">
<div className="mx-auto flex w-full max-w-6xl flex-col gap-8">
<header className="flex flex-col gap-2 border-b pb-6">
<p className="text-sm font-medium text-muted-foreground">
Interner Arbeitsbereich
</p>
<h1 className="text-3xl font-semibold tracking-normal">
Dashboard-Platzhalter
</h1>
<p className="max-w-2xl text-sm leading-6 text-muted-foreground">
Hier entsteht der scanbare Funnel fuer Recherche, Audit, Review,
Versand und Follow-up.
</p>
</header>
<section className="grid gap-3 md:grid-cols-4">
{pipelineSteps.map((step) => {
const Icon = step.icon;
return (
<article
className="rounded-lg border bg-card p-4 text-card-foreground"
key={step.title}
>
<Icon className="mb-4 size-5 text-muted-foreground" />
<h2 className="text-sm font-medium">{step.title}</h2>
<p className="mt-2 text-sm leading-6 text-muted-foreground">
{step.description}
</p>
</article>
);
})}
</section>
</div>
</main>
);
}

View File

@@ -13,8 +13,8 @@ const geistMono = Geist_Mono({
});
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
title: "WebDev Pipeline",
description: "Interner Akquise-Agent fuer lokale Webdesign-Leads",
};
export default function RootLayout({
@@ -24,7 +24,7 @@ export default function RootLayout({
}>) {
return (
<html
lang="en"
lang="de"
className={`${geistSans.variable} ${geistMono.variable} h-full antialiased`}
>
<body className="min-h-full flex flex-col">{children}</body>

19
app/login/page.tsx Normal file
View File

@@ -0,0 +1,19 @@
import { LockKeyhole } from "lucide-react";
export default function LoginPage() {
return (
<main className="flex min-h-dvh items-center justify-center bg-background px-6 py-12">
<section className="w-full max-w-md rounded-lg border bg-card p-6 text-card-foreground">
<LockKeyhole className="mb-5 size-6 text-muted-foreground" />
<h1 className="text-2xl font-semibold tracking-normal">
Login-Platzhalter
</h1>
<p className="mt-3 text-sm leading-6 text-muted-foreground">
Better Auth wird in einem spaeteren Task angebunden. Bis dahin bleibt
diese Route als definierter Einstiegspunkt fuer den Admin-Login
bestehen.
</p>
</section>
</main>
);
}

View File

@@ -1,65 +1,81 @@
import Image from "next/image";
import Link from "next/link";
import { ArrowRight, FileText, LayoutDashboard, LockKeyhole } from "lucide-react";
import { Button } from "@/components/ui/button";
export default function Home() {
return (
<div className="flex flex-col flex-1 items-center justify-center bg-zinc-50 font-sans dark:bg-black">
<main className="flex flex-1 w-full max-w-3xl flex-col items-center justify-between py-32 px-16 bg-white dark:bg-black sm:items-start">
<Image
className="dark:invert"
src="/next.svg"
alt="Next.js logo"
width={100}
height={20}
priority
/>
<div className="flex flex-col items-center gap-6 text-center sm:items-start sm:text-left">
<h1 className="max-w-xs text-3xl font-semibold leading-10 tracking-tight text-black dark:text-zinc-50">
To get started, edit the page.tsx file.
<main className="flex flex-1 bg-background">
<section className="mx-auto flex min-h-dvh w-full max-w-5xl flex-col justify-center gap-10 px-6 py-12">
<div className="max-w-3xl space-y-5">
<p className="text-sm font-medium text-muted-foreground">
WebDev Pipeline MVP
</p>
<h1 className="text-4xl font-semibold tracking-normal text-foreground sm:text-5xl">
Lokale Webdesign-Leads recherchieren, auditieren und respektvoll
kontaktieren.
</h1>
<p className="max-w-md text-lg leading-8 text-zinc-600 dark:text-zinc-400">
Looking for a starting point or more instructions? Head over to{" "}
<a
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
className="font-medium text-zinc-950 dark:text-zinc-50"
>
Templates
</a>{" "}
or the{" "}
<a
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
className="font-medium text-zinc-950 dark:text-zinc-50"
>
Learning
</a>{" "}
center.
<p className="max-w-2xl text-base leading-7 text-muted-foreground sm:text-lg">
Diese Foundation setzt die ersten App-Routen fuer Dashboard,
Anmeldung und oeffentliche Audit-Seiten auf. Die Integrationen
folgen in den naechsten Backlog-Tasks.
</p>
</div>
<div className="flex flex-col gap-4 text-base font-medium sm:flex-row">
<a
className="flex h-12 w-full items-center justify-center gap-2 rounded-full bg-foreground px-5 text-background transition-colors hover:bg-[#383838] dark:hover:bg-[#ccc] md:w-[158px]"
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
<Image
className="dark:invert"
src="/vercel.svg"
alt="Vercel logomark"
width={16}
height={16}
/>
Deploy Now
</a>
<a
className="flex h-12 w-full items-center justify-center rounded-full border border-solid border-black/[.08] px-5 transition-colors hover:border-transparent hover:bg-black/[.04] dark:border-white/[.145] dark:hover:bg-[#1a1a1a] md:w-[158px]"
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
target="_blank"
rel="noopener noreferrer"
>
Documentation
</a>
<div className="grid gap-3 sm:grid-cols-3">
<Button asChild size="lg" className="justify-between">
<Link href="/dashboard">
<span className="inline-flex items-center gap-2">
<LayoutDashboard />
Dashboard
</span>
<ArrowRight />
</Link>
</Button>
<Button asChild size="lg" variant="outline" className="justify-between">
<Link href="/login">
<span className="inline-flex items-center gap-2">
<LockKeyhole />
Login
</span>
<ArrowRight />
</Link>
</Button>
<Button asChild size="lg" variant="outline" className="justify-between">
<Link href="/audit/example">
<span className="inline-flex items-center gap-2">
<FileText />
Audit
</span>
<ArrowRight />
</Link>
</Button>
</div>
</main>
</div>
<dl className="grid gap-4 border-t pt-8 sm:grid-cols-3">
<div>
<dt className="text-sm font-medium text-foreground">Recherche</dt>
<dd className="mt-2 text-sm leading-6 text-muted-foreground">
Kampagnen, Places-Quellen und Lead-Qualitaet werden spaeter im
Dashboard gebuendelt.
</dd>
</div>
<div>
<dt className="text-sm font-medium text-foreground">Audit</dt>
<dd className="mt-2 text-sm leading-6 text-muted-foreground">
Oeffentliche Audit-Seiten starten als sichere Platzhalter ohne
freigegebene Inhalte.
</dd>
</div>
<div>
<dt className="text-sm font-medium text-foreground">Outreach</dt>
<dd className="mt-2 text-sm leading-6 text-muted-foreground">
Versand bleibt im MVP an manuelle Pruefung und Freigabe
gekoppelt.
</dd>
</div>
</dl>
</section>
</main>
);
}

View File

@@ -1,9 +1,10 @@
---
id: TASK-1
title: Scaffold the Next.js MVP foundation
status: To Do
status: In Progress
assignee: []
created_date: '2026-06-03 19:12'
updated_date: '2026-06-03 19:42'
labels:
- mvp
- foundation
@@ -23,19 +24,28 @@ Set up the application foundation for the WebDev Pipeline MVP: Next.js App Route
## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 Next.js App Router project exists with TypeScript and a working local dev script
- [ ] #2 Tailwind and shadcn/ui are configured and at least one shared UI component renders correctly
- [ ] #3 Base routes exist for dashboard, login placeholder, and public audit placeholder
- [ ] #4 Environment variable conventions are documented for Coolify, Convex, Google, OpenRouter, SMTP, and Rybbit
- [ ] #5 A basic smoke test or build command verifies the scaffold compiles
- [x] #1 Next.js App Router project exists with TypeScript and a working local dev script
- [x] #2 Tailwind and shadcn/ui are configured and at least one shared UI component renders correctly
- [x] #3 Base routes exist for dashboard, login placeholder, and public audit placeholder
- [x] #4 Environment variable conventions are documented for Coolify, Convex, Google, OpenRouter, SMTP, and Rybbit
- [x] #5 A basic smoke test or build command verifies the scaffold compiles
<!-- AC:END -->
## Implementation Plan
<!-- SECTION:PLAN:BEGIN -->
1. Create or verify the Next.js App Router project structure.
2. Configure Tailwind, shadcn/ui, path aliases, and shared UI utilities.
3. Add base route groups for internal dashboard and public audit pages.
4. Add environment variable examples and keep all secrets out of source control.
5. Run the initial build/typecheck and record any setup notes.
1. Migrate package manager to pnpm
2. Verify scaffold
3. Replace starter UI
4. Add base routes
5. Document env conventions
6. Run lint and build
<!-- SECTION:PLAN:END -->
## Implementation Notes
<!-- SECTION:NOTES:BEGIN -->
Implemented pnpm-based Next.js MVP foundation on branch task-1-scaffold-foundation.
Verified pnpm install, pnpm lint, pnpm build, and local route smoke checks for /, /dashboard, /login, and /audit/example.
Note: pnpm requires approved build scripts for msw, sharp, and unrs-resolver, recorded in pnpm-workspace.yaml. Build needs network access for next/font/google unless fonts are later self-hosted.
<!-- SECTION:NOTES:END -->

11651
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -2,6 +2,7 @@
"name": "webdev-pipeline",
"version": "0.1.0",
"private": true,
"packageManager": "pnpm@11.5.1",
"scripts": {
"dev": "next dev",
"build": "next build",
@@ -26,7 +27,7 @@
"@types/react": "^19",
"@types/react-dom": "^19",
"eslint": "^9",
"eslint-config-next": "16.2.7",
"eslint-config-next": "16.2.6",
"tailwindcss": "^4",
"typescript": "^5"
}

7789
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

4
pnpm-workspace.yaml Normal file
View File

@@ -0,0 +1,4 @@
allowBuilds:
msw: true
sharp: true
unrs-resolver: true