Scaffold Next.js MVP foundation with pnpm
This commit is contained in:
27
.env.example
Normal file
27
.env.example
Normal 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
2
.gitignore
vendored
@@ -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
|
||||
|
||||
44
README.md
44
README.md
@@ -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
27
app/audit/[slug]/page.tsx
Normal 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
64
app/dashboard/page.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -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
19
app/login/page.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
126
app/page.tsx
126
app/page.tsx
@@ -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>
|
||||
|
||||
<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>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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
11651
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -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
7789
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
4
pnpm-workspace.yaml
Normal file
4
pnpm-workspace.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
allowBuilds:
|
||||
msw: true
|
||||
sharp: true
|
||||
unrs-resolver: true
|
||||
Reference in New Issue
Block a user