commit bae48fab2992c9cd8e6845becdaf48a1fe2572c5 Author: mahdahar <89adham@gmail.com> Date: Sun Feb 8 17:39:53 2026 +0700 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bc9339d --- /dev/null +++ b/.gitignore @@ -0,0 +1,25 @@ +node_modules + +# Output +.output +.vercel +.netlify +.wrangler +/.svelte-kit +/build + +# OS +.DS_Store +Thumbs.db + +# Env +.env +.env.* +!.env.example +!.env.test + +# Vite +vite.config.js.timestamp-* +vite.config.ts.timestamp-* + +/.serena \ No newline at end of file diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..b6f27f1 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +engine-strict=true diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..a7ef3b4 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,162 @@ +# CLQMS Frontend - Agent Guidelines + +## Build Commands + +```bash +# Development +pnpm dev # Start dev server (http://localhost:5173) +# Production +pnpm build # Build for production (outputs to build/) +pnpm preview # Preview production build locally +# Type Checking +pnpm check # Run svelte-check TypeScript validation +pnpm check:watch # Run type checker in watch mode +# Package Management +pnpm install # Install dependencies +pnpm prepare # Sync SvelteKit (runs automatically) +``` + +## Code Style Guidelines + +### TypeScript & Svelte +- Use TypeScript strict mode (configured in tsconfig.json) +- Prefer explicit types over `any` +- Use Svelte 5 runes: `$state`, `$derived`, `$effect`, `$props` +- Components use `.svelte` extension with ` +{title} + +
+``` + +### Styling (Tailwind + DaisyUI) +- Use Tailwind utility classes +- DaisyUI components: `btn`, `card`, `input`, `modal`, etc. +- Theme: Forest (medical green primary: #2d6a4f) +- Color utilities: `text-emerald-600`, `bg-emerald-100` +- Responsive: `sm:`, `md:`, `lg:` prefixes +- Spacing: 4px base unit (e.g., `p-4` = 16px) + +### Error Handling +```typescript +import { api } from '$server/api'; +async function fetchData() { + const { data, error, success } = await api.get('/api/patients'); + if (!success || error) { + console.error('Failed to fetch:', error?.message); + return; + } + // Use data +} +``` + +### Form Handling (Superforms + Zod) +```typescript +import { superForm } from 'sveltekit-superforms'; +import { zodClient } from 'sveltekit-superforms/adapters'; +import { z } from 'zod'; +const schema = z.object({ + username: z.string().min(1, 'Required'), + email: z.string().email('Invalid email') +}); +const { form, errors, enhance } = superForm(data?.form, { + validators: zodClient(schema) +}); +``` + +### State Management +```typescript +import { writable } from 'svelte/store'; +interface AuthState { + user: User | null; + isAuthenticated: boolean; +} +function createAuthStore() { + const { subscribe, set, update } = writable({ + user: null, + isAuthenticated: false + }); + return { + subscribe, + login: (user: User) => update(s => ({ ...s, user, isAuthenticated: true })), + logout: () => set({ user: null, isAuthenticated: false }) + }; +} +export const auth = createAuthStore(); +``` + +### Route Structure +``` +src/routes/ +├── +layout.svelte # Root layout +├── +page.svelte # Landing page (redirects) +├── (app)/ # Group: Protected routes +│ ├── +layout.svelte # App layout with sidebar +│ └── dashboard/ +│ └── +page.svelte +└── (auth)/ # Group: Public routes + ├── +layout.svelte # Auth layout (centered) + └── login/ + └── +page.svelte +``` + +### API Client Usage +```typescript +import { api } from '$server/api'; +// GET +const { data } = await api.get('/api/users'); +// POST +const { data } = await api.post('/api/users', { name: 'John' }); +// PATCH +const { data } = await api.patch('/api/users/1', { name: 'Jane' }); +// DELETE +const { data } = await api.delete('/api/users/1'); +``` + +### Environment Variables +```typescript +// Use only PUBLIC_ prefix for client-side +import { PUBLIC_API_BASE_URL } from '$env/static/public'; +``` + +### Git Workflow +- Commit messages: imperative mood (e.g., "Add patient form validation") +- Branch naming: `feature/`, `fix/`, `refactor/` prefixes +- Never commit secrets or `.env.local` + +## Testing (To Be Configured) +Test framework not yet configured. When added: +- Unit tests: Vitest (planned per checklist) +- E2E tests: Playwright (planned per checklist) diff --git a/CLQMS-CHECKLIST.md b/CLQMS-CHECKLIST.md new file mode 100644 index 0000000..01ffb16 --- /dev/null +++ b/CLQMS-CHECKLIST.md @@ -0,0 +1,734 @@ +# CLQMS Frontend Development Checklist + +> **Framework:** SvelteKit +> **Key Change:** Value Sets moved to Phase 3 (from Phase 10) - needed for dropdowns in all forms + +## Phase 1: Project Setup + +- [x] Initialize project with SvelteKit +- [x] Install and configure TypeScript +- [ ] Set up ESLint and Prettier +- [x] Create SvelteKit folder structure (`src/routes/`, `src/lib/components/`, `src/lib/stores/`, `src/lib/server/`) +- [x] Configure SvelteKit adapter (auto/vercel/node for deployment target) +- [x] Set up API client using SvelteKit's fetch or axios wrapper +- [x] Configure handle hook for auth middleware (`src/hooks.server.ts`) +- [x] Set up Svelte stores for state management +- [x] Install UI component library or design system (Skeleton, Flowbite, or custom) +- [x] Create base layout components (`+layout.svelte` with Header, Sidebar, Footer) +- [x] Configure environment variables (`.env`, `.env.local`) +- [x] Set up build scripts (`vite build`, `vite preview`) +- [x] Configure form validation library (Superforms with Zod) +- [ ] Configure CI/CD pipeline (GitHub Actions/GitLab CI) +- [x] Create README with setup instructions + +**Acceptance Criteria:** +- [x] Project builds without errors (`npm run build`) +- [ ] Linting and formatting configured +- [ ] API client successfully connects to backend +- [x] SvelteKit file-based routing works +- [x] Handle hook properly intercepts requests + +--- + +## Phase 2: Authentication Module + +- [x] Create login page component +- [x] Implement login form with username/password fields +- [x] Add form validation (required fields) +- [ ] Implement login API call to `/api/auth/login` +- [ ] Handle JWT token from HTTP-only cookie +- [ ] Create logout functionality (call `/api/auth/logout`) +- [ ] Implement auth state management (store user data, auth status) +- [ ] Create protected route using SvelteKit `+page.server.ts` load functions or `+layout.server.ts` +- [ ] Implement auth check using SvelteKit hooks (`src/hooks.server.ts`) +- [ ] Handle 401 errors globally (redirect to login) +- [ ] Create password change form (current password, new password) +- [ ] Implement password change API call to `/api/auth/change_pass` +- [ ] Add loading states for auth operations +- [ ] Add error messages for failed login attempts +- [ ] Create registration page (optional, if needed) + +**Acceptance Criteria:** +- User can login successfully +- JWT token is stored/managed correctly +- Protected routes redirect unauthenticated users +- Logout clears auth state and token +- Password change works with proper validation + +--- + +## Phase 4: Organization Module + +### Accounts +- [ ] Create accounts list page +- [ ] Implement account creation form +- [ ] Implement account edit form +- [ ] Implement account delete with confirmation +- [ ] Add search/filter for accounts + +### Sites +- [ ] Create sites list page (filtered by selected account) +- [ ] Implement site creation form (with AccountID) +- [ ] Implement site edit form +- [ ] Implement site delete with confirmation +- [ ] Add site search/filter + +### Disciplines +- [ ] Create disciplines list page +- [ ] Implement discipline creation form +- [ ] Implement discipline edit form +- [ ] Implement discipline delete with confirmation +- [ ] Add discipline search/filter + +### Departments +- [ ] Create departments list page (filtered by site) +- [ ] Implement department creation form (with SiteID) +- [ ] Implement department edit form +- [ ] Implement department delete with confirmation +- [ ] Add department search/filter + +### Workstations +- [ ] Create workstations list page (filtered by department) +- [ ] Implement workstation creation form (with SiteID, DepartmentID) +- [ ] Implement workstation edit form +- [ ] Implement workstation delete with confirmation +- [ ] Add workstation search/filter + +### Hierarchy Navigation +- [ ] Create organization tree view component +- [ ] Implement breadcrumb navigation +- [ ] Add filters to cascade (Account → Site → Department → Workstation) + +**Acceptance Criteria:** +- All CRUD operations work for each entity +- Filtering and cascading selection works +- Delete operations have confirmation dialogs +- List pages support pagination + +--- + +## Phase 5: Master Data Module + +### Locations +- [ ] Create locations list page +- [ ] Implement location creation/edit form +- [ ] Implement location delete with confirmation +- [ ] Add search/filter by location code, name, type + +### Contacts +- [ ] Create contacts list page +- [ ] Implement contact creation/edit form +- [ ] Implement contact delete with confirmation +- [ ] Add search/filter by name, type, specialty + +### Occupations +- [ ] Create occupations list page +- [ ] Implement occupation creation/edit form +- [ ] Implement occupation delete with confirmation + +### Medical Specialties +- [ ] Create medical specialties list page +- [ ] Implement specialty creation/edit form +- [ ] Implement specialty delete with confirmation + +### Counters +- [ ] Create counters list page +- [ ] Implement counter creation/edit form +- [ ] Implement counter delete with confirmation +- [ ] Add ability to reset counter value + +### Geographical Areas +- [ ] Create provinces list page (API: `/api/areageo/provinces`) +- [ ] Create cities list page (API: `/api/areageo/cities` with province_id filter) +- [ ] Create province → city dropdown components +- [ ] Add caching for geographical data + +**Acceptance Criteria:** +- All master data CRUD operations work +- Province/city dropdowns cascade correctly +- Search and filtering works across all entities +- Geographical data is cached appropriately + +--- + +## Phase 6: Patient Management Module + +### Patient List & Search +- [ ] Create patients list page +- [ ] Implement search by PatientID or name (API parameter) +- [ ] Add pagination controls +- [ ] Implement patient detail view (click to view) +- [ ] Add quick actions (view, edit, delete) + +### Patient Registration +- [ ] Create patient registration form with all fields +- [ ] Implement field validation: + - [ ] PatientID: alphanumeric, max 30 chars, pattern validation + - [ ] Sex: required, dropdown (1=Female, 2=Male) + - [ ] NameFirst: required, 1-60 chars, letters/spaces/periods + - [ ] Birthdate: required, date-time picker + - [ ] Email: format validation + - [ ] Phone/Mobile: regex validation (8-15 digits, optional +) + - [ ] ZIP: numeric only, max 10 chars +- [ ] Add optional fields (NameMiddle, NameMaiden, NameLast, etc.) +- [ ] Add identifier section (IdentifierType, Identifier) +- [ ] Implement patient check existence (API: `/api/patient/check`) +- [ ] Add duplicate patient warning if exists + +### Patient Edit +- [ ] Create patient edit form (same as registration) +- [ ] Pre-fill with existing patient data +- [ ] Implement PATCH API call +- [ ] Add save confirmation + +### Patient Delete +- [ ] Implement delete confirmation dialog +- [ ] Call DELETE API with PatientID +- [ ] Show success/error feedback + +### Patient Visits +- [ ] Create visits list page +- [ ] Filter visits by PatientID +- [ ] Implement visit creation form +- [ ] Implement visit edit form +- [ ] Implement visit delete with confirmation +- [ ] Add visit detail view + +### ADT Events +- [ ] Create ADT event form +- [ ] Implement ADT event types: + - [ ] A01: Admit + - [ ] A02: Transfer + - [ ] A03: Discharge + - [ ] A04: Register + - [ ] A08: Update +- [ ] Add validation for ADT events +- [ ] Call `/api/patvisitadt` endpoint + +**Acceptance Criteria:** +- Patient CRUD operations work with proper validation +- Search finds patients by ID or name +- Duplicate patient check prevents duplicates +- Visit management works correctly +- ADT events are handled properly + +--- + +## Phase 7: Test Catalog Module + +### Test Definitions +- [ ] Create test definitions list page +- [ ] Add filter by TestType (TEST, PARAM, CALC, GROUP, TITLE) +- [ ] Add filter by DisciplineID +- [ ] Add filter by DepartmentID +- [ ] Add pagination +- [ ] Implement test detail view +- [ ] Display test metadata (code, name, unit, formula, specimen type) + +### Test Browser +- [ ] Create test browser/search component +- [ ] Add search by TestCode or TestName +- [ ] Add filter by SpecimenType +- [ ] Add multi-select functionality (for ordering) +- [ ] Display test details on hover/click + +### Panels & Groups +- [ ] Identify and display test groups (TestType = GROUP) +- [ ] Expand/collapse group functionality +- [ ] Show tests within groups +- [ ] Add section headers (TestType = TITLE) + +### Test Mapping +- [ ] Create test mapping view (if needed for admin) +- [ ] Display HostCode ↔ ClientCode mappings +- [ ] Filter by HostType or ClientType + +**Acceptance Criteria:** +- Test catalog browsable with filters +- Multi-select works for test ordering +- Groups and panels display correctly +- Search returns relevant results + +--- + +## Phase 8: Specimen Module + +### Specimen Management +- [ ] Create specimens list page +- [ ] Add filter by SpecimenStatus +- [ ] Add filter by PatientID +- [ ] Add filter by SpecimenType +- [ ] Implement specimen creation form +- [ ] Implement specimen edit form +- [ ] Implement specimen status update +- [ ] Add specimen detail view + +### Container Definitions +- [ ] Create container definitions list page +- [ ] Implement container creation form (code, name, category, size, cap color) +- [ ] Implement container edit form +- [ ] Implement container delete with confirmation +- [ ] Display container metadata in specimen view + +### Specimen Preparations +- [ ] Create preparations list page +- [ ] Implement preparation creation form +- [ ] Implement preparation edit form +- [ ] Implement preparation delete + +### Specimen Statuses +- [ ] Create specimen statuses list page +- [ ] Implement status creation form +- [ ] Implement status edit form +- [ ] Display status activity labels + +### Collection Methods +- [ ] Create collection methods list page +- [ ] Implement method creation form (code, name, method, additive, role) +- [ ] Implement method edit form +- [ ] Display method metadata + +### Specimen Tracking +- [ ] Create specimen timeline/status view +- [ ] Visual status indicators (color-coded) +- [ ] History log display (if available) +- [ ] Barcode display (SpecimenID) + +**Acceptance Criteria:** +- Specimen CRUD operations work +- Containers, preparations, statuses, methods manageable +- Specimen status tracking is visible +- Filters work correctly + +--- + +## Phase 9: Orders Module + +### Orders List +- [ ] Create orders list page +- [ ] Add filter by OrderStatus (pending, in-progress, completed, cancelled) +- [ ] Add filter by PatientID +- [ ] Add filter by date range +- [ ] Add pagination +- [ ] Display order priority (color-coded: stat=red, urgent=orange, routine=gray) +- [ ] Display order status badges +- [ ] Add quick actions (view, edit, delete) + +### Order Creation +- [ ] Create order creation form +- [ ] Patient selection (search by PatientID) +- [ ] Visit selection (optional, dropdown) +- [ ] Priority selection (routine, stat, urgent) +- [ ] Site selection (dropdown) +- [ ] Requesting physician (text input) +- [ ] Test selection (multi-select from test catalog) +- [ ] Specimen type selection per test (if needed) +- [ ] Validate required fields (PatientID, Tests) +- [ ] Call POST `/api/ordertest` + +### Order Detail View +- [ ] Display order information (OrderID, PatientID, VisitID, Date, Status, Priority) +- [ ] Display ordered tests with specimen types +- [ ] Display current status +- [ ] Add edit button +- [ ] Add delete button +- [ ] Add status change action + +### Order Status Update +- [ ] Implement status change dropdown +- [ ] Call POST `/api/ordertest/status` with OrderID and OrderStatus +- [ ] Update local state on success +- [ ] Add confirmation for status changes + +### Order Cancellation +- [ ] Add cancel button +- [ ] Show confirmation dialog +- [ ] Update status to 'cancelled' +- [ ] Show success/error feedback + +### Order Deletion +- [ ] Implement delete confirmation +- [ ] Call DELETE `/api/ordertest` +- [ ] Show success/error feedback + +**Acceptance Criteria:** +- Order creation works with all required fields +- Order list filters work +- Priority and status display correctly +- Status updates work +- Cancellation and deletion work with confirmation + +--- + +## Phase 10: Results Module + +### Results List +- [ ] Create results list page +- [ ] Add filter by PatientID +- [ ] Add filter by date range +- [ ] Add pagination +- [ ] Display patient info with results +- [ ] Add click to view details + +### Results Detail View +- [ ] Display patient information +- [ ] Display order information +- [ ] Display test results in table format +- [ ] Show result values with units +- [ ] Display flags (H=High, L=Low, N=Normal, A=Abnormal) with color coding +- [ ] Highlight abnormal/critical results +- [ ] Add result reference ranges (if available) + +### Results Entry (Manual) +- [ ] Create results entry form +- [ ] Select order (from pending orders) +- [ ] Display tests for order +- [ ] Input result values for each test +- [ ] Validate result values (numeric for quantitative tests) +- [ ] Auto-calculate flags (H/L/N/A) if ranges available +- [ ] Add units display +- [ ] Add remarks/comment field +- [ ] Save results to API + +### Results Approval Workflow +- [ ] Add "Review" status for results +- [ ] Add "Approved" status +- [ ] Implement approve/reject actions (if required) +- [ ] Show reviewer info +- [ ] Lock approved results (if needed) + +### Critical Results Alerting +- [ ] Identify critical results (flagged as critical) +- [ ] Show prominent alerts/warnings +- [ ] Add "acknowledge" action for critical results +- [ ] Log critical result acknowledgments + +### Results Export/Print +- [ ] Add print results button +- [ ] Generate printable report format +- [ ] Add export to PDF +- [ ] Add export to CSV (if needed) + +**Acceptance Criteria:** +- Results display correctly with flags +- Abnormal results highlighted +- Manual entry works with validation +- Approval workflow works (if implemented) +- Critical results are prominent +- Print/export functions work + +--- + +## Phase 3: Value Sets Module + +### Value Set Definitions +- [ ] Create value sets list page +- [ ] Display VSetCode, VSetName, Description, Category +- [ ] Add search/filter by code or name +- [ ] Add refresh button (call `/api/valueset/refresh`) +- [ ] Show last refresh time + +### Value Set Items +- [ ] Create value set items page +- [ ] Filter by VSetID +- [ ] Display items in list/table (VValue, VLabel, VSeq, IsActive) +- [ ] Implement item creation form +- [ ] Implement item edit form +- [ ] Implement item delete with confirmation +- [ ] Add active/inactive toggle +- [ ] Implement sorting by VSeq + +### Dynamic Dropdowns +- [ ] Create reusable dropdown component using value sets +- [ ] Load dropdown options from `/api/valueset/items?VSetID=X` +- [ ] Cache dropdown options in store +- [ ] Implement refresh/reload dropdown data +- [ ] Use value sets for: + - [ ] Patient prefixes + - [ ] Patient marital status + - [ ] Specimen types + - [ ] Collection methods + - [ ] Specimen statuses + - [ ] Order priorities + - [ ] Order statuses + - [ ] Test types + - [ ] Any other configurable dropdowns + +### Value Set Caching +- [ ] Implement cache strategy for value sets +- [ ] Load value sets on app initialization +- [ ] Refresh cache when `/api/valueset/refresh` called +- [ ] Use stale-while-revalidate pattern for dropdowns + +**Acceptance Criteria:** +- Value sets display correctly +- Items CRUD works +- Refresh clears cache and reloads +- Dynamic dropdowns populate from value sets +- Caching reduces API calls + +--- + +## Phase 11: Dashboard Module + +### Dashboard Summary Cards +- [ ] Create dashboard page +- [ ] Display pending orders count (API: `/api/dashboard`) +- [ ] Display today's results count +- [ ] Display critical results count +- [ ] Display active patients count +- [ ] Add refresh button + +### Orders Trend Chart +- [ ] Create orders over time chart +- [ ] Filter by date range +- [ ] Group by status +- [ ] Use charting library (Chart.js/Recharts/etc.) + +### Results Volume Chart +- [ ] Create results volume chart +- [ ] Filter by date range +- [ ] Group by test type or department +- [ ] Use charting library + +### Real-time Status Indicators +- [ ] Implement polling or WebSocket for real-time updates +- [ ] Update dashboard metrics automatically +- [ ] Show last update time +- [ ] Add auto-refresh toggle (e.g., every 30 seconds) + +### Department Performance +- [ ] Display department-wise metrics +- [ ] Orders processed per department +- [ ] Average turnaround time +- [ ] Results volume per department + +**Acceptance Criteria:** +- Dashboard displays all summary metrics +- Charts render correctly +- Real-time updates work +- Filters apply to charts +- Performance metrics are accurate + +--- + +## Phase 12: Edge API Integration + +### Instrument Status Monitoring +- [ ] Create instrument status page +- [ ] Display instrument list with status +- [ ] Color-code status (online=green, offline=gray, error=red, maintenance=orange) +- [ ] Implement status polling or WebSocket +- [ ] Display last status update timestamp +- [ ] Display status message (if available) + +### Real-time Result Display +- [ ] Implement result streaming (WebSocket or SSE polling) +- [ ] Display incoming results as they arrive +- [ ] Parse EdgeResultRequest format (sample_id, instrument_id, results array) +- [ ] Display results in real-time feed +- [ ] Show test_code, result_value, unit, flags +- [ ] Add sound/notification for new results + +### Order Acknowledgment +- [ ] Create edge orders list (API: `/api/edge/orders`) +- [ ] Filter by instrument_id +- [ ] Filter by status (pending, acknowledged) +- [ ] Display orders for instruments +- [ ] Implement acknowledge action (POST `/api/edge/orders/{orderId}/ack`) +- [ ] Update status to acknowledged +- [ ] Show acknowledgment confirmation + +### Instrument Status Logging +- [ ] Create status log view +- [ ] Display status history per instrument +- [ ] Show timestamp, status, message +- [ ] Call `/api/edge/status` when receiving status updates + +### Result Queue Management +- [ ] Display queued results (EdgeResultResponse: edge_res_id) +- [ ] Show queue status +- [ ] Manual trigger for processing (if needed) + +**Acceptance Criteria:** +- Instrument status displays correctly +- Real-time results stream in +- Order acknowledgment works +- Status history is visible +- Result queue is manageable + +--- + +## Phase 13: Testing & QA + +### Unit Tests +- [ ] Set up testing framework (Jest, Vitest, etc.) +- [ ] Write unit tests for components +- [ ] Write unit tests for services/API calls +- [ ] Write unit tests for utility functions +- [ ] Write unit tests for state management +- [ ] Achieve >80% code coverage + +### Integration Tests +- [ ] Set up API mocking (MSW, Mirage, etc.) +- [ ] Write tests for authentication flow +- [ ] Write tests for patient CRUD operations +- [ ] Write tests for order creation flow +- [ ] Write tests for specimen tracking +- [ ] Write tests for results entry + +### E2E Tests (Critical Flows) +- [ ] Set up E2E testing (Cypress, Playwright) +- [ ] Test login → dashboard flow +- [ ] Test patient registration → order creation flow +- [ ] Test order → specimen → results flow +- [ ] Test critical results alerting +- [ ] Test logout + +### Form Validation Testing +- [ ] Test all required field validations +- [ ] Test format validations (email, phone, regex) +- [ ] Test duplicate detection +- [ ] Test error message display + +### Error Handling Testing +- [ ] Test 401 error handling (redirect to login) +- [ ] Test 404 error handling +- [ ] Test 500 error handling +- [ ] Test network error handling +- [ ] Test timeout handling + +### Performance Testing +- [ ] Measure page load times +- [ ] Measure list rendering with large datasets +- [ ] Optimize bundle size (code splitting, lazy loading) +- [ ] Test with slow network (3G) +- [ ] Audit with Lighthouse (score >90) + +### Accessibility Audit +- [ ] Test keyboard navigation +- [ ] Test screen reader compatibility +- [ ] Check color contrast ratios +- [ ] Ensure proper ARIA labels +- [ ] Test with accessibility tools (axe, WAVE) + +**Acceptance Criteria:** +- All tests pass +- Code coverage >80% +- E2E tests cover critical flows +- Lighthouse score >90 +- Accessibility audit passes + +--- + +## Phase 14: Deployment + +### Production Build +- [ ] Create production build +- [ ] Optimize assets (minify, compress) +- [ ] Generate source maps +- [ ] Verify build output +- [ ] Check bundle size + +### Environment Configuration +- [ ] Set up production environment variables +- [ ] Configure production API base URL +- [ ] Disable development tools +- [ ] Enable error tracking (Sentry, etc.) +- [ ] Configure analytics (if needed) + +### API Endpoint Verification +- [ ] Verify all endpoints are accessible +- [ ] Verify CORS configuration +- [ ] Test authentication flow with production backend +- [ ] Test with production data + +### Database/Storage (if needed) +- [ ] Configure any client-side storage (IndexedDB, localStorage) +- [ ] Set up data migration scripts (if needed) +- [ ] Test storage operations + +### Pre-deployment Checklist +- [ ] Run all tests and ensure they pass +- [ ] Review and merge all PRs +- [ ] Update version number +- [ ] Update CHANGELOG +- [ ] Create release notes + +### Deployment +- [ ] Deploy to staging environment +- [ ] Smoke test on staging +- [ ] Deploy to production +- [ ] Verify production deployment +- [ ] Monitor error logs +- [ ] Monitor performance metrics + +### Post-deployment +- [ ] Verify critical flows work in production +- [ ] Check user feedback +- [ ] Monitor for errors +- [ ] Rollback plan in case of issues + +**Acceptance Criteria:** +- Production build completes without errors +- Application works in production environment +- All critical features verified +- No critical errors in logs +- Performance meets targets + +--- + +## Notes + +### API Base URL +- Development: `http://localhost/clqms01/` +- Production: `https://clqms01-api.services-summit.my.id/` + +### Authentication +- JWT token stored in HTTP-only cookie +- Bearer token in Authorization header for API calls +- Check auth status on app load + +### Common Patterns +- All list pages support pagination +- All forms include validation +- All delete operations require confirmation +- All API calls include error handling +- Loading states for async operations + +### Dependencies Between Phases +- Phase 2 (Authentication) must be completed before protected routes +- Phase 3 (Value Sets) needed for dropdowns across all modules - **do this early!** +- Phase 4 (Organization) data needed for filtering in other modules +- Phase 6 (Patients) needed for Phase 9 (Orders) +- Phase 7 (Test Catalog) needed for Phase 9 (Orders) +- Phase 9 (Orders) needed for Phase 10 (Results) + +### Testing Priority +- E2E: Login → Dashboard, Patient → Order → Result flow +- Unit: All services, all components with logic +- Integration: API calls, auth flows + +--- + +## Completed Phases Tracker + +- [x] Phase 1: Project Setup +- [ ] Phase 2: Authentication Module +- [ ] Phase 3: Value Sets Module +- [ ] Phase 4: Organization Module +- [ ] Phase 5: Master Data Module +- [ ] Phase 6: Patient Management Module +- [ ] Phase 7: Test Catalog Module +- [ ] Phase 8: Specimen Module +- [ ] Phase 9: Orders Module +- [ ] Phase 10: Results Module +- [ ] Phase 11: Dashboard Module +- [ ] Phase 12: Edge API Integration +- [ ] Phase 13: Testing & QA +- [ ] Phase 14: Deployment + +--- + +**Last Updated:** 2026-02-08 +**Version:** 1.0.0 diff --git a/README.md b/README.md new file mode 100644 index 0000000..7fb9e7f --- /dev/null +++ b/README.md @@ -0,0 +1,108 @@ +# CLQMS Frontend - Phase 1 Complete + +## Project Setup Summary + +**Framework:** SvelteKit with TypeScript +**Styling:** Tailwind CSS v4 + DaisyUI v5 +**Package Manager:** pnpm +**Deployment:** Static adapter (SPA mode) + +## Quick Start + +```bash +# Install dependencies +pnpm install + +# Start development server +pnpm dev + +# Build for production +pnpm build + +# Preview production build +pnpm preview +``` + +## Project Structure + +``` +src/ +├── app.css # Global styles with DaisyUI theme +├── app.d.ts # TypeScript declarations +├── app.html # HTML template +├── hooks.server.ts # Server hooks (auth middleware) +├── lib/ +│ ├── components/ +│ │ ├── index.ts # Component exports +│ │ ├── layout/ +│ │ │ ├── Header.svelte # App header with user menu +│ │ │ └── Sidebar.svelte # Navigation sidebar +│ ├── server/ +│ │ └── api.ts # API client wrapper +│ ├── stores/ +│ │ ├── auth.ts # Auth state management +│ │ └── index.ts # Store exports +│ └── types/ # TypeScript types (to be added) +└── routes/ + ├── +layout.svelte # Root layout + ├── +page.svelte # Landing page (redirects to login) + ├── (app)/ # Protected app routes + │ ├── +layout.svelte # App layout with sidebar + │ └── dashboard/ + │ └── +page.svelte # Dashboard page + └── (auth)/ # Public auth routes + ├── +layout.svelte # Auth layout (no sidebar) + └── login/ + └── +page.svelte # Login page + +build/ # Production build output +``` + +## Configuration Files + +- `svelte.config.js` - SvelteKit config with static adapter +- `vite.config.ts` - Vite config with Tailwind plugin +- `tsconfig.json` - TypeScript configuration +- `.env` - Environment variables + +## Features Implemented + +- SvelteKit project initialized with TypeScript +- Tailwind CSS v4 + DaisyUI v5 configured +- Forest theme with medical green colors +- Static adapter for static hosting +- API client with fetch wrapper +- Auth store for state management +- Server hooks for auth middleware +- Login page (converted from template) +- Dashboard page with stats cards +- Sidebar navigation component +- Header component with user menu +- Responsive layout + +## Environment Variables + +Create a `.env.local` file for local overrides: + +```bash +PUBLIC_API_BASE_URL=http://localhost/clqms01/ +PUBLIC_API_BASE_URL_PROD=https://clqms01-api.services-summit.my.id/ +``` + +## Next Steps (Phase 2) + +1. Implement actual login API integration +2. Add JWT token handling +3. Create protected routes +4. Add logout functionality +5. Implement password change + +## Build Output + +The production build is generated in the `build/` folder and can be deployed to any static hosting service (Netlify, Vercel, GitHub Pages, etc.). + +## Notes + +- DaisyUI v5 uses Tailwind v4's new CSS-first configuration +- The forest theme uses medical green (#2d6a4f) as primary color +- Static adapter configured with fallback for SPA routing diff --git a/api-docs.yaml b/api-docs.yaml new file mode 100644 index 0000000..320a8f1 --- /dev/null +++ b/api-docs.yaml @@ -0,0 +1,2826 @@ +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: string + description: Province area code + City: + type: string + description: City area code + Country: + type: string + maxLength: 100 + 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: + VisitID: + type: string + PatientID: + type: string + VisitDate: + type: string + format: date-time + VisitType: + type: string + SiteID: + type: integer + LocationID: + type: integer + DepartmentID: + type: integer + AttendingPhysician: + type: string + ReferringPhysician: + type: string + + # 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 + DepartmentID: + type: integer + SpecimenType: + type: string + Unit: + type: string + Formula: + type: string + + 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 + description: Order status code + OrderStatusLabel: + type: string + description: Order status display text + Priority: + type: string + description: Priority code + 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 + ValueSetDef: + type: object + properties: + id: + type: integer + VSetCode: + type: string + VSetName: + type: string + Description: + type: string + Category: + type: string + + ValueSetItem: + type: object + properties: + id: + type: integer + VSetID: + type: integer + VValue: + type: string + VLabel: + type: string + VSeq: + type: integer + IsActive: + type: boolean + + # Master Data + Location: + type: object + properties: + id: + type: integer + LocationCode: + type: string + LocationName: + type: string + LocationType: + type: string + SiteID: + type: integer + + Contact: + type: object + properties: + id: + type: integer + ContactName: + type: string + ContactType: + type: string + Phone: + type: string + Email: + type: string + Address: + type: string + Specialty: + type: string + description: Specialty code + SpecialtyLabel: + type: string + description: Specialty display text + Occupation: + type: string + description: Occupation code + OccupationLabel: + type: string + description: Occupation display text + + Occupation: + type: object + properties: + id: + type: integer + OccupationCode: + type: string + OccupationName: + type: string + + MedicalSpecialty: + type: object + properties: + id: + type: integer + SpecialtyCode: + type: string + SpecialtyName: + type: string + + Counter: + type: object + properties: + id: + type: integer + CounterName: + type: string + Prefix: + type: string + CurrentValue: + type: integer + SiteID: + type: integer + + # 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: search + in: query + schema: + type: string + description: Search by PatientID or name + 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 + security: + - bearerAuth: [] + parameters: + - name: PatientID + in: query + required: true + schema: + type: string + responses: + '200': + description: Patient deleted successfully + + /api/patient/check: + get: + tags: [Patients] + summary: Check if patient exists + security: + - bearerAuth: [] + parameters: + - name: PatientID + in: query + required: true + schema: + type: string + 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 + data: + type: array + items: + $ref: '#/components/schemas/PatientVisit' + + post: + tags: [Patient Visits] + summary: Create patient visit + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/PatientVisit' + responses: + '201': + description: Visit created successfully + + patch: + tags: [Patient Visits] + summary: Update patient visit + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/PatientVisit' + responses: + '200': + description: Visit updated successfully + + 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 + responses: + '200': + description: Visit details + + /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: string + responses: + '200': + description: Patient visits list + + /api/patvisitadt: + post: + tags: [Patient Visits] + summary: Create ADT visit (Admission/Discharge/Transfer) + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + type: object + properties: + PatientID: + type: string + VisitType: + type: string + ADTEvent: + type: string + enum: [A01, A02, A03, A04, A08] + description: | + A01: Admit + A02: Transfer + A03: Discharge + A04: Register + A08: Update + responses: + '201': + description: ADT visit created + + patch: + tags: [Patient Visits] + summary: Update ADT visit + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/PatientVisit' + responses: + '200': + description: ADT visit updated + + # ======================================== + # Organization - Account Routes + # ======================================== + /api/organization/account: + get: + tags: [Organization] + summary: List accounts + security: + - bearerAuth: [] + responses: + '200': + description: List of accounts + content: + application/json: + schema: + type: object + properties: + status: + type: string + data: + type: array + items: + $ref: '#/components/schemas/Account' + + post: + tags: [Organization] + summary: Create account + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Account' + responses: + '201': + description: Account created + + patch: + tags: [Organization] + summary: Update account + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Account' + responses: + '200': + description: Account updated + + delete: + tags: [Organization] + summary: Delete account + security: + - bearerAuth: [] + responses: + '200': + description: Account deleted + + /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 + + # ======================================== + # 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: + $ref: '#/components/schemas/Site' + responses: + '200': + description: Site updated + + delete: + tags: [Organization] + summary: Delete site + security: + - bearerAuth: [] + 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: [] + responses: + '200': + description: Discipline updated + + delete: + tags: [Organization] + summary: Delete discipline + security: + - bearerAuth: [] + 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: [] + responses: + '200': + description: Department updated + + delete: + tags: [Organization] + summary: Delete department + security: + - bearerAuth: [] + 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: [] + responses: + '200': + description: Workstation updated + + delete: + tags: [Organization] + summary: Delete workstation + security: + - bearerAuth: [] + 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: [] + 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 + + /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: TestType + in: query + schema: + type: string + enum: [TEST, PARAM, CALC, GROUP, TITLE] + description: Filter by test type + 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: OrderStatus + in: query + schema: + type: string + enum: [pending, in-progress, completed, cancelled] + - name: PatientID + in: query + schema: + type: string + 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: [routine, stat, 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: [pending, in-progress, completed, cancelled] + 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 value set definitions + security: + - bearerAuth: [] + parameters: + - name: VSetCode + in: path + description: Optional value set code to get specific valueset + schema: + type: string + responses: + '200': + description: List of value sets + content: + application/json: + schema: + type: object + properties: + status: + type: string + data: + type: array + items: + $ref: '#/components/schemas/ValueSetDef' + + /api/valueset/refresh: + post: + tags: [ValueSets] + summary: Refresh ValueSet cache + description: Clear and reload the ValueSet library cache + security: + - bearerAuth: [] + responses: + '200': + description: Cache refreshed + + /api/valueset/items: + get: + tags: [ValueSets] + summary: List value set items + security: + - bearerAuth: [] + parameters: + - name: VSetID + in: query + schema: + type: integer + description: Filter by ValueSet ID + responses: + '200': + description: List of value set items + content: + application/json: + schema: + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/ValueSetItem' + + post: + tags: [ValueSets] + summary: Create value set item + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ValueSetItem' + responses: + '201': + description: Value set item created + + /api/valueset/items/{id}: + get: + tags: [ValueSets] + summary: Get value set item by ID + security: + - bearerAuth: [] + parameters: + - name: id + in: path + required: true + schema: + type: integer + responses: + '200': + description: Value set item details + + put: + tags: [ValueSets] + summary: Update value set item + security: + - bearerAuth: [] + parameters: + - name: id + in: path + required: true + schema: + type: integer + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ValueSetItem' + responses: + '200': + description: Value set item updated + + delete: + tags: [ValueSets] + summary: Delete value set item + security: + - bearerAuth: [] + parameters: + - name: id + in: path + required: true + schema: + type: integer + responses: + '200': + description: Value set item deleted + + /api/valuesetdef: + get: + tags: [ValueSets] + summary: List value set definitions + security: + - bearerAuth: [] + responses: + '200': + description: List of value set definitions + + post: + tags: [ValueSets] + summary: Create value set definition + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ValueSetDef' + responses: + '201': + description: Value set definition created + + /api/valuesetdef/{id}: + get: + tags: [ValueSets] + summary: Get value set definition by ID + security: + - bearerAuth: [] + parameters: + - name: id + in: path + required: true + schema: + type: integer + responses: + '200': + description: Value set definition details + + put: + tags: [ValueSets] + summary: Update value set definition + security: + - bearerAuth: [] + parameters: + - name: id + in: path + required: true + schema: + type: integer + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/ValueSetDef' + responses: + '200': + description: Value set definition updated + + delete: + tags: [ValueSets] + summary: Delete value set definition + security: + - bearerAuth: [] + parameters: + - name: id + in: path + required: true + schema: + type: integer + responses: + '200': + description: Value set definition deleted + + # ======================================== + # Master Data Routes + # ======================================== + /api/location: + get: + tags: [Master Data] + summary: List locations + security: + - bearerAuth: [] + responses: + '200': + description: List of locations + + post: + tags: [Master Data] + summary: Create location + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Location' + responses: + '201': + description: Location created + + patch: + tags: [Master Data] + summary: Update location + security: + - bearerAuth: [] + responses: + '200': + description: Location updated + + delete: + tags: [Master Data] + summary: Delete location + security: + - bearerAuth: [] + 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 + responses: + '200': + description: Location details + + /api/contact: + get: + tags: [Master Data] + summary: List contacts + security: + - bearerAuth: [] + responses: + '200': + description: List of contacts + + post: + tags: [Master Data] + summary: Create contact + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Contact' + responses: + '201': + description: Contact created + + patch: + tags: [Master Data] + summary: Update contact + security: + - bearerAuth: [] + responses: + '200': + description: Contact updated + + delete: + tags: [Master Data] + summary: Delete contact + security: + - bearerAuth: [] + 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 + responses: + '200': + description: Contact details + + /api/occupation: + get: + tags: [Master Data] + summary: List occupations + security: + - bearerAuth: [] + responses: + '200': + description: List of occupations + + post: + tags: [Master Data] + summary: Create occupation + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Occupation' + responses: + '201': + description: Occupation created + + patch: + tags: [Master Data] + summary: Update occupation + security: + - bearerAuth: [] + 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 + responses: + '200': + description: Occupation details + + /api/medicalspecialty: + get: + tags: [Master Data] + summary: List medical specialties + security: + - bearerAuth: [] + responses: + '200': + description: List of medical specialties + + post: + tags: [Master Data] + summary: Create medical specialty + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/MedicalSpecialty' + responses: + '201': + description: Medical specialty created + + patch: + tags: [Master Data] + summary: Update medical specialty + security: + - bearerAuth: [] + 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 + responses: + '200': + description: Medical specialty details + + /api/counter: + get: + tags: [Master Data] + summary: List counters + security: + - bearerAuth: [] + responses: + '200': + description: List of counters + + post: + tags: [Master Data] + summary: Create counter + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Counter' + responses: + '201': + description: Counter created + + patch: + tags: [Master Data] + summary: Update counter + security: + - bearerAuth: [] + responses: + '200': + description: Counter updated + + delete: + tags: [Master Data] + summary: Delete counter + security: + - bearerAuth: [] + 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 + responses: + '200': + description: Counter details + + /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/package.json b/package.json new file mode 100644 index 0000000..ad2a0a1 --- /dev/null +++ b/package.json @@ -0,0 +1,29 @@ +{ + "name": "fe", + "private": true, + "version": "0.0.1", + "type": "module", + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "prepare": "svelte-kit sync || echo ''", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch" + }, + "devDependencies": { + "@sveltejs/adapter-static": "^3.0.10", + "@sveltejs/kit": "^2.50.2", + "@sveltejs/vite-plugin-svelte": "^6.2.4", + "@tailwindcss/vite": "^4.1.18", + "daisyui": "^5.5.18", + "lucide-svelte": "^0.563.0", + "svelte": "^5.49.2", + "svelte-check": "^4.3.6", + "sveltekit-superforms": "^2.29.1", + "tailwindcss": "^4.1.18", + "typescript": "^5.9.3", + "vite": "^7.3.1", + "zod": "^4.3.6" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..0dfd776 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,1744 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + devDependencies: + '@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)(typescript@5.9.3)(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)(typescript@5.9.3)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)) + '@sveltejs/vite-plugin-svelte': + specifier: ^6.2.4 + version: 6.2.4(svelte@5.50.0)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)) + '@tailwindcss/vite': + specifier: ^4.1.18 + version: 4.1.18(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)) + daisyui: + specifier: ^5.5.18 + version: 5.5.18 + lucide-svelte: + specifier: ^0.563.0 + version: 0.563.0(svelte@5.50.0) + svelte: + specifier: ^5.49.2 + version: 5.50.0 + svelte-check: + specifier: ^4.3.6 + version: 4.3.6(picomatch@4.0.3)(svelte@5.50.0)(typescript@5.9.3) + sveltekit-superforms: + specifier: ^2.29.1 + version: 2.29.1(@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)(typescript@5.9.3)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)))(svelte@5.50.0)(typescript@5.9.3) + tailwindcss: + specifier: ^4.1.18 + version: 4.1.18 + typescript: + specifier: ^5.9.3 + version: 5.9.3 + vite: + specifier: ^7.3.1 + version: 7.3.1(jiti@2.6.1)(lightningcss@1.30.2) + zod: + specifier: ^4.3.6 + version: 4.3.6 + +packages: + + '@ark/schema@0.56.0': + resolution: {integrity: sha512-ECg3hox/6Z/nLajxXqNhgPtNdHWC9zNsDyskwO28WinoFEnWow4IsERNz9AnXRhTZJnYIlAJ4uGn3nlLk65vZA==} + + '@ark/util@0.56.0': + resolution: {integrity: sha512-BghfRC8b9pNs3vBoDJhcta0/c1J1rsoS1+HgVUreMFPdhz/CRAKReAu57YEllNaSy98rWAdY1gE+gFup7OXpgA==} + + '@babel/runtime@7.28.6': + resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==} + engines: {node: '>=6.9.0'} + + '@esbuild/aix-ppc64@0.27.3': + resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.27.3': + resolution: {integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.27.3': + resolution: {integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.27.3': + resolution: {integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.27.3': + resolution: {integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.27.3': + resolution: {integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.27.3': + resolution: {integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.27.3': + resolution: {integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.27.3': + resolution: {integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.27.3': + resolution: {integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.27.3': + resolution: {integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.27.3': + resolution: {integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.27.3': + resolution: {integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.27.3': + resolution: {integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.27.3': + resolution: {integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.27.3': + resolution: {integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.27.3': + resolution: {integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.27.3': + resolution: {integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.27.3': + resolution: {integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.27.3': + resolution: {integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.27.3': + resolution: {integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.27.3': + resolution: {integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.27.3': + resolution: {integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.27.3': + resolution: {integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.27.3': + resolution: {integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.27.3': + resolution: {integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@exodus/schemasafe@1.3.0': + resolution: {integrity: sha512-5Aap/GaRupgNx/feGBwLLTVv8OQFfv3pq2lPRzPg9R+IOBnDgghTGW7l7EuVXOvg5cc/xSAlRW8rBrjIC3Nvqw==} + + '@hapi/hoek@9.3.0': + resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==} + + '@hapi/topo@5.1.0': + resolution: {integrity: sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==} + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@polka/url@1.0.0-next.29': + resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} + + '@poppinss/macroable@1.1.0': + resolution: {integrity: sha512-y/YKzZDuG8XrpXpM7Z1RdQpiIc0MAKyva24Ux1PB4aI7RiSI/79K8JVDcdyubriTm7vJ1LhFs8CrZpmPnx/8Pw==} + + '@rollup/rollup-android-arm-eabi@4.57.1': + resolution: {integrity: sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.57.1': + resolution: {integrity: sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.57.1': + resolution: {integrity: sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.57.1': + resolution: {integrity: sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.57.1': + resolution: {integrity: sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.57.1': + resolution: {integrity: sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.57.1': + resolution: {integrity: sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm-musleabihf@4.57.1': + resolution: {integrity: sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==} + cpu: [arm] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-arm64-gnu@4.57.1': + resolution: {integrity: sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm64-musl@4.57.1': + resolution: {integrity: sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-loong64-gnu@4.57.1': + resolution: {integrity: sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==} + cpu: [loong64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-loong64-musl@4.57.1': + resolution: {integrity: sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==} + cpu: [loong64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-ppc64-gnu@4.57.1': + resolution: {integrity: sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-ppc64-musl@4.57.1': + resolution: {integrity: sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==} + cpu: [ppc64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-riscv64-gnu@4.57.1': + resolution: {integrity: sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-riscv64-musl@4.57.1': + resolution: {integrity: sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-s390x-gnu@4.57.1': + resolution: {integrity: sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-gnu@4.57.1': + resolution: {integrity: sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-musl@4.57.1': + resolution: {integrity: sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rollup/rollup-openbsd-x64@4.57.1': + resolution: {integrity: sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.57.1': + resolution: {integrity: sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.57.1': + resolution: {integrity: sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.57.1': + resolution: {integrity: sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.57.1': + resolution: {integrity: sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.57.1': + resolution: {integrity: sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==} + cpu: [x64] + os: [win32] + + '@sideway/address@4.1.5': + resolution: {integrity: sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==} + + '@sideway/formula@3.0.1': + resolution: {integrity: sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==} + + '@sideway/pinpoint@2.0.0': + resolution: {integrity: sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==} + + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + + '@sveltejs/acorn-typescript@1.0.8': + resolution: {integrity: sha512-esgN+54+q0NjB0Y/4BomT9samII7jGwNy/2a3wNZbT2A2RpmXsXwUt24LvLhx6jUq2gVk4cWEvcRO6MFQbOfNA==} + peerDependencies: + acorn: ^8.9.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'} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.0.0 + '@sveltejs/vite-plugin-svelte': ^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 + svelte: ^4.0.0 || ^5.0.0-next.0 + typescript: ^5.3.3 + vite: ^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + typescript: + optional: true + + '@sveltejs/vite-plugin-svelte-inspector@5.0.2': + resolution: {integrity: sha512-TZzRTcEtZffICSAoZGkPSl6Etsj2torOVrx6Uw0KpXxrec9Gg6jFWQ60Q3+LmNGfZSxHRCZL7vXVZIWmuV50Ig==} + engines: {node: ^20.19 || ^22.12 || >=24} + peerDependencies: + '@sveltejs/vite-plugin-svelte': ^6.0.0-next.0 + svelte: ^5.0.0 + vite: ^6.3.0 || ^7.0.0 + + '@sveltejs/vite-plugin-svelte@6.2.4': + resolution: {integrity: sha512-ou/d51QSdTyN26D7h6dSpusAKaZkAiGM55/AKYi+9AGZw7q85hElbjK3kEyzXHhLSnRISHOYzVge6x0jRZ7DXA==} + engines: {node: ^20.19 || ^22.12 || >=24} + peerDependencies: + svelte: ^5.0.0 + vite: ^6.3.0 || ^7.0.0 + + '@tailwindcss/node@4.1.18': + resolution: {integrity: sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ==} + + '@tailwindcss/oxide-android-arm64@4.1.18': + resolution: {integrity: sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.1.18': + resolution: {integrity: sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.1.18': + resolution: {integrity: sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.1.18': + resolution: {integrity: sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18': + resolution: {integrity: sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.18': + resolution: {integrity: sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-arm64-musl@4.1.18': + resolution: {integrity: sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-linux-x64-gnu@4.1.18': + resolution: {integrity: sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-x64-musl@4.1.18': + resolution: {integrity: sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-wasm32-wasi@4.1.18': + resolution: {integrity: sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.18': + resolution: {integrity: sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.1.18': + resolution: {integrity: sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.1.18': + resolution: {integrity: sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A==} + engines: {node: '>= 10'} + + '@tailwindcss/vite@4.1.18': + resolution: {integrity: sha512-jVA+/UpKL1vRLg6Hkao5jldawNmRo7mQYrZtNHMIVpLfLhDml5nMRUo/8MwoX2vNXvnaXNNMedrMfMugAVX1nA==} + peerDependencies: + vite: ^5.2.0 || ^6 || ^7 + + '@types/cookie@0.6.0': + resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/validator@13.15.10': + resolution: {integrity: sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==} + + '@typeschema/class-validator@0.3.0': + resolution: {integrity: sha512-OJSFeZDIQ8EK1HTljKLT5CItM2wsbgczLN8tMEfz3I1Lmhc5TBfkZ0eikFzUC16tI3d1Nag7um6TfCgp2I2Bww==} + peerDependencies: + class-validator: ^0.14.1 + peerDependenciesMeta: + class-validator: + optional: true + + '@typeschema/core@0.14.0': + resolution: {integrity: sha512-Ia6PtZHcL3KqsAWXjMi5xIyZ7XMH4aSnOQes8mfMLx+wGFGtGRNlwe6Y7cYvX+WfNK67OL0/HSe9t8QDygV0/w==} + peerDependencies: + '@types/json-schema': ^7.0.15 + peerDependenciesMeta: + '@types/json-schema': + optional: true + + '@valibot/to-json-schema@1.5.0': + resolution: {integrity: sha512-GE7DmSr1C2UCWPiV0upRH6mv0cCPsqYGs819fb6srCS1tWhyXrkGGe+zxUiwzn/L1BOfADH4sNjY/YHCuP8phQ==} + peerDependencies: + valibot: ^1.2.0 + + '@vinejs/compiler@3.0.0': + resolution: {integrity: sha512-v9Lsv59nR56+bmy2p0+czjZxsLHwaibJ+SV5iK9JJfehlJMa501jUJQqqz4X/OqKXrxtE3uTQmSqjUqzF3B2mw==} + engines: {node: '>=18.0.0'} + + '@vinejs/vine@3.0.1': + resolution: {integrity: sha512-ZtvYkYpZOYdvbws3uaOAvTFuvFXoQGAtmzeiXu+XSMGxi5GVsODpoI9Xu9TplEMuD/5fmAtBbKb9cQHkWkLXDQ==} + engines: {node: '>=18.16.0'} + + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} + hasBin: true + + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + + arkregex@0.0.5: + resolution: {integrity: sha512-ncYjBdLlh5/QnVsAA8De16Tc9EqmYM7y/WU9j+236KcyYNUXogpz3sC4ATIZYzzLxwI+0sEOaQLEmLmRleaEXw==} + + arktype@2.1.29: + resolution: {integrity: sha512-jyfKk4xIOzvYNayqnD8ZJQqOwcrTOUbIU4293yrzAjA3O1dWh61j71ArMQ6tS/u4pD7vabSPe7nG3RCyoXW6RQ==} + + axobject-query@4.1.0: + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} + engines: {node: '>= 0.4'} + + camelcase@8.0.0: + resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==} + engines: {node: '>=16'} + + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + + class-validator@0.14.3: + resolution: {integrity: sha512-rXXekcjofVN1LTOSw+u4u9WXVEUvNBVjORW154q/IdmYWy1nMbOU9aNtZB0t8m+FJQ9q91jlr2f9CwwUFdFMRA==} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + cookie@0.6.0: + resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} + engines: {node: '>= 0.6'} + + daisyui@5.5.18: + resolution: {integrity: sha512-VVzjpOitMGB6DWIBeRSapbjdOevFqyzpk9u5Um6a4tyId3JFrU5pbtF0vgjXDth76mJZbueN/j9Ok03SPrh/og==} + + dayjs@1.11.19: + resolution: {integrity: sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==} + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + devalue@5.6.2: + resolution: {integrity: sha512-nPRkjWzzDQlsejL1WVifk5rvcFi/y1onBRxjaFMjZeR9mFpqu2gmAZ9xUB9/IEanEP/vBtGeGganC/GO1fmufg==} + + dlv@1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + + effect@3.19.16: + resolution: {integrity: sha512-7+XC3vGrbAhCHd8LTFHvnZjRpZKZ8YHRZqJTkpNoxcJ2mCyNs2SwI+6VkV/ij8Y3YW7wfBN4EbU06/F5+m/wkQ==} + + enhanced-resolve@5.19.0: + resolution: {integrity: sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==} + engines: {node: '>=10.13.0'} + + esbuild@0.27.3: + resolution: {integrity: sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==} + engines: {node: '>=18'} + hasBin: true + + esm-env@1.2.2: + resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==} + + esrap@2.2.3: + resolution: {integrity: sha512-8fOS+GIGCQZl/ZIlhl59htOlms6U8NvX6ZYgYHpRU/b6tVSh3uHkOHZikl3D4cMbYM0JlpBe+p/BkZEi8J9XIQ==} + + fast-check@3.23.2: + resolution: {integrity: sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==} + engines: {node: '>=8.0.0'} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + is-reference@3.0.3: + resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==} + + jiti@2.6.1: + resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} + hasBin: true + + joi@17.13.3: + resolution: {integrity: sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==} + + json-schema-to-ts@3.1.1: + resolution: {integrity: sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==} + engines: {node: '>=16'} + + kleur@4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + + libphonenumber-js@1.12.36: + resolution: {integrity: sha512-woWhKMAVx1fzzUnMCyOzglgSgf6/AFHLASdOBcchYCyvWSGWt12imw3iu2hdI5d4dGZRsNWAmWiz37sDKUPaRQ==} + + lightningcss-android-arm64@1.30.2: + resolution: {integrity: sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.30.2: + resolution: {integrity: sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.30.2: + resolution: {integrity: sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.30.2: + resolution: {integrity: sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.30.2: + resolution: {integrity: sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.30.2: + resolution: {integrity: sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.30.2: + resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.30.2: + resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.30.2: + resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.30.2: + resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.30.2: + resolution: {integrity: sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.30.2: + resolution: {integrity: sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==} + engines: {node: '>= 12.0.0'} + + locate-character@3.0.0: + resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==} + + lucide-svelte@0.563.0: + resolution: {integrity: sha512-pjZKw7TpQcamfQrx7YdbOHgmrcNeKiGGMD0tKZQaVktwSsbqw28CsKc2Q97ttwjytiCWkJyOa8ij2Q+Og0nPfQ==} + peerDependencies: + svelte: ^3 || ^4 || ^5.0.0-next.42 + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + memoize-weak@1.0.2: + resolution: {integrity: sha512-gj39xkrjEw7nCn4nJ1M5ms6+MyMlyiGmttzsqAUsAKn6bYKwuTHh/AO3cKPF8IBrTIYTxb0wWXFs3E//Y8VoWQ==} + + mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + + mrmime@2.0.1: + resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} + engines: {node: '>=10'} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + normalize-url@8.1.1: + resolution: {integrity: sha512-JYc0DPlpGWB40kH5g07gGTrYuMqV653k3uBKY6uITPWds3M0ov3GaWGp9lbE3Bzngx8+XkfzgvASb9vk9JDFXQ==} + engines: {node: '>=14.16'} + + obug@2.1.1: + resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + + property-expr@2.0.6: + resolution: {integrity: sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==} + + pure-rand@6.1.0: + resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} + + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} + + rollup@4.57.1: + resolution: {integrity: sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + sade@1.8.1: + resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} + engines: {node: '>=6'} + + set-cookie-parser@3.0.1: + resolution: {integrity: sha512-n7Z7dXZhJbwuAHhNzkTti6Aw9QDDjZtm3JTpTGATIdNzdQz5GuFs22w90BcvF4INfnrL5xrX3oGsuqO5Dx3A1Q==} + + sirv@3.0.2: + resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} + engines: {node: '>=18'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + superstruct@2.0.2: + resolution: {integrity: sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A==} + engines: {node: '>=14.0.0'} + + svelte-check@4.3.6: + resolution: {integrity: sha512-uBkz96ElE3G4pt9E1Tw0xvBfIUQkeH794kDQZdAUk795UVMr+NJZpuFSS62vcmO/DuSalK83LyOwhgWq8YGU1Q==} + engines: {node: '>= 18.0.0'} + hasBin: true + peerDependencies: + svelte: ^4.0.0 || ^5.0.0-next.0 + typescript: '>=5.0.0' + + svelte@5.50.0: + resolution: {integrity: sha512-FR9kTLmX5i0oyeQ5j/+w8DuagIkQ7MWMuPpPVioW2zx9Dw77q+1ufLzF1IqNtcTXPRnIIio4PlasliVn43OnbQ==} + engines: {node: '>=18'} + + sveltekit-superforms@2.29.1: + resolution: {integrity: sha512-9Cv1beOVPgm8rb8NZBqLdlZ9cBqRBTk0+6/oHn7DWvHQoAFie1EPjh1e4NHO3Qouv1Zq9QTGrZNDbYcetkuOVw==} + peerDependencies: + '@sveltejs/kit': 1.x || 2.x + svelte: 3.x || 4.x || >=5.0.0-next.51 + + tailwindcss@4.1.18: + resolution: {integrity: sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==} + + tapable@2.3.0: + resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} + engines: {node: '>=6'} + + tiny-case@1.0.3: + resolution: {integrity: sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==} + + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + + toposort@2.0.2: + resolution: {integrity: sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==} + + totalist@3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} + + ts-algebra@2.0.0: + resolution: {integrity: sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==} + + ts-deepmerge@7.0.3: + resolution: {integrity: sha512-Du/ZW2RfwV/D4cmA5rXafYjBQVuvu4qGiEEla4EmEHVHgRdx68Gftx7i66jn2bzHPwSVZY36Ae6OuDn9el4ZKA==} + engines: {node: '>=14.13.1'} + + type-fest@2.19.0: + resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} + engines: {node: '>=12.20'} + + typebox@1.0.81: + resolution: {integrity: sha512-bCslZUmZESHhBn4kHDghzH2oo3qu8m2W89xDLxQHv/aPvY4i81Nd1jvijlBp9wSpsVytDSfSoosbiBAjgsNb2Q==} + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + valibot@1.2.0: + resolution: {integrity: sha512-mm1rxUsmOxzrwnX5arGS+U4T25RdvpPjPN4yR0u9pUBov9+zGVtO84tif1eY4r6zWxVxu3KzIyknJy3rxfRZZg==} + peerDependencies: + typescript: '>=5' + peerDependenciesMeta: + typescript: + optional: true + + validator@13.15.26: + resolution: {integrity: sha512-spH26xU080ydGggxRyR1Yhcbgx+j3y5jbNXk/8L+iRvdIEQ4uTRH2Sgf2dokud6Q4oAtsbNvJ1Ft+9xmm6IZcA==} + engines: {node: '>= 0.10'} + + vite@7.3.1: + resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitefu@1.1.1: + resolution: {integrity: sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ==} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0 + peerDependenciesMeta: + vite: + optional: true + + yup@1.7.1: + resolution: {integrity: sha512-GKHFX2nXul2/4Dtfxhozv701jLQHdf6J34YDh2cEkpqoo8le5Mg6/LrdseVLrFarmFygZTlfIhHx/QKfb/QWXw==} + + zimmerframe@1.1.4: + resolution: {integrity: sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==} + + zod-v3-to-json-schema@4.0.0: + resolution: {integrity: sha512-KixLrhX/uPmRFnDgsZrzrk4x5SSJA+PmaE5adbfID9+3KPJcdxqRobaHU397EfWBqfQircrjKqvEqZ/mW5QH6w==} + peerDependencies: + zod: ^3.25 || ^4.0.14 + + zod@4.3.6: + resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} + +snapshots: + + '@ark/schema@0.56.0': + dependencies: + '@ark/util': 0.56.0 + optional: true + + '@ark/util@0.56.0': + optional: true + + '@babel/runtime@7.28.6': + optional: true + + '@esbuild/aix-ppc64@0.27.3': + optional: true + + '@esbuild/android-arm64@0.27.3': + optional: true + + '@esbuild/android-arm@0.27.3': + optional: true + + '@esbuild/android-x64@0.27.3': + optional: true + + '@esbuild/darwin-arm64@0.27.3': + optional: true + + '@esbuild/darwin-x64@0.27.3': + optional: true + + '@esbuild/freebsd-arm64@0.27.3': + optional: true + + '@esbuild/freebsd-x64@0.27.3': + optional: true + + '@esbuild/linux-arm64@0.27.3': + optional: true + + '@esbuild/linux-arm@0.27.3': + optional: true + + '@esbuild/linux-ia32@0.27.3': + optional: true + + '@esbuild/linux-loong64@0.27.3': + optional: true + + '@esbuild/linux-mips64el@0.27.3': + optional: true + + '@esbuild/linux-ppc64@0.27.3': + optional: true + + '@esbuild/linux-riscv64@0.27.3': + optional: true + + '@esbuild/linux-s390x@0.27.3': + optional: true + + '@esbuild/linux-x64@0.27.3': + optional: true + + '@esbuild/netbsd-arm64@0.27.3': + optional: true + + '@esbuild/netbsd-x64@0.27.3': + optional: true + + '@esbuild/openbsd-arm64@0.27.3': + optional: true + + '@esbuild/openbsd-x64@0.27.3': + optional: true + + '@esbuild/openharmony-arm64@0.27.3': + optional: true + + '@esbuild/sunos-x64@0.27.3': + optional: true + + '@esbuild/win32-arm64@0.27.3': + optional: true + + '@esbuild/win32-ia32@0.27.3': + optional: true + + '@esbuild/win32-x64@0.27.3': + optional: true + + '@exodus/schemasafe@1.3.0': + optional: true + + '@hapi/hoek@9.3.0': + optional: true + + '@hapi/topo@5.1.0': + dependencies: + '@hapi/hoek': 9.3.0 + optional: true + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@polka/url@1.0.0-next.29': {} + + '@poppinss/macroable@1.1.0': + optional: true + + '@rollup/rollup-android-arm-eabi@4.57.1': + optional: true + + '@rollup/rollup-android-arm64@4.57.1': + optional: true + + '@rollup/rollup-darwin-arm64@4.57.1': + optional: true + + '@rollup/rollup-darwin-x64@4.57.1': + optional: true + + '@rollup/rollup-freebsd-arm64@4.57.1': + optional: true + + '@rollup/rollup-freebsd-x64@4.57.1': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.57.1': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.57.1': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.57.1': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.57.1': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.57.1': + optional: true + + '@rollup/rollup-linux-loong64-musl@4.57.1': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.57.1': + optional: true + + '@rollup/rollup-linux-ppc64-musl@4.57.1': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.57.1': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.57.1': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.57.1': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.57.1': + optional: true + + '@rollup/rollup-linux-x64-musl@4.57.1': + optional: true + + '@rollup/rollup-openbsd-x64@4.57.1': + optional: true + + '@rollup/rollup-openharmony-arm64@4.57.1': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.57.1': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.57.1': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.57.1': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.57.1': + optional: true + + '@sideway/address@4.1.5': + dependencies: + '@hapi/hoek': 9.3.0 + optional: true + + '@sideway/formula@3.0.1': + optional: true + + '@sideway/pinpoint@2.0.0': + optional: true + + '@standard-schema/spec@1.1.0': {} + + '@sveltejs/acorn-typescript@1.0.8(acorn@8.15.0)': + dependencies: + acorn: 8.15.0 + + '@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)(typescript@5.9.3)(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)(typescript@5.9.3)(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)(typescript@5.9.3)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2))': + dependencies: + '@standard-schema/spec': 1.1.0 + '@sveltejs/acorn-typescript': 1.0.8(acorn@8.15.0) + '@sveltejs/vite-plugin-svelte': 6.2.4(svelte@5.50.0)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)) + '@types/cookie': 0.6.0 + acorn: 8.15.0 + cookie: 0.6.0 + devalue: 5.6.2 + esm-env: 1.2.2 + kleur: 4.1.5 + magic-string: 0.30.21 + mrmime: 2.0.1 + sade: 1.8.1 + set-cookie-parser: 3.0.1 + sirv: 3.0.2 + svelte: 5.50.0 + vite: 7.3.1(jiti@2.6.1)(lightningcss@1.30.2) + optionalDependencies: + typescript: 5.9.3 + + '@sveltejs/vite-plugin-svelte-inspector@5.0.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/vite-plugin-svelte': 6.2.4(svelte@5.50.0)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)) + obug: 2.1.1 + svelte: 5.50.0 + vite: 7.3.1(jiti@2.6.1)(lightningcss@1.30.2) + + '@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.50.0)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2))': + dependencies: + '@sveltejs/vite-plugin-svelte-inspector': 5.0.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)) + deepmerge: 4.3.1 + magic-string: 0.30.21 + obug: 2.1.1 + svelte: 5.50.0 + vite: 7.3.1(jiti@2.6.1)(lightningcss@1.30.2) + vitefu: 1.1.1(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)) + + '@tailwindcss/node@4.1.18': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.19.0 + jiti: 2.6.1 + lightningcss: 1.30.2 + magic-string: 0.30.21 + source-map-js: 1.2.1 + tailwindcss: 4.1.18 + + '@tailwindcss/oxide-android-arm64@4.1.18': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.1.18': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.1.18': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.1.18': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.18': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.1.18': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.1.18': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.1.18': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.1.18': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.18': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.1.18': + optional: true + + '@tailwindcss/oxide@4.1.18': + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.1.18 + '@tailwindcss/oxide-darwin-arm64': 4.1.18 + '@tailwindcss/oxide-darwin-x64': 4.1.18 + '@tailwindcss/oxide-freebsd-x64': 4.1.18 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.18 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.18 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.18 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.18 + '@tailwindcss/oxide-linux-x64-musl': 4.1.18 + '@tailwindcss/oxide-wasm32-wasi': 4.1.18 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.18 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.18 + + '@tailwindcss/vite@4.1.18(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2))': + dependencies: + '@tailwindcss/node': 4.1.18 + '@tailwindcss/oxide': 4.1.18 + tailwindcss: 4.1.18 + vite: 7.3.1(jiti@2.6.1)(lightningcss@1.30.2) + + '@types/cookie@0.6.0': {} + + '@types/estree@1.0.8': {} + + '@types/validator@13.15.10': + optional: true + + '@typeschema/class-validator@0.3.0(class-validator@0.14.3)': + dependencies: + '@typeschema/core': 0.14.0 + optionalDependencies: + class-validator: 0.14.3 + transitivePeerDependencies: + - '@types/json-schema' + optional: true + + '@typeschema/core@0.14.0': + optional: true + + '@valibot/to-json-schema@1.5.0(valibot@1.2.0(typescript@5.9.3))': + dependencies: + valibot: 1.2.0(typescript@5.9.3) + optional: true + + '@vinejs/compiler@3.0.0': + optional: true + + '@vinejs/vine@3.0.1': + dependencies: + '@poppinss/macroable': 1.1.0 + '@types/validator': 13.15.10 + '@vinejs/compiler': 3.0.0 + camelcase: 8.0.0 + dayjs: 1.11.19 + dlv: 1.1.3 + normalize-url: 8.1.1 + validator: 13.15.26 + optional: true + + acorn@8.15.0: {} + + aria-query@5.3.2: {} + + arkregex@0.0.5: + dependencies: + '@ark/util': 0.56.0 + optional: true + + arktype@2.1.29: + dependencies: + '@ark/schema': 0.56.0 + '@ark/util': 0.56.0 + arkregex: 0.0.5 + optional: true + + axobject-query@4.1.0: {} + + camelcase@8.0.0: + optional: true + + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + + class-validator@0.14.3: + dependencies: + '@types/validator': 13.15.10 + libphonenumber-js: 1.12.36 + validator: 13.15.26 + optional: true + + clsx@2.1.1: {} + + cookie@0.6.0: {} + + daisyui@5.5.18: {} + + dayjs@1.11.19: + optional: true + + deepmerge@4.3.1: {} + + detect-libc@2.1.2: {} + + devalue@5.6.2: {} + + dlv@1.1.3: + optional: true + + effect@3.19.16: + dependencies: + '@standard-schema/spec': 1.1.0 + fast-check: 3.23.2 + optional: true + + enhanced-resolve@5.19.0: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.0 + + esbuild@0.27.3: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.3 + '@esbuild/android-arm': 0.27.3 + '@esbuild/android-arm64': 0.27.3 + '@esbuild/android-x64': 0.27.3 + '@esbuild/darwin-arm64': 0.27.3 + '@esbuild/darwin-x64': 0.27.3 + '@esbuild/freebsd-arm64': 0.27.3 + '@esbuild/freebsd-x64': 0.27.3 + '@esbuild/linux-arm': 0.27.3 + '@esbuild/linux-arm64': 0.27.3 + '@esbuild/linux-ia32': 0.27.3 + '@esbuild/linux-loong64': 0.27.3 + '@esbuild/linux-mips64el': 0.27.3 + '@esbuild/linux-ppc64': 0.27.3 + '@esbuild/linux-riscv64': 0.27.3 + '@esbuild/linux-s390x': 0.27.3 + '@esbuild/linux-x64': 0.27.3 + '@esbuild/netbsd-arm64': 0.27.3 + '@esbuild/netbsd-x64': 0.27.3 + '@esbuild/openbsd-arm64': 0.27.3 + '@esbuild/openbsd-x64': 0.27.3 + '@esbuild/openharmony-arm64': 0.27.3 + '@esbuild/sunos-x64': 0.27.3 + '@esbuild/win32-arm64': 0.27.3 + '@esbuild/win32-ia32': 0.27.3 + '@esbuild/win32-x64': 0.27.3 + + esm-env@1.2.2: {} + + esrap@2.2.3: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + fast-check@3.23.2: + dependencies: + pure-rand: 6.1.0 + optional: true + + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + + fsevents@2.3.3: + optional: true + + graceful-fs@4.2.11: {} + + is-reference@3.0.3: + dependencies: + '@types/estree': 1.0.8 + + jiti@2.6.1: {} + + joi@17.13.3: + dependencies: + '@hapi/hoek': 9.3.0 + '@hapi/topo': 5.1.0 + '@sideway/address': 4.1.5 + '@sideway/formula': 3.0.1 + '@sideway/pinpoint': 2.0.0 + optional: true + + json-schema-to-ts@3.1.1: + dependencies: + '@babel/runtime': 7.28.6 + ts-algebra: 2.0.0 + optional: true + + kleur@4.1.5: {} + + libphonenumber-js@1.12.36: + optional: true + + lightningcss-android-arm64@1.30.2: + optional: true + + lightningcss-darwin-arm64@1.30.2: + optional: true + + lightningcss-darwin-x64@1.30.2: + optional: true + + lightningcss-freebsd-x64@1.30.2: + optional: true + + lightningcss-linux-arm-gnueabihf@1.30.2: + optional: true + + lightningcss-linux-arm64-gnu@1.30.2: + optional: true + + lightningcss-linux-arm64-musl@1.30.2: + optional: true + + lightningcss-linux-x64-gnu@1.30.2: + optional: true + + lightningcss-linux-x64-musl@1.30.2: + optional: true + + lightningcss-win32-arm64-msvc@1.30.2: + optional: true + + lightningcss-win32-x64-msvc@1.30.2: + optional: true + + lightningcss@1.30.2: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.30.2 + lightningcss-darwin-arm64: 1.30.2 + lightningcss-darwin-x64: 1.30.2 + lightningcss-freebsd-x64: 1.30.2 + lightningcss-linux-arm-gnueabihf: 1.30.2 + lightningcss-linux-arm64-gnu: 1.30.2 + lightningcss-linux-arm64-musl: 1.30.2 + lightningcss-linux-x64-gnu: 1.30.2 + lightningcss-linux-x64-musl: 1.30.2 + lightningcss-win32-arm64-msvc: 1.30.2 + lightningcss-win32-x64-msvc: 1.30.2 + + locate-character@3.0.0: {} + + lucide-svelte@0.563.0(svelte@5.50.0): + dependencies: + svelte: 5.50.0 + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + memoize-weak@1.0.2: {} + + mri@1.2.0: {} + + mrmime@2.0.1: {} + + nanoid@3.3.11: {} + + normalize-url@8.1.1: + optional: true + + obug@2.1.1: {} + + picocolors@1.1.1: {} + + picomatch@4.0.3: {} + + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + property-expr@2.0.6: + optional: true + + pure-rand@6.1.0: + optional: true + + readdirp@4.1.2: {} + + rollup@4.57.1: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.57.1 + '@rollup/rollup-android-arm64': 4.57.1 + '@rollup/rollup-darwin-arm64': 4.57.1 + '@rollup/rollup-darwin-x64': 4.57.1 + '@rollup/rollup-freebsd-arm64': 4.57.1 + '@rollup/rollup-freebsd-x64': 4.57.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.57.1 + '@rollup/rollup-linux-arm-musleabihf': 4.57.1 + '@rollup/rollup-linux-arm64-gnu': 4.57.1 + '@rollup/rollup-linux-arm64-musl': 4.57.1 + '@rollup/rollup-linux-loong64-gnu': 4.57.1 + '@rollup/rollup-linux-loong64-musl': 4.57.1 + '@rollup/rollup-linux-ppc64-gnu': 4.57.1 + '@rollup/rollup-linux-ppc64-musl': 4.57.1 + '@rollup/rollup-linux-riscv64-gnu': 4.57.1 + '@rollup/rollup-linux-riscv64-musl': 4.57.1 + '@rollup/rollup-linux-s390x-gnu': 4.57.1 + '@rollup/rollup-linux-x64-gnu': 4.57.1 + '@rollup/rollup-linux-x64-musl': 4.57.1 + '@rollup/rollup-openbsd-x64': 4.57.1 + '@rollup/rollup-openharmony-arm64': 4.57.1 + '@rollup/rollup-win32-arm64-msvc': 4.57.1 + '@rollup/rollup-win32-ia32-msvc': 4.57.1 + '@rollup/rollup-win32-x64-gnu': 4.57.1 + '@rollup/rollup-win32-x64-msvc': 4.57.1 + fsevents: 2.3.3 + + sade@1.8.1: + dependencies: + mri: 1.2.0 + + set-cookie-parser@3.0.1: {} + + sirv@3.0.2: + dependencies: + '@polka/url': 1.0.0-next.29 + mrmime: 2.0.1 + totalist: 3.0.1 + + source-map-js@1.2.1: {} + + superstruct@2.0.2: + optional: true + + svelte-check@4.3.6(picomatch@4.0.3)(svelte@5.50.0)(typescript@5.9.3): + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + chokidar: 4.0.3 + fdir: 6.5.0(picomatch@4.0.3) + picocolors: 1.1.1 + sade: 1.8.1 + svelte: 5.50.0 + typescript: 5.9.3 + transitivePeerDependencies: + - picomatch + + svelte@5.50.0: + dependencies: + '@jridgewell/remapping': 2.3.5 + '@jridgewell/sourcemap-codec': 1.5.5 + '@sveltejs/acorn-typescript': 1.0.8(acorn@8.15.0) + '@types/estree': 1.0.8 + acorn: 8.15.0 + aria-query: 5.3.2 + axobject-query: 4.1.0 + clsx: 2.1.1 + devalue: 5.6.2 + esm-env: 1.2.2 + esrap: 2.2.3 + is-reference: 3.0.3 + locate-character: 3.0.0 + magic-string: 0.30.21 + zimmerframe: 1.1.4 + + sveltekit-superforms@2.29.1(@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)(typescript@5.9.3)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)))(svelte@5.50.0)(typescript@5.9.3): + 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)(typescript@5.9.3)(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)) + devalue: 5.6.2 + memoize-weak: 1.0.2 + svelte: 5.50.0 + ts-deepmerge: 7.0.3 + optionalDependencies: + '@exodus/schemasafe': 1.3.0 + '@typeschema/class-validator': 0.3.0(class-validator@0.14.3) + '@valibot/to-json-schema': 1.5.0(valibot@1.2.0(typescript@5.9.3)) + '@vinejs/vine': 3.0.1 + arktype: 2.1.29 + class-validator: 0.14.3 + effect: 3.19.16 + joi: 17.13.3 + json-schema-to-ts: 3.1.1 + superstruct: 2.0.2 + typebox: 1.0.81 + valibot: 1.2.0(typescript@5.9.3) + yup: 1.7.1 + zod: 4.3.6 + zod-v3-to-json-schema: 4.0.0(zod@4.3.6) + transitivePeerDependencies: + - '@types/json-schema' + - typescript + + tailwindcss@4.1.18: {} + + tapable@2.3.0: {} + + tiny-case@1.0.3: + optional: true + + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + toposort@2.0.2: + optional: true + + totalist@3.0.1: {} + + ts-algebra@2.0.0: + optional: true + + ts-deepmerge@7.0.3: {} + + type-fest@2.19.0: + optional: true + + typebox@1.0.81: + optional: true + + typescript@5.9.3: {} + + valibot@1.2.0(typescript@5.9.3): + optionalDependencies: + typescript: 5.9.3 + optional: true + + validator@13.15.26: + optional: true + + vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2): + dependencies: + esbuild: 0.27.3 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.57.1 + tinyglobby: 0.2.15 + optionalDependencies: + fsevents: 2.3.3 + jiti: 2.6.1 + lightningcss: 1.30.2 + + vitefu@1.1.1(vite@7.3.1(jiti@2.6.1)(lightningcss@1.30.2)): + optionalDependencies: + vite: 7.3.1(jiti@2.6.1)(lightningcss@1.30.2) + + yup@1.7.1: + dependencies: + property-expr: 2.0.6 + tiny-case: 1.0.3 + toposort: 2.0.2 + type-fest: 2.19.0 + optional: true + + zimmerframe@1.1.4: {} + + zod-v3-to-json-schema@4.0.0(zod@4.3.6): + dependencies: + zod: 4.3.6 + optional: true + + zod@4.3.6: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..efc037a --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,2 @@ +onlyBuiltDependencies: + - esbuild diff --git a/src/app.css b/src/app.css new file mode 100644 index 0000000..eecea6c --- /dev/null +++ b/src/app.css @@ -0,0 +1,91 @@ +@import "tailwindcss"; + +/* DaisyUI Import */ +@plugin "daisyui" { + themes: light --default, dark --prefersdark; +} + +/* Custom Theme Configuration */ +@plugin "daisyui/theme" { + name: "forest"; + default: true; + prefersdark: false; + color-scheme: "light"; + + /* Primary - Medical Green */ + --color-primary: #2d6a4f; + --color-primary-content: #ffffff; + + /* Secondary - Light Green */ + --color-secondary: #52b788; + --color-secondary-content: #ffffff; + + /* Accent */ + --color-accent: #d8f3dc; + --color-accent-content: #1b4332; + + /* Neutral */ + --color-neutral: #40916c; + --color-neutral-content: #ffffff; + + /* Base colors */ + --color-base-100: #ffffff; + --color-base-200: #f8fafc; + --color-base-300: #e2e8f0; + --color-base-content: #1e293b; + + /* Info */ + --color-info: #3b82f6; + --color-info-content: #ffffff; + + /* Success */ + --color-success: #22c55e; + --color-success-content: #ffffff; + + /* Warning */ + --color-warning: #f59e0b; + --color-warning-content: #ffffff; + + /* Error */ + --color-error: #ef4444; + --color-error-content: #ffffff; +} + +/* Custom utilities */ +@layer utilities { + .text-balance { + text-wrap: balance; + } +} + +/* Smooth transitions */ +* { + transition-property: color, background-color, border-color, text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter, backdrop-filter; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} + +/* Focus styles */ +*:focus-visible { + outline: 2px solid var(--color-primary); + outline-offset: 2px; +} + +/* Scrollbar styling */ +::-webkit-scrollbar { + width: 8px; + height: 8px; +} + +::-webkit-scrollbar-track { + background: var(--color-base-200); +} + +::-webkit-scrollbar-thumb { + background: var(--color-primary); + border-radius: 4px; +} + +::-webkit-scrollbar-thumb:hover { + background: var(--color-neutral); +} diff --git a/src/app.d.ts b/src/app.d.ts new file mode 100644 index 0000000..2bb25d0 --- /dev/null +++ b/src/app.d.ts @@ -0,0 +1,15 @@ +// See https://svelte.dev/docs/kit/types#app.d.ts +// for information about these interfaces +declare global { + namespace App { + // interface Error {} + interface Locals { + user: { authenticated: boolean } | null; + } + // interface PageData {} + // interface PageState {} + // interface Platform {} + } +} + +export {}; diff --git a/src/app.html b/src/app.html new file mode 100644 index 0000000..e948c10 --- /dev/null +++ b/src/app.html @@ -0,0 +1,11 @@ + + + + + + %sveltekit.head% + + +
%sveltekit.body%
+ + diff --git a/src/hooks.server.ts b/src/hooks.server.ts new file mode 100644 index 0000000..5cf3bc9 --- /dev/null +++ b/src/hooks.server.ts @@ -0,0 +1,12 @@ +import type { Handle } from '@sveltejs/kit'; + +export const handle: Handle = async ({ event, resolve }) => { + // Get auth token from cookies + const authToken = event.cookies.get('auth_token'); + + // Add auth token to locals for use in load functions + event.locals.user = authToken ? { authenticated: true } : null; + + const response = await resolve(event); + return response; +}; diff --git a/src/lib/assets/favicon.svg b/src/lib/assets/favicon.svg new file mode 100644 index 0000000..cc5dc66 --- /dev/null +++ b/src/lib/assets/favicon.svg @@ -0,0 +1 @@ +svelte-logo \ No newline at end of file diff --git a/src/lib/components/index.ts b/src/lib/components/index.ts new file mode 100644 index 0000000..15b99b8 --- /dev/null +++ b/src/lib/components/index.ts @@ -0,0 +1,2 @@ +export { default as Sidebar } from './layout/Sidebar.svelte'; +export { default as Header } from './layout/Header.svelte'; diff --git a/src/lib/components/layout/Header.svelte b/src/lib/components/layout/Header.svelte new file mode 100644 index 0000000..b7a29a6 --- /dev/null +++ b/src/lib/components/layout/Header.svelte @@ -0,0 +1,45 @@ + + +
+
+ +
+ + +
+
+ +
+ + +
+ +
+ + {user?.username?.charAt(0).toUpperCase() || 'U'} + +
+
+
+
diff --git a/src/lib/components/layout/Sidebar.svelte b/src/lib/components/layout/Sidebar.svelte new file mode 100644 index 0000000..eedc497 --- /dev/null +++ b/src/lib/components/layout/Sidebar.svelte @@ -0,0 +1,79 @@ + + + diff --git a/src/lib/index.ts b/src/lib/index.ts new file mode 100644 index 0000000..856f2b6 --- /dev/null +++ b/src/lib/index.ts @@ -0,0 +1 @@ +// place files you want to import through the `$lib` alias in this folder. diff --git a/src/lib/server/api.ts b/src/lib/server/api.ts new file mode 100644 index 0000000..d98f5d1 --- /dev/null +++ b/src/lib/server/api.ts @@ -0,0 +1,139 @@ +// API Client for CLQMS +// Wrapper around fetch for making API calls to the backend + +import { browser } from '$app/environment'; +import { PUBLIC_API_BASE_URL, PUBLIC_API_BASE_URL_PROD } from '$env/static/public'; + +const API_BASE_URL = browser + ? (import.meta.env.DEV ? PUBLIC_API_BASE_URL : PUBLIC_API_BASE_URL_PROD) + : PUBLIC_API_BASE_URL; + +interface ApiError { + message: string; + status: number; + code?: string; +} + +interface ApiResponse { + data: T | null; + error: ApiError | null; + success: boolean; +} + +class ApiClient { + private baseUrl: string; + private defaultHeaders: Record; + + constructor(baseUrl: string = API_BASE_URL) { + this.baseUrl = baseUrl.replace(/\/$/, ''); + this.defaultHeaders = { + 'Content-Type': 'application/json', + 'Accept': 'application/json' + }; + } + + private async request( + endpoint: string, + options: RequestInit = {} + ): Promise> { + const url = `${this.baseUrl}/${endpoint.replace(/^\//, '')}`; + + const config: RequestInit = { + ...options, + headers: { + ...this.defaultHeaders, + ...options.headers + }, + credentials: 'include' + }; + + try { + const response = await fetch(url, config); + + if (!response.ok) { + const errorData = await response.json().catch(() => ({})); + return { + data: null, + error: { + message: errorData.message || `HTTP Error: ${response.status}`, + status: response.status, + code: errorData.code + }, + success: false + }; + } + + const contentType = response.headers.get('content-type'); + let data: T; + + if (contentType?.includes('application/json')) { + data = await response.json(); + } else { + data = await response.text() as unknown as T; + } + + return { + data, + error: null, + success: true + }; + + } catch (error) { + return { + data: null, + error: { + message: error instanceof Error ? error.message : 'Network error', + status: 0 + }, + success: false + }; + } + } + + async get(endpoint: string, headers?: Record): Promise> { + return this.request(endpoint, { method: 'GET', headers }); + } + + async post( + endpoint: string, + body: unknown, + headers?: Record + ): Promise> { + return this.request(endpoint, { + method: 'POST', + body: JSON.stringify(body), + headers + }); + } + + async patch( + endpoint: string, + body: unknown, + headers?: Record + ): Promise> { + return this.request(endpoint, { + method: 'PATCH', + body: JSON.stringify(body), + headers + }); + } + + async put( + endpoint: string, + body: unknown, + headers?: Record + ): Promise> { + return this.request(endpoint, { + method: 'PUT', + body: JSON.stringify(body), + headers + }); + } + + async delete(endpoint: string, headers?: Record): Promise> { + return this.request(endpoint, { method: 'DELETE', headers }); + } +} + +export const api = new ApiClient(); +export type { ApiResponse, ApiError }; diff --git a/src/lib/stores/auth.ts b/src/lib/stores/auth.ts new file mode 100644 index 0000000..49e9fc2 --- /dev/null +++ b/src/lib/stores/auth.ts @@ -0,0 +1,47 @@ +import { writable } from 'svelte/store'; + +interface User { + id: string; + username: string; + email?: string; + role?: string; +} + +interface AuthState { + user: User | null; + isAuthenticated: boolean; + isLoading: boolean; +} + +function createAuthStore() { + const { subscribe, set, update } = writable({ + user: null, + isAuthenticated: false, + isLoading: true + }); + + return { + subscribe, + login: (user: User) => { + update(state => ({ + ...state, + user, + isAuthenticated: true, + isLoading: false + })); + }, + logout: () => { + set({ + user: null, + isAuthenticated: false, + isLoading: false + }); + }, + setLoading: (loading: boolean) => { + update(state => ({ ...state, isLoading: loading })); + } + }; +} + +export const auth = createAuthStore(); +export type { User, AuthState }; diff --git a/src/lib/stores/index.ts b/src/lib/stores/index.ts new file mode 100644 index 0000000..af8dbe0 --- /dev/null +++ b/src/lib/stores/index.ts @@ -0,0 +1 @@ +export { auth, type User, type AuthState } from './auth'; diff --git a/src/routes/(app)/+layout.svelte b/src/routes/(app)/+layout.svelte new file mode 100644 index 0000000..65225e0 --- /dev/null +++ b/src/routes/(app)/+layout.svelte @@ -0,0 +1,15 @@ + + +
+ +
+
+
+ +
+
+
diff --git a/src/routes/(app)/dashboard/+page.svelte b/src/routes/(app)/dashboard/+page.svelte new file mode 100644 index 0000000..0d2a22e --- /dev/null +++ b/src/routes/(app)/dashboard/+page.svelte @@ -0,0 +1,184 @@ + + + + CLQMS - Dashboard + + +
+ +
+
+

Dashboard

+

+ + {new Date().toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })} +

+
+ +
+ + +
+ +
+
+
+
+

Total Patients

+

{stats.totalPatients}

+
+
+ +
+
+
+ + +12% from last month +
+
+
+ + +
+
+
+
+

Pending Orders

+

{stats.pendingOrders}

+
+
+ +
+
+
+ Requires attention +
+
+
+ + +
+
+
+
+

Today's Results

+

{stats.todayResults}

+
+
+ +
+
+
+ Processed today +
+
+
+ + +
+
+
+
+

Critical Results

+

{stats.criticalResults}

+
+
+ +
+
+
+ Immediate attention required +
+
+
+
+ + + + + +
+
+

Recent Activity

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TimeActionUserStatus
10:30 AMNew patient registeredDr. SmithCompleted
10:15 AMLab order createdNurse JohnsonPending
09:45 AMResults uploadedLab Tech MikeCompleted
+
+
+
+
diff --git a/src/routes/(auth)/+layout.svelte b/src/routes/(auth)/+layout.svelte new file mode 100644 index 0000000..b122ea8 --- /dev/null +++ b/src/routes/(auth)/+layout.svelte @@ -0,0 +1,7 @@ + + +
+ +
diff --git a/src/routes/(auth)/login/+page.svelte b/src/routes/(auth)/login/+page.svelte new file mode 100644 index 0000000..b20a087 --- /dev/null +++ b/src/routes/(auth)/login/+page.svelte @@ -0,0 +1,162 @@ + + + + CLQMS - Login + + +
+
+ +
+
+ + + +
+

CLQMS

+

Clinical Laboratory Quality Management System

+
+ + +
+
+

Welcome Back

+ + {#if errorMessage} +
+ {errorMessage} +
+ {/if} + +
+ +
+ +
+ + + + +
+
+ + +
+ +
+ + + + +
+
+ + +
+ + Forgot password? +
+ + +
+ +
+
+ + +
or
+ + +

+ Need help? Contact your system administrator +

+
+
+ + +
+

2024 CLQMS. All rights reserved.

+
+
+
diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte new file mode 100644 index 0000000..96f5db4 --- /dev/null +++ b/src/routes/+layout.svelte @@ -0,0 +1,12 @@ + + + + + + +{@render children()} diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte new file mode 100644 index 0000000..898373a --- /dev/null +++ b/src/routes/+page.svelte @@ -0,0 +1,19 @@ + + + + + + +
+
+
+

Redirecting...

+
+
diff --git a/static/robots.txt b/static/robots.txt new file mode 100644 index 0000000..b6dd667 --- /dev/null +++ b/static/robots.txt @@ -0,0 +1,3 @@ +# allow crawling everything by default +User-agent: * +Disallow: diff --git a/svelte.config.js b/svelte.config.js new file mode 100644 index 0000000..00ec4fe --- /dev/null +++ b/svelte.config.js @@ -0,0 +1,22 @@ +import adapter from '@sveltejs/adapter-static'; + +/** @type {import('@sveltejs/kit').Config} */ +const config = { + kit: { + adapter: adapter({ + pages: 'build', + assets: 'build', + fallback: 'index.html', + precompress: false, + strict: false + }), + alias: { + '$components': 'src/lib/components', + '$stores': 'src/lib/stores', + '$types': 'src/lib/types', + '$server': 'src/lib/server' + } + } +}; + +export default config; diff --git a/templates/dashboard.html b/templates/dashboard.html new file mode 100644 index 0000000..132a1f4 --- /dev/null +++ b/templates/dashboard.html @@ -0,0 +1,297 @@ + + + + + + CLQMS - Dashboard + + + + + + + + + +
+ + + + +
+ + + + +
+ +
+
+
+ +
+
Pending Orders
+
24
+
Jan 1 - Feb 8
+
+
+
+ +
+
Today's Results
+
156
+
↗︎ 14% more than yesterday
+
+
+
+ +
+
Critical Results
+
3
+
Requires attention
+
+
+
+ +
+
Active Patients
+
89
+
Currently in system
+
+
+ + +
+
+
+

+ + Orders Trend +

+
+

[Chart: Orders over time]

+
+
+
+
+
+

+ + Results Volume +

+
+

[Chart: Results by department]

+
+
+
+
+ + +
+
+

+ + Recent Activity +

+
    +
  • +
    +
    + +
    +
    +
    + +
    Order #12345 created
    +
    Patient: John Doe (P-1001)
    +
    +
    +
  • +
  • +
    +
    + +
    +
    +
    + +
    Result received
    +
    Sample: ABC123 - Instrument: CBC-M01
    +
    +
    +
  • +
  • +
    +
    + +
    +
    +
    + +
    Patient registered
    +
    Patient ID: P-1001 - Jane Smith
    +
    +
  • +
+
+
+
+
+
+ + diff --git a/templates/login.html b/templates/login.html new file mode 100644 index 0000000..c3991ba --- /dev/null +++ b/templates/login.html @@ -0,0 +1,98 @@ + + + + + + CLQMS - Login + + + + + +
+
+ +
+
+ + + +
+

CLQMS

+

Clinical Laboratory Quality Management System

+
+ + +
+
+

Welcome Back

+ +
+ +
+ +
+ + + + + + +
+
+ + +
+ +
+ + + + + + +
+
+ + +
+ + Forgot password? +
+ + +
+ +
+
+ + +
or
+ + +

+ Need help? Contact your system administrator +

+
+
+ + +
+

© 2024 CLQMS. All rights reserved.

+
+
+
+ + diff --git a/templates/master-data.html b/templates/master-data.html new file mode 100644 index 0000000..96b0210 --- /dev/null +++ b/templates/master-data.html @@ -0,0 +1,325 @@ + + + + + + CLQMS - Master Data - Locations + + + + + + + + + +
+ + + + +
+ + + + +
+ +
+
+
+
+ + +
+ +
+
+
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Location CodeLocation NameLocation TypeSiteActions
LOC-001Main Ward
Ward
Site A +
+ + +
+
LOC-002Clinic A
Clinic
Site A +
+ + +
+
LOC-003Emergency Lab
Laboratory
Site B +
+ + +
+
LOC-004Main Pharmacy
Pharmacy
Site A +
+ + +
+
+
+ + +
+ + + + + + + +
+
+
+
+
+ + + + + + + + diff --git a/templates/report-print.html b/templates/report-print.html new file mode 100644 index 0000000..8707d8b --- /dev/null +++ b/templates/report-print.html @@ -0,0 +1,331 @@ + + + + + + CLQMS - Results Report + + + + + + + + + +
+ + + + +
+ + + + +
+
+
+ +
+

CLQMS

+

Clinical Laboratory Quality Management System

+
+

Laboratory Results Report

+
+ + +
+

Patient Information

+
+
+ Patient Name: + John Doe +
+
+ Patient ID: + P-1001 +
+
+ Date of Birth: + 1985-03-15 +
+
+ Sex: + Male +
+
+
+ +
+ + +
+

Order Information

+
+
+ Order ID: + ORD-2024001 +
+
+ Order Date: + 2024-02-08 10:30 +
+
+ Priority: +
Routine
+
+
+ Requesting Physician: + Dr. Smith +
+
+
+ +
+ + +
+

Test Results

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Test CodeTest NameResultUnitFlagReference Range
CBC-HGBHemoglobin145g/L
N
130-170
CBC-WBCWhite Blood Cell Count12.510^9/L
H
4.0-11.0
CBC-RBCRed Blood Cell Count4.810^12/L
N
4.5-5.5
CBC-PLTPlatelet Count25010^9/L
N
150-400
CBC-HCTHematocrit42%
N
40-50
+
+ +
+ + +
+

Report Generated: 2024-02-08 14:30:00

+

Verified By: Dr. Lab User

+
+ +
+ + Interpretation: Results show elevated WBC count, may indicate infection. Follow-up recommended. +
+
+
+
+
+
+ + diff --git a/templates/result-entry.html b/templates/result-entry.html new file mode 100644 index 0000000..2a52d90 --- /dev/null +++ b/templates/result-entry.html @@ -0,0 +1,318 @@ + + + + + + CLQMS - Result Entry + + + + + + + + + +
+ + + + +
+ + + + +
+ +
+
+

Select Order

+
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ + +
+
+

Order Information

+
+
+ + +
+
+ + +
+
+ + +
+
+ +
Routine
+
+
+
+
+ + +
+
+
+

Test Results

+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Test CodeTest NameResult ValueUnitFlagReference Range
CBC-HGBHemoglobin + + g/L
N
130-170
CBC-WBCWhite Blood Cell Count + + 10^9/L
H
4.0-11.0
CBC-RBCRed Blood Cell Count + + 10^12/L
-
4.5-5.5
CBC-PLTPlatelet Count + + 10^9/L
-
150-400
CBC-HCTHematocrit + + %
-
40-50
+
+ + +
+ + + +
+
+
+
+
+
+ + diff --git a/templates/shared.css b/templates/shared.css new file mode 100644 index 0000000..1e97862 --- /dev/null +++ b/templates/shared.css @@ -0,0 +1,5 @@ +/* shared.css - Override forest theme colors with hex */ +[data-theme="forest"] { + --color-primary: #2d6a4f; /* Dark medical green */ + --color-primary-content: #ffffff; +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..2c2ed3c --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "rewriteRelativeImportExtensions": true, + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "moduleResolution": "bundler" + } + // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias + // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files + // + // To make changes to top-level options such as include and exclude, we recommend extending + // the generated config; see https://svelte.dev/docs/kit/configuration#typescript +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..2f222db --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,7 @@ +import { sveltekit } from '@sveltejs/kit/vite'; +import { defineConfig } from 'vite'; +import tailwindcss from '@tailwindcss/vite'; + +export default defineConfig({ + plugins: [tailwindcss(), sveltekit()] +});