diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2a65274 --- /dev/null +++ b/.gitignore @@ -0,0 +1,29 @@ +# Dependencies +node_modules/ + +# Build output +dist/ + +# Local env files +.env +.env.local +.env.*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea/ +*.swp +*.swo +*~ + +# OS files +.DS_Store +Thumbs.db + +# Logs +*.log +npm-debug.log* + +# TypeScript cache +*.tsbuildinfo diff --git a/PLAN_SESSION_NOTE_EXPORT.md b/PLAN_SESSION_NOTE_EXPORT.md new file mode 100644 index 0000000..65d947e --- /dev/null +++ b/PLAN_SESSION_NOTE_EXPORT.md @@ -0,0 +1,301 @@ +# Implementation Plan: Parent Session Note Export Feature + +## Overview +Add the ability to export professional session notes for parents in PDF and Word (.docx) formats. The document will follow the ABA session note template with company branding, session data, service documentation, and signature sections. + +## Company Information +- **Company Name**: ABA Spot +- **Address**: 816 Pennsylvania Ave, Saint Cloud, FL, 34769 +- **Email**: ABASpotFL@gmail.com + +--- + +## Phase 1: Update Data Model + +### File: `src/db/database.ts` + +Add new fields to the `Session` interface: + +```typescript +// New type for service codes +export type ServiceType = '97155' | '97153' | '97156'; + +export interface Session { + // ... existing fields ... + + // New fields for parent session note + sessionFocus?: string; // Renamed from "Chief Complaint" + location?: string; // Session location + totalUnits?: number; // Billing units + serviceType?: ServiceType; // CPT code selection + parentParticipation?: boolean; // Did parent participate? + parentParticipationNotes?: string; // If no, why not? + + // Service-specific documentation fields + protocolModification?: string; // 97155: Description of Modification & Client Responses + protocolDescription?: string; // 97153: Activities, Protocol Description & Client Responses + familyTrainingDescription?: string; // 97156: Family Training Description + generalNotes?: string; // General Notes, Environmental Changes, Recommendations +} +``` + +### Database Migration +- Increment database version from 2 to 3 +- Add migration to handle existing sessions (set new fields to undefined) + +--- + +## Phase 2: Update Session Recording UI + +### File: `src/pages/SessionPage.tsx` + +Add new input sections to capture session note data: + +1. **Header Section** (after session timer): + - Session Focus (text input) + - Location (text input, can auto-fill from client profile) + - Total Units (number input) + +2. **Service Type Selection** (new section): + - Radio buttons or tabs for: + - 97155 - Behavior Treatment with Protocol Modification (BCBA/BCaBA) + - 97153 - Behavior Treatment by Protocol (Direct service/RBT) + - 97156 - Family Training (BCBA/BCaBA) + - Show all by default, user selects which applies + +3. **Service Documentation Sections** (based on selection): + - 97155: "Description of Modification & Client Responses" textarea + - 97153: "Activities, Protocol Description & Client Responses" textarea + - 97156: "Family Training Description" textarea + +4. **Parent Participation Section**: + - Toggle: "Did Parent(s)/Caregiver(s) participate?" + - If No: "Why not?" text input + +5. **General Notes Section**: + - "General Notes, Environmental Changes, Recommendations, etc." textarea + - This is separate from the existing session notes + +### State Updates +- Add state variables for all new fields +- Update `saveSession()` function to include new fields +- Auto-save includes new fields + +--- + +## Phase 3: Install Word Document Library + +### Package Installation +```bash +npm install docx file-saver +npm install --save-dev @types/file-saver +``` + +The `docx` library allows creating Word documents programmatically with: +- Headers, footers +- Tables +- Text formatting +- Sections and paragraphs + +--- + +## Phase 4: Create Export Functions + +### File: `src/utils/export.ts` + +Add two new export functions: + +### 4.1 `exportParentSessionNotePDF(session, client)` + +Creates a PDF matching the template layout: + +**Page 1:** +``` ++------------------------------------------+ +| ABA Spot | +| 816 Pennsylvania Ave, Saint Cloud, FL | +| ABASpotFL@gmail.com | ++------------------------------------------+ +| Name: [client] | Date: [date] | Therapist: [name] | +| Start: [time] | End: [time] | Total Units: [#] | +| Session Focus: [text] | Location: [text] | ++------------------------------------------+ +| Summary of Services (choose only 1): | ++------------------------------------------+ +| [ ] 97155 - Behavior Treatment with Protocol Modification | +| Description of Modification & Client Responses: | +| [text area content] | ++------------------------------------------+ +| [ ] 97153 - Behavior Treatment by Protocol | +| Activities, Protocol Description & Client Responses: | +| [text area content] | ++------------------------------------------+ +| Did Parent(s)/Caregiver(s) participate? [Yes/No] | +| If No, why not? [text] | ++------------------------------------------+ +``` + +**Page 2:** +``` ++------------------------------------------+ +| [ ] 97156 - Family Training | +| Description: | +| [text area content] | ++------------------------------------------+ +| General Notes, Environmental Changes, | +| Recommendations, etc. | +| [text area content] | ++------------------------------------------+ +| Behavior Data Summary Table | +| Behavior | Type | Value | +| ---------|------|------------------------ | +| [data rows] | ++------------------------------------------+ +| Caregiver Signature: ________________ | +| | +| Provider Signature: _________________ | ++------------------------------------------+ +``` + +### 4.2 `exportParentSessionNoteDocx(session, client)` + +Creates a Word document with the same layout as PDF: +- Uses `docx` library +- Includes company header +- Tables for header info +- Service sections with checkboxes +- Behavior data table +- Signature lines at bottom + +### Helper Functions +- `createCompanyHeader()` - Returns header with company info +- `createSessionInfoTable()` - Returns table with session metadata +- `createServiceSection()` - Returns formatted service documentation +- `createBehaviorDataTable()` - Returns behavior summary table +- `createSignatureSection()` - Returns signature lines + +--- + +## Phase 5: Update Export UI + +### File: `src/pages/SessionDetailPage.tsx` + +Update the export modal to include new options: + +```tsx + +
+

Data Export

+ + +
+ +
+

Parent Session Note

+ + +
+ + {session.notes && ( +
+

Notes Only

+ +
+ )} +
+``` + +--- + +## Phase 6: Update Session Detail View + +### File: `src/pages/SessionDetailPage.tsx` + +Add display sections for new fields: +- Show service type and documentation +- Show parent participation status +- Show general notes separately from session notes + +--- + +## File Changes Summary + +| File | Changes | +|------|---------| +| `src/db/database.ts` | Add ServiceType, new Session fields, bump version | +| `src/pages/SessionPage.tsx` | Add UI for new fields, update save logic | +| `src/utils/export.ts` | Add `exportParentSessionNotePDF()`, `exportParentSessionNoteDocx()` | +| `src/pages/SessionDetailPage.tsx` | Update export modal, display new fields | +| `package.json` | Add `docx` and `file-saver` dependencies | + +--- + +## Document Layout Details + +### Header (both formats) +- Company name: "ABA Spot" (bold, centered) +- Address: "816 Pennsylvania Ave, Saint Cloud, FL, 34769" +- Email: "ABASpotFL@gmail.com" + +### Session Info Table +| Field | Value | +|-------|-------| +| Name | Client name | +| Date | Session date | +| Therapist | (from app user or manual entry) | +| Start Time | Session start | +| End Time | Session end | +| Total Units | User input | +| Session Focus | User input | +| Location | User input or from client profile | + +### Service Sections +- Checkbox indicator for selected service +- Section header with CPT code and description +- Text content area + +### Behavior Data Table +| Behavior | Type | Category | Value | +|----------|------|----------|-------| +| Auto-populated from session data | + +### Signature Section +``` +Caregiver Signature: _______________________________ + +Provider Signature: ________________________________ +``` + +--- + +## Questions/Decisions + +1. **Therapist Name**: Should this come from: + - A new "therapist" field in settings? + - Manual entry per session? + - Default to empty for now? + +2. **Auto-fill Location**: Should location auto-fill from client profile if available? + +3. **Service Type Default**: Should all service types be shown, or only the selected one in the export? + +--- + +## Implementation Order + +1. Update database schema and types +2. Install dependencies (`docx`, `file-saver`) +3. Update SessionPage.tsx with new input fields +4. Create PDF export function +5. Create Word export function +6. Update SessionDetailPage.tsx export modal +7. Test all exports +8. Commit and push + +--- + +## Estimated Complexity +- Database changes: Low +- UI changes: Medium (multiple new form fields) +- Export functions: High (PDF/Word generation with tables) +- Total new lines of code: ~500-700 diff --git a/package-lock.json b/package-lock.json index d86a837..dc8a50f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,8 @@ "date-fns": "^4.1.0", "dexie": "^4.2.1", "dexie-react-hooks": "^4.2.0", + "docx": "^9.5.1", + "file-saver": "^2.0.5", "jspdf": "^3.0.4", "jspdf-autotable": "^5.0.2", "react": "^19.2.0", @@ -21,6 +23,7 @@ }, "devDependencies": { "@eslint/js": "^9.39.1", + "@types/file-saver": "^2.0.7", "@types/node": "^24.10.1", "@types/react": "^19.2.5", "@types/react-dom": "^19.2.3", @@ -68,7 +71,6 @@ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -2908,6 +2910,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/file-saver": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/file-saver/-/file-saver-2.0.7.tgz", + "integrity": "sha512-dNKVfHd/jk0SkR/exKGj2ggkB45MAkzvWCaqLUUgkyjITkGNzH8H+yUwr+BLJUBjZOe9w8X3wgmXhZDRg1ED6A==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -2919,9 +2928,7 @@ "version": "24.10.4", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.4.tgz", "integrity": "sha512-vnDVpYPMzs4wunl27jHrfmwojOGKya0xyM3sH+UE5iv5uPS6vX7UIoh6m+vQc5LGBq52HBKPIn/zcSZVzeDEZg==", - "dev": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~7.16.0" } @@ -2944,7 +2951,6 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz", "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==", "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -3031,7 +3037,6 @@ "integrity": "sha512-6/cmF2piao+f6wSxUsJLZjck7OQsYyRtcOZS02k7XINSNlz93v6emM8WutDQSXnroG2xwYlEVHJI+cPA7CPM3Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.50.0", "@typescript-eslint/types": "8.50.0", @@ -3283,7 +3288,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3536,7 +3540,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -3775,6 +3778,12 @@ "url": "https://opencollective.com/core-js" } }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -4082,8 +4091,7 @@ "version": "4.2.1", "resolved": "https://registry.npmjs.org/dexie/-/dexie-4.2.1.tgz", "integrity": "sha512-Ckej0NS6jxQ4Po3OrSQBFddayRhTCic2DoCAG5zacOfOVB9P2Q5Xc5uL/nVa7ZVs+HdMnvUPzLFCB/JwpB6Csg==", - "license": "Apache-2.0", - "peer": true + "license": "Apache-2.0" }, "node_modules/dexie-react-hooks": { "version": "4.2.0", @@ -4096,6 +4104,41 @@ "react": ">=16" } }, + "node_modules/docx": { + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/docx/-/docx-9.5.1.tgz", + "integrity": "sha512-ABDI7JEirFD2+bHhOBlsGZxaG1UgZb2M/QMKhLSDGgVNhxDesTCDcP+qoDnDGjZ4EOXTRfUjUgwHVuZ6VSTfWQ==", + "license": "MIT", + "dependencies": { + "@types/node": "^24.0.1", + "hash.js": "^1.1.7", + "jszip": "^3.10.1", + "nanoid": "^5.1.3", + "xml": "^1.0.1", + "xml-js": "^1.6.8" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/docx/node_modules/nanoid": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.6.tgz", + "integrity": "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^18 || >=20" + } + }, "node_modules/dompurify": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.1.tgz", @@ -4375,7 +4418,6 @@ "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -4653,6 +4695,12 @@ "node": ">=16.0.0" } }, + "node_modules/file-saver": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz", + "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==", + "license": "MIT" + }, "node_modules/filelist": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", @@ -5097,6 +5145,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -5158,6 +5216,12 @@ "node": ">= 4" } }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "license": "MIT" + }, "node_modules/immer": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/immer/-/immer-10.2.0.tgz", @@ -5195,6 +5259,12 @@ "node": ">=0.8.19" } }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, "node_modules/internal-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", @@ -5807,7 +5877,6 @@ "resolved": "https://registry.npmjs.org/jspdf/-/jspdf-3.0.4.tgz", "integrity": "sha512-dc6oQ8y37rRcHn316s4ngz/nOjayLF/FFxBF4V9zamQKRqXxyiH1zagkCdktdWhtoQId5K20xt1lB90XzkB+hQ==", "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.28.4", "fast-png": "^6.2.0", @@ -5829,6 +5898,24 @@ "jspdf": "^2 || ^3" } }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/jszip/node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)" + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -5863,6 +5950,15 @@ "node": ">= 0.8.0" } }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -5937,6 +6033,12 @@ "node": ">= 0.4" } }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "license": "ISC" + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -6281,6 +6383,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -6316,7 +6424,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -6326,7 +6433,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz", "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -6346,7 +6452,6 @@ "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz", "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==", "license": "MIT", - "peer": true, "dependencies": { "@types/use-sync-external-store": "^0.0.6", "use-sync-external-store": "^1.4.0" @@ -6413,6 +6518,33 @@ "react-dom": ">=18" } }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, "node_modules/recharts": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/recharts/-/recharts-3.6.0.tgz", @@ -6447,8 +6579,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/redux-thunk": { "version": "3.1.0", @@ -6743,6 +6874,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/sax": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.4.4.tgz", + "integrity": "sha512-1n3r/tGXO6b6VXMdFT54SHzT9ytu9yr7TaELowdYpMqY/Ao7EnlQGmAQ1+RatX7Tkkdm6hONI2owqNx2aZj5Sw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, "node_modules/scheduler": { "version": "0.27.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", @@ -6824,6 +6964,12 @@ "node": ">= 0.4" } }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "license": "MIT" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -7020,6 +7166,21 @@ "node": ">= 0.4" } }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -7320,7 +7481,6 @@ "integrity": "sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==", "dev": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.15.0", @@ -7500,7 +7660,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -7556,7 +7715,6 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", - "dev": true, "license": "MIT" }, "node_modules/unicode-canonical-property-names-ecmascript": { @@ -7687,6 +7845,12 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, "node_modules/utrie": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz", @@ -7738,7 +7902,6 @@ "integrity": "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -8130,7 +8293,6 @@ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -8188,7 +8350,6 @@ "integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==", "dev": true, "license": "MIT", - "peer": true, "bin": { "rollup": "dist/bin/rollup" }, @@ -8431,6 +8592,24 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/xml": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", + "integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==", + "license": "MIT" + }, + "node_modules/xml-js": { + "version": "1.6.11", + "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz", + "integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==", + "license": "MIT", + "dependencies": { + "sax": "^1.2.4" + }, + "bin": { + "xml-js": "bin/cli.js" + } + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", @@ -8457,7 +8636,6 @@ "integrity": "sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw==", "dev": true, "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/package.json b/package.json index a744c20..cd07f6b 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,8 @@ "date-fns": "^4.1.0", "dexie": "^4.2.1", "dexie-react-hooks": "^4.2.0", + "docx": "^9.5.1", + "file-saver": "^2.0.5", "jspdf": "^3.0.4", "jspdf-autotable": "^5.0.2", "react": "^19.2.0", @@ -23,6 +25,7 @@ }, "devDependencies": { "@eslint/js": "^9.39.1", + "@types/file-saver": "^2.0.7", "@types/node": "^24.10.1", "@types/react": "^19.2.5", "@types/react-dom": "^19.2.3", diff --git a/src/db/database.ts b/src/db/database.ts index 30debd0..27ec6a3 100644 --- a/src/db/database.ts +++ b/src/db/database.ts @@ -2,6 +2,8 @@ import Dexie, { type EntityTable } from 'dexie'; export type DataType = 'frequency' | 'duration' | 'interval' | 'event' | 'deceleration'; export type BehaviorCategory = 'acquisition' | 'deceleration'; +export type ServiceType = '97155' | '97153' | '97156'; +export type LocationType = 'Home' | 'Clinic'; // Predefined ABC options export const ANTECEDENT_OPTIONS = [ @@ -51,6 +53,7 @@ export interface Client { phone?: string; address?: string; location?: string; + defaultSessionLocation?: LocationType; targetBehaviors: TargetBehavior[]; createdAt: string; updatedAt: string; @@ -89,6 +92,17 @@ export interface Session { notes: string; createdAt: string; updatedAt: string; + // Parent session note fields + sessionFocus?: string; + sessionLocation?: LocationType; + totalUnits?: number; + serviceType?: ServiceType; + parentParticipation?: boolean; + parentParticipationNotes?: string; + protocolModification?: string; + protocolDescription?: string; + familyTrainingDescription?: string; + generalNotes?: string; } const db = new Dexie('ABADataApp') as Dexie & { @@ -110,4 +124,9 @@ db.version(2).stores({ }); }); +db.version(3).stores({ + clients: 'id, name, createdAt, updatedAt', + sessions: 'id, clientId, startTime, createdAt' +}); + export { db }; diff --git a/src/pages/SessionDetailPage.tsx b/src/pages/SessionDetailPage.tsx index 1006597..fdcef12 100644 --- a/src/pages/SessionDetailPage.tsx +++ b/src/pages/SessionDetailPage.tsx @@ -3,7 +3,7 @@ import { useNavigate, useParams } from 'react-router-dom' import { useLiveQuery } from 'dexie-react-hooks' import { db, type BehaviorData } from '../db/database' import { formatDuration, formatDateTime } from '../utils/time' -import { exportSessionToCSV, exportSessionToPDF, exportNotesToText } from '../utils/export' +import { exportSessionToCSV, exportSessionToPDF, exportNotesToText, exportParentSessionNotePDF, exportParentSessionNoteDocx } from '../utils/export' import ConfirmDialog from '../components/ConfirmDialog' import Modal from '../components/Modal' @@ -93,14 +93,92 @@ export default function SessionDetailPage() {
{formatDateTime(session.startTime)}
-
+
Duration: {formatDuration(session.durationMs ?? 0)}
+ {session.sessionLocation && ( +
+ Location: + {session.sessionLocation} +
+ )} + {session.totalUnits && ( +
+ Units: + {session.totalUnits} +
+ )}
+ {session.sessionFocus && ( +
+ Session Focus: + {session.sessionFocus} +
+ )}
+ {/* Session Note Details */} + {(session.serviceType || session.parentParticipation !== undefined || session.generalNotes) && ( + <> +

Session Note Details

+
+ {session.serviceType && ( +
+ Service Type: + + {session.serviceType === '97155' && '97155 - Behavior Treatment with Protocol Modification'} + {session.serviceType === '97153' && '97153 - Behavior Treatment by Protocol'} + {session.serviceType === '97156' && '97156 - Family Training'} + +
+ )} + + {session.serviceType === '97155' && session.protocolModification && ( +
+
Description of Modification & Client Responses:
+

{session.protocolModification}

+
+ )} + + {session.serviceType === '97153' && session.protocolDescription && ( +
+
Activities, Protocol Description & Client Responses:
+

{session.protocolDescription}

+
+ )} + + {session.serviceType === '97156' && session.familyTrainingDescription && ( +
+
Family Training Description:
+

{session.familyTrainingDescription}

+
+ )} + + {session.parentParticipation !== undefined && ( +
+ Parent/Caregiver Participation: + {session.parentParticipation ? 'Yes' : 'No'} + {session.parentParticipation === false && session.parentParticipationNotes && ( +
+ Reason: + {session.parentParticipationNotes} +
+ )} +
+ )} + + {session.generalNotes && ( +
+
General Notes, Environmental Changes, Recommendations:
+

{session.generalNotes}

+
+ )} +
+ + )} +

Behavior Data

{session.behaviorData.map(bd => ( @@ -259,8 +337,29 @@ export default function SessionDetailPage() { title="Export Session" >
+
Parent Session Note
+ + +
Data Export
+ {session.notes && (
+ {/* Session Note Fields Toggle */} + + + {/* Collapsible Session Note Fields */} + {showSessionNoteFields && ( +
+ {/* Basic Info Row */} +
+
+ + setSessionFocus(e.target.value)} + placeholder="Enter session focus..." + /> +
+
+ +
+
+ + +
+
+ + setTotalUnits(e.target.value ? Number(e.target.value) : '')} + placeholder="0" + min={0} + /> +
+
+ + {/* Service Type Selection */} +
+ +
+ + + +
+
+ + {/* Service-specific documentation */} + {serviceType === '97155' && ( +
+ +