A login-gated, fully wired analytics dashboard for a fictional product studio. Multilingual, theme-aware, animated, and grounded in deterministic mock data so it ships and demos identically on any host.
Next.js 14 · TypeScript · Tailwind CSS · Recharts · NextAuth · Radix UI · Groq
Live demo → · Source · NOVA Agency landing (live) · NOVA Agency (source)
Lumen Analytics is the second half of a two-part portfolio piece.
The first half — NOVA Agency — is a full marketing site for a fictional product studio. The hero claims that the studio ships "Lumen Analytics — a calm control room for SaaS metrics". This repository is that control room.
So the two projects together form one continuous narrative:
NOVA Agency landing page sets up the story and pitches the product → Lumen Analytics is the actual product the visitor lands inside.
Everything is hand-crafted: every chart, every animation, every translation, every page. There is no CMS, no boilerplate template, no design system bought off the shelf — and there is no real database either. Dashboard data is generated by a seeded PRNG so the demo is deterministic, runs without infrastructure, and looks identical on Railway, Vercel, or your laptop.
Disclaimer. Lumen Analytics is not a real product. NOVA Agency is not a real company. The brand, copy, customers, invoices and metrics are fictional, written as a portfolio piece. Designed and built by Тимур Валерьевич.
/— marketing splash with hero, KPI strip, "what's inside" feature grid and a link to the NOVA Agency landing page./login— split-screen login. Aurora gradient on the left, credentials form on the right, demo creds visible./dashboard— overview: 4 KPI cards (MRR, active customers, churn, ARPU) with sparklines, revenue area chart, plan-distribution donut, user growth, churn & NRR, billing health, geography list and onboarding checklist./revenue— MRR / ARR / Net new tabs, expansion vs contraction, MRR composition table, plan distribution and revenue by country./growth— daily signup-vs-churn bar chart, monthly retention cohorts (M0 → M9), signup funnel and acquisition channels./customers— sortable, filterable, URL-synced table with avatar gradient, status pills, CSV export, and per-customer detail pages at/customers/[id](health score, MRR sparkline, invoices and the customer's own activity feed)./pricing— public pricing page with plan comparison, FAQ and CTA./insights— AI insights grid, 30-day MRR forecast, anomaly alerts and an "Ask Lumen AI" CTA./activity— searchable, filterable event log + GitHub-style hour-of-day × day-of-week heatmap./team— seats, members, role pills, invite flow, role permissions matrix./billing— current plan, usage meters, payment method, plans comparison and recent invoices with CSV export./developers,/webhooks,/audit,/settings,/help— API keys, webhook endpoints, audit log, workspace + integrations + notifications, and an FAQ + status page.
Four locales served from one codebase via a custom dictionary loader, identical to the NOVA Agency landing page:
| Code | Language | Default |
|---|---|---|
en |
English | yes |
ru |
Русский | |
uk |
Українська | |
es |
Español |
Each locale has its own metadata, OG title, navigation labels, table headers, KPI tooltips and AI prompts. The locale picker lives in the topbar and persists via the NEXT_LOCALE cookie.
Every animation honours prefers-reduced-motion.
- Theme switch with a radial clip-path reveal that originates from the toggle button.
- Staggered fade-in-up on KPI cards, funnel rows and tables (each row delayed by
index × 60ms). - Animated
ai-gradient(purple → pink loop) + inner shimmer sweep on every "AI summary" / "Ask Lumen AI" CTA. - Animated mini-sparklines in KPI cards with unique gradient ids via
React.useId. - Live pulse dot (two synchronized rings) next to the topbar AI CTA and the activity sidebar item.
- Recharts custom tooltips with
backdrop-blur,allowEscapeViewBoxand a 20-px offset so they never overlap their parent chart. - Hover lift on every card (
translateY(-2px)+ softened primary-tinted shadow) and an icon micro-rotation on the AI buttons. - Hover-state highlights between the plan-distribution donut and its legend rows (active slice scales, others fade).
- Aurora blobs on the marketing and login pages, plus a faint grid background masked with a radial gradient.
⌘K/Ctrl+Kcommand palette (layout-independent — works with Russian keyboards too) with theme switcher, sign-out and recent-customer / country / plan jump.?opens the keyboard-shortcuts dialog.g d,g r,g g,g i,g a,g cfor navigation.ttoggles theme.rrefreshes data.- Notifications dropdown with unread badge and seeded alerts.
- Click-to-copy API key and webhook secret with toast confirmation.
- Skip-to-content link, global
:focus-visiblerings, ARIA-correct disclosure widgets. - Tables that scroll horizontally instead of breaking on narrow viewports.
A streaming chat sheet grounded in live KPIs, the funnel, cohorts, top customers and recent events.
- Model:
llama-3.3-70b-versatileon Groq's free tier. - System prompt is regenerated on every request from
lib/ai-context.tsagainst the seeded dataset, so the model can quote real numbers. - Open with the topbar button,
⌘K → Ask Lumen AI, or⌘J. - If
GROQ_API_KEYis not set, the dashboard still works — only the chat returns a graceful 503.
- Locale-aware
metadata, OpenGraph, Twitter cards. - Custom
app/opengraph-image.tsxrendered at the edge. not-found.tsx,error.tsx,favicon.svgand a brand wordmark component.
All endpoints live under /api/*, are session-gated by NextAuth middleware, and read from a single seeded PRNG (lib/data.ts):
| Route | Returns |
|---|---|
/api/metrics/summary |
top-line KPI summary (MRR, ARPU, NRR, churn, …). |
/api/metrics/mrr |
daily MRR series + new / expansion / contraction / churn split. |
/api/metrics/users |
daily signups vs churn series. |
/api/metrics/churn |
churn rate + NRR series. |
/api/customers |
filterable, sortable list of customers. |
/api/events |
activity-feed events with type / actor / target metadata. |
/api/ai |
Groq chat completion proxy (streamed, KPI-grounded prompt). |
/api/auth/[...nextauth] |
NextAuth credentials provider. |
| Layer | Tooling |
|---|---|
| Framework | Next.js 14 (App Router, RSC) |
| Styling | Tailwind CSS + HSL CSS variables |
| Charts | Recharts |
| Auth | NextAuth (credentials, JWT sessions) |
| Primitives | Radix UI + shadcn/ui |
| Icons | Lucide |
| Animations | Tailwind keyframes + tailwindcss-animate |
| Forms | Native <form> + Server Actions / NextAuth credentials |
| Theme | next-themes + View Transitions API |
| Command palette | cmdk |
| Toasts | Sonner |
| AI | Groq — llama-3.3-70b-versatile |
| Hosting | Railway / Vercel |
npm install
cp .env.example .env.local
# at minimum set NEXTAUTH_SECRET — see "Environment variables" below
npm run devThen open http://localhost:3000 and sign in with the demo credentials shown on the login page.
npm run dev # development server on :3000
npm run build # production build (strict TypeScript)
npm run start # serve the production build
npm run lint # ESLint with the Next.js config
npm run typecheck # tsc --noEmitdemo@lumenanalytics.io
lumen-demo-2026
These are seeded in lib/auth.ts — there is no signup flow because there is no database. The login page renders them right next to the form so reviewers don't have to dig.
| Variable | Purpose |
|---|---|
NEXTAUTH_SECRET |
Required. Used by NextAuth to sign JWTs. Generate with openssl rand -base64 32. |
NEXTAUTH_URL |
Required in production. Must match the public URL exactly (https://…, no trailing slash). |
GROQ_API_KEY |
Optional. Without it, "Ask Lumen AI" returns a graceful 503 — everything else still works. |
src/
├── app/
│ ├── (app)/ # authenticated app shell
│ │ ├── dashboard/ # /dashboard
│ │ ├── revenue/ # /revenue
│ │ ├── growth/ # /growth
│ │ ├── customers/ # /customers, /customers/[id]
│ │ ├── insights/ # /insights
│ │ ├── activity/ # /activity
│ │ ├── billing/ # /billing
│ │ ├── team/ # /team
│ │ ├── developers/ # /developers
│ │ ├── webhooks/ # /webhooks
│ │ ├── audit/ # /audit
│ │ ├── settings/ # /settings
│ │ └── help/ # /help
│ ├── api/ # session-gated mock APIs + Groq proxy
│ │ ├── auth/[...nextauth]/route.ts
│ │ ├── metrics/{summary,mrr,users,churn}/route.ts
│ │ ├── customers/route.ts
│ │ ├── events/route.ts
│ │ └── ai/route.ts
│ ├── login/ # /login (split-screen)
│ ├── pricing/ # /pricing (marketing pricing page)
│ ├── opengraph-image.tsx # default OG image
│ ├── globals.css
│ ├── layout.tsx
│ └── page.tsx # marketing splash at /
├── components/
│ ├── charts/ # all Recharts wrappers (custom tooltips)
│ ├── dashboard/ # KpiCard, FunnelList, CustomersTable, Sidebar, Topbar, …
│ ├── ui/ # button, badge, card, tabs, animated-number, live-pulse, …
│ ├── theme-toggle.tsx
│ ├── radial-theme.tsx # radial clip-path reveal between themes
│ ├── locale-switcher.tsx
│ └── locale-provider.tsx
└── lib/
├── auth.ts # NextAuth credentials provider + demo user
├── data.ts # seeded PRNG + every metric / customer / event
├── ai-context.ts # KPI-grounded system prompt for Groq
├── csv.ts # CSV export helper used by tables
├── i18n/ # locales, dictionary loader, format helpers
└── utils.ts # cn(), formatCurrency, percent helpers
Themes are driven by HSL CSS variables in globals.css and orchestrated by next-themes with attribute="class". The theme switch uses the View Transitions API with a radial clip-path reveal that originates from the toggle button (components/radial-theme.tsx) and falls back to a CSS-only crossfade on browsers without VT support. Dark is the default.
Every chart, badge, button and tooltip is built against the same --primary / --success / --warning / --destructive tokens, so adding a third theme is a matter of redefining the variables.
npm run lint— clean, zero warnings.npm run typecheck— strict TypeScript, zero errors.npm run build— production build passes.- All animations honour
prefers-reduced-motion. - WCAG AA contrast on dark and light themes.
- Keyboard-accessible: every interactive element is reachable, every focus state is visible.
- Tables, charts, sheets and dropdowns all behave correctly on touch + narrow viewports.
This is a vanilla Next.js app and runs on any Node host.
-
Create a new service from this repo. Railway autodetects the Next.js build (
npm run build/npm run start). -
Open the service → Settings → Networking → Public Networking and click Generate Domain to get a
*.up.railway.appURL. -
Open Variables and add:
NEXTAUTH_SECRET=<output of `openssl rand -base64 32`> NEXTAUTH_URL=https://<your-service>.up.railway.app GROQ_API_KEY=<your Groq key> # optional, only for Ask Lumen AI
No quotes, no trailing slash. Use a fresh secret per environment. Without
GROQ_API_KEYthe dashboard works but "Ask Lumen AI" returns a 503. -
Railway redeploys automatically when variables change. After ~30 s, open the public URL and sign in with the demo credentials.
Do you need a database? No. Dashboard data is seeded mock data and the AI endpoint is stateless (it just proxies to Groq).
Troubleshooting "Server error / There is a problem with the server configuration." This is NextAuth's canonical error when
NEXTAUTH_SECRETis missing in production. Add it in Railway → Variables and redeploy. If it persists, double-check thatNEXTAUTH_URLmatches the actual public URL exactly.
- Import the repo into Vercel — the framework preset and build command are auto-detected.
- Add
NEXTAUTH_SECRET,NEXTAUTH_URLand (optionally)GROQ_API_KEYunder Project → Settings → Environment Variables. - Deploy.
Designed and built by Тимур Валерьевич as a portfolio piece. Lumen Analytics is fictional — the brand, the customers, the invoices and the AI persona exist only inside this repository and its companion NOVA Agency landing page.