|
|
||
|---|---|---|
| .github/workflows | ||
| .w4c | ||
| app | ||
| components | ||
| docker | ||
| lib | ||
| messages | ||
| prisma | ||
| public | ||
| tests | ||
| .dockerignore | ||
| .env.example | ||
| .eslintrc.json | ||
| .gitignore | ||
| middleware.ts | ||
| next.config.ts | ||
| package-lock.json | ||
| package.json | ||
| postcss.config.mjs | ||
| README.md | ||
| tsconfig.json | ||
| vitest.config.ts | ||
SaaS / Web App — Next.js (templates2)
A ready-to-run multi-tenant app forked from templates2.Base.Next: organizations, roles, a
dashboard, a subscription (mock billing) and server-enforced usage limits — plus the whole base core
(auth, mock email, Docker, CI).
Quickstart
npm ci
cp .env.example .env # SQLite + mock providers by default — no keys needed
npm run db:setup # prisma db push + seed (demo org "Acme Inc")
npm run dev # http://localhost:3000
Demo accounts (seeded, all members of "Acme Inc"): owner@example.com (owner),
admin@example.com (admin), member@example.com (member) — password password.
Flow
Sign up → create an organization (/onboarding, you become the owner) → /dashboard (members,
plan, usage) → /members (add members, change roles; seat limit enforced) → /billing (mock
upgrade/downgrade, invoices) → /reports (Pro-gated, enforced server-side).
What this case adds over the base
- Data model —
Organization,Membership(owner/admin/member),OrgSubscription,Invoice. - Roles —
lib/org.tsis the pure authority (canManageMembers,canManageBilling,withinSeats,canUseFeature); every/api/orgs/[id]/*route first checks membership, so an organization's data never leaks to another tenant. - Subscription — plan selection with the mock provider (no keys) creates a subscription and
an invoice; Stripe activates from env. Reports are gated by plan on the server (
402), not just hidden in the UI. - Usage limits — the free plan allows 2 seats; adding a third member is rejected with a clear message until the org upgrades.
Marketing site & artwork
The public pages are a bespoke design, not a generic starter: a hero with a real-DOM app preview
(components/marketing/AppPreview.tsx), an asymmetric feature bento, a materials/engineering
section, a metrics band, testimonials, a team band and a dark CTA — on a small design system
(--canvas, --accent-ink, --warn-ink, .panel, .glass, .bg-grid, gradient text) defined in
app/globals.css, in both light and dark themes.
The imagery is a cohesive set of generated 3D renders and one editorial photo in public/art/
(hero, materials, security, integrations, mesh and CTA bands, team, OG), used across the marketing
pages and as the OG image. Prompts live in ../tools/art/saas.json; regenerate them with
node ../tools/generate-images.mjs ../tools/art/saas.json --out /tmp/saas-art (needs
OPENROUTER_API_KEY), then convert to WebP into public/art/.
Tests
- Unit:
tests/unit/org.test.ts— role ranks, member/billing permissions, seat caps, feature gates. - E2E smoke:
tests/e2e/smoke.mjs— sign-up → create org → member/seat rules → mock subscribe → feature unlock → tenant isolation (403 on a foreign org).
Quality
node templates2/qa.mjs Saas.Next
See ../CONTRACT.md for the Definition of Done.