02 / WORK / CASE STUDY
AI Nail Platform
From AI nail recognition and AR try-on to designer marketplace, CRM and POS.
- TYPE
- FULL STACK
- SCOPE
- AI × AR × Commerce × POS
- STACK
- AI · AR · DJANGO · REACT · B2B
- LINKS
- Internal project (private)
01 / PROBLEM
One platform, two faces: consumers get AI nail recognition and AR try-on; merchants and internal teams get an operations console covering products, orders, inventory, B2B quoting and settlement, promotions and finance. This case study covers what can be shared today — the ops console frontend, spanning ~50 business modules.
02 / CONSTRAINTS
- A commercial, closed-source project; business details stay private.
- The scope is huge and still growing: one SPA has to hold the line on bundle size, consistency and maintainability.
- Frontend and backend share no types: the API contract survives on conventions and centralized error normalization.
03 / ARCHITECTURE
The platform is multiple services: a main business API (Django REST Framework) plus a standalone approval microservice. The ops console is a React 18 + Ant Design SPA — ~600k lines, a service layer split into 128 domain files with 1,951 API call sites, and components organized into 50+ module folders.
Cross-cutting concerns converge in a single Axios interceptor layer: automatic JWT attachment, a 401 refresh queue, global 403 broadcasting, deduped error toasts, and json-bigint for numeric precision. In-browser object detection runs on TensorFlow.js, auth supports WebAuthn/Passkey, 30+ feature flags gate functionality, and CI enforces a bundle-size budget.
04 / RESPONSIBILITIES
The ops console frontend: sole contributor since taking over in September 2025 (330 commits, 72.8% of the repo) — the original team built the initial skeleton in Aug–Sep 2025. Since the takeover: unified the responsive and error-handling architecture, rebuilt dark mode and the design tokens, and ran four cross-module audits with fixes landed. Also responsible for backend (Django) API design and development on the platform.
05 / CHALLENGES → SOLUTIONS
CHALLENGE
The moment a JWT expires, dozens of in-flight requests all try to refresh at once.
SOLUTION
Every 401 joins a refresh queue: one refresh fires, then all queued requests replay. The same interceptor normalizes per-field validation errors from the backend, so forms display them directly instead of parsing on their own.
CHALLENGE
Bundling 50+ modules eagerly would sink the first paint.
SOLUTION
Route-level lazy loading plus feature flags gate the surface area; a CI bundle-size budget makes every size increase a visible decision, never an accident.
CHALLENGE
An enterprise audit across workflows, forms, permissions, accessibility and performance produced 163 raw findings — the highest-leverage class being a “fake success” anti-pattern recurring in four-plus modules: forms toasting success without persisting to the backend, buttons reporting success on silently disabled features.
SOLUTION
The 163 were deduplicated to 49; the 21 Critical/High items were each cross-verified (21/21 confirmed real) before any fix, then landed across 18 batched commits — repairing the fake-success actions and returning eight commission files to the unified errorHandler. The remaining 28 Medium/Low findings stay tracked as open, with no progress inflated.
CHALLENGE
A stock-size table and its alert kept growing downward until they burst the container — but only in browsers with space-occupying scrollbars. Headless testing uses overlay scrollbars, so automated screenshots showed nothing wrong and the page was once misjudged as stable.
SOLUTION
The root cause: the table’s own scroll plus a fixed first column, stacked on a global overflow-x:auto, formed two nested horizontal scroll containers — and the fixed column kept re-triggering Ant Design’s ResizeObserver in a “scrollbar appears → container narrows → remeasure” loop. Removing the table-level scroll/fixed and letting the global wrapper own a single scroll layer fixed it, verified with Playwright at 1280/900/600/375 widths.
06 / SYSTEM FACTS
91
admin routes
~600k
lines of frontend code
1,193
test cases (Jest + E2E)
72.8%
of commits mine (330/453)
07 / LESSONS
Without shared types, the highest-leverage contract investment is normalizing error shapes in the interceptor — it beats per-form handling by an order of magnitude.
Tech debt gets paid only when it has a number: audited, prioritized items get fixed one by one — 'when we have time' never arrives on its own.
“The UI said success” and “the data was actually written” are two different claims to verify: the audit’s most repeated anti-pattern was fake success — a toast fires while nothing persists. Every write operation now gets checked on both sides before shipping: the feedback the user sees, and the state the backend holds.
NEXT
Naily — AI Nail Commerce App