Front-End Coding Task
Build a small but complete exam-taking flow in React from a Figma design, backed by a hosted API. A clean, working core beats a pile of half-finished features.
Three screens. Follow the Figma for layout, spacing, and colors.
| # | Screen | What it does | Figma |
|---|---|---|---|
| 1 | Exam | Timed test. One question at a time, four options (single choice), previous / next, live countdown, autosave each answer. | Exam โ |
| 2 | Result | Trophy + pass / fail message, final score %, and two tiles: number correct and number wrong. | Result โ |
| 3 | Answer Review | Walk the questions showing the student's answer vs. the correct answer, with the explanation. | Review โ |
timeLeft. When it hits zero, auto-submit./exam/:id โ /result โ /review, with guards (can't open the result before submitting).start (savedAnswer + timeLeft).aria-checked) and focus management.maxSelections; our exam is single-select, but designing for it is a plus).fetch or axios. No XMLHttpRequest.VITE_API_URL).| Token | Value |
|---|---|
| Screen background | #050F2C |
| Card / option background | #06205B |
| Card border | 1px solid #123780 |
| Selected option border / accent | #00C8FF (cyan) |
| Primary gradient | linear-gradient(135deg, #00C8FF, #5285FF) |
| Correct (green) | #05CD99 |
| Wrong (red) | #EE5D6C |
| Muted / meta text | #92C9FF |
| Radius | cards 32px, options ~16px |
| Font | Cairo (Google Fonts) โ weights 400/500/700/800/900 |
The API is already running โ you don't need to start anything. It's seeded with one real exam (ุงุฎุชุจุงุฑ ุงูููุฑุจูุฉ โ ูู ูุฐุฌ A, 8 single-choice questions, 30 minutes, pass mark 50%). Every endpoint adds a small artificial delay so your loading states are visible.
start response gives options without any isCorrect flag โ you cannot grade on the client. The score comes from the API after you submit.index (1..4), not by array position. Send the picked index to autosave.Lists available exams.
{
"exams": [
{
"id": 2,
"name": "ุงุฎุชุจุงุฑ ุงูููุฑุจูุฉ โ ูู
ูุฐุฌ A",
"time": 30,
"totalQuestions": 8,
"resultsMode": "instant",
"attemptsUsed": 0,
"maxAttempts": null,
"attempt": null
}
]
}
Starts (or resumes) an attempt and returns the questions. Before you submit, calling it again resumes the same attempt (refresh-safe); after you submit, it starts a fresh attempt.
{
"entryId": 1001,
"examId": 2,
"name": "ุงุฎุชุจุงุฑ ุงูููุฑุจูุฉ โ ูู
ูุฐุฌ A",
"timeLeft": 1800,
"endsAt": "2026-07-25T12:00:00.000Z",
"questions": [
{
"id": 7,
"order": 1,
"type": "choose",
"title": "ุญุณุงุจ ุงูู
ูุงูู
ุฉ R",
"body": "ูู ุงูุฏุงุฆุฑุฉ ุงูู
ูุงุจูุฉ ...",
"equation": null,
"isLatin": false,
"mark": 1,
"maxSelections": 1,
"images": [],
"options": [
{ "index": 1, "text": "8" },
{ "index": 2, "text": "12" },
{ "index": 3, "text": "16" },
{ "index": 4, "text": "24" }
],
"savedAnswer": []
}
]
}
timeLeft (seconds) and endsAt are server-authoritative โ seed your countdown from them, don't trust the client clock.savedAnswer is the previously autosaved answer (array of indexes) โ non-empty when resuming.images is [] here (real questions have circuit diagrams on a CDN). Render the array if present; otherwise just show the text.Autosave a single answer as the student picks it. Use the stable option index.
// request body
{ "answer": "2" } // single, or "1,3" / [1,3] for multi-select
// response
{ "saved": true, "answer": [2] }
Submits the attempt.
{ "status": "submitted", "entryId": 1001, "resultsMode": "instant", "resultsAt": null }
Returns the graded result (this exam releases instantly). Use it for both the Result and Review screens. While the attempt is still running it returns { "status": "running", "timeLeft": โฆ } instead.
{
"entryId": 1001,
"examId": 2,
"status": "marked",
"released": true,
"examMark": 6,
"totalMark": 8,
"passMark": 50,
"passed": true,
"submittedAt": "2026-07-25T12:05:00.000Z",
"questions": [
{
"id": 7,
"order": 1,
"title": "ุญุณุงุจ ุงูู
ูุงูู
ุฉ R",
"mark": 1,
"options": [
{ "index": 1, "text": "8", "isCorrect": false },
{ "index": 2, "text": "12", "isCorrect": true },
{ "index": 3, "text": "16", "isCorrect": false },
{ "index": 4, "text": "24", "isCorrect": false }
],
"yourAnswer": [2],
"questionMark": 1,
"solutionStatus": "correct",
"explanation": "ุงูุญู: ุจุชุทุจูู ูุงููู ุฃูู
..."
}
]
}
solutionStatus is correct | incorrect | pending (pending = unanswered).examMark / totalMark ร 100. passed is true when that โฅ passMark.Good luck โ we're looking forward to seeing what you build. ๐
Questions about the design or API? Just reach out.