diff --git a/docs/orders.yaml b/docs/orders.yaml new file mode 100644 index 0000000..444caf0 --- /dev/null +++ b/docs/orders.yaml @@ -0,0 +1,265 @@ +/api/ordertest: + get: + tags: [Orders] + summary: List orders + security: + - bearerAuth: [] + parameters: + - name: page + in: query + schema: + type: integer + - name: perPage + in: query + schema: + type: integer + - name: InternalPID + in: query + schema: + type: integer + description: Filter by internal patient ID + - name: OrderStatus + in: query + schema: + type: string + enum: [ORD, SCH, ANA, VER, REV, REP] + description: | + ORD: Ordered + SCH: Scheduled + ANA: Analysis + VER: Verified + REV: Reviewed + REP: Reported + - name: include + in: query + schema: + type: string + enum: [details] + description: Include specimens and tests in response + responses: + '200': + description: List of orders + content: + application/json: + schema: + type: object + properties: + status: + type: string + message: + type: string + data: + type: array + items: + $ref: '../components/schemas/orders.yaml#/OrderTest' + + post: + tags: [Orders] + summary: Create order with specimens and tests + description: Creates an order with associated specimens and patres records. Tests are grouped by container type to minimize specimen creation. + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - InternalPID + - Tests + properties: + OrderID: + type: string + description: Optional custom order ID (auto-generated if not provided) + InternalPID: + type: integer + description: Patient internal ID + PatVisitID: + type: integer + description: Visit ID + SiteID: + type: integer + default: 1 + PlacerID: + type: string + Priority: + type: string + enum: [R, S, U] + default: R + description: | + R: Routine + S: Stat + U: Urgent + ReqApp: + type: string + description: Requesting application + Comment: + type: string + Tests: + type: array + items: + type: object + required: + - TestSiteID + properties: + TestSiteID: + type: integer + description: Test definition site ID + TestID: + type: integer + description: Alias for TestSiteID + responses: + '201': + description: Order created successfully with specimens and tests + content: + application/json: + schema: + type: object + properties: + status: + type: string + example: success + message: + type: string + data: + $ref: '../components/schemas/orders.yaml#/OrderTest' + '400': + description: Validation error + '500': + description: Server error + + patch: + tags: [Orders] + summary: Update order + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - OrderID + properties: + OrderID: + type: string + Priority: + type: string + enum: [R, S, U] + OrderStatus: + type: string + enum: [ORD, SCH, ANA, VER, REV, REP] + OrderingProvider: + type: string + DepartmentID: + type: integer + WorkstationID: + type: integer + responses: + '200': + description: Order updated + content: + application/json: + schema: + type: object + properties: + status: + type: string + message: + type: string + data: + $ref: '../components/schemas/orders.yaml#/OrderTest' + + delete: + tags: [Orders] + summary: Delete order + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - OrderID + properties: + OrderID: + type: string + responses: + '200': + description: Order deleted + +/api/ordertest/status: + post: + tags: [Orders] + summary: Update order status + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - OrderID + - OrderStatus + properties: + OrderID: + type: string + OrderStatus: + type: string + enum: [ORD, SCH, ANA, VER, REV, REP] + description: | + ORD: Ordered + SCH: Scheduled + ANA: Analysis + VER: Verified + REV: Reviewed + REP: Reported + responses: + '200': + description: Order status updated + content: + application/json: + schema: + type: object + properties: + status: + type: string + message: + type: string + data: + $ref: '../components/schemas/orders.yaml#/OrderTest' + +/api/ordertest/{id}: + get: + tags: [Orders] + summary: Get order by ID + description: Returns order details with associated specimens and tests + security: + - bearerAuth: [] + parameters: + - name: id + in: path + required: true + schema: + type: string + description: Order ID (e.g., 0025030300001) + responses: + '200': + description: Order details with specimens and tests + content: + application/json: + schema: + type: object + properties: + status: + type: string + message: + type: string + data: + $ref: '../components/schemas/orders.yaml#/OrderTest' diff --git a/docs/patient-visits.yaml b/docs/patient-visits.yaml new file mode 100644 index 0000000..888d424 --- /dev/null +++ b/docs/patient-visits.yaml @@ -0,0 +1,519 @@ +/api/patvisit: + get: + tags: [Patient Visits] + summary: List patient visits + security: + - bearerAuth: [] + parameters: + - name: InternalPID + in: query + schema: + type: integer + description: Filter by internal patient ID (exact match) + - name: PVID + in: query + schema: + type: string + description: Filter by visit ID (partial match) + - name: PatientID + in: query + schema: + type: string + description: Filter by patient ID (partial match) + - name: PatientName + in: query + schema: + type: string + description: Search by patient name (searches in both first and last name) + - name: CreateDateFrom + in: query + schema: + type: string + format: date-time + description: Filter visits created on or after this date + - name: CreateDateTo + in: query + schema: + type: string + format: date-time + description: Filter visits created on or before this date + - name: page + in: query + schema: + type: integer + - name: perPage + in: query + schema: + type: integer + responses: + '200': + description: List of patient visits + content: + application/json: + schema: + type: object + properties: + status: + type: string + message: + type: string + data: + type: array + items: + $ref: '../components/schemas/patient-visit.yaml#/PatientVisit' + total: + type: integer + description: Total number of records + page: + type: integer + description: Current page number + per_page: + type: integer + description: Number of records per page + + post: + tags: [Patient Visits] + summary: Create patient visit + description: | + Creates a new patient visit. PVID is auto-generated with 'DV' prefix if not provided. + Can optionally include PatDiag (diagnosis) and PatVisitADT (ADT information). + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - InternalPID + properties: + PVID: + type: string + description: Visit ID (auto-generated with DV prefix if not provided) + InternalPID: + type: integer + description: Patient ID (required) + EpisodeID: + type: string + description: Episode identifier + SiteID: + type: integer + description: Site reference + PatDiag: + type: object + description: Optional diagnosis information + properties: + DiagCode: + type: string + Diagnosis: + type: string + PatVisitADT: + type: object + description: Optional ADT information + properties: + ADTCode: + type: string + enum: [A01, A02, A03, A04, A08] + LocationID: + type: integer + AttDoc: + type: integer + RefDoc: + type: integer + AdmDoc: + type: integer + CnsDoc: + type: integer + responses: + '201': + description: Visit created successfully + content: + application/json: + schema: + type: object + properties: + status: + type: string + message: + type: string + data: + type: object + properties: + PVID: + type: string + InternalPVID: + type: integer + + patch: + tags: [Patient Visits] + summary: Update patient visit + description: | + Updates an existing patient visit. InternalPVID is required. + Can update main visit data, PatDiag, and add new PatVisitADT records. + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - InternalPVID + properties: + InternalPVID: + type: integer + description: Visit ID (required) + PVID: + type: string + InternalPID: + type: integer + EpisodeID: + type: string + SiteID: + type: integer + PatDiag: + type: object + description: Diagnosis information (will update if exists) + properties: + DiagCode: + type: string + Diagnosis: + type: string + PatVisitADT: + type: array + description: Array of ADT records to add (new records only) + items: + type: object + properties: + ADTCode: + type: string + enum: [A01, A02, A03, A04, A08] + LocationID: + type: integer + AttDoc: + type: integer + RefDoc: + type: integer + AdmDoc: + type: integer + CnsDoc: + type: integer + sequence: + type: integer + description: Used for ordering multiple ADT records + responses: + '200': + description: Visit updated successfully + content: + application/json: + schema: + type: object + properties: + status: + type: string + message: + type: string + data: + type: object + properties: + PVID: + type: string + InternalPVID: + type: integer + + delete: + tags: [Patient Visits] + summary: Delete patient visit + security: + - bearerAuth: [] + responses: + '200': + description: Visit deleted successfully + +/api/patvisit/{id}: + get: + tags: [Patient Visits] + summary: Get visit by ID + security: + - bearerAuth: [] + parameters: + - name: id + in: path + required: true + schema: + type: string + description: PVID (visit identifier like DV00001) + responses: + '200': + description: Visit details + content: + application/json: + schema: + type: object + properties: + status: + type: string + message: + type: string + data: + $ref: '../components/schemas/patient-visit.yaml#/PatientVisit' + +/api/patvisit/patient/{patientId}: + get: + tags: [Patient Visits] + summary: Get visits by patient ID + security: + - bearerAuth: [] + parameters: + - name: patientId + in: path + required: true + schema: + type: integer + description: Internal Patient ID (InternalPID) + responses: + '200': + description: Patient visits list + content: + application/json: + schema: + type: object + properties: + status: + type: string + data: + type: array + items: + $ref: '../components/schemas/patient-visit.yaml#/PatientVisit' + +/api/patvisitadt: + post: + tags: [Patient Visits] + summary: Create ADT record + description: Create a new Admission/Discharge/Transfer record + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '../components/schemas/patient-visit.yaml#/PatVisitADT' + responses: + '201': + description: ADT record created successfully + content: + application/json: + schema: + $ref: '../components/schemas/common.yaml#/SuccessResponse' + + patch: + tags: [Patient Visits] + summary: Update ADT record + description: Update an existing ADT record + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + $ref: '../components/schemas/patient-visit.yaml#/PatVisitADT' + responses: + '200': + description: ADT record updated successfully + content: + application/json: + schema: + $ref: '../components/schemas/common.yaml#/SuccessResponse' + + delete: + tags: [Patient Visits] + summary: Delete ADT visit (soft delete) + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - PVADTID + properties: + PVADTID: + type: integer + description: ADT record ID to delete + responses: + '200': + description: ADT visit deleted successfully + +/api/patvisitadt/visit/{visitId}: + get: + tags: [Patient Visits] + summary: Get ADT history by visit ID + description: Retrieve the complete Admission/Discharge/Transfer history for a visit, including all locations and doctors + security: + - bearerAuth: [] + parameters: + - name: visitId + in: path + required: true + schema: + type: integer + description: Internal Visit ID (InternalPVID) + responses: + '200': + description: ADT history retrieved successfully + content: + application/json: + schema: + type: object + properties: + status: + type: string + example: success + message: + type: string + example: ADT history retrieved + data: + type: array + items: + type: object + properties: + PVADTID: + type: integer + InternalPVID: + type: integer + ADTCode: + type: string + enum: [A01, A02, A03, A04, A08] + LocationID: + type: integer + LocationName: + type: string + AttDoc: + type: integer + AttDocFirstName: + type: string + AttDocLastName: + type: string + RefDoc: + type: integer + RefDocFirstName: + type: string + RefDocLastName: + type: string + AdmDoc: + type: integer + AdmDocFirstName: + type: string + AdmDocLastName: + type: string + CnsDoc: + type: integer + CnsDocFirstName: + type: string + CnsDocLastName: + type: string + CreateDate: + type: string + format: date-time + EndDate: + type: string + format: date-time + delete: + tags: [Patient Visits] + summary: Delete ADT visit (soft delete) + security: + - bearerAuth: [] + requestBody: + required: true + content: + application/json: + schema: + type: object + required: + - PVADTID + properties: + PVADTID: + type: integer + description: ADT record ID to delete + responses: + '200': + description: ADT visit deleted successfully + +/api/patvisitadt/{id}: + get: + tags: [Patient Visits] + summary: Get ADT record by ID + description: Retrieve a single ADT record by its ID, including location and doctor details + security: + - bearerAuth: [] + parameters: + - name: id + in: path + required: true + schema: + type: integer + description: ADT record ID (PVADTID) + responses: + '200': + description: ADT record retrieved successfully + content: + application/json: + schema: + type: object + properties: + status: + type: string + example: success + message: + type: string + example: ADT record retrieved + data: + type: object + properties: + PVADTID: + type: integer + InternalPVID: + type: integer + ADTCode: + type: string + enum: [A01, A02, A03, A04, A08] + LocationID: + type: integer + LocationName: + type: string + AttDoc: + type: integer + AttDocFirstName: + type: string + AttDocLastName: + type: string + RefDoc: + type: integer + RefDocFirstName: + type: string + RefDocLastName: + type: string + AdmDoc: + type: integer + AdmDocFirstName: + type: string + AdmDocLastName: + type: string + CnsDoc: + type: integer + CnsDocFirstName: + type: string + CnsDocLastName: + type: string + CreateDate: + type: string + format: date-time + EndDate: + type: string + format: date-time diff --git a/src/lib/api/orders.js b/src/lib/api/orders.js new file mode 100644 index 0000000..3936479 --- /dev/null +++ b/src/lib/api/orders.js @@ -0,0 +1,117 @@ +import { get, post, patch, del } from './client.js'; + +/** + * Fetch orders list with optional filters and pagination + * @param {Object} params - Query parameters + * @param {number} [params.page=1] - Page number + * @param {number} [params.perPage=20] - Items per page + * @param {number} [params.InternalPID] - Filter by internal patient ID + * @param {string} [params.OrderStatus] - Filter by order status (ORD, SCH, ANA, VER, REV, REP) + * @param {string} [params.include] - Include details (set to 'details' to include specimens and tests) + * @returns {Promise} API response with orders data and pagination + */ +export async function fetchOrders(params = {}) { + const query = new URLSearchParams(params).toString(); + return get(query ? `/api/ordertest?${query}` : '/api/ordertest'); +} + +/** + * Fetch a single order by ID with details + * @param {string} id - Order ID (e.g., 0025030300001) + * @returns {Promise} API response with order details including specimens and tests + */ +export async function fetchOrderById(id) { + return get(`/api/ordertest/${id}`); +} + +/** + * Create a new order with specimens and tests + * @param {Object} data - Order data + * @param {number} data.InternalPID - Patient internal ID (required) + * @param {Array} data.Tests - Array of test objects with TestSiteID (required) + * @param {string} [data.OrderID] - Optional custom order ID (auto-generated if not provided) + * @param {number} [data.PatVisitID] - Visit ID + * @param {number} [data.SiteID=1] - Site ID + * @param {string} [data.PlacerID] - Placer ID + * @param {string} [data.Priority='R'] - Priority (R: Routine, S: Stat, U: Urgent) + * @param {string} [data.ReqApp] - Requesting application + * @param {string} [data.Comment] - Order comment + * @returns {Promise} API response with created order data + */ +export async function createOrder(data) { + return post('/api/ordertest', data); +} + +/** + * Update an existing order + * @param {Object} data - Order data + * @param {string} data.OrderID - Order ID (required) + * @param {string} [data.Priority] - Priority (R, S, U) + * @param {string} [data.OrderStatus] - Order status (ORD, SCH, ANA, VER, REV, REP) + * @param {string} [data.OrderingProvider] - Ordering provider + * @param {number} [data.DepartmentID] - Department ID + * @param {number} [data.WorkstationID] - Workstation ID + * @returns {Promise} API response with updated order data + */ +export async function updateOrder(data) { + return patch('/api/ordertest', data); +} + +/** + * Delete an order + * @param {string} orderId - Order ID to delete + * @returns {Promise} API response + */ +export async function deleteOrder(orderId) { + return del('/api/ordertest', { body: JSON.stringify({ OrderID: orderId }) }); +} + +/** + * Update order status + * @param {Object} data - Status update data + * @param {string} data.OrderID - Order ID (required) + * @param {string} data.OrderStatus - New status (ORD, SCH, ANA, VER, REV, REP) + * @returns {Promise} API response with updated order data + */ +export async function updateOrderStatus(data) { + return post('/api/ordertest/status', data); +} + +/** + * Order status mapping for display + */ +export const ORDER_STATUS = { + ORD: { code: 'ORD', label: 'Ordered', color: 'badge-neutral', description: 'Order has been placed' }, + SCH: { code: 'SCH', label: 'Scheduled', color: 'badge-info', description: 'Order is scheduled' }, + ANA: { code: 'ANA', label: 'Analysis', color: 'badge-warning', description: 'Analysis in progress' }, + VER: { code: 'VER', label: 'Verified', color: 'badge-success', description: 'Results verified' }, + REV: { code: 'REV', label: 'Reviewed', color: 'badge-primary', description: 'Results reviewed' }, + REP: { code: 'REP', label: 'Reported', color: 'badge-secondary', description: 'Report generated' } +}; + +/** + * Order priority mapping for display + */ +export const ORDER_PRIORITY = { + R: { code: 'R', label: 'Routine', color: 'badge-neutral' }, + S: { code: 'S', label: 'Stat', color: 'badge-error' }, + U: { code: 'U', label: 'Urgent', color: 'badge-warning' } +}; + +/** + * Get status display info + * @param {string} statusCode - Status code + * @returns {Object} Status display info + */ +export function getStatusInfo(statusCode) { + return ORDER_STATUS[statusCode] || { code: statusCode, label: statusCode, color: 'badge-ghost' }; +} + +/** + * Get priority display info + * @param {string} priorityCode - Priority code + * @returns {Object} Priority display info + */ +export function getPriorityInfo(priorityCode) { + return ORDER_PRIORITY[priorityCode] || { code: priorityCode, label: priorityCode, color: 'badge-ghost' }; +} diff --git a/src/lib/api/tests.js b/src/lib/api/tests.js index 41fa9af..f9318e1 100644 --- a/src/lib/api/tests.js +++ b/src/lib/api/tests.js @@ -52,6 +52,7 @@ function buildPayload(formData, isUpdate = false) { VisibleScr: formData.VisibleScr ? 1 : 0, VisibleRpt: formData.VisibleRpt ? 1 : 0, CountStat: formData.CountStat ? 1 : 0, + Requestable: formData.Requestable ? 1 : 0, // StartDate is auto-set by backend (created_at) details: {}, refnum: [], diff --git a/src/routes/(app)/master-data/tests/test-modal/TestFormModal.svelte b/src/routes/(app)/master-data/tests/test-modal/TestFormModal.svelte index 6b77659..09674c7 100644 --- a/src/routes/(app)/master-data/tests/test-modal/TestFormModal.svelte +++ b/src/routes/(app)/master-data/tests/test-modal/TestFormModal.svelte @@ -113,6 +113,7 @@ VisibleScr: true, VisibleRpt: true, CountStat: true, + Requestable: true, details: { DisciplineID: null, DepartmentID: null, @@ -168,6 +169,7 @@ VisibleScr: test.VisibleScr === '1' || test.VisibleScr === 1 || test.VisibleScr === true, VisibleRpt: test.VisibleRpt === '1' || test.VisibleRpt === 1 || test.VisibleRpt === true, CountStat: test.CountStat === '1' || test.CountStat === 1 || test.CountStat === true, + Requestable: test.Requestable === '1' || test.Requestable === 1 || test.Requestable === true, details: { DisciplineID: test.DisciplineID || null, DepartmentID: test.DepartmentID || null, diff --git a/src/routes/(app)/master-data/tests/test-modal/tabs/BasicInfoTab.svelte b/src/routes/(app)/master-data/tests/test-modal/tabs/BasicInfoTab.svelte index f0c0b15..51b263e 100644 --- a/src/routes/(app)/master-data/tests/test-modal/tabs/BasicInfoTab.svelte +++ b/src/routes/(app)/master-data/tests/test-modal/tabs/BasicInfoTab.svelte @@ -187,7 +187,7 @@

Display Settings

-
+
@@ -255,6 +255,20 @@ Count
+ + +
+ Request + +
diff --git a/src/routes/(app)/master-data/tests/test-modal/tabs/RefNumTab.svelte b/src/routes/(app)/master-data/tests/test-modal/tabs/RefNumTab.svelte index 3a91668..c3cafff 100644 --- a/src/routes/(app)/master-data/tests/test-modal/tabs/RefNumTab.svelte +++ b/src/routes/(app)/master-data/tests/test-modal/tabs/RefNumTab.svelte @@ -20,7 +20,7 @@ AgeEnd: '', AgeUnit: 'years', SpcType: '', - Display: 0, + Display: 1, Flag: '', Interpretation: '', Notes: '' @@ -96,7 +96,7 @@ AgeEnd: '', AgeUnit: 'years', SpcType: '', - Display: 0, + Display: 1, Flag: '', Interpretation: '', Notes: '' diff --git a/src/routes/(app)/orders/+page.svelte b/src/routes/(app)/orders/+page.svelte new file mode 100644 index 0000000..0179945 --- /dev/null +++ b/src/routes/(app)/orders/+page.svelte @@ -0,0 +1,341 @@ + + +
+ +
+
+

Orders Management

+

Manage laboratory orders and track status

+
+
+ + +
+
+ + + + + +
+ +
+
+ + + orderForm = { open: false, order: null, loading: false }} +/> + + + orderDetail = { open: false, order: null, loading: false }} +/> + + + +
+

+ Delete order + {deleteModal.order?.OrderID}? +

+

This action cannot be undone.

+
+ {#snippet footer()} + + + {/snippet} +
diff --git a/src/routes/(app)/orders/OrderDetailModal.svelte b/src/routes/(app)/orders/OrderDetailModal.svelte new file mode 100644 index 0000000..8d3886e --- /dev/null +++ b/src/routes/(app)/orders/OrderDetailModal.svelte @@ -0,0 +1,262 @@ + + + + {#if loading} +
+ +
+ {:else if order} +
+ +
+
+
+ + {order.OrderID} +
+

+ Created: {formatDate(order.OrderDate)} +

+
+
+ {#if order.OrderStatus} + {@const status = getStatusInfo(order.OrderStatus)} + + {status.label} + + {/if} + {#if order.Priority} + {@const priority = getPriorityInfo(order.Priority)} + + {priority.label} Priority + + {:else} + {@const priority = getPriorityInfo('R')} + + {priority.label} Priority + + {/if} +
+
+ + + {#if order.PatientID || order.InternalPID} +
+

+ + Patient Information +

+
+
+ Patient ID: + {order.PatientID || '-'} +
+
+ Internal PID: + {order.InternalPID} +
+ {#if order.PatientName} +
+ Name: + {order.PatientName} +
+ {/if} +
+
+ {/if} + + +
+

+ + Order Information +

+
+ {#if order.PlacerID} +
+ Placer ID: + {order.PlacerID} +
+ {/if} + {#if order.PatVisitID} +
+ Visit ID: + {order.PatVisitID} +
+ {/if} + {#if order.SiteID} +
+ Site ID: + {order.SiteID} +
+ {/if} + {#if order.ReqApp} +
+ Requesting App: + {order.ReqApp} +
+ {/if} + {#if order.OrderingProvider} +
+ Ordering Provider: + {order.OrderingProvider} +
+ {/if} +
+ + {#if order.Comment} +
+ Comment: +

{order.Comment}

+
+ {/if} +
+ + + {#if order.Tests && order.Tests.length > 0} +
+

+ + Tests ({order.Tests.length}) +

+
+ + + + + + + + + + {#each order.Tests as test, index (test.TestSiteID || index)} + {@const testStatus = getStatusInfo(test.Status || order.OrderStatus)} + + + + + + {/each} + +
Test Site IDTest IDStatus
{test.TestSiteID}{test.TestID || '-'} + + {testStatus.label} + +
+
+
+ {/if} + + + {#if order.Specimens && order.Specimens.length > 0} +
+

+ + Specimens ({order.Specimens.length}) +

+
+ {#each order.Specimens as specimen, index (specimen.SpecimenID || index)} +
+ +
+

{specimen.SpecimenID || 'Specimen'}

+ {#if specimen.ContainerType} +

Container: {specimen.ContainerType}

+ {/if} +
+ {#if specimen.CollectionDate} + + {formatShortDate(specimen.CollectionDate)} + + {/if} +
+ {/each} +
+
+ {/if} + + + {#if order.StatusHistory && order.StatusHistory.length > 0} +
+

Status History

+
+ {#each order.StatusHistory as history, index (history.Date || index)} + {@const histStatus = getStatusInfo(history.Status)} +
+ {formatDate(history.Date)} + {histStatus.label} + {#if history.By} + by {history.By} + {/if} +
+ {/each} +
+
+ {/if} +
+ {:else} +
+ +

No order data available

+
+ {/if} + + {#snippet footer()} + + {/snippet} +
diff --git a/src/routes/(app)/orders/OrderFormModal.svelte b/src/routes/(app)/orders/OrderFormModal.svelte new file mode 100644 index 0000000..d2432ed --- /dev/null +++ b/src/routes/(app)/orders/OrderFormModal.svelte @@ -0,0 +1,464 @@ + + + +
+ + {#if formError} +
+ + {formError} +
+ {/if} + + +
+ + +
+ + +
+

+ + Patient * +

+ + {#if selectedPatient} +
+
+
+

{selectedPatient.FullName || selectedPatient.PatientID}

+

ID: {selectedPatient.PatientID}

+

Internal PID: {selectedPatient.InternalPID}

+
+ {#if !order} + + {/if} +
+
+ {:else} +
+
+
+ + e.key === 'Enter' && searchPatients()} + /> +
+ +
+ + {#if showPatientSearch && patientSearchResults.length > 0} +
+ {#each patientSearchResults as patient (patient.InternalPID)} + + {/each} +
+ {:else if showPatientSearch} +

No patients found

+ {/if} +
+ {/if} +
+ + +
+

+ + Order Details +

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

+ + Tests * + {#if formData.Tests.length > 0} + {formData.Tests.length} + {/if} +

+ + +
+
+ + e.key === 'Enter' && (e.preventDefault(), addTest())} + /> +
+ +
+ + +
+ {#if formData.Tests.length > 0} +
+ {#each formData.Tests as test, index (test.TestSiteID)} +
+
+
+ {index + 1} +
+
+

Test Site ID

+

{test.TestSiteID}

+
+
+ +
+ {/each} +
+ {:else} +
+ +

No tests added yet

+

Enter a Test Site ID and click Add

+
+ {/if} +
+
+
+
+ + {#snippet footer()} + + + {/snippet} +
diff --git a/src/routes/(app)/orders/OrderList.svelte b/src/routes/(app)/orders/OrderList.svelte new file mode 100644 index 0000000..b5c184e --- /dev/null +++ b/src/routes/(app)/orders/OrderList.svelte @@ -0,0 +1,177 @@ + + +
+ +
+ + {#snippet cell({ column, row, value })} + {#if column.key === 'OrderStatus'} + {@const status = getStatusInfo(value)} + + {status.label} + + {:else if column.key === 'Priority'} + {@const priority = getPriorityInfo(value || 'R')} + + {priority.label} + + {:else if column.key === 'OrderDate'} + {formatDate(value)} + {:else if column.key === 'PatientName'} + + {value || '-'} + + {:else if column.key === 'OrderingProvider'} + + {value || '-'} + + {:else if column.key === 'actions'} +
+ + + +
+ {:else} + + {value || '-'} + + {/if} + {/snippet} +
+
+ + + {#if totalItems > 0} +
+
+ Showing {(currentPage - 1) * perPage + 1} - + {Math.min(currentPage * perPage, totalItems)} of + {totalItems} orders +
+ +
+ + + + Page {currentPage} of {totalPages} + + + +
+
+ {/if} +
diff --git a/src/routes/(app)/orders/OrderSearchBar.svelte b/src/routes/(app)/orders/OrderSearchBar.svelte new file mode 100644 index 0000000..1195bbc --- /dev/null +++ b/src/routes/(app)/orders/OrderSearchBar.svelte @@ -0,0 +1,166 @@ + + +
+
+ +
+ +
+ + +
+ + +
+ + +
+ + +
+ + +
+
+ + +
+ +
+ + +
+ + +
+ + +
+ + +
+ {#if hasFilters()} + + {:else} +
+ {/if} + +
+
+
+
diff --git a/src/routes/(app)/visits/+page.svelte b/src/routes/(app)/visits/+page.svelte index 8a5bb10..e3b9743 100644 --- a/src/routes/(app)/visits/+page.svelte +++ b/src/routes/(app)/visits/+page.svelte @@ -6,6 +6,7 @@ import VisitList from './VisitList.svelte'; import VisitFormModal from './VisitFormModal.svelte'; import VisitADTHistoryModal from './VisitADTHistoryModal.svelte'; + import ADTFormModal from './ADTFormModal.svelte'; import Modal from '$lib/components/Modal.svelte'; import { Plus, LayoutGrid, List } from 'lucide-svelte'; @@ -52,6 +53,12 @@ visit: null }); + let adtForm = $state({ + open: false, + visit: null, + adt: null + }); + onMount(() => { // Don't auto-load - wait for search visits = []; @@ -201,6 +208,15 @@ toastError(err.message || 'Failed to delete visit'); } } + + // ADT Form + function openADTForm(visit) { + adtForm = { open: true, visit, adt: null }; + } + + function handleADTSaved() { + handleSearch(); + }
@@ -259,6 +275,7 @@ onDeleteVisit={confirmDelete} onViewHistory={openADTHistory} onDischarge={openDischargeModal} + onAddADT={openADTForm} />
@@ -336,3 +353,11 @@ {/snippet} + + + diff --git a/src/routes/(app)/visits/ADTFormModal.svelte b/src/routes/(app)/visits/ADTFormModal.svelte new file mode 100644 index 0000000..e775876 --- /dev/null +++ b/src/routes/(app)/visits/ADTFormModal.svelte @@ -0,0 +1,288 @@ + + + + {#if loading} +
+ +
+ {:else} +
e.preventDefault()}> + + {#if visit} +
+
+ + Visit: + {visit.PVID} + {#if visit.PatientID} + | Patient: {visit.PatientID} + {/if} +
+
+ {/if} + +
+ + +
+ + + {#if formErrors.CreateDate} + {formErrors.CreateDate} + {/if} +
+
+ +
+ + +
+ + +
+
+ +
+

+ + Doctors +

+
+ + + + + + + +
+
+
+ {/if} + + {#snippet footer()} +
+ + +
+ {/snippet} +
diff --git a/src/routes/(app)/visits/VisitList.svelte b/src/routes/(app)/visits/VisitList.svelte index 27b3c75..f900511 100644 --- a/src/routes/(app)/visits/VisitList.svelte +++ b/src/routes/(app)/visits/VisitList.svelte @@ -1,5 +1,5 @@
@@ -88,22 +101,33 @@ {#snippet cell({ column, row })} {#if column.key === 'Status'} {@html getStatusBadge(row)} + {:else if column.key === 'PatientName'} + {getPatientName(row)} {:else if column.key === 'actions'}
- - + {#if onAddADT} + + {/if}
{:else} {row[column.key] || '-'}