Skip to content

Latest commit

 

History

History
143 lines (127 loc) · 8.92 KB

CLAUDE.md

File metadata and controls

143 lines (127 loc) · 8.92 KB

Tax Utilities - CLAUDE.md

Project overview

A React + TypeScript single-page app that provides small, focused Indian tax tools. Everything runs 100% client-side - no server, no uploads, no data leaves the browser.

  • Stack: React 18, TypeScript, Vite, Tailwind CSS v3, shadcn/ui (Radix primitives), SheetJS (xlsx), react-dropzone, sql.js-httpvfs (client-side SQLite over HTTP), country-flag-icons
  • Dev: npm run dev - Vite dev server
  • Build: npm run build - tsc -b && vite build
  • Lint/type-check: npm run lint - tsc -b --noEmit (no ESLint configured)
  • Format: npm run format (Prettier, writes) / npm run format:check (CI-safe). Prettier uses prettier-plugin-tailwindcss to sort classes; src/components/ui (generated shadcn) is ignored.

Key constraints

  • No brand icons from lucide-react - they are deprecated. Use inline SVGs from simpleicons.org instead.
  • No sample files bundled - users must supply their own broker exports; sample.xlsx in the repo root is for local dev only, never import or reference it from source.
  • No "upcoming"/"coming soon" placeholders - the UPCOMING registry export and any UI that renders disabled/greyed-out future tools has been removed. New tools ship when they are ready.
  • No comments unless the WHY is non-obvious; never explain what code does.

Directory layout

src/
  components/         # Shared UI (AppSidebar, Overview, FileDropzone, tables, shadcn/ui)
  lib/tax/            # Domain logic - parser, periods, report, format (no React)
  tools/
    registry.ts       # TOOLS array + getTool() - the only place to register a tool
    quarterly-capital-gains/  # First live tool
    sbi-forex-rates/          # SBI forex card rates (reads remote SQLite)
  lib/forex/db.ts     # sql.js-httpvfs client for the remote sbi_rates.db
  App.tsx             # Top-level router (reads activeId, renders the right tool)
  main.tsx

Adding a new tool

  1. Create src/tools/<slug>/<ComponentName>.tsx.
  2. Add an entry to TOOLS in src/tools/registry.ts - id, name, shortName, description, icon (non-brand lucide icon), component.
  3. Done - the sidebar and overview card appear automatically.

Heavy tools (e.g. anything pulling in a WASM engine) are lazy()-loaded in the registry so their code stays out of the initial bundle; App.tsx wraps the rendered tool in <Suspense>.

SBI Forex Rates tool (no backend)

  • The committed SQLite file is the API. A separate scraper repo (sbi-forex-util) commits sbi_rates.db; the browser queries it in place via sql.js-httpvfs using HTTP Range requests. Never introduce a server or hosted DB.
  • Data URL: https://raw.githubusercontent.com/Ankitz007/sbi-forex-util/main/sbi_rates.db (raw.githubusercontent.com serves Accept-Ranges: bytes + CORS *).
  • Use serverMode: "full" - it uses synchronous XHR in the worker, so it needs no SharedArrayBuffer and no COOP/COEP headers (works on GitHub Pages).
  • Schema forex_rates(id, currency, ticker, tt_buy/sell, bill_buy/sell, ftc_buy/sell, cn_buy/sell, date, category). date is ISO YYYY-MM-DD. Categories are exactly BELOW_10 and BETWEEN_10_20.
  • currency is the full name ("AUSTRALIAN DOLLAR"); the 3-letter ISO code ("AUD") is in ticker. Flag lookup and priority sorting must key on ticker, not currency.
  • Tax-rate guidance (keep accurate). The tool converts foreign amounts using SBI's TT Buying rate (TTBR), but the relevant date AND the governing law differ by ITR schedule:
    • Capital gains (Schedule CG): governed by Rule 115 - use the TTBR of the last day of the month immediately preceding the transaction date.
    • Foreign assets (Schedule FA): Rule 115 does not apply; the CBDT ITR instructions require the TTBR on the relevant event / reporting date.
    • The UI shows a red best-effort disclaimer at the top; never present values, tips or notes as authoritative.
  • No em-dashes / en-dashes anywhere (UI copy, comments, docs). Use a plain hyphen -.
  • Benign console warning: sql.js-httpvfs logs "The server did not respond with Accept-Ranges=bytes". raw.githubusercontent.com does honour Range (206s), but sends no Access-Control-Expose-Headers, so browser JS can't read Accept-Ranges/Content-Range. The warning is cosmetic and unfixable client-side; data loads correctly.
  • Vite gotcha (do not regress): sql.js-httpvfs is a UMD/CommonJS bundle, so it MUST be pre-bundled - optimizeDeps.include: ["sql.js-httpvfs"], NOT exclude. Excluding it breaks the dev server with doesn't provide an export named 'createDbWorker'. The worker/wasm are imported with the ?url suffix (sql.js-httpvfs/dist/sqlite.worker.js?url, .../sql-wasm.wasm?url) so they emit as hashed static assets that resolve under base: "./". After changing optimizeDeps, clear node_modules/.vite.

UI conventions

  • shadcn/ui components live in src/components/ui/; generate with npx shadcn@latest add <component> if a new primitive is needed.
  • Lucide icons are fine for non-brand glyphs (LineChart, Landmark, X, etc.). For brand logos (GitHub etc.) use an inline SVG.
  • Responsive layout: sidebar is a fixed drawer on mobile (lg: prefix for desktop behaviour).

Tax domain

  • Supported brokers: Zerodha and Groww only. The UI asks which broker first.
    • Zerodha: one workbook (multiple sheets). Stocks + mutual funds both live in it; auto-detected.
    • Groww: two separate files (Stocks, Mutual Funds); upload either or both.
  • Sections detected heuristically from row shape, not hard-coded positions - scanSections() in src/lib/tax/parser.ts turns a worksheet into titled sections with resolved column maps.
  • Statement-format layer src/lib/tax/formats.ts: detectFormat() + per-format extractors (zerodha, groww-stocks, groww-mf) that all emit the same Trade[], each trade tagged with a category (its table) and a section (its term).
  • Zerodha mutual funds is a two-sheet join. The Tradewise Exits... sheet has dated MF lots but no STCG/LTCG or equity/debt label; the Mutual Funds sheet has the classification (ST/LT x Equity/Debt) but no dates. Each dated lot is joined to the classification by full fund name (Symbol). We trust Zerodha's own buckets - no tax law is re-encoded; the Realized Profit Breakdown is used only for a non-fatal reconciliation warning.
  • Groww mutual funds two-column rule. A row carries the gain in a Short Term-Capital Gain and/or Long Term-Capital Gain column; each populated column becomes one Trade. The asset class comes from the per-row Category column (Equity / Debt (Specified) / Debt (Unspecified)), NOT the section title - Groww omits a title row before the last asset class and just repeats the header, so title-based bucketing would misfile it.
  • Buyback trades are excluded for every format.
  • Report aggregation in src/lib/tax/report.ts. buildStatementReports() returns a StatementReports: a stocks report, a combined mf report, one CategoryReport per MF asset class (mfCategories), the grand combined report, and a turnover breakdown. Advance-tax periods in src/lib/tax/periods.ts.
  • Output layout (three periodic tables). The UI renders at most three period-by-period tables: Stocks (QuarterlyTable), Mutual Funds (MutualFundsTable) and Total (QuarterlyTable, only when both stocks and MF are present). The MF table is a single card whose columns are grouped by asset class - each class (Equity, Debt, ...) contributes a pair of STCG/LTCG columns across a two-row header, the five advance-tax periods are the rows, and a trailing Total column plus footer total row close it out. There is no separate per-asset-class table.
  • Buy / Sell Value table (TurnoverTable) ordering. One flat table ordered Stocks, then each MF category, then a Mutual Funds subtotal row (shown only when it adds information: more than one MF category, or MF alongside stocks), then a grand Total footer. The footer sums the category rows only, so the subtotal is never double-counted. Driven by StatementReports.turnover (CategoryTurnover[]) + turnoverTotal.
  • Clear button. Shown once any file is loaded; clearAll() resets every workbook/file-name slot, the value-column choice and the FY override, returning the tool to its empty state. Switching broker resets the same file state.
  • The report/table layer is broker-agnostic; only formats.ts/parser.ts know broker-specific labels. Extractors match columns by name and skip unknown sections so an unexpected layout degrades to a warning rather than a crash. All formats are validated against real sample/ exports (Zerodha taxpnl-*.xlsx, Groww Stocks_*/Mutual_Funds_*).