Imported from Arn-v/Conflow (
AGENTS.md). Install upstream withnpx skills add Arn-v/Conflow. Copyright stays with the author.
AGENTS.md
This file provides guidance to Codex (Codex.ai/code) when working with code in this repository.
What this project is
A Unified Commerce Intelligence Platform for Indian D2C brands — a Next.js web app that connects Shopify, Razorpay, Zepto, Blinkit, and Meta Ads into a single dashboard showing true profit across all channels. It consists of two distinct surfaces: a marketing landing page (scroll-animated, storytelling) and an operational dashboard (data-rich, light mode only).
Commands
npm run dev # Start Next.js dev server at http://localhost:3000
npm run build # Production build
npm run lint # ESLint via next lint
npm run test # Run unit tests once (Vitest)
npm run test:watch # Run unit tests in watch mode
npm run test:e2e # Run Playwright E2E tests (requires dev server)
Run a single unit test file:
npx vitest run tests/unit/metrics/daily-profit.test.ts
Run a single E2E spec:
npx playwright test tests/e2e/dashboard.spec.ts
Architecture
App Router structure
/→ Landing page (src/app/page.tsx→src/components/landing/landing-page.tsx) — fully implemented/onboarding→ Onboarding wizard (src/app/onboarding/page.tsx) — fully implemented/dashboard→ Dashboard overview (src/app/dashboard/page.tsx) — fully implemented, server component callinggetDashboardSnapshot()+getAppState()/dashboard/settings/sources→ Source health management — fully implemented/dashboard/alerts→ stub only (route file exists, no implementation)/dashboard/customers→ stub only/dashboard/channels/[channel]→ stub only (per-channel drill-down not yet built)
API routes live in src/app/api/onboarding/ and handle signup, connector configuration, sync orchestration, and state polling. Endpoints: POST /signup, POST /connect, POST /sync/start, GET /sync/status, GET /state.
The adapter pattern (core data layer)
Every data source implements SourceAdapter (defined in src/lib/adapters/contracts.ts):
getHealth() / fetchOrders() / fetchSettlements() / fetchAdSpend() / fetchInventory()
Each source has a live/ adapter (real API calls) and a mock/ adapter (deterministic fake data). resolveAdapter() in src/lib/orchestration/sourceRouter.ts selects between them at runtime — if the live adapter's getHealth() returns "failed" and allowFallback is true, the mock activates as a "contingency mock."
Current adapter status:
- Shopify: live (GraphQL Admin API) + mock + mappers (
src/lib/adapters/shopify/) - Razorpay: live (REST settlements via Basic Auth) + mock + mappers (
src/lib/adapters/razorpay/). Note:fetchOrders()returns[]by design — Razorpay is payments-only; orders come from Shopify/Zepto/Blinkit. - Zepto: CSV parser (
csv.ts+ Zod schema) + mock (src/lib/adapters/zepto/) — no REST API, parses uploaded CSV text. CSV is stored inAppState.connectors.zepto.csvText; integration into the warehouse ingest is partial. - Blinkit: mock only (
src/lib/adapters/blinkit/mock.ts) - Meta Ads: mock only (
src/lib/adapters/meta/mock.ts) —getHealth()always returns"failed"
Note: resolveAdapter()'s contingency-mock fallback exists but is not currently invoked from the onboarding API routes — they pick live or mock directly without auto-failover.
Dashboard assembly
getDashboardSnapshot() in src/lib/dashboard/snapshot.ts is the central function that:
- Reads
AppStatefrom file storage to determine which connectors are enabled - Pulls 30 days of orders, settlements, ad spend, and inventory from the warehouse via
src/lib/warehouse/queries.ts(Prisma) - Computes
trueProfitPaise = revenue - gateway fees - shipping - returns - ad spend - Builds a 7-day daily metrics series and calls metric computers under
src/lib/metrics/: Blended CAC, Channel Overlap, LTV summary - Generates operational alerts via the rules engine in
src/lib/alerts/ - Returns a typed
DashboardSnapshotconsumed by the dashboard UI
Warehouse + metrics layers
src/lib/warehouse/queries.ts— Prisma queries:getOrdersInRange(),getSettlementsInRange(),getAdSpendInRange(),getInventoryInRange(),getAllCustomers()src/lib/warehouse/ingest.ts—runSync()callsresolveAllAdapters()and writes adapter output into Prisma tables. Persistence logic is partially stubbed — adapters are invoked but final upsert paths are incomplete.src/lib/metrics/— pure functions:dailyProfit.ts,blendedCac.ts,ltv.ts,channelOverlap.tssrc/lib/identity/normalize.ts— email/phone normalization for cross-channel customer matchingsrc/lib/alerts/rules.ts— alert rule evaluator
State persistence (hybrid: file-based AppState + Prisma warehouse)
Onboarding/account state lives in ./data/app-state.json via src/lib/storage/appState.ts. Warehouse data (orders, customers, settlements, ad spend daily, inventory snapshots, sync jobs, source status) is persisted in SQLite via Prisma — see prisma/schema.prisma. Both layers are active; do not assume Prisma is unused.
AppState shape (defined in src/lib/onboarding/state.ts):
account— brand email + name after signupconnectors— enabled flag + credentials per sourcesync— job ID, progress, status for background sync
deriveOnboardingStep() computes the current step (signup → connect → sync → complete) purely from AppState.
Money handling
All monetary values are in paise (₹1 = 100 paise) throughout the entire codebase. Never store or compute in rupees. Use formatInrPaise() from src/lib/utils/currency.ts for display.
Path alias
@ maps to ./src in both TypeScript and Vitest.
Design constraints (non-negotiable)
- Light mode only — no dark mode anywhere, no
dark:Tailwind classes - No emoji as icons — Lucide React icons only
- Color palette defined in
DESIGN-PROMPT.md: backgrounds#FAFAFA/#FFFFFF, accent#6366F1(indigo-500), profit#10B981, loss#EF4444 - Channel colors are defined in
DESIGN-PROMPT.mdandsrc/components/landing/data.ts— use consistently - Animation libraries in use: Framer Motion (scroll + transitions), react-countup (number animations), Lenis (
@studio-freight/lenis) for smooth scroll on landing page - Charts use Recharts (installed, v3.x) — see
src/components/dashboard/revenue-trend-chart.tsxandchannel-breakdown-chart.tsx - No shadcn/ui yet — components are hand-built with Tailwind
Component organization
src/components/ is split by surface:
landing/—landing-page.tsx,hero-scene.tsx,problem-scene.tsx,convergence-scene.tsx,solution-scene.tsx,stats-band.tsx,platform-marquee.tsx,cta-closer.tsx,smooth-scroll.tsx(Lenis wrapper),top-nav.tsx, plusdata.ts(channel colors, copy)dashboard/—dashboard-shell.tsx,dashboard-overview.tsx,kpi-card.tsx,metric-card.tsx,revenue-trend-chart.tsx(Recharts AreaChart),channel-breakdown-chart.tsx(Recharts BarChart),alerts-feed.tsx,customer-ltv-table.tsxonboarding/—onboarding-wizard.tsx(4-step form, polls/api/onboarding/sync/status)sources/—source-health-card.tsx,source-settings-panel.tsx
Key type definitions
All shared types live in src/lib/types/unified.ts:
SourcePlatform—"shopify" | "razorpay" | "zepto" | "blinkit" | "meta_ads"UnifiedOrder— canonical order shape with all amounts in paiseDashboardSnapshot— whatgetDashboardSnapshot()returns, consumed by dashboard UISourceHealth/SourceMode— health state and live-vs-mock mode per connector
Testing conventions
- Unit tests:
tests/unit/**/*.test.ts, run with Vitest innodeenvironment - E2E tests:
tests/e2e/**/*.spec.ts, run with Playwright againsthttp://localhost:3000 - Mock adapters generate deterministic data seeded by brand profile — see
src/lib/adapters/mock/baseGenerator.ts - Zepto CSV parsing is tested against fixture CSV strings in
tests/unit/adapters/zepto-csv.test.ts