A small Next.js task to talk through architecture together. Plan for about an hour of coding plus discussion.
Requires Node.js 20+.
npm install
npm run devOpen http://localhost:3000 — you'll land on /settings/greeting.
We're building a SaaS for call centers. Each organization has an auto-greeting — a short message played automatically when a customer calls in.
You build the page at /settings/greeting:
- Everyone in the org sees the current greeting text.
- Admins also see a form that lets them edit and save the greeting.
The form itself is your call — a text input or textarea, a save button, whatever loading state and feedback you think the user needs. Make sane UX choices; you don't need to overengineer it.
A Mock auth bar sits at the top of every page with three buttons: Login as Admin / Login as Member / Logout. Use it to switch roles and verify each scenario.
| # | Acting as | Expected behaviour |
|---|---|---|
| 1 | admin | Sees the current greeting and the edit form. Saving updates the text; refreshing the page shows the saved value. |
| 2 | member | Sees the current greeting. The form is not rendered. |
| 3 | logged out | Visiting /settings/greeting redirects to /sign-in. |
| 4 | member, but malicious | Calls the update endpoint directly (DevTools fetch / curl) bypassing the UI. The request must be rejected on the server. |
Scenario #4 is the one we'll actively try to break — build it knowing we'll poke.
Two async functions. They persist to data/state.json, so changes survive page
refreshes.
backend.getGreeting(orgId: string): Promise<{ text: string }>
backend.updateGreeting(orgId: string, text: string): Promise<void>One async function. Reads the cookie set by the mock auth bar.
auth.getCurrentUser(): Promise<{
id: string;
orgId: string;
role: 'admin' | 'member';
} | null>app/
├── layout.tsx Root layout, mounts the mock auth bar
├── page.tsx Redirects "/" → "/settings/greeting"
├── sign-in/page.tsx Placeholder for the redirect target
├── settings/greeting/page.tsx ← your work goes here
├── _components/RoleSwitcher The mock auth bar (server component)
└── _actions/setRole Server action behind the bar's buttons
lib/
├── backend.ts Persisted state, fs-backed
└── auth.ts getCurrentUser, cookie-backed
data/state.json The persisted "database"
Everything here is modifiable. Refactor, rename, replace, restructure — just be ready to explain why. The scaffolding is here to save you setup time, not to constrain your design.
- Any libraries are fair game —
zod,react-hook-form,react-query,tRPC,NextAuth, anything else. Install withnpm i <name>. - Any AI assistant is fair game — Cursor, Claude, ChatGPT. Be ready to explain each meaningful decision and defend it if challenged.
- Think out loud. Saying "not sure, let me check the docs" is a strong answer; confidently guessing is a weak one.
- Ask if anything's ambiguous. Don't guess your way around requirements.
We're not grading "did your solution match our codebase". We're listening for how you reason about:
- Trust boundaries — who decides what, and who is allowed to lie?
- Validation — when and where do you check the shape of data?
- Types — single source of truth, or duplicated by hand?
- Composition — how do you avoid copy-pasting similar checks?
- Server vs client — what runs where, and why does it matter?
The size of the diff is irrelevant. The quality of the decisions behind it is everything. Good luck — and have fun.