From 5aab10df044110a5e55fe270a4209bc477efaab6 Mon Sep 17 00:00:00 2001 From: mahdahar <89adham@gmail.com> Date: Mon, 16 Feb 2026 15:58:06 +0700 Subject: [PATCH] chore: cleanup docs and update configs - Remove outdated documentation files (MVP plan, API docs, frontend plan) - Add deployment configuration (DEPLOY.md, apache-config) - Update AGENTS.md with build commands - Fix geography and tests API endpoints - Update Tests page with improved functionality - Update package.json and svelte.config.js --- AGENTS.md | 4 + DEPLOY.md | 154 + apache-config/clqms.conf | 41 + docs/COMPREHENSIVE_IMPLEMENTATION_PLAN.md | 613 --- docs/MVP_IMPLEMENTATION_PLAN.md | 266 - docs/api-docs.yaml | 4476 ----------------- docs/frontend-implementation-plan.md | 806 --- package.json | 1 + pnpm-lock.yaml | 12 + src/lib/api/geography.js | 2 +- src/lib/api/tests.js | 9 + .../(app)/master-data/tests/+page.svelte | 65 +- svelte.config.js | 13 +- 13 files changed, 279 insertions(+), 6183 deletions(-) create mode 100644 DEPLOY.md create mode 100644 apache-config/clqms.conf delete mode 100644 docs/COMPREHENSIVE_IMPLEMENTATION_PLAN.md delete mode 100644 docs/MVP_IMPLEMENTATION_PLAN.md delete mode 100644 docs/api-docs.yaml delete mode 100644 docs/frontend-implementation-plan.md diff --git a/AGENTS.md b/AGENTS.md index 60400e3..213236f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -160,6 +160,10 @@ const API_URL = import.meta.env.VITE_API_URL || ''; API requests to `/api` are proxied to `http://localhost:8000` in dev. +## API Documentation + +API Reference (Swagger UI): https://clqms01-api.services-summit.my.id/swagger/ + ## Important Notes - No ESLint or Prettier configured yet - add if needed diff --git a/DEPLOY.md b/DEPLOY.md new file mode 100644 index 0000000..132859e --- /dev/null +++ b/DEPLOY.md @@ -0,0 +1,154 @@ +# Apache Deployment Guide for CLQMS + +## Quick Start + +The app is now configured for static hosting. Follow these steps to deploy: + +## 1. Build the Application + +```bash +pnpm run build +``` + +This creates static files in the `build/` folder. + +## 2. Deploy to Apache + +### Option A: Copy to DocumentRoot + +```bash +# Create directory +sudo mkdir -p /var/www/html/clqms + +# Copy build files +sudo cp -r build/* /var/www/html/clqms/ + +# Set permissions +sudo chown -R www-data:www-data /var/www/html/clqms +sudo chmod -R 755 /var/www/html/clqms +``` + +### Option B: Using VirtualHost (Recommended) + +1. Copy the build files: +```bash +sudo mkdir -p /var/www/html/clqms +sudo cp -r build/* /var/www/html/clqms/ +sudo chown -R www-data:www-data /var/www/html/clqms +``` + +2. Copy the Apache config: +```bash +sudo cp apache-config/clqms.conf /etc/apache2/sites-available/ +sudo a2ensite clqms +``` + +3. Enable required modules: +```bash +sudo a2enmod rewrite +sudo a2enmod deflate +sudo a2enmod expires +sudo a2enmod headers +sudo systemctl restart apache2 +``` + +## 3. API Configuration + +**Important**: The dev proxy (`/api` → `localhost:8000`) doesn't work in production. + +### Option 1: Configure API URL + +Create a `.env.production` file: +``` +VITE_API_URL=https://your-api-server.com/api +``` + +Then rebuild: +```bash +pnpm run build +``` + +### Option 2: Apache Reverse Proxy + +Add to your VirtualHost: +```apache +ProxyPass /api http://localhost:8000/api +ProxyPassReverse /api http://localhost:8000/api +``` + +Enable proxy module: +```bash +sudo a2enmod proxy +sudo a2enmod proxy_http +sudo systemctl restart apache2 +``` + +## 4. SSL/HTTPS (Recommended) + +Use Let's Encrypt: +```bash +sudo apt install certbot python3-certbot-apache +sudo certbot --apache -d clqms.example.com +``` + +## 5. Subdirectory Deployment + +If deploying to a subdirectory (e.g., `/clqms/`): + +1. Update `svelte.config.js`: +```javascript +adapter: adapter({ + pages: 'build', + assets: 'build', + fallback: 'index.html' +}), +paths: { + base: '/clqms' +} +``` + +2. Rebuild and deploy to `/var/www/html/clqms/` + +3. Update `.htaccess` RewriteBase: +```apache +RewriteBase /clqms/ +``` + +## Troubleshooting + +### 404 on refresh +- Ensure `mod_rewrite` is enabled +- Check `.htaccess` is in the build folder +- Verify `AllowOverride All` in Apache config + +### API not working +- Check browser console for CORS errors +- Verify `VITE_API_URL` is set correctly +- Test API endpoint directly + +### Blank page +- Check browser console for errors +- Verify all assets loaded (check Network tab) +- Ensure `fallback: 'index.html'` is set + +## File Structure After Deployment + +``` +/var/www/html/clqms/ +├── index.html +├── .htaccess +├── _app/ +│ ├── immutable/ +│ └── ... +├── favicon.png +└── ... +``` + +## Production Checklist + +- [ ] Set production API URL +- [ ] Enable HTTPS/SSL +- [ ] Configure firewall (allow 80/443) +- [ ] Set up log rotation +- [ ] Configure backup strategy +- [ ] Test all routes work after refresh diff --git a/apache-config/clqms.conf b/apache-config/clqms.conf new file mode 100644 index 0000000..0b8affd --- /dev/null +++ b/apache-config/clqms.conf @@ -0,0 +1,41 @@ + + ServerName clqms.example.com + ServerAdmin admin@example.com + + DocumentRoot /var/www/html/clqms + + + Options -Indexes +FollowSymLinks + AllowOverride All + Require all granted + + + # Handle SPA routing - fallback to index.html + + RewriteEngine On + RewriteBase / + RewriteRule ^index\.html$ - [L] + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + RewriteRule . /index.html [L] + + + # Enable gzip compression + + AddOutputFilterByType DEFLATE text/html text/css text/javascript application/javascript application/json + + + # Cache static assets + + ExpiresActive On + ExpiresByType text/css "access plus 1 year" + ExpiresByType application/javascript "access plus 1 year" + ExpiresByType image/png "access plus 1 year" + ExpiresByType image/jpg "access plus 1 year" + ExpiresByType image/jpeg "access plus 1 year" + ExpiresByType image/svg+xml "access plus 1 year" + + + ErrorLog ${APACHE_LOG_DIR}/clqms_error.log + CustomLog ${APACHE_LOG_DIR}/clqms_access.log combined + diff --git a/docs/COMPREHENSIVE_IMPLEMENTATION_PLAN.md b/docs/COMPREHENSIVE_IMPLEMENTATION_PLAN.md deleted file mode 100644 index c3e6e82..0000000 --- a/docs/COMPREHENSIVE_IMPLEMENTATION_PLAN.md +++ /dev/null @@ -1,613 +0,0 @@ -# Comprehensive Implementation Plan - CLQMS Frontend - -**Goal:** Complete implementation of all use cases -**Estimated Time:** 6-8 weeks (after MVP) -**This document:** All features beyond MVP scope - ---- - -## Phase 1: Authentication Enhancements (UC-01) -**Use Case:** UC-01 - Complete authentication flow -**Priority:** HIGH - Security & Multi-tenancy -**Dependencies:** Backend support for multi-site, password policies - -### 1.1 Multi-Site Selection -**Current:** Login with single site -**Required:** Support for users with access to multiple sites - -#### API Changes -``` -POST /api/auth/login -Response changes: -{ - "token": "...", - "user": { ... }, - "sites": [ - {"SiteID": "1", "SiteName": "Lab Jakarta", "SiteCode": "JK01"}, - {"SiteID": "2", "SiteName": "Lab Bandung", "SiteCode": "BD01"} - ], - "requiresSiteSelection": true -} -``` - -#### Frontend Implementation -**New Files:** -- `src/routes/login/SiteSelectionModal.svelte` -- `src/lib/stores/site.js` - Current site store - -**Modify:** -- `src/routes/login/+page.svelte` - Add site selection step - -**Features:** -- If personal email used: Show site selection dialog -- If site email used: Auto-select matching site -- Remember site selection in localStorage -- Site switcher in header/navigation -- Site context shown in UI (current site name) - -### 1.2 Password Expiration -**Requirements:** -- Track password expiry date -- Force password change when expired -- Grace period warning (7 days before expiry) - -#### API Changes -``` -POST /api/auth/login -Response includes: -{ - "passwordStatus": "valid" | "expired" | "expiring", - "passwordExpiryDate": "2025-03-01", - "daysUntilExpiry": 5 -} - -POST /api/auth/change-password -``` - -#### Frontend Implementation -**New Files:** -- `src/routes/login/PasswordExpiredModal.svelte` -- `src/routes/login/ChangePasswordModal.svelte` - -**Features:** -- Check password status on login -- If expired: Block access, show change password modal -- If expiring: Show warning banner with countdown -- Password strength requirements display -- Confirm password match validation - -### 1.3 Account Lockout -**Requirements:** -- Count failed login attempts -- Lock account after X failed attempts (configurable) -- Lockout duration: X hours (configurable) -- Clear attempts on successful login - -#### API Changes -``` -POST /api/auth/login -Error responses: -{ - "error": "ACCOUNT_LOCKED", - "message": "Please try again in 4 hours or contact system administrator", - "lockoutEndsAt": "2025-02-14T18:00:00Z", - "hoursRemaining": 4 -} - -POST /api/auth/login -On invalid password: -{ - "error": "INVALID_LOGIN", - "attemptsRemaining": 2 -} -``` - -#### Frontend Implementation -**Modify:** -- `src/routes/login/+page.svelte` - Handle lockout states - -**Features:** -- Display attempts remaining -- Show lockout timer with countdown -- "Contact administrator" link/info -- Visual indicator of account status - -### 1.4 Audit Logging Display -**Requirements:** -- View own login history -- Admin view: All user login history -- Filter by date, user, action - -#### API Changes -``` -GET /api/audit/login-logs?userId=&from=&to=&page= -Response: -{ - "logs": [ - { - "AuditID": 1, - "UserID": "admin", - "Action": "LOGIN_SUCCESS", - "Device": "Chrome 120.0 / Windows 10", - "IPAddress": "192.168.1.1", - "Timestamp": "2025-02-14T10:30:00Z" - } - ] -} -``` - -#### Frontend Implementation -**New Files:** -- `src/routes/(app)/admin/audit-logs/+page.svelte` -- `src/lib/api/audit.js` - -**Features:** -- Paginated log table -- Filters: Date range, User, Action type -- Export to CSV -- Device/IP details - ---- - -## Phase 2: Advanced Patient Management - -### 2.1 Enhanced Patient Registration (UC-02a Enhancements) -**Missing Features:** -- Duplicate identifier detection with "Multiple IDs found" handling -- UC-02b link suggestion when duplicates detected - -#### API Changes -``` -POST /api/patient/check-duplicate -{ - "IdentifierType": "KTP", - "Identifier": "1234567890" -} -Response: -{ - "hasDuplicates": true, - "duplicates": [ - {"PID": "P001", "Name": "John Doe", "DOB": "1990-01-01"} - ] -} -``` - -#### Frontend Implementation -**Modify:** -- `src/routes/(app)/patients/PatientFormModal.svelte` - -**New Files:** -- `src/routes/(app)/patients/DuplicateWarningModal.svelte` - -**Features:** -- Check for duplicates on identifier blur -- Show "Multiple IDs found" warning -- Options: - 1. Continue (create new, ignore duplicate) - 2. Link patients (redirect to UC-02b) - 3. Cancel -- Display duplicate patient details for comparison - -### 2.2 Patient Unlink (UC-02c) -**Requirements:** -- Reverse patient link operation -- Only Supervisor Lab, Manajer Lab -- Restore source PID to active status - -#### API Changes -``` -POST /api/patient/unlink -{ - "SourcePID": "P001", - "DestinationPID": "P002" -} -Creates ADT: A37 (Unlink Patient Information) -``` - -#### Frontend Implementation -**New Files:** -- `src/routes/(app)/patients/unlink/+page.svelte` - -**Features:** -- Search by PID to find linked patients -- Display destination with all linked sources -- Uncheck source PIDs to unlink -- Confirmation dialog -- Restore source PID edit/order capabilities - ---- - -## Phase 3: Advanced Visit Management - -### 3.1 Cancel Admission (UC-03b) -**Requirements:** -- Cancel patient admission -- Only Supervisor Lab, Manajer Lab -- Reason for cancellation -- ADT Code: A11 (Cancel Admit) - -#### API Changes -``` -POST /api/patvisit/:pvid/cancel-admission -{ - "Reason": "Insurance invalid", - "Notes": "..." -} -Creates ADT: A11 (Cancel Admit) -``` - -#### Frontend Implementation -**New Files:** -- `src/routes/(app)/patients/CancelAdmissionModal.svelte` - -**Features:** -- Available in visit actions (supervisor only) -- Reason dropdown + notes field -- Confirm cancellation -- Mark visit as cancelled -- Prevent new orders on cancelled visits - -### 3.2 Change Attending Doctor (UC-03c) -**Requirements:** -- Change primary doctor (DPJP) -- ADT Code: A54 (Change Attending Doctor) -- Track doctor history - -#### API Changes -``` -POST /api/patvisit/:pvid/change-attending-doctor -{ - "NewAttendingDoctorID": "DOC001" -} -Creates ADT: A54 -``` - -#### Frontend Implementation -**Modify:** -- `src/routes/(app)/patients/VisitFormModal.svelte` - -**Features:** -- Doctor dropdown with search -- Show current attending doctor -- Change history in ADT log -- Validation: Doctor must exist in contacts - -### 3.3 Change Consulting Doctor (UC-03d) -**Requirements:** -- Change consulting doctor (konsulen) -- ADT Code: A61 (Change Consulting Doctor) - -#### API Changes -``` -POST /api/patvisit/:pvid/change-consulting-doctor -{ - "NewConsultingDoctorID": "DOC002" -} -Creates ADT: A61 -``` - -#### Frontend Implementation -**Modify:** -- `src/routes/(app)/patients/VisitFormModal.svelte` - -**Features:** -- Similar to attending doctor change -- Separate field for consulting doctor -- History tracking - ---- - -## Phase 4: Test Ordering Enhancements - -### 4.1 Future Orders -**Requirements:** -- Schedule test order for future date -- Effective date validation - -#### Frontend Implementation -**Modify:** -- `src/routes/(app)/orders/new/+page.svelte` - -**Features:** -- "Effective Date" field (default: today) -- Future date validation (max 30 days?) -- Separate OID generation for future orders? -- View scheduled orders separately - -### 4.2 Order from PID (Alternative Flow) -**Requirements:** -- Create order using PID instead of PVID -- Show list of active PVIDs for patient -- Select PVID then proceed - -#### Frontend Implementation -**Modify:** -- `src/routes/(app)/orders/new/+page.svelte` - -**Features:** -- Toggle: "Use PID instead of PVID" -- PID search -- Display patient's active visits (not discharged) -- Select PVID to proceed - -### 4.3 Non-Patient Orders -**Requirements:** -- Orders without patient (QC, calibration, research) -- Different order type/flow - -#### API Changes -``` -POST /api/orders/non-patient -{ - "OrderType": "QC", - "Description": "...", - "Tests": [...] -} -``` - -#### Frontend Implementation -**New Files:** -- `src/routes/(app)/orders/non-patient/+page.svelte` - ---- - -## Phase 5: Specimen Management - -### 5.1 Specimen Tracking -**Requirements:** -- Full specimen lifecycle tracking -- Collection, receipt, rejection, disposal -- Barcode/label generation - -#### API Changes -``` -GET /api/specimens/:sid/status-history -PATCH /api/specimens/:sid/status -{ - "Status": "COLLECTED", // COLLECTED, RECEIVED, REJECTED, DISPOSED - "Notes": "..." -} -``` - -#### Frontend Implementation -**New Files:** -- `src/routes/(app)/specimens/+page.svelte` - Specimen list -- `src/routes/(app)/specimens/[sid]/+page.svelte` - Specimen detail -- `src/lib/api/specimens.js` (extend) - -**Features:** -- Specimen search (SID, OID, Patient) -- Status timeline/history -- Collection/receipt timestamps -- Rejection reason logging -- Label printing (integration with printer) - -### 5.2 Label Printing Integration -**Requirements:** -- Print patient labels -- Print order labels -- Print specimen labels -- Support for label printers - -#### Frontend Implementation -**New Files:** -- `src/lib/utils/labelPrinting.js` -- `src/routes/(app)/print/labels/+page.svelte` - -**Features:** -- Label templates (patient, order, specimen) -- Print dialog with printer selection -- Preview before print -- Batch printing -- Integration with browser print API or direct printer communication - ---- - -## Phase 6: Reporting & Analytics - -### 6.1 Cumulative Patient Reports -**Requirements:** -- View all test results across visits for linked patients -- Chronological view -- Filter by test type, date range - -#### API Changes -``` -GET /api/reports/patient-cumulative/:pid?from=&to=&tests= -Response: -{ - "patient": {...}, - "visits": [...], - "results": [ - { - "Date": "...", - "Test": "Glucose", - "Result": "120", - "Unit": "mg/dL", - "ReferenceRange": "70-100" - } - ] -} -``` - -#### Frontend Implementation -**New Files:** -- `src/routes/(app)/reports/patient-cumulative/+page.svelte` - -**Features:** -- Patient search -- Date range filter -- Test type filter -- Table view with trend indicators -- Export to PDF/Excel - -### 6.2 Workload Statistics -**Requirements:** -- Orders by time period -- Tests performed -- Turnaround time analysis - -#### Frontend Implementation -**New Files:** -- `src/routes/(app)/reports/workload/+page.svelte` - ---- - -## Phase 7: Administration Features - -### 7.1 User Management -**Requirements:** -- Create/edit users -- Role assignment -- Site access management -- Password reset - -#### Frontend Implementation -**New Files:** -- `src/routes/(app)/admin/users/+page.svelte` -- `src/routes/(app)/admin/users/UserFormModal.svelte` - -### 7.2 Role & Permission Management -**Requirements:** -- Define roles -- Configure menu access per role -- Action-level permissions - -### 7.3 System Configuration -**Requirements:** -- Global settings -- ADT code configuration -- Password policy settings -- Lockout configuration - ---- - -## Phase 8: Integration Features - -### 8.1 HL7/ADT Message Integration -**Requirements:** -- Receive ADT messages from HIS -- Send order updates to HIS -- Message queue management - -### 8.2 Instrument Integration -**Requirements:** -- Bidirectional communication with lab instruments -- Auto-result entry -- QC data import - ---- - -## Implementation Priority Matrix - -### Critical (Month 1-2) -1. Authentication: Multi-site selection -2. Patient: Enhanced duplicate detection -3. Visit: Cancel admission, doctor changes -4. Orders: Future orders, PID-based ordering - -### High (Month 2-3) -1. Authentication: Password expiration -2. Authentication: Account lockout -3. Patient: Unlink functionality -4. Specimens: Full tracking -5. Reports: Cumulative patient view - -### Medium (Month 3-4) -1. Authentication: Audit logs -2. Specimens: Label printing integration -3. Administration: User management -4. Administration: Role management - -### Low (Month 4+) -1. Non-patient orders -2. Workload statistics -3. System configuration UI -4. Instrument integration - ---- - -## Technical Considerations - -### Performance -- Implement pagination for all list endpoints -- Use virtual scrolling for large tables -- Cache patient/order data appropriately -- Lazy load audit logs - -### Security -- All admin actions require elevated permissions -- Audit all sensitive operations -- Encrypt sensitive data at rest -- Secure password storage (backend) - -### Scalability -- Support for multiple sites/locations -- Concurrent user handling -- Large dataset performance (100k+ patients) - ---- - -## Dependencies - -### Backend Must Support: -- [ ] Multi-site user management -- [ ] Password expiration tracking -- [ ] Failed login attempt tracking -- [ ] All ADT codes (A11, A54, A61, A24, A37) -- [ ] Advanced patient search (duplicate detection) -- [ ] Specimen status workflow -- [ ] Audit logging for all operations -- [ ] Role-based access control (RBAC) -- [ ] Label generation endpoints - -### Infrastructure: -- [ ] Label printer setup -- [ ] Barcode scanner integration -- [ ] HIS/HL7 integration (if needed) -- [ ] Backup and disaster recovery - ---- - -## Success Criteria - -### Phase 1 Complete: -- [ ] Users can select site on login -- [ ] Password expiration enforced -- [ ] Account lockout working -- [ ] Multi-site data isolation - -### Phase 2 Complete: -- [ ] Duplicate detection on registration -- [ ] Patient link/unlink functional -- [ ] Link validation (no multi-level) - -### Phase 3 Complete: -- [ ] All visit operations functional -- [ ] Complete ADT history for all actions -- [ ] Doctor change tracking - -### Phase 4 Complete: -- [ ] Future order scheduling -- [ ] PID-based ordering -- [ ] Non-patient order support - -### Phase 5 Complete: -- [ ] Full specimen lifecycle tracking -- [ ] Label printing operational -- [ ] Specimen status history - -### Phase 6 Complete: -- [ ] Cumulative reports for linked patients -- [ ] Workload statistics -- [ ] Export functionality - -### Phase 7 Complete: -- [ ] User management UI -- [ ] Role configuration -- [ ] System settings - -### Phase 8 Complete: -- [ ] HIS integration -- [ ] Instrument integration -- [ ] Message queue management diff --git a/docs/MVP_IMPLEMENTATION_PLAN.md b/docs/MVP_IMPLEMENTATION_PLAN.md deleted file mode 100644 index 6bbd9a9..0000000 --- a/docs/MVP_IMPLEMENTATION_PLAN.md +++ /dev/null @@ -1,266 +0,0 @@ -# MVP Implementation Plan - CLQMS Frontend - -**Goal:** Deliver core functionality for daily laboratory operations within shortest timeframe. -**Estimated Time:** 2-3 weeks -**Priority:** Critical features for production use - ---- - -## Phase 1: Test Ordering System (Week 1) -**Use Cases:** UC-5a, UC-5b -**Priority:** HIGHEST - Core lab functionality - -### API Endpoints Needed (Backend) -``` -POST /api/orders # Create new test order -GET /api/orders/:oid # Get order details -PATCH /api/orders/:oid # Update order -GET /api/orders # List/search orders -POST /api/specimens # Create specimen records -GET /api/specimens/:sid # Get specimen details -GET /api/orders/:oid/specimens # List specimens for order -``` - -### Frontend Implementation - -#### 1.1 Create Order Management Module -**New Files:** -- `src/routes/(app)/orders/+page.svelte` - Order list/search page -- `src/routes/(app)/orders/new/+page.svelte` - New order creation -- `src/routes/(app)/orders/[oid]/+page.svelte` - Order detail/edit -- `src/lib/api/orders.js` - Order API client -- `src/lib/api/specimens.js` - Specimen API client - -#### 1.2 Order List Page Features -- Search by OID, Patient ID, Patient Name -- Filter by status (SC, IP, CM, CL) -- Date range filter -- Quick actions: View, Edit, Print labels -- Table columns: OID, Patient, Date, Tests, Status - -#### 1.3 New Order Page Features -- Patient/PVID selection (search existing or quick admit) -- Test selection with search -- Specimen requirements display -- Comments/attachments -- Print label options (checkboxes): - - Print Patient Label - - Print Order Label - - Print Specimen Label -- Auto-generate OID and SID on save - -#### 1.4 Order Status Validation -- Prevent editing if status = CL (Closed) -- Prevent editing if status = AC (Archived) -- Prevent editing if status = DL (Deleted) -- Prevent removing tests that have results - -#### 1.5 Specimen Management -- Auto-create specimen records per test -- Display SID list after order creation -- Basic specimen tracking view - ---- - -## Phase 2: Patient Discharge (Week 1-2) -**Use Cases:** UC-04a, UC-04b -**Priority:** HIGH - Essential for visit lifecycle - -### API Endpoints Needed (Backend) -``` -POST /api/patvisit/:pvid/discharge # Create discharge ADT (A03) -POST /api/patvisit/:pvid/cancel-discharge # Cancel discharge (A13) -GET /api/orders/pvid/:pvid/open # Check for open orders -``` - -### Frontend Implementation - -#### 2.1 Update Visit Management -**Modify:** -- `src/routes/(app)/patients/+page.svelte` - Add discharge actions -- `src/routes/(app)/patients/VisitFormModal.svelte` - Add discharge button - -**New Files:** -- `src/routes/(app)/patients/DischargeModal.svelte` - Discharge confirmation -- `src/routes/(app)/patients/CancelDischargeModal.svelte` - Cancel discharge - -#### 2.2 Discharge Features -- Discharge button on active visits -- Validation: Check for open test orders (status: A, IP, SC, HD) -- If open orders exist: Show warning, block discharge -- ADT Code: A03 (Discharge) -- Required fields: Discharge date -- After discharge: Visit marked as closed (no more edits) - -#### 2.3 Cancel Discharge Features -- Available only for Supervisor Lab, Manajer Lab -- ADT Code: A13 (Cancel Discharge) -- Re-opens visit for editing -- Available in visit actions menu - ---- - -## Phase 3: Patient Transfer (Week 2) -**Use Cases:** UC-04 -**Priority:** MEDIUM-HIGH - Common daily operation - -### API Endpoints Needed (Backend) -``` -POST /api/patvisit/:pvid/transfer # Create transfer ADT (A02) -``` - -### Frontend Implementation - -#### 3.1 Transfer Feature -**Modify:** -- `src/routes/(app)/patients/VisitFormModal.svelte` - Add transfer section - -**New Files:** -- `src/routes/(app)/patients/TransferModal.svelte` - Location transfer - -#### 3.2 Transfer Features -- Transfer button on active visits -- Location dropdown (from master data) -- ADT Code: A02 (Patient Transfer) -- Update visit LocationID -- Track location history via ADT - ---- - -## Phase 4: Patient Link (Basic) (Week 2-3) -**Use Cases:** UC-02b -**Priority:** MEDIUM - Data quality improvement - -### API Endpoints Needed (Backend) -``` -GET /api/patient/search-duplicates # Find potential duplicates -POST /api/patient/link # Link patients (A24) -GET /api/patient/:pid/linked # Get linked patients -``` - -### Frontend Implementation - -#### 4.1 Patient Link Module -**New Files:** -- `src/routes/(app)/patients/link/+page.svelte` - Patient link page -- `src/lib/api/patient-links.js` - Link API client - -#### 4.2 Link Features -- Search by: Identifier (KTP/Passport), Name + DOB + Address -- Display matching patients -- Select destination (surviving) PID -- Select source PID(s) to link -- Confirmation dialog -- ADT Code: A24 (Link Patient) -- Validation: Prevent multi-level links, prevent linking already-linked sources - -#### 4.3 Visual Indicator -- Show "Linked" badge on patient records -- Display linked PIDs in patient detail - ---- - -## Navigation Updates - -### Sidebar Changes -Add to Laboratory section: -``` -Laboratory -├── Patients (existing) -├── Orders (NEW) -├── Specimens (NEW) -└── Patient Links (NEW) -``` - ---- - -## Database Schema Requirements (Backend) - -### Orders Table -```sql -ordertest: - - OrderID (OID) - - InternalOID - - PVID - - EffDate (future orders) - - EndDate (closed) - - ArchiveDate - - DelDate - - Comments - - CreatedBy, CreatedDate - -orderstatus: - - InternalOID - - OrderStatus (SC, IP, CM, CL, AC, DL) - - StatusDate - -ordertestdetail: - - InternalOID - - TestSiteID - - TestSequence -``` - -### Specimens Table -```sql -specimens: - - SpecimenID (SID) - - InternalSID - - InternalOID - - TestSiteID - - CollectionDate - - ReceivedDate - - ContainerID - - Status -``` - ---- - -## Testing Checklist - -### Test Ordering -- [ ] Create new order with multiple tests -- [ ] Search orders by patient name -- [ ] Edit pending order (SC status) -- [ ] Attempt to edit closed order (should fail) -- [ ] Print labels (UI only) -- [ ] View specimen list - -### Discharge -- [ ] Discharge patient with no open orders -- [ ] Attempt discharge with open orders (should block) -- [ ] Cancel discharge (supervisor only) -- [ ] Verify visit locked after discharge - -### Transfer -- [ ] Transfer patient to new location -- [ ] Verify location history updated - -### Patient Link -- [ ] Search duplicates by identifier -- [ ] Link two patients -- [ ] Verify destination patient shows cumulative data -- [ ] Verify source patient marked as linked - ---- - -## Out of Scope (MVP) -The following features are **NOT** included in MVP but documented for future phases: -- Multi-site authentication (UC-01 enhancements) -- Password expiration/lockout -- Patient Unlink (UC-02c) -- Cancel Admission (UC-03b) -- Change Attending/Consulting Doctor (UC-03c, UC-03d) -- Advanced duplicate detection with address matching -- Audit log viewing -- Specimen label printing integration -- Result entry integration - ---- - -## Success Criteria -1. Users can create test orders linked to patients -2. Users can discharge patients (when no open orders) -3. Users can transfer patients between locations -4. Users can link duplicate patient records -5. Order status validation prevents invalid edits -6. All operations create proper ADT audit records diff --git a/docs/api-docs.yaml b/docs/api-docs.yaml deleted file mode 100644 index 890ad8e..0000000 --- a/docs/api-docs.yaml +++ /dev/null @@ -1,4476 +0,0 @@ -openapi: 3.0.3 -info: - title: CLQMS - Clinical Laboratory Quality Management System API - description: | - API for Clinical Laboratory Quality Management System supporting patient management, - specimen tracking, test ordering, instrument integration, and laboratory operations. - version: 1.0.0 - contact: - name: CLQMS API Support - license: - name: Proprietary - -servers: - - url: http://localhost/clqms01/ - description: Local development server - - url: https://clqms01-api.services-summit.my.id/ - description: Production server - -tags: - - name: Authentication - description: User authentication and session management - - name: Patients - description: Patient registration and management - - name: Patient Visits - description: Patient visit/encounter management - - name: Organization - description: Organization structure (accounts, sites, disciplines, departments, workstations) - - name: Specimen - description: Specimen and container management - - name: Tests - description: Test definitions and test catalog - - name: Orders - description: Laboratory order management - - name: Results - description: Patient results reporting - - name: Edge API - description: Instrument integration endpoints - - name: Master Data - description: Lookup and reference data - - name: ValueSets - description: Value set definitions and items - - name: Demo - description: Demo/test endpoints (no authentication) - -components: - securitySchemes: - bearerAuth: - type: http - scheme: bearer - bearerFormat: JWT - description: JWT token from login endpoint - cookieAuth: - type: apiKey - in: cookie - name: token - description: JWT token stored in HTTP-only cookie - - schemas: - # Common Schemas - SuccessResponse: - type: object - properties: - status: - type: string - example: success - message: - type: string - code: - type: integer - example: 200 - - ErrorResponse: - type: object - properties: - status: - type: string - example: error - message: - type: string - errors: - type: object - - # Authentication - LoginRequest: - type: object - required: - - username - - password - properties: - username: - type: string - example: labuser01 - password: - type: string - format: password - example: secret123 - - LoginResponse: - type: object - properties: - status: - type: string - example: success - code: - type: integer - example: 200 - message: - type: string - example: Login successful - - RegisterRequest: - type: object - required: - - username - - password - - email - properties: - username: - type: string - password: - type: string - format: password - email: - type: string - format: email - full_name: - type: string - - # Patient - PatientIdentifier: - type: object - properties: - IdentifierType: - type: string - enum: [KTP, PASS, SSN, SIM, KTAS] - description: | - KTP: 16 digit numeric - PASS: alphanumeric max 9 - SSN: 9 digit numeric - SIM: 19-20 digit numeric - KTAS: 11 digit numeric - Identifier: - type: string - maxLength: 255 - - LinkedPatient: - type: object - description: Linked patient reference - properties: - InternalPID: - type: integer - description: Internal patient ID of the linked patient - PatientID: - type: string - description: Patient ID of the linked patient - - Custodian: - type: object - description: Patient custodian/guardian - properties: - InternalPID: - type: integer - description: Internal patient ID of the custodian - PatientID: - type: string - description: Patient ID of the custodian - - PatAttEntry: - type: object - description: Patient address/attorney entry - properties: - Address: - type: string - description: Address text - - Patient: - type: object - required: - - PatientID - - Sex - - NameFirst - - Birthdate - properties: - PatientID: - type: string - maxLength: 30 - pattern: '^[A-Za-z0-9]+$' - description: Internal patient identifier - AlternatePID: - type: string - maxLength: 30 - pattern: '^[A-Za-z0-9]+$' - Prefix: - type: string - maxLength: 10 - enum: [Mr, Mrs, Ms, Dr, Prof] - Sex: - type: string - enum: ['1', '2'] - description: '1: Female, 2: Male' - NameFirst: - type: string - minLength: 1 - maxLength: 60 - pattern: "^[A-Za-z'\\. ]+$" - NameMiddle: - type: string - minLength: 1 - maxLength: 60 - NameMaiden: - type: string - minLength: 1 - maxLength: 60 - NameLast: - type: string - minLength: 1 - maxLength: 60 - Suffix: - type: string - maxLength: 10 - Birthdate: - type: string - format: date-time - description: ISO 8601 UTC datetime - PlaceOfBirth: - type: string - maxLength: 100 - Citizenship: - type: string - maxLength: 100 - Street_1: - type: string - maxLength: 255 - Street_2: - type: string - maxLength: 255 - Street_3: - type: string - maxLength: 255 - ZIP: - type: string - maxLength: 10 - pattern: '^[0-9]+$' - Phone: - type: string - pattern: "^\\+?[0-9]{8,15}$" - MobilePhone: - type: string - pattern: "^\\+?[0-9]{8,15}$" - EmailAddress1: - type: string - format: email - maxLength: 100 - EmailAddress2: - type: string - format: email - maxLength: 100 - PatIdt: - $ref: '#/components/schemas/PatientIdentifier' - LinkTo: - type: array - description: Array of linked patient references - items: - $ref: '#/components/schemas/LinkedPatient' - Custodian: - $ref: '#/components/schemas/Custodian' - DeathIndicator: - type: string - enum: [Y, N] - description: 'Y: Yes (deceased), N: No (alive)' - TimeOfDeath: - type: string - format: date-time - description: ISO 8601 UTC datetime of death - PatCom: - type: string - description: Patient comment/notes - PatAtt: - type: array - description: Patient address entries - items: - $ref: '#/components/schemas/PatAttEntry' - Province: - type: integer - description: Province AreaGeoID (foreign key to areageo table) - ProvinceLabel: - type: string - description: Province name (resolved from areageo) - City: - type: integer - description: City AreaGeoID (foreign key to areageo table) - CityLabel: - type: string - description: City name (resolved from areageo) - Country: - type: string - maxLength: 10 - description: Country ISO 3-letter code (e.g., IDN, USA) - CountryLabel: - type: string - description: Country name (resolved from valueset) - Race: - type: string - maxLength: 100 - MaritalStatus: - type: string - enum: [A, B, D, M, S, W] - description: 'A: Annulled, B: Separated, D: Divorced, M: Married, S: Single, W: Widowed' - Religion: - type: string - maxLength: 100 - Ethnic: - type: string - maxLength: 100 - - PatientListResponse: - type: object - properties: - status: - type: string - example: success - data: - type: array - items: - $ref: '#/components/schemas/Patient' - pagination: - type: object - properties: - page: - type: integer - perPage: - type: integer - total: - type: integer - - # Patient Visit - PatientVisit: - type: object - properties: - InternalPVID: - type: integer - description: Primary key (auto-generated) - PVID: - type: string - description: Visit ID (auto-generated with DV prefix if not provided) - InternalPID: - type: integer - description: Reference to patient - EpisodeID: - type: string - description: Episode identifier - SiteID: - type: integer - description: Site reference - CreateDate: - type: string - format: date-time - EndDate: - type: string - format: date-time - ArchivedDate: - type: string - format: date-time - DelDate: - type: string - format: date-time - PatDiag: - type: object - description: Diagnosis information (optional) - properties: - DiagCode: - type: string - Diagnosis: - type: string - PatVisitADT: - type: object - description: ADT (Admission/Discharge/Transfer) information (optional) - properties: - ADTCode: - type: string - enum: [A01, A02, A03, A04, A08] - LocationID: - type: integer - AttDoc: - type: integer - description: Attending physician ContactID - RefDoc: - type: integer - description: Referring physician ContactID - AdmDoc: - type: integer - description: Admitting physician ContactID - CnsDoc: - type: integer - description: Consulting physician ContactID - - # PatVisitADT (for standalone ADT operations) - PatVisitADT: - type: object - properties: - PVADTID: - type: integer - description: Primary key (auto-generated) - InternalPVID: - type: integer - description: Reference to patient visit - ADTCode: - type: string - enum: [A01, A02, A03, A04, A08] - description: | - A01: Admit - A02: Transfer - A03: Discharge - A04: Register - A08: Update - LocationID: - type: integer - description: Location/ward reference - AttDoc: - type: integer - description: Attending physician ContactID - RefDoc: - type: integer - description: Referring physician ContactID - AdmDoc: - type: integer - description: Admitting physician ContactID - CnsDoc: - type: integer - description: Consulting physician ContactID - CreateDate: - type: string - format: date-time - EndDate: - type: string - format: date-time - ArchivedDate: - type: string - format: date-time - DelDate: - type: string - format: date-time - - # Organization - Account: - type: object - properties: - id: - type: integer - AccountName: - type: string - AccountCode: - type: string - AccountType: - type: string - - Site: - type: object - properties: - id: - type: integer - SiteName: - type: string - SiteCode: - type: string - AccountID: - type: integer - - Discipline: - type: object - properties: - id: - type: integer - DisciplineName: - type: string - DisciplineCode: - type: string - - Department: - type: object - properties: - id: - type: integer - DeptName: - type: string - DeptCode: - type: string - SiteID: - type: integer - - Workstation: - type: object - properties: - id: - type: integer - WorkstationName: - type: string - WorkstationCode: - type: string - SiteID: - type: integer - DepartmentID: - type: integer - - # Specimen - Specimen: - type: object - properties: - id: - type: integer - SpecimenID: - type: string - PatientID: - type: string - SpecimenType: - type: string - description: Specimen type code - SpecimenTypeLabel: - type: string - description: Specimen type display text - CollectionDate: - type: string - format: date-time - CollectionMethod: - type: string - description: Collection method code - CollectionMethodLabel: - type: string - description: Collection method display text - ContainerID: - type: integer - SpecimenStatus: - type: string - description: Specimen status code - SpecimenStatusLabel: - type: string - description: Specimen status display text - BodySite: - type: string - description: Body site code - BodySiteLabel: - type: string - description: Body site display text - - ContainerDef: - type: object - properties: - id: - type: integer - ContainerCode: - type: string - ContainerName: - type: string - ConCategory: - type: string - description: Container category code - ConCategoryLabel: - type: string - description: Container category display text - ConSize: - type: string - description: Container size code - ConSizeLabel: - type: string - description: Container size display text - CapColor: - type: string - description: Cap color code - CapColorLabel: - type: string - description: Cap color display text - - SpecimenPrep: - type: object - properties: - id: - type: integer - PrepCode: - type: string - PrepName: - type: string - Description: - type: string - - SpecimenStatus: - type: object - properties: - id: - type: integer - StatusCode: - type: string - StatusName: - type: string - Description: - type: string - Status: - type: string - description: Status code - StatusLabel: - type: string - description: Status display text - Activity: - type: string - description: Activity code - ActivityLabel: - type: string - description: Activity display text - - SpecimenCollection: - type: object - properties: - id: - type: integer - CollectionCode: - type: string - CollectionName: - type: string - Description: - type: string - CollectionMethod: - type: string - description: Collection method code - CollectionMethodLabel: - type: string - description: Collection method display text - Additive: - type: string - description: Additive code - AdditiveLabel: - type: string - description: Additive display text - SpecimenRole: - type: string - description: Specimen role code - SpecimenRoleLabel: - type: string - description: Specimen role display text - - # Tests - TestDefinition: - type: object - properties: - id: - type: integer - TestCode: - type: string - TestName: - type: string - TestType: - type: string - enum: [TEST, PARAM, CALC, GROUP, TITLE] - description: | - TEST: Technical test - PARAM: Parameter - CALC: Calculated - GROUP: Panel/Profile - TITLE: Section header - DisciplineID: - type: integer - DisciplineName: - type: string - DepartmentID: - type: integer - DepartmentName: - type: string - Unit: - type: string - Formula: - type: string - refnum: - type: array - description: Numeric reference ranges (optional). Mutually exclusive with reftxt - a test can only have ONE reference type. - items: - type: object - properties: - RefNumID: - type: integer - NumRefType: - type: string - enum: [NMRC, THOLD] - description: NMRC=Numeric range, THOLD=Threshold - NumRefTypeLabel: - type: string - RangeType: - type: string - RangeTypeLabel: - type: string - Sex: - type: string - SexLabel: - type: string - LowSign: - type: string - LowSignLabel: - type: string - HighSign: - type: string - HighSignLabel: - type: string - High: - type: number - format: float - Low: - type: number - format: float - AgeStart: - type: integer - AgeEnd: - type: integer - Flag: - type: string - Interpretation: - type: string - reftxt: - type: array - description: Text reference ranges (optional). Mutually exclusive with refnum - a test can only have ONE reference type. - items: - type: object - properties: - RefTxtID: - type: integer - TxtRefType: - type: string - enum: [TEXT, VSET] - description: TEXT=Free text, VSET=Value set - TxtRefTypeLabel: - type: string - Sex: - type: string - SexLabel: - type: string - AgeStart: - type: integer - AgeEnd: - type: integer - RefTxt: - type: string - Flag: - type: string - examples: - $1 Unit: mg/dL - refnum: - - RefNumID: 1 - NumRefType: NMRC - NumRefTypeLabel: Numeric - RangeType: REF - RangeTypeLabel: Reference Range - Sex: '2' - SexLabel: Male - LowSign: GE - LowSignLabel: ">=" - HighSign: LE - HighSignLabel: "<=" - Low: 70 - High: 100 - AgeStart: 18 - AgeEnd: 99 - Flag: N - Interpretation: Normal - TEST_numeric_thold: - summary: Technical test - threshold reference (THOLD) - value: - id: 1 - TestCode: GLU - TestName: Glucose - TestType: TEST - DisciplineID: 1 - DepartmentID: 1 - Unit: mg/dL - refnum: - - RefNumID: 2 - NumRefType: THOLD - NumRefTypeLabel: Threshold - RangeType: PANIC - RangeTypeLabel: Panic Range - Sex: '1' - SexLabel: Female - LowSign: LT - LowSignLabel: "<" - High: 40 - AgeStart: 0 - AgeEnd: 120 - Flag: L - Interpretation: Critical Low - $1 Unit: null - reftxt: - - RefTxtID: 1 - TxtRefType: TEXT - TxtRefTypeLabel: Text - Sex: '2' - SexLabel: Male - AgeStart: 18 - AgeEnd: 99 - RefTxt: 'NORM=Normal;HYPO=Hypochromic;MACRO=Macrocytic' - Flag: N - TEST_text_vset: - summary: Technical test - text reference (VSET) - value: - id: 1 - TestCode: STAGE - TestName: Disease Stage - TestType: TEST - DisciplineID: 1 - DepartmentID: 1 - Unit: null - reftxt: - - RefTxtID: 2 - TxtRefType: VSET - TxtRefTypeLabel: Value Set - Sex: '1' - SexLabel: Female - AgeStart: 0 - AgeEnd: 120 - RefTxt: 'STG1=Stage 1;STG2=Stage 2;STG3=Stage 3;STG4=Stage 4' - Flag: N - PARAM: - summary: Parameter - no reference range allowed - value: - id: 2 - TestCode: GLU_FAST - TestName: Fasting Glucose - TestType: PARAM - DisciplineID: 1 - DepartmentID: 1 - Unit: mg/dL - $1 Unit: null - Formula: "BUN / Creatinine" - refnum: - - RefNumID: 5 - NumRefType: NMRC - NumRefTypeLabel: Numeric - RangeType: REF - RangeTypeLabel: Reference Range - Sex: '1' - SexLabel: Female - LowSign: GE - LowSignLabel: ">=" - HighSign: LE - HighSignLabel: "<=" - Low: 10 - High: 20 - AgeStart: 18 - AgeEnd: 120 - Flag: N - Interpretation: Normal - $1 Unit: null - Formula: "BUN / Creatinine" - refnum: - - RefNumID: 6 - NumRefType: THOLD - NumRefTypeLabel: Threshold - RangeType: PANIC - RangeTypeLabel: Panic Range - Sex: '1' - SexLabel: Female - LowSign: GT - LowSignLabel: ">" - Low: 20 - AgeStart: 18 - AgeEnd: 120 - Flag: H - Interpretation: Elevated - possible prerenal cause - - GROUP: - summary: Panel/Profile - no reference range allowed - value: - id: 4 - TestCode: LIPID - TestName: Lipid Panel - TestType: GROUP - DisciplineID: 1 - DepartmentID: 1 - Unit: null - TITLE: - summary: Section header - no reference range allowed - value: - id: 5 - TestCode: CHEM_HEADER - TestName: '--- CHEMISTRY ---' - TestType: TITLE - DisciplineID: 1 - DepartmentID: 1 - Unit: null - - TestMap: - type: object - properties: - id: - type: integer - TestMapID: - type: integer - TestCode: - type: string - HostCode: - type: string - HostName: - type: string - ClientCode: - type: string - ClientName: - type: string - HostType: - type: string - description: Host type code - HostTypeLabel: - type: string - description: Host type display text - ClientType: - type: string - description: Client type code - ClientTypeLabel: - type: string - description: Client type display text - - # Orders - OrderTest: - type: object - properties: - OrderID: - type: string - PatientID: - type: string - VisitID: - type: string - OrderDate: - type: string - format: date-time - OrderStatus: - type: string - enum: [ORD, SCH, ANA, VER, REV, REP] - description: | - ORD: Ordered - SCH: Scheduled - ANA: Analysis - VER: Verified - REV: Reviewed - REP: Reported - OrderStatusLabel: - type: string - description: Order status display text - Priority: - type: string - enum: [R, S, U] - description: | - R: Routine - S: Stat - U: Urgent - PriorityLabel: - type: string - description: Priority display text - SiteID: - type: integer - RequestingPhysician: - type: string - - OrderItem: - type: object - properties: - id: - type: integer - OrderID: - type: string - TestID: - type: integer - SpecimenID: - type: string - Status: - type: string - - # Edge API - EdgeResultRequest: - type: object - required: - - sample_id - - instrument_id - properties: - sample_id: - type: string - description: Sample barcode/identifier - instrument_id: - type: string - description: Instrument identifier - patient_id: - type: string - description: Patient identifier (optional) - results: - type: array - items: - type: object - properties: - test_code: - type: string - result_value: - type: string - unit: - type: string - flags: - type: string - enum: [H, L, N, A] - description: H=High, L=Low, N=Normal, A=Abnormal - - EdgeResultResponse: - type: object - properties: - status: - type: string - example: success - message: - type: string - example: Result received and queued - data: - type: object - properties: - edge_res_id: - type: integer - sample_id: - type: string - instrument_id: - type: string - - EdgeOrder: - type: object - properties: - OrderID: - type: string - PatientID: - type: string - SampleID: - type: string - Tests: - type: array - items: - type: object - properties: - TestCode: - type: string - TestName: - type: string - SpecimenType: - type: string - Priority: - type: string - DueDateTime: - type: string - format: date-time - - # ValueSets - ValueSetLibItem: - type: object - description: Library/system value set item from JSON files - properties: - value: - type: string - description: The value/key code - label: - type: string - description: The display label - - ValueSetDef: - type: object - description: User-defined value set definition (from database) - properties: - VSetID: - type: integer - description: Primary key - SiteID: - type: integer - description: Site reference - VSName: - type: string - description: Value set name - VSDesc: - type: string - description: Value set description - CreateDate: - type: string - format: date-time - description: Creation timestamp - EndDate: - type: string - format: date-time - nullable: true - description: Soft delete timestamp - ItemCount: - type: integer - description: Number of items in this value set - - ValueSetItem: - type: object - description: User-defined value set item (from database) - properties: - VID: - type: integer - description: Primary key - SiteID: - type: integer - description: Site reference - VSetID: - type: integer - description: Reference to value set definition - VOrder: - type: integer - description: Display order - VValue: - type: string - description: The value code - VDesc: - type: string - description: The display description/label - VCategory: - type: string - description: Category code - CreateDate: - type: string - format: date-time - description: Creation timestamp - EndDate: - type: string - format: date-time - nullable: true - description: Soft delete timestamp - VSName: - type: string - description: Value set name (from joined definition) - - # Master Data - Location: - type: object - properties: - LocationID: - type: integer - description: Primary key - SiteID: - type: integer - description: Reference to site - LocCode: - type: string - maxLength: 6 - description: Location code (short identifier) - Parent: - type: integer - nullable: true - description: Parent location ID for hierarchical locations - LocFull: - type: string - maxLength: 255 - description: Full location name - Description: - type: string - maxLength: 255 - description: Location description - LocType: - type: string - description: Location type code (e.g., ROOM, WARD, BUILDING) - CreateDate: - type: string - format: date-time - EndDate: - type: string - format: date-time - nullable: true - - Contact: - type: object - properties: - ContactID: - type: integer - description: Primary key - NameFirst: - type: string - description: First name (required) - NameLast: - type: string - description: Last name - Title: - type: string - description: Title (e.g., Dr, Mr, Mrs) - Initial: - type: string - description: Middle initial - Birthdate: - type: string - format: date-time - description: Date of birth - EmailAddress1: - type: string - format: email - description: Primary email address - EmailAddress2: - type: string - format: email - description: Secondary email address - Phone: - type: string - description: Primary phone number - MobilePhone1: - type: string - description: Primary mobile number - MobilePhone2: - type: string - description: Secondary mobile number - Specialty: - type: string - description: Medical specialty code - SubSpecialty: - type: string - description: Sub-specialty code - CreateDate: - type: string - format: date-time - EndDate: - type: string - format: date-time - description: Occupation display text - - Occupation: - type: object - properties: - OccupationID: - type: integer - description: Primary key - OccCode: - type: string - description: Occupation code - OccText: - type: string - description: Occupation name/text - Description: - type: string - description: Additional description - CreateDate: - type: string - format: date-time - - MedicalSpecialty: - type: object - properties: - SpecialtyID: - type: integer - description: Primary key - SpecialtyText: - type: string - description: Specialty name/text - Parent: - type: integer - description: Parent specialty ID for hierarchical structure - Title: - type: string - description: Title/abbreviation - CreateDate: - type: string - format: date-time - EndDate: - type: string - format: date-time - - Counter: - type: object - properties: - CounterID: - type: integer - description: Primary key - CounterDesc: - type: string - description: Counter description/name - CounterValue: - type: integer - description: Current counter value - CounterStart: - type: integer - description: Starting value for the counter - CounterEnd: - type: integer - description: Ending value (for auto-reset) - CounterReset: - type: string - description: Reset pattern (e.g., D=Daily, M=Monthly, Y=Yearly) - CreateDate: - type: string - format: date-time - EndDate: - type: string - format: date-time - - # Dashboard - DashboardSummary: - type: object - properties: - pendingOrders: - type: integer - todayResults: - type: integer - criticalResults: - type: integer - activePatients: - type: integer - -paths: - # ======================================== - # Authentication Routes - # ======================================== - /api/auth/login: - post: - tags: [Authentication] - summary: User login - description: Authenticate user and receive JWT token via HTTP-only cookie - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/LoginRequest' - application/x-www-form-urlencoded: - schema: - $ref: '#/components/schemas/LoginRequest' - responses: - '200': - description: Login successful - headers: - Set-Cookie: - description: JWT token in HTTP-only cookie - schema: - type: string - content: - application/json: - schema: - $ref: '#/components/schemas/LoginResponse' - '400': - description: Missing username - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - '401': - description: Invalid credentials - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - - /api/auth/logout: - post: - tags: [Authentication] - summary: User logout - description: Clear JWT token cookie - security: - - bearerAuth: [] - responses: - '200': - description: Logout successful - content: - application/json: - schema: - $ref: '#/components/schemas/SuccessResponse' - - /api/auth/check: - get: - tags: [Authentication] - summary: Check authentication status - security: - - bearerAuth: [] - - cookieAuth: [] - responses: - '200': - description: Authenticated - content: - application/json: - schema: - type: object - properties: - authenticated: - type: boolean - user: - type: object - '401': - description: Not authenticated - - /api/auth/register: - post: - tags: [Authentication] - summary: Register new user - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/RegisterRequest' - responses: - '201': - description: User created - content: - application/json: - schema: - $ref: '#/components/schemas/SuccessResponse' - - /api/auth/change_pass: - post: - tags: [Authentication] - summary: Change password - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - current_password - - new_password - properties: - current_password: - type: string - format: password - new_password: - type: string - format: password - responses: - '200': - description: Password changed successfully - - # ======================================== - # V2 Authentication Routes - # ======================================== - /v2/auth/login: - post: - tags: [Authentication] - summary: V2 User login - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/LoginRequest' - responses: - '200': - description: Login successful - content: - application/json: - schema: - $ref: '#/components/schemas/LoginResponse' - - /v2/auth/logout: - post: - tags: [Authentication] - summary: V2 User logout - responses: - '200': - description: Logout successful - - /v2/auth/check: - get: - tags: [Authentication] - summary: V2 Check authentication - responses: - '200': - description: Auth check result - - /v2/auth/register: - post: - tags: [Authentication] - summary: V2 Register new user - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/RegisterRequest' - responses: - '201': - description: User created - - # ======================================== - # Patient Routes - # ======================================== - /api/patient: - get: - tags: [Patients] - summary: List patients - security: - - bearerAuth: [] - parameters: - - name: page - in: query - schema: - type: integer - default: 1 - - name: perPage - in: query - schema: - type: integer - default: 20 - - name: InternalPID - in: query - schema: - type: integer - description: Filter by internal patient ID - - name: PatientID - in: query - schema: - type: string - description: Filter by patient ID - - name: Name - in: query - schema: - type: string - description: Search by patient name - - name: Birthdate - in: query - schema: - type: string - format: date - description: Filter by birthdate (YYYY-MM-DD) - responses: - '200': - description: List of patients - content: - application/json: - schema: - $ref: '#/components/schemas/PatientListResponse' - - post: - tags: [Patients] - summary: Create new patient - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/Patient' - responses: - '201': - description: Patient created successfully - content: - application/json: - schema: - $ref: '#/components/schemas/SuccessResponse' - '422': - description: Validation error - content: - application/json: - schema: - $ref: '#/components/schemas/ErrorResponse' - - patch: - tags: [Patients] - summary: Update patient - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/Patient' - responses: - '200': - description: Patient updated successfully - - delete: - tags: [Patients] - summary: Delete patient (soft delete) - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - InternalPID - properties: - InternalPID: - type: integer - description: Internal patient record ID - responses: - '200': - description: Patient deleted successfully - - /api/patient/check: - get: - tags: [Patients] - summary: Check if patient exists - security: - - bearerAuth: [] - parameters: - - name: PatientID - in: query - schema: - type: string - description: Patient ID to check - - name: EmailAddress1 - in: query - schema: - type: string - format: email - description: Email address to check - responses: - '200': - description: Patient check result - content: - application/json: - schema: - type: object - properties: - exists: - type: boolean - data: - $ref: '#/components/schemas/Patient' - - /api/patient/{id}: - get: - tags: [Patients] - summary: Get patient by ID - security: - - bearerAuth: [] - parameters: - - name: id - in: path - required: true - schema: - type: integer - description: Internal patient record ID - responses: - '200': - description: Patient details - content: - application/json: - schema: - type: object - properties: - status: - type: string - data: - $ref: '#/components/schemas/Patient' - - # ======================================== - # Patient Visit Routes - # ======================================== - /api/patvisit: - get: - tags: [Patient Visits] - summary: List patient visits - security: - - bearerAuth: [] - parameters: - - name: page - in: query - schema: - type: integer - - name: perPage - in: query - schema: - type: integer - responses: - '200': - description: List of patient visits - content: - application/json: - schema: - type: object - properties: - status: - type: string - message: - type: string - data: - type: array - items: - $ref: '#/components/schemas/PatientVisit' - total: - type: integer - description: Total number of records - page: - type: integer - description: Current page number - per_page: - type: integer - description: Number of records per page - - post: - tags: [Patient Visits] - summary: Create patient visit - description: | - Creates a new patient visit. PVID is auto-generated with 'DV' prefix if not provided. - Can optionally include PatDiag (diagnosis) and PatVisitADT (ADT information). - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - InternalPID - properties: - PVID: - type: string - description: Visit ID (auto-generated with DV prefix if not provided) - InternalPID: - type: integer - description: Patient ID (required) - EpisodeID: - type: string - description: Episode identifier - SiteID: - type: integer - description: Site reference - PatDiag: - type: object - description: Optional diagnosis information - properties: - DiagCode: - type: string - Diagnosis: - type: string - PatVisitADT: - type: object - description: Optional ADT information - properties: - ADTCode: - type: string - enum: [A01, A02, A03, A04, A08] - LocationID: - type: integer - AttDoc: - type: integer - RefDoc: - type: integer - AdmDoc: - type: integer - CnsDoc: - type: integer - responses: - '201': - description: Visit created successfully - content: - application/json: - schema: - type: object - properties: - status: - type: string - message: - type: string - data: - type: object - properties: - PVID: - type: string - InternalPVID: - type: integer - - patch: - tags: [Patient Visits] - summary: Update patient visit - description: | - Updates an existing patient visit. InternalPVID is required. - Can update main visit data, PatDiag, and add new PatVisitADT records. - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - InternalPVID - properties: - InternalPVID: - type: integer - description: Visit ID (required) - PVID: - type: string - InternalPID: - type: integer - EpisodeID: - type: string - SiteID: - type: integer - PatDiag: - type: object - description: Diagnosis information (will update if exists) - properties: - DiagCode: - type: string - Diagnosis: - type: string - PatVisitADT: - type: array - description: Array of ADT records to add (new records only) - items: - type: object - properties: - ADTCode: - type: string - enum: [A01, A02, A03, A04, A08] - LocationID: - type: integer - AttDoc: - type: integer - RefDoc: - type: integer - AdmDoc: - type: integer - CnsDoc: - type: integer - sequence: - type: integer - description: Used for ordering multiple ADT records - responses: - '200': - description: Visit updated successfully - content: - application/json: - schema: - type: object - properties: - status: - type: string - message: - type: string - data: - type: object - properties: - PVID: - type: string - InternalPVID: - type: integer - - delete: - tags: [Patient Visits] - summary: Delete patient visit - security: - - bearerAuth: [] - responses: - '200': - description: Visit deleted successfully - - /api/patvisit/{id}: - get: - tags: [Patient Visits] - summary: Get visit by ID - security: - - bearerAuth: [] - parameters: - - name: id - in: path - required: true - schema: - type: string - description: PVID (visit identifier like DV00001) - responses: - '200': - description: Visit details - content: - application/json: - schema: - type: object - properties: - status: - type: string - message: - type: string - data: - $ref: '#/components/schemas/PatientVisit' - - /api/patvisit/patient/{patientId}: - get: - tags: [Patient Visits] - summary: Get visits by patient ID - security: - - bearerAuth: [] - parameters: - - name: patientId - in: path - required: true - schema: - type: integer - description: Internal Patient ID (InternalPID) - responses: - '200': - description: Patient visits list - content: - application/json: - schema: - type: object - properties: - status: - type: string - data: - type: array - items: - $ref: '#/components/schemas/PatientVisit' - - /api/patvisitadt: - post: - tags: [Patient Visits] - summary: Create ADT record - description: Create a new Admission/Discharge/Transfer record - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/PatVisitADT' - responses: - '201': - description: ADT record created successfully - content: - application/json: - schema: - $ref: '#/components/schemas/SuccessResponse' - - patch: - tags: [Patient Visits] - summary: Update ADT record - description: Update an existing ADT record - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/PatVisitADT' - responses: - '200': - description: ADT record updated successfully - content: - application/json: - schema: - $ref: '#/components/schemas/SuccessResponse' - - delete: - tags: [Patient Visits] - summary: Delete ADT visit (soft delete) - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - PVADTID - properties: - PVADTID: - type: integer - description: ADT record ID to delete - responses: - '200': - description: ADT visit deleted successfully - - /api/patvisitadt/visit/{visitId}: - get: - tags: [Patient Visits] - summary: Get ADT history by visit ID - description: Retrieve the complete Admission/Discharge/Transfer history for a visit, including all locations and doctors - security: - - bearerAuth: [] - parameters: - - name: visitId - in: path - required: true - schema: - type: integer - description: Internal Visit ID (InternalPVID) - responses: - '200': - description: ADT history retrieved successfully - content: - application/json: - schema: - type: object - properties: - status: - type: string - example: success - message: - type: string - example: ADT history retrieved - data: - type: array - items: - type: object - properties: - PVADTID: - type: integer - InternalPVID: - type: integer - ADTCode: - type: string - enum: [A01, A02, A03, A04, A08] - LocationID: - type: integer - LocationName: - type: string - AttDoc: - type: integer - AttDocFirstName: - type: string - AttDocLastName: - type: string - RefDoc: - type: integer - RefDocFirstName: - type: string - RefDocLastName: - type: string - AdmDoc: - type: integer - AdmDocFirstName: - type: string - AdmDocLastName: - type: string - CnsDoc: - type: integer - CnsDocFirstName: - type: string - CnsDocLastName: - type: string - CreateDate: - type: string - format: date-time - EndDate: - type: string - format: date-time - delete: - tags: [Patient Visits] - summary: Delete ADT visit (soft delete) - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - PVADTID - properties: - PVADTID: - type: integer - description: ADT record ID to delete - responses: - '200': - description: ADT visit deleted successfully - - /api/patvisitadt/{id}: - get: - tags: [Patient Visits] - summary: Get ADT record by ID - description: Retrieve a single ADT record by its ID, including location and doctor details - security: - - bearerAuth: [] - parameters: - - name: id - in: path - required: true - schema: - type: integer - description: ADT record ID (PVADTID) - responses: - '200': - description: ADT record retrieved successfully - content: - application/json: - schema: - type: object - properties: - status: - type: string - example: success - message: - type: string - example: ADT record retrieved - data: - type: object - properties: - PVADTID: - type: integer - InternalPVID: - type: integer - ADTCode: - type: string - enum: [A01, A02, A03, A04, A08] - LocationID: - type: integer - LocationName: - type: string - AttDoc: - type: integer - AttDocFirstName: - type: string - AttDocLastName: - type: string - RefDoc: - type: integer - RefDocFirstName: - type: string - RefDocLastName: - type: string - AdmDoc: - type: integer - AdmDocFirstName: - type: string - AdmDocLastName: - type: string - CnsDoc: - type: integer - CnsDocFirstName: - type: string - CnsDocLastName: - type: string - CreateDate: - type: string - format: date-time - EndDate: - type: string - format: date-time - - /api/organization/account/{id}: - get: - tags: [Organization] - summary: Get account by ID - security: - - bearerAuth: [] - parameters: - - name: id - in: path - required: true - schema: - type: integer - responses: - '200': - description: Account details - content: - application/json: - schema: - $ref: '#/components/schemas/Account' - - # ======================================== - # Organization - Site Routes - # ======================================== - /api/organization/site: - get: - tags: [Organization] - summary: List sites - security: - - bearerAuth: [] - responses: - '200': - description: List of sites - - post: - tags: [Organization] - summary: Create site - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/Site' - responses: - '201': - description: Site created - - patch: - tags: [Organization] - summary: Update site - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - id - properties: - id: - type: integer - SiteName: - type: string - SiteCode: - type: string - AccountID: - type: integer - responses: - '200': - description: Site updated - - delete: - tags: [Organization] - summary: Delete site - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - id - properties: - id: - type: integer - responses: - '200': - description: Site deleted - - /api/organization/site/{id}: - get: - tags: [Organization] - summary: Get site by ID - security: - - bearerAuth: [] - parameters: - - name: id - in: path - required: true - schema: - type: integer - responses: - '200': - description: Site details - - # ======================================== - # Organization - Discipline Routes - # ======================================== - /api/organization/discipline: - get: - tags: [Organization] - summary: List disciplines - security: - - bearerAuth: [] - responses: - '200': - description: List of disciplines - - post: - tags: [Organization] - summary: Create discipline - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/Discipline' - responses: - '201': - description: Discipline created - - patch: - tags: [Organization] - summary: Update discipline - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - id - properties: - id: - type: integer - DisciplineName: - type: string - DisciplineCode: - type: string - responses: - '200': - description: Discipline updated - - delete: - tags: [Organization] - summary: Delete discipline - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - id - properties: - id: - type: integer - responses: - '200': - description: Discipline deleted - - /api/organization/discipline/{id}: - get: - tags: [Organization] - summary: Get discipline by ID - security: - - bearerAuth: [] - parameters: - - name: id - in: path - required: true - schema: - type: integer - responses: - '200': - description: Discipline details - - # ======================================== - # Organization - Department Routes - # ======================================== - /api/organization/department: - get: - tags: [Organization] - summary: List departments - security: - - bearerAuth: [] - responses: - '200': - description: List of departments - - post: - tags: [Organization] - summary: Create department - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/Department' - responses: - '201': - description: Department created - - patch: - tags: [Organization] - summary: Update department - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - id - properties: - id: - type: integer - DeptName: - type: string - DeptCode: - type: string - SiteID: - type: integer - responses: - '200': - description: Department updated - - delete: - tags: [Organization] - summary: Delete department - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - id - properties: - id: - type: integer - responses: - '200': - description: Department deleted - - /api/organization/department/{id}: - get: - tags: [Organization] - summary: Get department by ID - security: - - bearerAuth: [] - parameters: - - name: id - in: path - required: true - schema: - type: integer - responses: - '200': - description: Department details - - # ======================================== - # Organization - Workstation Routes - # ======================================== - /api/organization/workstation: - get: - tags: [Organization] - summary: List workstations - security: - - bearerAuth: [] - responses: - '200': - description: List of workstations - - post: - tags: [Organization] - summary: Create workstation - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/Workstation' - responses: - '201': - description: Workstation created - - patch: - tags: [Organization] - summary: Update workstation - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - id - properties: - id: - type: integer - WorkstationName: - type: string - WorkstationCode: - type: string - SiteID: - type: integer - DepartmentID: - type: integer - responses: - '200': - description: Workstation updated - - delete: - tags: [Organization] - summary: Delete workstation - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - id - properties: - id: - type: integer - responses: - '200': - description: Workstation deleted - - /api/organization/workstation/{id}: - get: - tags: [Organization] - summary: Get workstation by ID - security: - - bearerAuth: [] - parameters: - - name: id - in: path - required: true - schema: - type: integer - responses: - '200': - description: Workstation details - - # ======================================== - # Specimen Routes - # ======================================== - /api/specimen: - get: - tags: [Specimen] - summary: List specimens - security: - - bearerAuth: [] - responses: - '200': - description: List of specimens - - post: - tags: [Specimen] - summary: Create specimen - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/Specimen' - responses: - '201': - description: Specimen created - - patch: - tags: [Specimen] - summary: Update specimen - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/Specimen' - responses: - '200': - description: Specimen updated - - /api/specimen/{id}: - get: - tags: [Specimen] - summary: Get specimen by ID - security: - - bearerAuth: [] - parameters: - - name: id - in: path - required: true - schema: - type: integer - responses: - '200': - description: Specimen details - - /api/specimen/container: - get: - tags: [Specimen] - summary: List container definitions - security: - - bearerAuth: [] - responses: - '200': - description: List of container definitions - - post: - tags: [Specimen] - summary: Create container definition - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/ContainerDef' - responses: - '201': - description: Container definition created - - patch: - tags: [Specimen] - summary: Update container definition - security: - - bearerAuth: [] - responses: - '200': - description: Container definition updated - - /api/specimen/container/{id}: - get: - tags: [Specimen] - summary: Get container definition by ID - security: - - bearerAuth: [] - parameters: - - name: id - in: path - required: true - schema: - type: integer - responses: - '200': - description: Container definition details - - # ContainerDef aliases (same as /api/specimen/container) - /api/specimen/containerdef: - get: - tags: [Specimen] - summary: List container definitions (alias) - security: - - bearerAuth: [] - responses: - '200': - description: List of container definitions - - post: - tags: [Specimen] - summary: Create container definition (alias) - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/ContainerDef' - responses: - '201': - description: Container definition created - - patch: - tags: [Specimen] - summary: Update container definition (alias) - security: - - bearerAuth: [] - responses: - '200': - description: Container definition updated - - /api/specimen/prep: - get: - tags: [Specimen] - summary: List specimen preparations - security: - - bearerAuth: [] - responses: - '200': - description: List of specimen preparations - - post: - tags: [Specimen] - summary: Create specimen preparation - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/SpecimenPrep' - responses: - '201': - description: Specimen preparation created - - patch: - tags: [Specimen] - summary: Update specimen preparation - security: - - bearerAuth: [] - responses: - '200': - description: Specimen preparation updated - - /api/specimen/prep/{id}: - get: - tags: [Specimen] - summary: Get specimen preparation by ID - security: - - bearerAuth: [] - parameters: - - name: id - in: path - required: true - schema: - type: integer - responses: - '200': - description: Specimen preparation details - - /api/specimen/status: - get: - tags: [Specimen] - summary: List specimen statuses - security: - - bearerAuth: [] - responses: - '200': - description: List of specimen statuses - - post: - tags: [Specimen] - summary: Create specimen status - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/SpecimenStatus' - responses: - '201': - description: Specimen status created - - patch: - tags: [Specimen] - summary: Update specimen status - security: - - bearerAuth: [] - responses: - '200': - description: Specimen status updated - - /api/specimen/status/{id}: - get: - tags: [Specimen] - summary: Get specimen status by ID - security: - - bearerAuth: [] - parameters: - - name: id - in: path - required: true - schema: - type: integer - responses: - '200': - description: Specimen status details - - /api/specimen/collection: - get: - tags: [Specimen] - summary: List specimen collection methods - security: - - bearerAuth: [] - responses: - '200': - description: List of collection methods - - post: - tags: [Specimen] - summary: Create specimen collection method - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/SpecimenCollection' - responses: - '201': - description: Collection method created - - patch: - tags: [Specimen] - summary: Update specimen collection method - security: - - bearerAuth: [] - responses: - '200': - description: Collection method updated - - /api/specimen/collection/{id}: - get: - tags: [Specimen] - summary: Get specimen collection method by ID - security: - - bearerAuth: [] - parameters: - - name: id - in: path - required: true - schema: - type: integer - responses: - '200': - description: Collection method details - - # ======================================== - # Tests Routes - # ======================================== - /api/tests: - get: - tags: [Tests] - summary: List test definitions - security: - - bearerAuth: [] - parameters: - - name: page - in: query - schema: - type: integer - - name: perPage - in: query - schema: - type: integer - - name: SiteID - in: query - schema: - type: integer - description: Filter by site ID - - name: TestType - in: query - schema: - type: string - enum: [TEST, PARAM, CALC, GROUP, TITLE] - description: Filter by test type - - name: VisibleScr - in: query - schema: - type: integer - enum: [0, 1] - description: Filter by screen visibility (0=hidden, 1=visible) - - name: VisibleRpt - in: query - schema: - type: integer - enum: [0, 1] - description: Filter by report visibility (0=hidden, 1=visible) - - name: TestSiteName - in: query - schema: - type: string - description: Search by test name or code - responses: - '200': - description: List of test definitions - content: - application/json: - schema: - type: object - properties: - status: - type: string - data: - type: array - items: - $ref: '#/components/schemas/TestDefinition' - - post: - tags: [Tests] - summary: Create test definition - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/TestDefinition' - responses: - '201': - description: Test definition created - - patch: - tags: [Tests] - summary: Update test definition - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/TestDefinition' - responses: - '200': - description: Test definition updated - - /api/tests/{id}: - get: - tags: [Tests] - summary: Get test definition by ID - security: - - bearerAuth: [] - parameters: - - name: id - in: path - required: true - schema: - type: integer - responses: - '200': - description: Test definition details - - # ======================================== - # Orders Routes - # ======================================== - /api/ordertest: - get: - tags: [Orders] - summary: List orders - security: - - bearerAuth: [] - parameters: - - name: page - in: query - schema: - type: integer - - name: perPage - in: query - schema: - type: integer - - name: InternalPID - in: query - schema: - type: integer - description: Filter by internal patient ID - - name: OrderStatus - in: query - schema: - type: string - enum: [ORD, SCH, ANA, VER, REV, REP] - description: | - ORD: Ordered - SCH: Scheduled - ANA: Analysis - VER: Verified - REV: Reviewed - REP: Reported - responses: - '200': - description: List of orders - - post: - tags: [Orders] - summary: Create order - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - PatientID - - Tests - properties: - PatientID: - type: string - VisitID: - type: string - Priority: - type: string - enum: [R, S, U] - description: | - R: Routine - S: Stat - U: Urgent - SiteID: - type: integer - RequestingPhysician: - type: string - Tests: - type: array - items: - type: object - properties: - TestID: - type: integer - SpecimenType: - type: string - responses: - '201': - description: Order created successfully - - patch: - tags: [Orders] - summary: Update order - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/OrderTest' - responses: - '200': - description: Order updated - - delete: - tags: [Orders] - summary: Delete order - security: - - bearerAuth: [] - responses: - '200': - description: Order deleted - - /api/ordertest/status: - post: - tags: [Orders] - summary: Update order status - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - OrderID - - OrderStatus - properties: - OrderID: - type: string - OrderStatus: - type: string - enum: [ORD, SCH, ANA, VER, REV, REP] - description: | - ORD: Ordered - SCH: Scheduled - ANA: Analysis - VER: Verified - REV: Reviewed - REP: Reported - responses: - '200': - description: Order status updated - - /api/ordertest/{id}: - get: - tags: [Orders] - summary: Get order by ID - security: - - bearerAuth: [] - parameters: - - name: id - in: path - required: true - schema: - type: string - responses: - '200': - description: Order details - - # ======================================== - # Edge API Routes - # ======================================== - /api/edge/results: - post: - tags: [Edge API] - summary: Receive results from instrument (tiny-edge) - description: | - Receives instrument results and stores them in the edgeres table for processing. - This endpoint is typically called by the tiny-edge middleware connected to laboratory analyzers. - requestBody: - required: true - content: - application/json: - schema: - $ref: '#/components/schemas/EdgeResultRequest' - responses: - '201': - description: Result received and queued - content: - application/json: - schema: - $ref: '#/components/schemas/EdgeResultResponse' - '400': - description: Invalid JSON payload - - /api/edge/orders: - get: - tags: [Edge API] - summary: Fetch pending orders for instruments - description: Returns orders that need to be sent to laboratory instruments for testing - parameters: - - name: instrument_id - in: query - schema: - type: string - description: Filter by instrument - - name: status - in: query - schema: - type: string - enum: [pending, acknowledged] - description: Filter by status - responses: - '200': - description: List of orders - content: - application/json: - schema: - type: object - properties: - status: - type: string - data: - type: array - items: - $ref: '#/components/schemas/EdgeOrder' - - /api/edge/orders/{orderId}/ack: - post: - tags: [Edge API] - summary: Acknowledge order delivery - description: Mark order as acknowledged by the instrument - parameters: - - name: orderId - in: path - required: true - schema: - type: integer - description: Edge order ID - responses: - '200': - description: Order acknowledged - content: - application/json: - schema: - $ref: '#/components/schemas/SuccessResponse' - - /api/edge/status: - post: - tags: [Edge API] - summary: Log instrument status update - description: Receive status updates from laboratory instruments - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - instrument_id - - status - properties: - instrument_id: - type: string - status: - type: string - enum: [online, offline, error, maintenance] - message: - type: string - timestamp: - type: string - format: date-time - responses: - '200': - description: Status logged - - # ======================================== - # ValueSet Routes - # ======================================== - /api/valueset: - get: - tags: [ValueSets] - summary: List lib value sets - description: List all library/system value sets from JSON files with item counts. Returns an object where keys are value set names and values are item counts. - security: - - bearerAuth: [] - parameters: - - name: search - in: query - schema: - type: string - description: Optional search term to filter value set names - responses: - '200': - description: List of lib value sets with item counts - content: - application/json: - schema: - type: object - properties: - status: - type: string - example: success - data: - type: object - additionalProperties: - type: integer - description: Number of items in each value set - example: - sex: 3 - marital_status: 6 - order_status: 6 - - /api/valueset/{key}: - get: - tags: [ValueSets] - summary: Get lib value set by key - description: | - Get a specific library/system value set from JSON files. - - **Available value set keys:** - - `activity_result` - Activity Result - - `additive` - Additive - - `adt_event` - ADT Event - - `area_class` - Area Class - - `body_site` - Body Site - - `collection_method` - Collection Method - - `container_cap_color` - Container Cap Color - - `container_class` - Container Class - - `container_size` - Container Size - - `country` - Country - - `death_indicator` - Death Indicator - - `did_type` - DID Type - - `enable_disable` - Enable/Disable - - `entity_type` - Entity Type - - `ethnic` - Ethnic - - `fasting_status` - Fasting Status - - `formula_language` - Formula Language - - `generate_by` - Generate By - - `identifier_type` - Identifier Type - - `location_type` - Location Type - - `marital_status` - Marital Status - - `math_sign` - Math Sign - - `numeric_ref_type` - Numeric Reference Type - - `operation` - Operation (CRUD) - - `order_priority` - Order Priority - - `order_status` - Order Status - - `race` - Race (Ethnicity) - - `range_type` - Range Type - - `reference_type` - Reference Type - - `religion` - Religion - - `requested_entity` - Requested Entity - - `result_type` - Result Type - - `result_unit` - Result Unit - - `sex` - Sex - - `site_class` - Site Class - - `site_type` - Site Type - - `specimen_activity` - Specimen Activity - - `specimen_condition` - Specimen Condition - - `specimen_role` - Specimen Role - - `specimen_status` - Specimen Status - - `specimen_type` - Specimen Type - - `test_activity` - Test Activity - - `test_type` - Test Type - - `text_ref_type` - Text Reference Type - - `unit` - Unit - - `v_category` - VCategory - - `ws_type` - Workstation Type - security: - - bearerAuth: [] - parameters: - - name: key - in: path - required: true - schema: - type: string - enum: [activity_result, additive, adt_event, area_class, body_site, collection_method, container_cap_color, container_class, container_size, country, death_indicator, did_type, enable_disable, entity_type, ethnic, fasting_status, formula_language, generate_by, identifier_type, location_type, marital_status, math_sign, numeric_ref_type, operation, order_priority, order_status, race, range_type, reference_type, religion, requested_entity, result_type, result_unit, sex, site_class, site_type, specimen_activity, specimen_condition, specimen_role, specimen_status, specimen_type, test_activity, test_type, text_ref_type, unit, v_category, ws_type] - description: Value set key name - responses: - '200': - description: Lib value set details - content: - application/json: - schema: - type: object - properties: - status: - type: string - data: - type: array - items: - $ref: '#/components/schemas/ValueSetLibItem' - - /api/valueset/refresh: - post: - tags: [ValueSets] - summary: Refresh lib ValueSet cache - description: Clear and reload the library/system ValueSet cache from JSON files. Call this after modifying JSON files in app/Libraries/Data/. - security: - - bearerAuth: [] - responses: - '200': - description: Lib ValueSet cache refreshed - content: - application/json: - schema: - type: object - properties: - status: - type: string - example: success - message: - type: string - example: Cache cleared - - /api/valueset/user/items: - get: - tags: [ValueSets] - summary: List user value set items - description: List value set items from database (user-defined) - security: - - bearerAuth: [] - parameters: - - name: VSetID - in: query - schema: - type: integer - description: Filter by ValueSet ID - - name: search - in: query - schema: - type: string - description: Search term to filter by VValue, VDesc, or VSName - - name: param - in: query - schema: - type: string - description: Alternative search parameter (alias for search) - responses: - '200': - description: List of user value set items - content: - application/json: - schema: - type: object - properties: - status: - type: string - data: - type: array - items: - $ref: '#/components/schemas/ValueSetItem' - - post: - tags: [ValueSets] - summary: Create user value set item - description: Create value set item in database (user-defined) - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - VSetID - properties: - SiteID: - type: integer - description: Site reference (default 1) - VSetID: - type: integer - description: Reference to value set definition (required) - VOrder: - type: integer - description: Display order (default 0) - VValue: - type: string - description: The value code - VDesc: - type: string - description: The display description/label - responses: - '201': - description: User value set item created - content: - application/json: - schema: - type: object - properties: - status: - type: string - message: - type: string - data: - $ref: '#/components/schemas/ValueSetItem' - - /api/valueset/user/items/{id}: - get: - tags: [ValueSets] - summary: Get user value set item by ID - description: Get value set item from database (user-defined) - security: - - bearerAuth: [] - parameters: - - name: id - in: path - required: true - schema: - type: integer - responses: - '200': - description: User value set item details - content: - application/json: - schema: - type: object - properties: - status: - type: string - data: - $ref: '#/components/schemas/ValueSetItem' - - put: - tags: [ValueSets] - summary: Update user value set item - description: Update value set item in database (user-defined) - security: - - bearerAuth: [] - parameters: - - name: id - in: path - required: true - schema: - type: integer - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - SiteID: - type: integer - description: Site reference - VSetID: - type: integer - description: Reference to value set definition - VOrder: - type: integer - description: Display order - VValue: - type: string - description: The value code - VDesc: - type: string - description: The display description/label - responses: - '200': - description: User value set item updated - content: - application/json: - schema: - type: object - properties: - status: - type: string - message: - type: string - data: - $ref: '#/components/schemas/ValueSetItem' - - delete: - tags: [ValueSets] - summary: Delete user value set item - description: Delete value set item from database (user-defined) - security: - - bearerAuth: [] - parameters: - - name: id - in: path - required: true - schema: - type: integer - responses: - '200': - description: User value set item deleted - content: - application/json: - schema: - type: object - properties: - status: - type: string - message: - type: string - - /api/valueset/user/def: - get: - tags: [ValueSets] - summary: List user value set definitions - description: List value set definitions from database (user-defined) - security: - - bearerAuth: [] - parameters: - - name: search - in: query - schema: - type: string - description: Optional search term to filter definitions - - name: page - in: query - schema: - type: integer - default: 1 - description: Page number for pagination - - name: limit - in: query - schema: - type: integer - default: 100 - description: Number of items per page - responses: - '200': - description: List of user value set definitions - content: - application/json: - schema: - type: object - properties: - status: - type: string - data: - type: array - items: - $ref: '#/components/schemas/ValueSetDef' - meta: - type: object - properties: - total: - type: integer - page: - type: integer - limit: - type: integer - - post: - tags: [ValueSets] - summary: Create user value set definition - description: Create value set definition in database (user-defined) - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - SiteID: - type: integer - description: Site reference (default 1) - VSName: - type: string - description: Value set name - VSDesc: - type: string - description: Value set description - responses: - '201': - description: User value set definition created - content: - application/json: - schema: - type: object - properties: - status: - type: string - message: - type: string - data: - $ref: '#/components/schemas/ValueSetDef' - - /api/valueset/user/def/{id}: - get: - tags: [ValueSets] - summary: Get user value set definition by ID - description: Get value set definition from database (user-defined) - security: - - bearerAuth: [] - parameters: - - name: id - in: path - required: true - schema: - type: integer - responses: - '200': - description: User value set definition details - content: - application/json: - schema: - type: object - properties: - status: - type: string - data: - $ref: '#/components/schemas/ValueSetDef' - - put: - tags: [ValueSets] - summary: Update user value set definition - description: Update value set definition in database (user-defined) - security: - - bearerAuth: [] - parameters: - - name: id - in: path - required: true - schema: - type: integer - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - SiteID: - type: integer - description: Site reference - VSName: - type: string - description: Value set name - VSDesc: - type: string - description: Value set description - responses: - '200': - description: User value set definition updated - content: - application/json: - schema: - type: object - properties: - status: - type: string - message: - type: string - data: - $ref: '#/components/schemas/ValueSetDef' - - delete: - tags: [ValueSets] - summary: Delete user value set definition - description: Delete value set definition from database (user-defined) - security: - - bearerAuth: [] - parameters: - - name: id - in: path - required: true - schema: - type: integer - responses: - '200': - description: User value set definition deleted - content: - application/json: - schema: - type: object - properties: - status: - type: string - message: - type: string - - # ======================================== - # Master Data Routes - # ======================================== - /api/location: - get: - tags: [Master Data] - summary: List locations - security: - - bearerAuth: [] - parameters: - - name: LocCode - in: query - schema: - type: string - description: Filter by location code - - name: LocName - in: query - schema: - type: string - description: Filter by location name (searches in LocFull) - responses: - '200': - description: List of locations - content: - application/json: - schema: - type: object - properties: - status: - type: string - message: - type: string - data: - type: array - items: - $ref: '#/components/schemas/Location' - - post: - tags: [Master Data] - summary: Create location - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - LocCode - - LocFull - properties: - SiteID: - type: integer - LocCode: - type: string - maxLength: 6 - Parent: - type: integer - LocFull: - type: string - maxLength: 255 - Description: - type: string - maxLength: 255 - LocType: - type: string - responses: - '201': - description: Location created - content: - application/json: - schema: - type: object - properties: - status: - type: string - message: - type: string - data: - type: object - - patch: - tags: [Master Data] - summary: Update location - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - LocCode - - LocFull - properties: - LocationID: - type: integer - SiteID: - type: integer - LocCode: - type: string - maxLength: 6 - Parent: - type: integer - LocFull: - type: string - maxLength: 255 - Description: - type: string - maxLength: 255 - LocType: - type: string - responses: - '200': - description: Location updated - content: - application/json: - schema: - type: object - properties: - status: - type: string - message: - type: string - data: - type: object - - delete: - tags: [Master Data] - summary: Delete location - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - LocationID - properties: - LocationID: - type: integer - responses: - '200': - description: Location deleted - - /api/location/{id}: - get: - tags: [Master Data] - summary: Get location by ID - security: - - bearerAuth: [] - parameters: - - name: id - in: path - required: true - schema: - type: integer - description: LocationID (primary key) - responses: - '200': - description: Location details - content: - application/json: - schema: - type: object - properties: - status: - type: string - message: - type: string - data: - $ref: '#/components/schemas/Location' - - /api/contact: - get: - tags: [Master Data] - summary: List contacts - security: - - bearerAuth: [] - responses: - '200': - description: List of contacts - content: - application/json: - schema: - type: object - properties: - status: - type: string - message: - type: string - data: - type: array - items: - $ref: '#/components/schemas/Contact' - - post: - tags: [Master Data] - summary: Create contact - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - NameFirst - properties: - NameFirst: - type: string - description: First name (required) - NameLast: - type: string - description: Last name - Title: - type: string - description: Title (e.g., Dr, Mr, Mrs) - Initial: - type: string - description: Middle initial - Birthdate: - type: string - format: date-time - EmailAddress1: - type: string - format: email - EmailAddress2: - type: string - format: email - Phone: - type: string - MobilePhone1: - type: string - MobilePhone2: - type: string - Specialty: - type: string - SubSpecialty: - type: string - responses: - '201': - description: Contact created - - patch: - tags: [Master Data] - summary: Update contact - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - NameFirst - properties: - ContactID: - type: integer - NameFirst: - type: string - NameLast: - type: string - Title: - type: string - Initial: - type: string - Birthdate: - type: string - format: date-time - EmailAddress1: - type: string - format: email - EmailAddress2: - type: string - format: email - Phone: - type: string - MobilePhone1: - type: string - MobilePhone2: - type: string - Specialty: - type: string - SubSpecialty: - type: string - responses: - '200': - description: Contact updated - - delete: - tags: [Master Data] - summary: Delete contact - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - ContactID - properties: - ContactID: - type: integer - responses: - '200': - description: Contact deleted - - /api/contact/{id}: - get: - tags: [Master Data] - summary: Get contact by ID - security: - - bearerAuth: [] - parameters: - - name: id - in: path - required: true - schema: - type: integer - description: ContactID (primary key) - responses: - '200': - description: Contact details - content: - application/json: - schema: - type: object - properties: - status: - type: string - message: - type: string - data: - $ref: '#/components/schemas/Contact' - - /api/occupation: - get: - tags: [Master Data] - summary: List occupations - security: - - bearerAuth: [] - responses: - '200': - description: List of occupations - content: - application/json: - schema: - type: object - properties: - status: - type: string - message: - type: string - data: - type: array - items: - $ref: '#/components/schemas/Occupation' - - post: - tags: [Master Data] - summary: Create occupation - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - OccCode - - OccText - properties: - OccCode: - type: string - description: Occupation code (required) - OccText: - type: string - description: Occupation name/text (required) - Description: - type: string - description: Additional description - responses: - '201': - description: Occupation created - - patch: - tags: [Master Data] - summary: Update occupation - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - OccCode - - OccText - properties: - OccupationID: - type: integer - OccCode: - type: string - OccText: - type: string - Description: - type: string - responses: - '200': - description: Occupation updated - - /api/occupation/{id}: - get: - tags: [Master Data] - summary: Get occupation by ID - security: - - bearerAuth: [] - parameters: - - name: id - in: path - required: true - schema: - type: integer - description: OccupationID (primary key) - responses: - '200': - description: Occupation details - content: - application/json: - schema: - type: object - properties: - status: - type: string - message: - type: string - data: - $ref: '#/components/schemas/Occupation' - - /api/medicalspecialty: - get: - tags: [Master Data] - summary: List medical specialties - security: - - bearerAuth: [] - responses: - '200': - description: List of medical specialties - content: - application/json: - schema: - type: object - properties: - status: - type: string - message: - type: string - data: - type: array - items: - $ref: '#/components/schemas/MedicalSpecialty' - - post: - tags: [Master Data] - summary: Create medical specialty - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - SpecialtyText - properties: - SpecialtyText: - type: string - description: Specialty name/text (required) - Parent: - type: integer - description: Parent specialty ID - Title: - type: string - description: Title/abbreviation - responses: - '201': - description: Medical specialty created - - patch: - tags: [Master Data] - summary: Update medical specialty - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - SpecialtyText - properties: - SpecialtyID: - type: integer - SpecialtyText: - type: string - Parent: - type: integer - Title: - type: string - responses: - '200': - description: Medical specialty updated - - /api/medicalspecialty/{id}: - get: - tags: [Master Data] - summary: Get medical specialty by ID - security: - - bearerAuth: [] - parameters: - - name: id - in: path - required: true - schema: - type: integer - description: SpecialtyID (primary key) - responses: - '200': - description: Medical specialty details - content: - application/json: - schema: - type: object - properties: - status: - type: string - message: - type: string - data: - $ref: '#/components/schemas/MedicalSpecialty' - - /api/counter: - get: - tags: [Master Data] - summary: List counters - security: - - bearerAuth: [] - responses: - '200': - description: List of counters - content: - application/json: - schema: - type: object - properties: - status: - type: string - message: - type: string - data: - type: array - items: - $ref: '#/components/schemas/Counter' - - post: - tags: [Master Data] - summary: Create counter - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - CounterDesc: - type: string - description: Counter description/name - CounterValue: - type: integer - description: Current counter value - CounterStart: - type: integer - description: Starting value - CounterEnd: - type: integer - description: Ending value (for auto-reset) - CounterReset: - type: string - description: Reset pattern (D=Daily, M=Monthly, Y=Yearly) - responses: - '201': - description: Counter created - - patch: - tags: [Master Data] - summary: Update counter - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - CounterID: - type: integer - CounterDesc: - type: string - CounterValue: - type: integer - CounterStart: - type: integer - CounterEnd: - type: integer - CounterReset: - type: string - responses: - '200': - description: Counter updated - - delete: - tags: [Master Data] - summary: Delete counter - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: - - CounterID - properties: - CounterID: - type: integer - responses: - '200': - description: Counter deleted - - /api/counter/{id}: - get: - tags: [Master Data] - summary: Get counter by ID - security: - - bearerAuth: [] - parameters: - - name: id - in: path - required: true - schema: - type: integer - description: CounterID (primary key) - responses: - '200': - description: Counter details - content: - application/json: - schema: - type: object - properties: - status: - type: string - message: - type: string - data: - $ref: '#/components/schemas/Counter' - - /api/areageo: - get: - tags: [Master Data] - summary: List geographical areas - security: - - bearerAuth: [] - responses: - '200': - description: List of geographical areas - - /api/areageo/provinces: - get: - tags: [Master Data] - summary: Get list of provinces - security: - - bearerAuth: [] - responses: - '200': - description: List of provinces - - /api/areageo/cities: - get: - tags: [Master Data] - summary: Get list of cities - security: - - bearerAuth: [] - parameters: - - name: province_id - in: query - schema: - type: integer - responses: - '200': - description: List of cities - - # ======================================== - # Dashboard & Results (Protected) - # ======================================== - /api/dashboard: - get: - tags: [Dashboard] - summary: Get dashboard summary - security: - - bearerAuth: [] - responses: - '200': - description: Dashboard summary data - content: - application/json: - schema: - $ref: '#/components/schemas/DashboardSummary' - - /api/result: - get: - tags: [Results] - summary: Get patient results - security: - - bearerAuth: [] - parameters: - - name: PatientID - in: query - schema: - type: string - - name: page - in: query - schema: - type: integer - responses: - '200': - description: List of results - - /api/sample: - get: - tags: [Specimen] - summary: Get samples - security: - - bearerAuth: [] - parameters: - - name: status - in: query - schema: - type: string - responses: - '200': - description: List of samples - - # ======================================== - # Demo Routes (No Auth) - # ======================================== - /api/demo/order: - post: - tags: [Demo] - summary: Create demo order (no authentication) - description: Test endpoint for creating orders without authentication - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - PatientID: - type: string - Tests: - type: array - items: - type: object - responses: - '201': - description: Demo order created - - /api/demo/orders: - get: - tags: [Demo] - summary: List demo orders (no authentication) - responses: - '200': - description: List of demo orders \ No newline at end of file diff --git a/docs/frontend-implementation-plan.md b/docs/frontend-implementation-plan.md deleted file mode 100644 index 2f03681..0000000 --- a/docs/frontend-implementation-plan.md +++ /dev/null @@ -1,806 +0,0 @@ -# CLQMS Frontend Implementation Plan - -## Project Overview -**CLQMS** (Clinical Laboratory Quality Management System) frontend built with SvelteKit using the KISS (Keep It Simple) principle. - -## Architecture Decisions (KISS Principles) - -- **No TypeScript** - Plain JavaScript to keep it simple -- **Tailwind CSS & DaisyUI** - Utility classes, minimal custom CSS -- **Simple Fetch API** - Manual wrapper functions, no codegen -- **SvelteKit File-based Routing** - Standard patterns -- **Server-side auth checking** - SvelteKit hooks for session validation - -## Project Structure\n\n```\nsrc/\n├── lib/\n│ ├── api/ # API service functions\n│ │ ├── client.js # Base fetch wrapper with auth\n│ │ ├── auth.js # Auth endpoints\n│ │ ├── valuesets.js # ValueSet endpoints\n│ │ ├── locations.js # Location endpoints\n│ │ ├── contacts.js # Contact endpoints\n│ │ ├── specialties.js # Specialty endpoints\n│ │ └── ... # Other endpoint modules\n│ ├── components/ # Reusable Svelte components\n│ │ ├── DataTable.svelte\n│ │ ├── Modal.svelte\n│ │ ├── SelectDropdown.svelte\n│ │ ├── Sidebar.svelte\n│ │ └── ToastContainer.svelte\n│ ├── stores/ # Svelte stores\n│ │ ├── auth.js # Auth state\n│ │ └── valuesets.js # Cached lookup values\n│ └── utils/ # Utility functions\n│ └── toast.js # Toast notifications\n├── routes/\n│ ├── +layout.svelte # Root layout with nav\n│ ├── +page.svelte # Redirect to login\n│ ├── login/\n│ │ └── +page.svelte\n│ └── (app)/ # Group: protected routes\n│ ├── +layout.svelte # Auth check with sidebar\n│ ├── dashboard/\n│ │ └── +page.svelte\n│ └── master-data/\n│ ├── +page.svelte\n│ ├── locations/\n│ ├── contacts/\n│ ├── specialties/\n│ └── valuesets/\n├── app.css # Global styles with Tailwind\n└── app.html # HTML template\n``` - -## Implementation Phases - -### Phase 0: Foundation ✅ COMPLETED -**Priority:** High | **Time Estimate:** 1-2 hours | **Status:** Done - -- [x] Initialize SvelteKit project with Tailwind CSS -- [x] Configure API base URL and environment variables -- [x] Create base API client with JWT token handling -- [x] Implement login/logout flow -- [x] Create root layout with navigation -- [x] Set up protected route group with auth checks -- [x] Create dashboard homepage - -**Key Files:** -- `src/lib/api/client.js` - Base fetch wrapper -- `src/lib/api/auth.js` - Auth endpoints -- `src/lib/stores/auth.js` - Auth state management -- `src/routes/+layout.svelte` - Root layout -- `src/routes/(app)/+layout.svelte` - Protected layout -- `src/routes/login/+page.svelte` - Login page - ---- - -### Phase 1: Foundation Data ✅ COMPLETED -**Priority:** High | **Time Estimate:** 4-6 hours | **Status:** Done - -**Why First:** These are prerequisites for all other modules. ValueSets provide dropdown options; Master Data provides reference entities. - -#### 1a. System ValueSets Module (Library) ✅ COMPLETED -System ValueSets are pre-defined lookup values used throughout the application (gender, marital status, specimen types, etc.). These are read-only libraries fetched from JSON files and cached. - -- [x] ValueSet browser page (view system value sets by key) -- [x] ValueSet items viewer (read-only) -- [x] Cache frequently used value sets in stores -- [x] SelectDropdown component using cached value sets -- [x] Refresh ValueSet cache - -**API Endpoints (Library/System ValueSets):** -- `GET /api/valueset` - List all library value sets with item counts -- `GET /api/valueset/{key}` - Get library value set by key (e.g., 'marital_status', 'sex') -- `POST /api/valueset/refresh` - Refresh library ValueSet cache from JSON files - -#### 1b. Master Data - Locations & Contacts ✅ COMPLETED -- [x] Locations list page with search (supports LocCode, LocName filters) -- [x] Locations create/edit form -- [x] Contacts (Physicians) list page -- [x] Contacts create/edit form - -**API Endpoints:** - -**Locations:** -- `GET /api/location` - List locations (query: LocCode, LocName) -- `POST /api/location` - Create location (required: LocCode, LocFull) -- `PATCH /api/location` - Update location (required: LocCode, LocFull) -- `DELETE /api/location` - Delete location (body: { LocationID }) -- `GET /api/location/{id}` - Get location details - -**Location Schema:** -- `LocationID` - Primary key (auto-generated) -- `SiteID` - Reference to site -- `LocCode` - Location code, max 6 chars (required) -- `Parent` - Parent location ID (for hierarchical locations) -- `LocFull` - Full location name, max 255 chars (required) -- `Description` - Description, max 255 chars -- `LocType` - Location type (e.g., ROOM, WARD, BUILDING) -- `CreateDate`, `EndDate` - Timestamps - -**Contacts:** -- `GET /api/contact` - List contacts -- `POST /api/contact` - Create contact (required: NameFirst) -- `PATCH /api/contact` - Update contact -- `DELETE /api/contact` - Delete contact -- `GET /api/contact/{id}` - Get contact details - -**Contact Schema:** -- `ContactID` - Primary key (auto-generated) -- `NameFirst` - First name (required) -- `NameLast` - Last name -- `Title` - Title (e.g., Dr, Mr, Mrs) -- `Initial` - Middle initial -- `Birthdate` - Date of birth (ISO 8601) -- `EmailAddress1`, `EmailAddress2` - Email addresses -- `Phone` - Primary phone -- `MobilePhone1`, `MobilePhone2` - Mobile numbers -- `Specialty` - Medical specialty code -- `SubSpecialty` - Sub-specialty code -- `CreateDate`, `EndDate` - Timestamps - -#### 1d. Master Data - Supporting Entities ✅ COMPLETED -- [x] Occupations management -- [x] Medical Specialties management -- [x] Counters management (for ID generation) - -**API Endpoints:** - -**Occupations:** -- `GET /api/occupation` - List occupations -- `POST /api/occupation` - Create occupation (required: OccCode, OccText) -- `PATCH /api/occupation` - Update occupation (required: OccCode, OccText) -- `GET /api/occupation/{id}` - Get occupation details - -**Occupation Schema:** -- `OccupationID` - Primary key (auto-generated) -- `OccCode` - Occupation code (required) -- `OccText` - Occupation name/text (required) -- `Description` - Additional description -- `CreateDate` - Creation timestamp - -**Medical Specialties:** -- `GET /api/medicalspecialty` - List specialties -- `POST /api/medicalspecialty` - Create specialty (required: SpecialtyText) -- `PATCH /api/medicalspecialty` - Update specialty (required: SpecialtyText) -- `GET /api/medicalspecialty/{id}` - Get specialty details - -**Medical Specialty Schema:** -- `SpecialtyID` - Primary key (auto-generated) -- `SpecialtyText` - Specialty name/text (required) -- `Parent` - Parent specialty ID (for hierarchical structure) -- `Title` - Title/abbreviation -- `CreateDate`, `EndDate` - Timestamps - -**Counters:** -- `GET /api/counter` - List counters -- `POST /api/counter` - Create counter -- `PATCH /api/counter` - Update counter -- `DELETE /api/counter` - Delete counter (body: { CounterID }) -- `GET /api/counter/{id}` - Get counter details - -**Counter Schema:** -- `CounterID` - Primary key (auto-generated) -- `CounterDesc` - Counter description/name -- `CounterValue` - Current counter value -- `CounterStart` - Starting value -- `CounterEnd` - Ending value (for auto-reset) -- `CounterReset` - Reset pattern (D=Daily, M=Monthly, Y=Yearly) - -#### 1e. Master Data - Geography ✅ COMPLETED -- [x] Geographical areas list -- [x] Provinces list (read-only dropdown) -- [x] Cities list with province filter - -**API Endpoints:** -- `GET /api/areageo` - List all geographical areas -- `GET /api/areageo/provinces` - List provinces -- `GET /api/areageo/cities?province_id={id}` - List cities (with province filter) - -**Notes:** -- Geography endpoints are read-only (no POST/PATCH/DELETE) -- Used for patient address province/city selection -- Returns hierarchical location data - ---- - -### Phase 2: Patient Management\n**Priority:** High | **Time Estimate:** 3-4 hours\n\n**Dependencies:** Master Data (locations, contacts, occupations, provinces/cities)\n\n#### 2a. Patient CRUD ✅ COMPLETED\n- [x] Patients list page with pagination and search\n- [x] Patient create form with validation (multi-step)\n- [x] Patient edit form (multi-step modal)\n- [x] Patient detail view (via edit modal)\n- [x] Patient delete with confirmation\n\n**API Endpoints:**\n- `GET /api/patient` - List patients (query: page, perPage, InternalPID, PatientID, Name, Birthdate)\n- `POST /api/patient` - Create patient\n- `PATCH /api/patient` - Update patient\n- `DELETE /api/patient` - Delete patient (soft delete, body: { InternalPID })\n- `GET /api/patient/{id}` - Get patient by ID\n- `GET /api/patient/check` - Check if patient exists (query: PatientID, EmailAddress1) - -#### 2b. Advanced Patient Features -- [ ] Patient identifier management (KTP, PASS, SSN, etc.) -- [ ] Patient linking (family relationships) -- [ ] Custodian/guardian assignment -- [ ] Patient address management - -**Fields to Implement:** -- PatientID, AlternatePID -- Prefix, NameFirst, NameMiddle, NameLast, NameMaiden, Suffix -- Sex, Birthdate, PlaceOfBirth, Citizenship -- Address fields (Street_1/2/3, ZIP, Province, City, Country) -- Contact (Phone, MobilePhone, EmailAddress1/2) -- Identifiers (PatIdt - type and number) -- Demographics (Race, MaritalStatus, Religion, Ethnic) -- Linked patients (LinkTo) -- Custodian -- DeathIndicator, TimeOfDeath -- Comments (PatCom) - ---- - -### Phase 3: Patient Visits -**Priority:** High | **Time Estimate:** 2-3 hours - -**Dependencies:** Patients, Master Data (locations, contacts) - -- [ ] Visits list page with filters -- [ ] Create visit form -- [ ] Edit visit form -- [ ] View visits by patient -- [ ] ADT workflow (Admit/Discharge/Transfer) - -**API Endpoints:** -- `GET /api/patvisit` - List visits -- `POST /api/patvisit` - Create visit -- `PATCH /api/patvisit` - Update visit -- `DELETE /api/patvisit` - Delete visit -- `GET /api/patvisit/{id}` - Get visit by ID -- `GET /api/patvisit/patient/{patientId}` - Get visits by patient -- `POST /api/patvisitadt` - Create ADT visit -- `PATCH /api/patvisitadt` - Update ADT visit - -**Fields:** -- VisitID, PatientID, VisitDate, VisitType -- SiteID, LocationID, DepartmentID -- AttendingPhysician, ReferringPhysician - ---- - -### Phase 4: Specimen Management -**Priority:** Medium | **Time Estimate:** 2-3 hours - -**Dependencies:** ValueSets (specimen types, collection methods, statuses) - -- [ ] Specimens list page -- [ ] Specimen create/edit forms -- [ ] Container definitions management -- [ ] Specimen preparation methods -- [ ] Specimen statuses -- [ ] Collection methods -- [ ] Samples list with status filtering - -**API Endpoints:** -- `GET /api/specimen` - List specimens -- `POST /api/specimen` - Create specimen -- `PATCH /api/specimen` - Update specimen -- `GET /api/specimen/{id}` - Get specimen details -- `GET /api/specimen/container` - List containers -- `POST /api/specimen/container` - Create container -- `PATCH /api/specimen/container` - Update container -- `GET /api/specimen/container/{id}` - Get container details -- `GET /api/specimen/prep` - List preparations -- `POST /api/specimen/prep` - Create preparation -- `PATCH /api/specimen/prep` - Update preparation -- `GET /api/specimen/prep/{id}` - Get preparation details -- `GET /api/specimen/status` - List statuses -- `POST /api/specimen/status` - Create status -- `PATCH /api/specimen/status` - Update status -- `GET /api/specimen/status/{id}` - Get status details -- `GET /api/specimen/collection` - List collection methods -- `POST /api/specimen/collection` - Create collection method -- `PATCH /api/specimen/collection` - Update collection method -- `GET /api/specimen/collection/{id}` - Get collection method details -- `GET /api/sample` - Get samples (with status filter) - ---- - -### Phase 5: Test Catalog\n**Priority:** Medium | **Time Estimate:** 2-3 hours\n\n**Dependencies:** Organization (disciplines, departments), ValueSets\n\n- [ ] Test definitions list with filtering\n- [ ] Create/edit test definitions\n- [ ] Test mapping management (host/client codes)\n\n**API Endpoints:**\n- `GET /api/tests` - List tests\n- `POST /api/tests` - Create test\n- `PATCH /api/tests` - Update test\n- `GET /api/tests/{id}` - Get test details\n\n**Query Parameters:**\n- `page`, `perPage` - Pagination\n- `SiteID` - Filter by site\n- `TestType` - Filter by type (TEST, PARAM, CALC, GROUP, TITLE)\n- `VisibleScr` - Filter by screen visibility (0=hidden, 1=visible)\n- `VisibleRpt` - Filter by report visibility (0=hidden, 1=visible)\n- `TestSiteName` - Search by test name or code\n\n**Test Types:**\n- `TEST` - Technical test\n- `PARAM` - Parameter\n- `CALC` - Calculated\n- `GROUP` - Panel/Profile\n- `TITLE` - Section header\n\n**Test Definition Schema:**\n- `id` - Primary key\n- `TestCode` - Test code\n- `TestName` - Test name\n- `TestType` - Type (TEST, PARAM, CALC, GROUP, TITLE)\n- `DisciplineID` - Reference to discipline\n- `DepartmentID` - Reference to department\n- `SpecimenType` - Specimen type code\n- `Unit` - Measurement unit\n- `Formula` - Calculation formula - ---- - -### Phase 6: Orders\n**Priority:** High | **Time Estimate:** 3-4 hours\n\n**Dependencies:** Patients, Visits, Tests, Specimen, ValueSets (priorities, statuses)\n\n- [ ] Orders list with status filtering\n- [ ] Create order with test selection\n- [ ] Order detail view\n- [ ] Update order status\n- [ ] Order items management\n\n**API Endpoints:**\n- `GET /api/ordertest` - List orders (filters: OrderStatus, InternalPID, page, perPage)\n- `POST /api/ordertest` - Create order (requires: PatientID, Tests array)\n- `PATCH /api/ordertest` - Update order\n- `DELETE /api/ordertest` - Delete order\n- `GET /api/ordertest/{id}` - Get order details\n- `POST /api/ordertest/status` - Update order status (body: { OrderID, OrderStatus })\n\n**Order Status Values:**\n- `ORD` - Ordered\n- `SCH` - Scheduled\n- `ANA` - Analysis\n- `VER` - Verified\n- `REV` - Reviewed\n- `REP` - Reported\n\n**Priority Values:**\n- `R` - Routine\n- `S` - Stat\n- `U` - Urgent\n\n**OrderTest Schema:**\n- `OrderID` - Order identifier\n- `PatientID` - Reference to patient\n- `VisitID` - Reference to visit\n- `OrderDate` - Order timestamp (ISO 8601)\n- `OrderStatus` - Status code (ORD, SCH, ANA, VER, REV, REP)\n- `Priority` - Priority code (R, S, U)\n- `SiteID` - Reference to site\n- `RequestingPhysician` - Requesting physician identifier\n\n**Create Order Request:**\n```json\n{\n \"PatientID\": \"string\",\n \"VisitID\": \"string\" (optional),\n \"Priority\": \"R|S|U\",\n \"SiteID\": integer (optional),\n \"RequestingPhysician\": \"string\" (optional),\n \"Tests\": [\n {\n \"TestID\": integer,\n \"SpecimenType\": \"string\" (optional)\n }\n ]\n}\n``` - ---- - -### Phase 7: Results & Dashboard -**Priority:** High | **Time Estimate:** 2-3 hours - -**Dependencies:** Orders - -- [ ] Dashboard with summary cards -- [ ] Results list with patient filtering -- [ ] Sample tracking view - -**API Endpoints:** -- `GET /api/dashboard` - Get dashboard summary - - Returns: pendingOrders, todayResults, criticalResults, activePatients -- `GET /api/result` - Get patient results (filter by PatientID, pagination with page) - -**Dashboard Metrics:** -- pendingOrders - Number of pending orders -- todayResults - Results processed today -- criticalResults - Critical/panic results -- activePatients - Currently active patients - ---- - -### Phase 8: User-defined ValueSets -**Priority:** Medium | **Time Estimate:** 3-4 hours -**Dependencies:** Test Catalog (Phase 5) - -User-defined ValueSets are created and managed by lab administrators in the database (not from JSON files). These require full CRUD operations and are used for test result interpretation (reference ranges, flags, interpretations). - -- [ ] User ValueSet definitions list page -- [ ] User ValueSet definition create/edit form -- [ ] User ValueSet items management (CRUD) - -**API Endpoints:** - -**User ValueSet Definitions:** -- `GET /api/valueset/user/def` - List user value set definitions -- `POST /api/valueset/user/def` - Create user value set definition -- `GET /api/valueset/user/def/{id}` - Get user value set definition by ID -- `PUT /api/valueset/user/def/{id}` - Update user value set definition -- `DELETE /api/valueset/user/def/{id}` - Delete user value set definition - -**User ValueSet Items:** -- `GET /api/valueset/user/items` - List user value set items (filter by VSetID) -- `POST /api/valueset/user/items` - Create user value set item -- `GET /api/valueset/user/items/{id}` - Get user value set item by ID -- `PUT /api/valueset/user/items/{id}` - Update user value set item -- `DELETE /api/valueset/user/items/{id}` - Delete user value set item - -**ValueSetDef Schema:** -- `id` - Primary key -- `VSetCode` - Value set code -- `VSetName` - Value set name -- `Description` - Description -- `Category` - Category - -**ValueSetItem Schema:** -- `id` - Primary key -- `VSetID` - Reference to ValueSet definition -- `VValue` - Value code -- `VLabel` - Display label -- `VSeq` - Sequence order -- `IsActive` - Active flag (boolean) - ---- - -### Phase 9: Organization Structure\n**Priority:** Medium | **Time Estimate:** 2-3 hours\n\n- [ ] Accounts management\n- [ ] Sites management\n- [ ] Disciplines management\n- [ ] Departments management\n- [ ] Workstations management\n\n**API Endpoints:**\n\n**Accounts:**\n- `GET /api/organization/account` - List accounts\n- `POST /api/organization/account` - Create account\n- `PATCH /api/organization/account` - Update account (body: { id, AccountName, AccountCode, AccountType })\n- `DELETE /api/organization/account` - Delete account (body: { id })\n- `GET /api/organization/account/{id}` - Get account details\n\n**Account Schema:**\n- `id` - Primary key\n- `AccountName` - Account name\n- `AccountCode` - Account code\n- `AccountType` - Account type\n\n**Sites:**\n- `GET /api/organization/site` - List sites\n- `POST /api/organization/site` - Create site\n- `PATCH /api/organization/site` - Update site (body: { id, SiteName, SiteCode, AccountID })\n- `DELETE /api/organization/site` - Delete site (body: { id })\n- `GET /api/organization/site/{id}` - Get site details\n\n**Site Schema:**\n- `id` - Primary key\n- `SiteName` - Site name\n- `SiteCode` - Site code\n- `AccountID` - Reference to account\n\n**Disciplines:**\n- `GET /api/organization/discipline` - List disciplines\n- `POST /api/organization/discipline` - Create discipline\n- `PATCH /api/organization/discipline` - Update discipline (body: { id, DisciplineName, DisciplineCode })\n- `DELETE /api/organization/discipline` - Delete discipline (body: { id })\n- `GET /api/organization/discipline/{id}` - Get discipline details\n\n**Discipline Schema:**\n- `id` - Primary key\n- `DisciplineName` - Discipline name\n- `DisciplineCode` - Discipline code\n\n**Departments:**\n- `GET /api/organization/department` - List departments\n- `POST /api/organization/department` - Create department\n- `PATCH /api/organization/department` - Update department (body: { id, DeptName, DeptCode, SiteID })\n- `DELETE /api/organization/department` - Delete department (body: { id })\n- `GET /api/organization/department/{id}` - Get department details\n\n**Department Schema:**\n- `id` - Primary key\n- `DeptName` - Department name\n- `DeptCode` - Department code\n- `SiteID` - Reference to site\n\n**Workstations:**\n- `GET /api/organization/workstation` - List workstations\n- `POST /api/organization/workstation` - Create workstation\n- `PATCH /api/organization/workstation` - Update workstation (body: { id, WorkstationName, WorkstationCode, SiteID, DepartmentID })\n- `DELETE /api/organization/workstation` - Delete workstation (body: { id })\n- `GET /api/organization/workstation/{id}` - Get workstation details\n\n**Workstation Schema:**\n- `id` - Primary key\n- `WorkstationName` - Workstation name\n- `WorkstationCode` - Workstation code\n- `SiteID` - Reference to site\n- `DepartmentID` - Reference to department - ---- - -### Phase 10: Edge API (Instrument Integration) -**Priority:** Low | **Time Estimate:** 2-3 hours - -- [ ] Edge results viewer -- [ ] Pending orders for instruments -- [ ] Instrument status monitoring - -**API Endpoints:** -- `GET /api/edge/orders` - Fetch pending orders (filter by instrument_id, status) -- `POST /api/edge/orders/{orderId}/ack` - Acknowledge order delivery -- `POST /api/edge/results` - Receive results from instrument (tiny-edge) -- `POST /api/edge/status` - Log instrument status (status: online, offline, error, maintenance) - ---- - -### Phase 11: Authentication -**Priority:** High | **Time Estimate:** 1-2 hours | **Status:** ✅ COMPLETED - -Foundation authentication system for user login/logout and session management. - -- [x] Login page with form validation -- [x] JWT token handling (HTTP-only cookie) -- [x] Logout functionality -- [x] Auth state management -- [x] Protected route guards -- [x] Auth status checking - -**API Endpoints:** - -**V1 Authentication:** -- `POST /api/auth/login` - User login (returns JWT in cookie) -- `POST /api/auth/logout` - User logout -- `GET /api/auth/check` - Check authentication status -- `POST /api/auth/register` - Register new user -- `POST /api/auth/change_pass` - Change password (authenticated) - -**V2 Authentication:** -- `POST /v2/auth/login` - V2 User login -- `POST /v2/auth/logout` - V2 User logout -- `GET /v2/auth/check` - V2 Check authentication -- `POST /v2/auth/register` - V2 Register new user - -**Security:** -- JWT tokens stored in HTTP-only cookies -- Bearer token support for API requests -- Automatic redirect to login on 401 responses - ---- - -## Reusable Components to Build - -### 1. FormInput.svelte -Text input with label, validation, and error display. - -**Status:** Not yet implemented - using native inputs with DaisyUI styling - -```svelte - -``` - -### 2. SelectDropdown.svelte ✅ COMPLETED -Dropdown populated from ValueSets or API data. - -```svelte - -``` - -### 3. DataTable.svelte ✅ COMPLETED -Sortable, paginated table with actions. - -```svelte - -``` - -### 4. SearchBar.svelte -Search input with debounce. - -**Status:** Not yet implemented - -### 5. Modal.svelte ✅ COMPLETED -Reusable modal for confirmations and forms. - ---- - -## Code Patterns - -### API Client Pattern -```javascript -// lib/api/client.js -const API_BASE = import.meta.env.VITE_API_URL || 'http://localhost/clqms01'; - -export async function apiFetch(endpoint, options = {}) { - const token = get(authToken); - - const res = await fetch(`${API_BASE}${endpoint}`, { - ...options, - headers: { - 'Content-Type': 'application/json', - ...(token && { 'Authorization': `Bearer ${token}` }), - ...options.headers - } - }); - - if (res.status === 401) { - // Handle unauthorized - authToken.set(null); - goto('/login'); - return; - } - - return res.json(); -} -``` - -### Form Handling Pattern -```svelte - - -
- - - -``` - -### Store Pattern for ValueSets -```javascript -// lib/stores/valuesets.js -import { writable } from 'svelte/store'; - -export const valueSets = writable({}); - -export async function loadValueSet(key) { - const res = await fetch(`/api/valueset/${key}`); - const data = await res.json(); - - valueSets.update(vs => ({ - ...vs, - [key]: data.data?.Items || [] - })); -} - -// Usage in component: -// onMount(() => loadValueSet('GENDER')); -``` - ---- - -## Environment Setup - -Create `.env` file: -``` -VITE_API_URL=http://localhost/clqms01 -``` - ---- - -## Getting Started - -1. **Initialize SvelteKit:** - ```bash - npm create svelte@latest clqms-fe - cd clqms-fe - npm install - npx svelte-add@latest tailwindcss - npm install - ``` - -2. **Configure tailwind.config.js:** - ```javascript - export default { - content: ['./src/**/*.{html,js,svelte,ts}'], - theme: { extend: {} }, - plugins: [] - }; - ``` - -3. **Start with Phase 0:** - - Create API client - - Set up auth stores - - Build login page - - Create protected layout - ---- - -## Navigation Structure - -``` -Dashboard (Home) -├── Master Data -│ ├── System ValueSets (read-only from JSON) -│ ├── Locations -│ ├── Contacts -│ ├── Occupations -│ ├── Medical Specialties -│ ├── Counters -│ └── Geography (Provinces/Cities) -├── Organization -│ ├── Accounts -│ ├── Sites -│ ├── Disciplines -│ ├── Departments -│ └── Workstations -├── Patients -│ ├── All Patients -│ └── Patient Visits -├── Laboratory -│ ├── Specimens -│ ├── Tests -│ ├── Orders -│ ├── Results -│ └── User ValueSets (CRUD in database) -└── Edge - └── Instrument Status -``` - ---- - -## Notes - -- Use `+layout.server.js` for server-side auth checks -- Use SvelteKit's `invalidateAll()` after mutations -- Cache ValueSets in localStorage for better UX -- Implement optimistic updates where appropriate -- Use loading states for all async operations -- Add toast notifications for success/error feedback - ---- - -## API Reference Summary - -### Authentication -| Method | Endpoint | Description | -|--------|----------|-------------| -| POST | `/api/auth/login` | User login (JWT in cookie) | -| POST | `/api/auth/logout` | User logout | -| GET | `/api/auth/check` | Check auth status | -| POST | `/api/auth/register` | Register new user | -| POST | `/api/auth/change_pass` | Change password | -| POST | `/v2/auth/login` | V2 Login | -| POST | `/v2/auth/logout` | V2 Logout | -| GET | `/v2/auth/check` | V2 Auth check | -| POST | `/v2/auth/register` | V2 Register | - -### ValueSets (Library/System) -| Method | Endpoint | Description | -|--------|----------|-------------| -| GET | `/api/valueset` | List all library value sets | -| GET | `/api/valueset/{key}` | Get value set by key | -| POST | `/api/valueset/refresh` | Refresh cache from JSON | - -### ValueSets (User-defined) -| Method | Endpoint | Description | -|--------|----------|-------------| -| GET | `/api/valueset/user/def` | List user value set definitions | -| POST | `/api/valueset/user/def` | Create definition | -| GET | `/api/valueset/user/def/{id}` | Get definition by ID | -| PUT | `/api/valueset/user/def/{id}` | Update definition | -| DELETE | `/api/valueset/user/def/{id}` | Delete definition | -| GET | `/api/valueset/user/items` | List items (filter: VSetID) | -| POST | `/api/valueset/user/items` | Create item | -| GET | `/api/valueset/user/items/{id}` | Get item by ID | -| PUT | `/api/valueset/user/items/{id}` | Update item | -| DELETE | `/api/valueset/user/items/{id}` | Delete item | - -### Master Data - Locations -| Method | Endpoint | Description | -|--------|----------|-------------| -| GET | `/api/location` | List locations (query: LocCode, LocName) | -| POST | `/api/location` | Create location | -| PATCH | `/api/location` | Update location | -| DELETE | `/api/location` | Delete location | -| GET | `/api/location/{id}` | Get location by ID | - -### Master Data - Contacts -| Method | Endpoint | Description | -|--------|----------|-------------| -| GET | `/api/contact` | List contacts | -| POST | `/api/contact` | Create contact | -| PATCH | `/api/contact` | Update contact | -| DELETE | `/api/contact` | Delete contact | -| GET | `/api/contact/{id}` | Get contact by ID | - -### Master Data - Supporting Entities -| Method | Endpoint | Description | -|--------|----------|-------------| -| GET | `/api/occupation` | List occupations | -| POST | `/api/occupation` | Create occupation | -| PATCH | `/api/occupation` | Update occupation | -| GET | `/api/occupation/{id}` | Get occupation by ID | -| GET | `/api/medicalspecialty` | List specialties | -| POST | `/api/medicalspecialty` | Create specialty | -| PATCH | `/api/medicalspecialty` | Update specialty | -| GET | `/api/medicalspecialty/{id}` | Get specialty by ID | -| GET | `/api/counter` | List counters | -| POST | `/api/counter` | Create counter | -| PATCH | `/api/counter` | Update counter | -| DELETE | `/api/counter` | Delete counter | -| GET | `/api/counter/{id}` | Get counter by ID | - -### Master Data - Geography (Read-only) -| Method | Endpoint | Description | -|--------|----------|-------------| -| GET | `/api/areageo` | List all geographical areas | -| GET | `/api/areageo/provinces` | List provinces | -| GET | `/api/areageo/cities` | List cities (query: province_id) | - -### Patients -| Method | Endpoint | Description | -|--------|----------|-------------| -| GET | `/api/patient` | List patients (query: page, perPage, Name, etc.) | -| POST | `/api/patient` | Create patient | -| PATCH | `/api/patient` | Update patient | -| DELETE | `/api/patient` | Delete patient (soft delete) | -| GET | `/api/patient/{id}` | Get patient by ID | -| GET | `/api/patient/check` | Check if patient exists | - -### Patient Visits -| Method | Endpoint | Description | -|--------|----------|-------------| -| GET | `/api/patvisit` | List visits (query: page, perPage) | -| POST | `/api/patvisit` | Create visit | -| PATCH | `/api/patvisit` | Update visit | -| DELETE | `/api/patvisit` | Delete visit | -| GET | `/api/patvisit/{id}` | Get visit by PVID | -| GET | `/api/patvisit/patient/{patientId}` | Get visits by patient | -| POST | `/api/patvisitadt` | Create ADT visit | -| PATCH | `/api/patvisitadt` | Update ADT visit | - -### Organization - Accounts -| Method | Endpoint | Description | -|--------|----------|-------------| -| GET | `/api/organization/account` | List accounts | -| POST | `/api/organization/account` | Create account | -| PATCH | `/api/organization/account` | Update account | -| DELETE | `/api/organization/account` | Delete account | -| GET | `/api/organization/account/{id}` | Get account by ID | - -### Organization - Sites -| Method | Endpoint | Description | -|--------|----------|-------------| -| GET | `/api/organization/site` | List sites | -| POST | `/api/organization/site` | Create site | -| PATCH | `/api/organization/site` | Update site | -| DELETE | `/api/organization/site` | Delete site | -| GET | `/api/organization/site/{id}` | Get site by ID | - -### Organization - Disciplines -| Method | Endpoint | Description | -|--------|----------|-------------| -| GET | `/api/organization/discipline` | List disciplines | -| POST | `/api/organization/discipline` | Create discipline | -| PATCH | `/api/organization/discipline` | Update discipline | -| DELETE | `/api/organization/discipline` | Delete discipline | -| GET | `/api/organization/discipline/{id}` | Get discipline by ID | - -### Organization - Departments -| Method | Endpoint | Description | -|--------|----------|-------------| -| GET | `/api/organization/department` | List departments | -| POST | `/api/organization/department` | Create department | -| PATCH | `/api/organization/department` | Update department | -| DELETE | `/api/organization/department` | Delete department | -| GET | `/api/organization/department/{id}` | Get department by ID | - -### Organization - Workstations -| Method | Endpoint | Description | -|--------|----------|-------------| -| GET | `/api/organization/workstation` | List workstations | -| POST | `/api/organization/workstation` | Create workstation | -| PATCH | `/api/organization/workstation` | Update workstation | -| DELETE | `/api/organization/workstation` | Delete workstation | -| GET | `/api/organization/workstation/{id}` | Get workstation by ID | - -### Specimen Management -| Method | Endpoint | Description | -|--------|----------|-------------| -| GET | `/api/specimen` | List specimens | -| POST | `/api/specimen` | Create specimen | -| PATCH | `/api/specimen` | Update specimen | -| GET | `/api/specimen/{id}` | Get specimen by ID | -| GET | `/api/specimen/container` | List containers | -| POST | `/api/specimen/container` | Create container | -| PATCH | `/api/specimen/container` | Update container | -| GET | `/api/specimen/container/{id}` | Get container by ID | -| GET | `/api/specimen/prep` | List preparations | -| POST | `/api/specimen/prep` | Create preparation | -| PATCH | `/api/specimen/prep` | Update preparation | -| GET | `/api/specimen/prep/{id}` | Get preparation by ID | -| GET | `/api/specimen/status` | List statuses | -| POST | `/api/specimen/status` | Create status | -| PATCH | `/api/specimen/status` | Update status | -| GET | `/api/specimen/status/{id}` | Get status by ID | -| GET | `/api/specimen/collection` | List collection methods | -| POST | `/api/specimen/collection` | Create collection method | -| PATCH | `/api/specimen/collection` | Update collection method | -| GET | `/api/specimen/collection/{id}` | Get collection method by ID | -| GET | `/api/sample` | Get samples | - -### Test Catalog -| Method | Endpoint | Description | -|--------|----------|-------------| -| GET | `/api/tests` | List tests (query: TestType, SiteID, etc.) | -| POST | `/api/tests` | Create test | -| PATCH | `/api/tests` | Update test | -| GET | `/api/tests/{id}` | Get test by ID | - -### Orders -| Method | Endpoint | Description | -|--------|----------|-------------| -| GET | `/api/ordertest` | List orders (query: OrderStatus, InternalPID) | -| POST | `/api/ordertest` | Create order | -| PATCH | `/api/ordertest` | Update order | -| DELETE | `/api/ordertest` | Delete order | -| GET | `/api/ordertest/{id}` | Get order by ID | -| POST | `/api/ordertest/status` | Update order status | - -### Results & Dashboard -| Method | Endpoint | Description | -|--------|----------|-------------| -| GET | `/api/dashboard` | Get dashboard summary | -| GET | `/api/result` | Get patient results (query: PatientID, page) | - -### Edge API (Instrument Integration) -| Method | Endpoint | Description | -|--------|----------|-------------| -| GET | `/api/edge/orders` | Fetch pending orders | -| POST | `/api/edge/orders/{orderId}/ack` | Acknowledge order | -| POST | `/api/edge/results` | Receive instrument results | -| POST | `/api/edge/status` | Log instrument status | - diff --git a/package.json b/package.json index ead458e..74470e5 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ }, "devDependencies": { "@sveltejs/adapter-auto": "^7.0.0", + "@sveltejs/adapter-static": "^3.0.10", "@sveltejs/kit": "^2.50.2", "@sveltejs/vite-plugin-svelte": "^6.2.4", "@tailwindcss/vite": "^4.1.18", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 20b28ae..2a8834d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15,6 +15,9 @@ importers: '@sveltejs/adapter-auto': specifier: ^7.0.0 version: 7.0.0(@sveltejs/kit@2.50.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.50.0)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)))(svelte@5.50.0)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2))) + '@sveltejs/adapter-static': + specifier: ^3.0.10 + version: 3.0.10(@sveltejs/kit@2.50.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.50.0)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)))(svelte@5.50.0)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2))) '@sveltejs/kit': specifier: ^2.50.2 version: 2.50.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.50.0)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)))(svelte@5.50.0)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)) @@ -371,6 +374,11 @@ packages: peerDependencies: '@sveltejs/kit': ^2.0.0 + '@sveltejs/adapter-static@3.0.10': + resolution: {integrity: sha512-7D9lYFWJmB7zxZyTE/qxjksvMqzMuYrrsyh1f4AlZqeZeACPRySjbC3aFiY55wb1tWUaKOQG9PVbm74JcN2Iew==} + peerDependencies: + '@sveltejs/kit': ^2.0.0 + '@sveltejs/kit@2.50.2': resolution: {integrity: sha512-875hTUkEbz+MyJIxWbQjfMaekqdmEKUUfR7JyKcpfMRZqcGyrO9Gd+iS1D/Dx8LpE5FEtutWGOtlAh4ReSAiOA==} engines: {node: '>=18.13'} @@ -1009,6 +1017,10 @@ snapshots: dependencies: '@sveltejs/kit': 2.50.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.50.0)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)))(svelte@5.50.0)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)) + '@sveltejs/adapter-static@3.0.10(@sveltejs/kit@2.50.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.50.0)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)))(svelte@5.50.0)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)))': + dependencies: + '@sveltejs/kit': 2.50.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.50.0)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)))(svelte@5.50.0)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)) + '@sveltejs/kit@2.50.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.50.0)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)))(svelte@5.50.0)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2))': dependencies: '@standard-schema/spec': 1.1.0 diff --git a/src/lib/api/geography.js b/src/lib/api/geography.js index 184948e..caf2920 100644 --- a/src/lib/api/geography.js +++ b/src/lib/api/geography.js @@ -9,6 +9,6 @@ export async function fetchProvinces() { } export async function fetchCities(provinceId = null) { - const query = provinceId ? `?province_id=${encodeURIComponent(provinceId)}` : ''; + const query = provinceId ? `?ProvinceID=${encodeURIComponent(provinceId)}` : ''; return get(`/api/areageo/cities${query}`); } diff --git a/src/lib/api/tests.js b/src/lib/api/tests.js index c95abf9..cd275b5 100644 --- a/src/lib/api/tests.js +++ b/src/lib/api/tests.js @@ -1,5 +1,14 @@ import { get, post, patch } from './client.js'; +/** + * Fetch tests list with optional filters, pagination, and search + * @param {Object} params - Query parameters + * @param {number} [params.page=1] - Page number + * @param {number} [params.perPage=20] - Items per page + * @param {string} [params.search] - Search by test code or name + * @param {string} [params.TestType] - Filter by test type (TEST, PARAM, CALC, GROUP, TITLE) + * @returns {Promise} API response with test data and pagination + */ export async function fetchTests(params = {}) { const query = new URLSearchParams(params).toString(); return get(query ? `/api/tests?${query}` : '/api/tests'); diff --git a/src/routes/(app)/master-data/tests/+page.svelte b/src/routes/(app)/master-data/tests/+page.svelte index c32f71e..fc26287 100644 --- a/src/routes/(app)/master-data/tests/+page.svelte +++ b/src/routes/(app)/master-data/tests/+page.svelte @@ -7,13 +7,19 @@ import Modal from '$lib/components/Modal.svelte'; import SelectDropdown from '$lib/components/SelectDropdown.svelte'; import HelpTooltip from '$lib/components/HelpTooltip.svelte'; - import { Plus, Edit2, Trash2, ArrowLeft, Filter, X, PlusCircle, Calculator, Ruler, FileText } from 'lucide-svelte'; + import { Plus, Edit2, Trash2, ArrowLeft, Filter, X, PlusCircle, Calculator, Ruler, FileText, Search } from 'lucide-svelte'; let loading = $state(false); let tests = $state([]); let disciplines = $state([]); let departments = $state([]); let modalOpen = $state(false); + + // Pagination state + let currentPage = $state(1); + let perPage = $state(20); + let totalItems = $state(0); + let totalPages = $state(1); let modalMode = $state('create'); let saving = $state(false); let activeTab = $state('basic'); // 'basic' or 'refrange' @@ -122,8 +128,9 @@ async function loadTests() { loading = true; try { - const params = {}; + const params = { page: currentPage, perPage }; if (selectedType) params.TestType = selectedType; + if (searchQuery.trim()) params.search = searchQuery.trim(); const response = await fetchTests(params); let allTests = Array.isArray(response.data) ? response.data : []; @@ -131,16 +138,16 @@ // Filter only active tests (soft delete support) allTests = allTests.filter(test => test.IsActive !== '0' && test.IsActive !== 0 && test.IsActive !== false); - // Client-side search by code or name - if (searchQuery.trim()) { - const query = searchQuery.toLowerCase().trim(); - allTests = allTests.filter(test => - (test.TestSiteCode && test.TestSiteCode.toLowerCase().includes(query)) || - (test.TestSiteName && test.TestSiteName.toLowerCase().includes(query)) - ); - } - tests = allTests; + + // Handle pagination response + if (response.pagination) { + totalItems = response.pagination.total || 0; + totalPages = Math.ceil(totalItems / perPage) || 1; + } else if (response.total) { + totalItems = response.total || 0; + totalPages = Math.ceil(totalItems / perPage) || 1; + } } catch (err) { toastError(err.message || 'Failed to load tests'); tests = []; @@ -397,15 +404,23 @@ } function handleFilter() { + currentPage = 1; loadTests(); } - function handleSearchKeydown(event) { - if (event.key === 'Enter') { + function handleSearch() { + currentPage = 1; + loadTests(); + } + + function handlePageChange(newPage) { + if (newPage >= 1 && newPage <= totalPages) { + currentPage = newPage; loadTests(); } } + const disciplineOptions = $derived( disciplines.map(d => ({ value: d.DisciplineID, label: d.DisciplineName })) ); @@ -444,14 +459,15 @@
-
+
e.key === 'Enter' && handleSearch()} /> +