This project is a developer test for new candidates, completed during a 1-hour in-person interview. The goal is to build a functional subscription management page by translating a Figma design into React + TypeScript code.
You will be given access to a Figma design file and tasked with building a subscription management page that displays subscriptions in a table format. The page should support:
- Draft rows: Allow creating new subscriptions that start in draft state
- Status grouping: Display and group subscriptions by their status (DRAFT, LIVE, ARCHIVED)
- Inline editing: Edit subscription details directly in the table
- Data persistence: Save changes to a local server running on port 4000
⚠️ Important: Draft Rows vs. DRAFT StatusDraft rows are local in-memory rows that haven't been persisted to the server yet. These are temporary rows in your React component state that exist only in the browser until the user saves them via the API. Do not confuse draft rows with subscriptions that have a
DRAFTstatus. A subscription withstatus: 'DRAFT'is a fully persisted subscription that exists on the server - it just happens to be in draft state. Draft rows, on the other hand, have no server representation until they are saved viaPOST /subscriptions.
Below is a reference image showing the expected final output of the subscriptions page:
This image demonstrates:
- The layout with sidebar navigation and main content area
- Subscription table with columns for Name, Description, Channel, Consent type, and Created date
- Grouping by status (Draft, Live, Archived) with section headers
- Search functionality and filtering dropdown
- "+ New subscription" button for creating new subscriptions
- Status indicators (colored dots) for each subscription
- Channel icons (Mobile, SMS, Email) and consent type labels
While you'll be working from a Figma design file, this reference image can help guide your implementation approach.
- React 19 with TypeScript
- Vite as the build tool
- Tailwind CSS (already configured) - though you may install a different CSS solution if you prefer
- FontAwesome icons (already included)
- Express server (already set up) running on port 4000
This is a monorepo using pnpm workspaces:
├── app/ # React frontend application
│ ├── src/
│ │ ├── App.tsx # Main app component
│ │ └── ui/ # UI components (Window, Text, etc.)
├── server/ # Express backend server
│ └── src/
│ ├── app.ts # Express app configuration
│ └── index.ts # Server entry point
└── packages/
└── api/ # Shared TypeScript types and schemas
- Node.js (v18 or higher)
- pnpm installed globally (
npm install -g pnpm)
# Install all dependencies
pnpm installStart both the frontend and backend servers:
pnpm devThis will start:
- Frontend: React app on
http://localhost:5173(Vite default port) - Backend: Express server on
http://localhost:4000(configured via PORT environment variable)
To run the server on port 4000 explicitly:
PORT=4000 pnpm --filter server devThe server provides the following REST endpoints for managing subscriptions:
http://localhost:4000
Retrieve all subscriptions.
Response:
[
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Weekly Newsletter",
"description": "Get the latest updates and news delivered to your inbox every week",
"status": "LIVE",
"createdAt": "2024-01-15T10:30:00.000Z",
"consentType": "OPT_IN",
"channel": "EMAIL"
}
]Create a new subscription.
Request Body:
{
"name": "Subscription Name",
"description": "Optional description",
"status": "DRAFT" | "LIVE" | "ARCHIVED",
"consentType": "OPT_IN" | "OPT_OUT",
"channel": "MOBILE" | "SMS" | "EMAIL"
}Response: Created subscription object (same structure as GET response)
Error Response (400):
{
"error": "Validation error",
"details": [...]
}Update an existing subscription.
URL Parameters:
id: Subscription UUID
Request Body:
{
"name": "Updated Name", // optional
"description": "Updated description", // optional (can be null)
"status": "LIVE", // optional
"consentType": "OPT_OUT" // optional
}Note: The channel field cannot be changed after creation. Any attempt to change it will result in a 400 error.
Response: Updated subscription object
Error Responses:
404: Subscription not found400: Validation error or attempt to change channel
{
id: string // UUID
name: string
description: string | null
status: 'DRAFT' | 'LIVE' | 'ARCHIVED'
createdAt: string // ISO 8601 date string
consentType: 'OPT_IN' | 'OPT_OUT'
channel: 'MOBILE' | 'SMS' | 'EMAIL'
}The subscription types and schemas are exported from @api package:
Subscription- TypeScript typeCreateSubscriptionInput- Input type for creating subscriptionsUpdateSubscriptionInput- Input type for updating subscriptions
-
Build the subscription table
- Display subscriptions in a table format matching the Figma design
- Show all subscription fields appropriately
-
Support draft rows
- Allow users to create new subscription rows locally in the UI (these are draft rows - not yet persisted)
- When saved, these draft rows should be sent to the server via
POST /subscriptions - Provide UI to add new rows (can be inline editing or a separate form)
- Note: Draft rows exist only in your React state until persisted to the server
-
Group by status
- Display subscriptions grouped by their status (DRAFT, LIVE, ARCHIVED)
- Use appropriate visual grouping (sections, headers, etc.)
-
Inline editing
- Allow editing subscription fields directly in the table
- Provide a way to save/cancel edits
- Update the server when changes are saved
-
Data persistence
- Fetch subscriptions from the server on page load
- Save new subscriptions via POST
- Update existing subscriptions via PUT
- Handle loading and error states appropriately
- Tailwind CSS is already configured and available
- You may use additional CSS libraries if preferred
- Match the Figma design as closely as possible
- Ensure the UI is responsive and accessible
The project includes some basic UI components in app/src/ui/:
Window- Main window containerWindowContent- Content area with Header and Body sectionsWindowSidebar- Sidebar componentText- Text component with layout and size propsPushButton- Button component with variants (default, primary, secondary, danger), border styles (bordered/unbordered), icon support, and disabled state
Feel free to modify these components or create new ones as needed.
You will be evaluated on:
- Code quality: Clean, readable, maintainable code
- Functionality: All core requirements working correctly
- Design fidelity: Matching the Figma design
- Type safety: Proper use of TypeScript types
- Error handling: Graceful handling of edge cases and errors
- User experience: Intuitive and responsive interface
- Start by understanding the existing code structure
- Use the shared types from
@apipackage to ensure type safety - Test your API calls - the server logs requests to the console
- Focus on getting core functionality working first, then refine the UI
- Ask questions if anything is unclear!
- The server uses in-memory storage - data will reset when the server restarts
- All API endpoints return JSON
- The server includes CORS headers for local development
- Error responses follow the format:
{ error: string, details?: any }
Good luck!
