Skip to content

Instantly share code, notes, and snippets.

@ashwch
Created October 22, 2025 19:59
Show Gist options
  • Select an option

  • Save ashwch/0de8890a9aa9cbfedda6b909718dd7cf to your computer and use it in GitHub Desktop.

Select an option

Save ashwch/0de8890a9aa9cbfedda6b909718dd7cf to your computer and use it in GitHub Desktop.
Full Stack Take-Home

Take-Home (2–3 hours, standalone)

Goal: Build a tiny “Manager Alerts” slice inspired by Optimo. Keep scope small; quality and clarity beat breadth.

API Spec (Exact Expectations)

Endpoints

  • GET /api/alerts?manager_id=<id>&scope=direct|subtree[&severity=high,medium&q=alex[&status=open,dismissed]]
    • manager_id required; scope defaults to direct if omitted.
    • Returns a JSON array of alerts for employees determined by scope.
    • Sort by created_at descending (ties: id ascending).
  • POST /api/alerts/{id}/dismiss
    • Sets status="dismissed" and returns the updated alert. Idempotent — dismissing an already-dismissed alert returns 200 with the unchanged resource.

Response Schema

Return each alert in this shape (array of these objects):

{
  "id": "A1",
  "employee": { "id": "E3", "name": "Jordan Lee" },
  "severity": "high",
  "category": "retention",
  "created_at": "2025-09-01T09:00:00Z",
  "status": "open"
}

Data

Use in-memory data, a JSON file, or SQLite. Seed ~20 employees with fields id, name, reports_to (nullable), and ~30 alerts with fields id, employee_id, severity in {low, medium, high}, category, created_at (ISO-8601 UTC), status in {open, dismissed}.

Traversal

Support direct reports and full subtree. Handle cycles or missing managers gracefully (ignore/break cycles using a visited set). Do not include the manager’s own alerts in the results.

Filtering & Validation

  • severity: comma-separated list; allowed values: low,medium,high. Unknown → 400 { "detail": "invalid severity" }.
  • status: comma-separated list; allowed values: open,dismissed. Unknown → 400 { "detail": "invalid status" }.
  • q: case-insensitive substring match on employee name.
  • scope: allowed values: direct,subtree. Unknown → 400 { "detail": "invalid scope" }.
  • Defaults: scope=direct if omitted; status defaults to all (open + dismissed).

Errors

  • Unknown manager_id404 { "detail": "manager not found" }.
  • Unknown alert id on dismiss → 404 { "detail": "alert not found" }.

Time & CORS

  • Time format: RFC3339/ISO-8601 UTC with trailing Z.
  • Dev CORS: use django-cors-headers and either allow all origins (dev only) or explicitly whitelist common ports (e.g., http://localhost:3000, http://localhost:5173). Literal http://localhost:* is not directly supported without a regex.

Backend (Django required: DRF or Django Ninja)

Implement the two endpoints above with the response schema and rules. Keep code straightforward and typed where helpful.

Frontend (React + TypeScript; any modern toolchain)

Build a single page that:

  • Calls the endpoint and displays a table: employee name, category, severity (color chip), status, created_at.
  • Includes a toggle for scope (direct vs subtree, default direct).
  • Filters by severity and text search (employee name).
  • Bonus: Persist filters in the URL query string.
  • Wire up “dismiss” with optimistic update (revert on failure).
  • Use any OSS component library; keep components simple and accessible.

Tests

  • Backend (pytest): at least one unit test for subtree traversal (cycle prevention) and one for the dismiss mutation (idempotency).
  • Frontend (Jest/Vitest): at least one test that filters to “high” severity and one that verifies optimistic update rollback on API failure.

Reference Dataset (Optional)

You may copy the following seed_data.json as-is. It includes a cycle among E6–E8 for traversal tests.

Employees

[
  {"id":"E1","name":"Taylor Reed","reports_to":null},
  {"id":"E2","name":"Alex Morgan","reports_to":"E1"},
  {"id":"E3","name":"Jordan Lee","reports_to":"E2"},
  {"id":"E4","name":"Casey Kim","reports_to":"E2"},
  {"id":"E5","name":"Riley Chen","reports_to":"E3"},
  {"id":"E6","name":"Sam Patel","reports_to":"E7"},
  {"id":"E7","name":"Jamie Singh","reports_to":"E8"},
  {"id":"E8","name":"Morgan Diaz","reports_to":"E6"},
  {"id":"E9","name":"Avery Brooks","reports_to":"E2"},
  {"id":"E10","name":"Quinn Park","reports_to":"E9"}
]

Alerts

[
  {"id":"A1","employee_id":"E3","severity":"high","category":"retention","created_at":"2025-09-01T09:00:00Z","status":"open"},
  {"id":"A2","employee_id":"E4","severity":"medium","category":"engagement","created_at":"2025-09-02T09:00:00Z","status":"open"},
  {"id":"A3","employee_id":"E5","severity":"low","category":"workload","created_at":"2025-09-03T09:00:00Z","status":"open"},
  {"id":"A4","employee_id":"E5","severity":"high","category":"retention","created_at":"2025-09-04T09:00:00Z","status":"open"},
  {"id":"A5","employee_id":"E9","severity":"medium","category":"engagement","created_at":"2025-09-05T09:00:00Z","status":"open"},
  {"id":"A6","employee_id":"E10","severity":"high","category":"retention","created_at":"2025-09-06T09:00:00Z","status":"dismissed"},
  {"id":"A7","employee_id":"E3","severity":"low","category":"workload","created_at":"2025-09-07T09:00:00Z","status":"open"},
  {"id":"A8","employee_id":"E6","severity":"medium","category":"engagement","created_at":"2025-09-08T09:00:00Z","status":"open"},
  {"id":"A9","employee_id":"E7","severity":"high","category":"retention","created_at":"2025-09-09T09:00:00Z","status":"open"},
  {"id":"A10","employee_id":"E8","severity":"low","category":"workload","created_at":"2025-09-10T09:00:00Z","status":"open"},
  {"id":"A11","employee_id":"E4","severity":"high","category":"retention","created_at":"2025-09-11T09:00:00Z","status":"open"},
  {"id":"A12","employee_id":"E9","severity":"low","category":"workload","created_at":"2025-09-12T09:00:00Z","status":"open"},
  {"id":"A13","employee_id":"E10","severity":"medium","category":"engagement","created_at":"2025-09-13T09:00:00Z","status":"open"},
  {"id":"A14","employee_id":"E2","severity":"low","category":"workload","created_at":"2025-09-14T09:00:00Z","status":"open"}
]

Examples & Expected Results (Using Reference Data)

  • Manager: E2 (direct reports: E3, E4, E9; subtree adds E5, E10)
    • GET /api/alerts?manager_id=E2&scope=direct → alerts for E3, E4, E9 only
      • Counts by severity: high 2 (A1, A11), medium 2 (A2, A5), low 2 (A7, A12)
    • GET /api/alerts?manager_id=E2&scope=subtree → adds E5, E10; status defaults to all (open + dismissed)
      • Counts by severity: high 4 (A1, A4, A6, A11), medium 3 (A2, A5, A13), low 3 (A3, A7, A12)
    • GET /api/alerts?manager_id=E2&scope=subtree&status=open&severity=highA1, A4, A11
  • Manager: E7 (cycle with E6 and E8)
    • GET /api/alerts?manager_id=E7&scope=subtree → must terminate; expected employees {E6, E8}; alerts {A8, A10}
  • Dismissal
    • POST /api/alerts/A1/dismiss → returns A1 with status:"dismissed"; subsequent calls return 200 with unchanged resource.

Deliverables

A public Git repository with a short README covering:

  • Setup & run (2–3 commands) and test commands.
  • Time spent and what you cut for time.
  • “How I used AI/LLMs” (if at all).
  • Self-review: why your solution is good, what you’d improve next.
  • Optional: Deployed URLs (e.g., Vercel/Netlify for UI; Railway/Render/Fly for API).

Comments are disabled for this gist.