# Developer Guide — Reimbursement Form This guide is for developers who want to fork, extend, or modify the reimbursement form. --- ## Repository layout ``` . ├── CLAUDE.md # Agent-oriented reference (architecture, state, tasks) ├── README.md ├── LICENSE ├── docs/ │ ├── user-guide.md │ ├── admin-guide.md │ └── developer-guide.md # this file └── app/ ├── index.html # Entire application (~870 lines) ├── config.yml # Runtime configuration └── assets/ └── logo.png # Optional organisation logo ``` --- ## Technology | Concern | Technology | |---|---| | UI | Vanilla JS — DOM manipulation, no framework, no virtual DOM | | PDF generation | [pdf-lib](https://pdf-lib.js.org/) 1.17.1 via CDN | | Config parsing | [js-yaml](https://github.com/nodeca/js-yaml) 4.1.0 via CDN | | Build | None — single HTML file served as-is | There is no `package.json`, no bundler, and no transpilation step. The file runs directly in any modern browser. --- ## Code structure inside `index.html` The JS is one immediately-invoked async function. Sections are separated by `// ===` banners: | Section | Line range | Purpose | |---|---|---| | UTILITIES | ~103–131 | `uid()`, `el()`, `fmtAmt()`, `defaultPeriod()` | | CONFIG | ~136–143 | `loadConfig()` — fetches and parses `config.yml` | | STATE | ~145–155 | `state` object, `newItem()`, `newLine()` | | CALCULATIONS | ~157–175 | `recalc()` — recomputes subtotals and grand total | | CURRENCY DROPDOWN | ~177–195 | `makeCDD()` — custom two-line currency picker | | SELECT HELPER | ~197–208 | `makeSelect()` — standard `` element cannot display two-line options (code on line 1, name on line 2) that collapse to code-only in the closed state. The custom dropdown is a positioned `div` with click-outside-to-close via a document-level listener. **FX rate direction:** The rate is "units of line currency per 1 base currency". This means `base_amount = line_amount / fx_rate`. Example: if base is USD and line is THB at rate 34.25, then 1000 THB = 1000/34.25 = 29.20 USD. **Receipt storage as ArrayBuffer:** Files are stored as `ArrayBuffer` in state (not as object URLs or base64). This keeps them ready for pdf-lib without re-reading. Memory use is proportional to total receipt file size. **No re-rendering:** DOM elements are created once by `render()`/`renderItem()`/`renderLine()` and mutated in-place by event listeners. The only exception is the receipt area (`buildReceiptArea`), which is replaced when the file list or receipt toggle changes. **`localStorage` for staff name:** Keyed as `reimb-staff`. This persists the most frequently retyped field across sessions without requiring a server. **Period default logic:** `defaultPeriod()` returns the previous calendar month in most cases. Exception: if today is the last day of the month, it returns the current month (on the assumption that the user is filing for the month just ending). --- ## Known limitations and planned work | Limitation | Notes | |---|---| | Custom TTF fonts not embedded | Config keys present but code uses StandardFonts only | | Password-protected PDF receipts fail | pdf-lib cannot decrypt; an error page is inserted instead | | No offline support | CDN-loaded libraries require internet on first load | | Large receipts stress browser memory | ArrayBuffer approach stores all bytes in RAM | | PDF text truncated with `…` | Long field values are cut to fit the fixed column width | | No multi-line PDF text in most fields | Only `intro` and `noReceiptExplanation` wrap; other fields truncate | --- ## Testing There is no automated test suite. To test manually: 1. Open `app/index.html` via a local HTTP server (e.g. `python3 -m http.server 8080` from the `app/` directory, then open `http://localhost:8080`). 2. Opening `index.html` as a `file://` URL will fail — `fetch('config.yml')` is blocked by browser security on `file://` origins. 3. Exercise the form: add items and lines, attach receipts (PDF, PNG, JPG), toggle the receipt Yes/No, use non-base currencies, and generate a PDF. 4. Verify the generated PDF: check totals, receipt embedding, page references, footers, and column layout. --- ## Deployment checklist - [ ] `config.yml` is updated for the target organisation - [ ] `assets/logo.png` placed if `logo: yes` - [ ] Web server serves `.yml` with a valid MIME type (`text/yaml`) - [ ] URL is accessible to intended users - [ ] Browser test: open form, fill in one claim, generate PDF, verify output