- + + diff --git a/src/css/style.css b/src/css/style.css index 44487da..d3b172c 100644 --- a/src/css/style.css +++ b/src/css/style.css @@ -507,14 +507,6 @@ ORIGINAL CUSTOM COMPONENTS ======================================== */ - /* Gradient text effect */ - .gradient-text { - background: linear-gradient(to right, var(--color-primary), var(--color-secondary), var(--color-accent)); - -webkit-background-clip: text; - background-clip: text; - -webkit-text-fill-color: transparent; - } - /* Glass morphism card - uses theme-aware colors */ .glass-card { background-color: var(--color-base-200); @@ -524,25 +516,19 @@ box-shadow: 0 10px 40px -10px oklch(0 0 0 / 0.2); } - /* Hero section styles */ - .hero-gradient { - background: linear-gradient(135deg, var(--color-base-100), var(--color-base-200), var(--color-base-100)); - background-size: 400% 400%; - animation: gradient 15s ease infinite; + /* Hero section styles - solid background */ + .hero-solid { + background-color: var(--color-base-100); } - @keyframes gradient { - 0% { - background-position: 0% 50%; - } + /* Brand text styling */ + .brand-text { + color: var(--color-primary); + font-weight: 700; + } - 50% { - background-position: 100% 50%; - } - - 100% { - background-position: 0% 50%; - } + .brand-text:hover { + opacity: 0.8; } /* Project card hover effect - uses theme-aware colors */ @@ -600,9 +586,18 @@ border-left: 2px solid var(--color-primary); } - /* Section container */ + /* Section container - narrow for reading */ .section-container { - max-width: 80rem; + max-width: 48rem; + margin-left: auto; + margin-right: auto; + padding-left: 1rem; + padding-right: 1rem; + } + + /* Wider container for grids and layouts */ + .section-container-wide { + max-width: 72rem; margin-left: auto; margin-right: auto; padding-left: 1rem; @@ -610,14 +605,16 @@ } @media (min-width: 640px) { - .section-container { + .section-container, + .section-container-wide { padding-left: 1.5rem; padding-right: 1.5rem; } } @media (min-width: 1024px) { - .section-container { + .section-container, + .section-container-wide { padding-left: 2rem; padding-right: 2rem; } @@ -968,4 +965,27 @@ -webkit-box-orient: vertical; overflow: hidden; } + + /* Custom scrollbar for sidenav */ + .scrollbar-thin { + scrollbar-width: thin; + scrollbar-color: var(--color-base-300) transparent; + } + + .scrollbar-thin::-webkit-scrollbar { + width: 6px; + } + + .scrollbar-thin::-webkit-scrollbar-track { + background: transparent; + } + + .scrollbar-thin::-webkit-scrollbar-thumb { + background-color: var(--color-base-300); + border-radius: 3px; + } + + .scrollbar-thin::-webkit-scrollbar-thumb:hover { + background-color: var(--color-neutral); + } } \ No newline at end of file diff --git a/src/index.njk b/src/index.njk index 09df572..9f1a54b 100644 --- a/src/index.njk +++ b/src/index.njk @@ -5,32 +5,30 @@ description: Innovative projects and ideas --- - + - - + + - - - + + Development Team - + Building the - Future of Tech. + Future of Tech. - + We are a team of passionate developers, designers, and innovators crafting digital experiences that matter. - - - - - Meet the Team - - + + + + + Meet the Team + @@ -38,7 +36,7 @@ description: Innovative projects and ideas - + Portfolio Our Work @@ -85,7 +83,7 @@ description: Innovative projects and ideas {% if collections.clqms.length > 0 %} - + CLQMS Project Documentation diff --git a/src/projects/clqms01/index.njk b/src/projects/clqms01/index.njk index 869a343..1590974 100644 --- a/src/projects/clqms01/index.njk +++ b/src/projects/clqms01/index.njk @@ -6,129 +6,179 @@ date: 2025-12-01 order: 0 --- - - - - - Project Workspace - Clinical Laboratory Quality Management System - - A robust, mission-critical API suite designed to streamline modern laboratory operations through precision, scalability, - and modular design. - - - - - - - - - - - - 🏗️ - Architecture - - Core system design, authentication, and strategic pillars + + + + - - - - - - - # - Core Documentation - - - - {% for post in collections.clqms %} - {# Filter to show only root-level docs, excluding index and sub-directories #} - {% set is_suggestion = "/suggestion/" in post.url %} - {% set is_review = "/review/" in post.url %} - {% if post.url != page.url and not is_suggestion and not is_review and post.data.title %} - - - - {{ post.data.title }} - - {{ post.data.description | default('Technical reference for CLQMS implementation.') }} - - - - {{ post.content | readingTime }} - - + + + + + + + + Suggestions - {% endif %} - {% endfor %} - - - - - - - - Strategic Pillars - - - - 🎯 - Precision & Accuracy - - Strict validation for all laboratory parameters and multi-variant reference - ranges. + + {% for post in collections.clqms %} + {% if "/suggestion/" in post.url and post.data.title and post.url != "/projects/clqms01/suggestion/" %} + + {{ post.data.title }} + + {% endif %} + {% endfor %} + - - - ⚡ - Scalability - - Optimized database and API architecture for high-volume diagnostic environments. - - - - 📜 - Compliance - - Built-in audit trails and granular status history for full medical traceability. - - - - 🔗 - Interoperability - - Modular drivers designed for seamless LIS, HIS, and analyzer integration. + + + + + + + + Reviews + + + {% for post in collections.clqms %} + {% if "/review/" in post.url and post.data.title and post.url != "/projects/clqms01/review/" %} + + {{ post.data.title }} + + {% endif %} + {% endfor %} + - + + + + + + + + + Project Workspace + Clinical Laboratory Quality Management System + + A robust, mission-critical API suite designed to streamline modern laboratory operations through precision, scalability, + and modular design. + + + + + + + + # + Quick Access + + + + + 🏗️ + Core Docs + + Architecture, auth, and technical docs + + + + 💡 + Suggestions + + Future proposals and roadmap ideas + + + + 🔍 + Reviews + + Expert code reviews and audits + + + + + + + + # + Core Documentation + + + {% for post in collections.clqms %} + {% set is_suggestion = "/suggestion/" in post.url %} + {% set is_review = "/review/" in post.url %} + {% if post.url != page.url and not is_suggestion and not is_review and post.data.title %} + + + + {{ post.data.title }} + + {{ post.data.description | default('Technical reference for CLQMS implementation.') }} + + + {{ post.content | readingTime }} + + + {% endif %} + {% endfor %} + + + + + + Strategic Pillars + + + + 🎯 + Precision & Accuracy + + Strict validation for all laboratory parameters and multi-variant reference ranges. + + + + ⚡ + Scalability + + Optimized database and API architecture for high-volume diagnostic environments. + + + + 📜 + Compliance + + Built-in audit trails and granular status history for full medical traceability. + + + + 🔗 + Interoperability + + Modular drivers designed for seamless LIS, HIS, and analyzer integration. + + + + + - \ No newline at end of file + diff --git a/src/projects/clqms01/suggestion/001-testdef.md b/src/projects/clqms01/suggestion/001-testdef.md index 96c63f7..305175d 100644 --- a/src/projects/clqms01/suggestion/001-testdef.md +++ b/src/projects/clqms01/suggestion/001-testdef.md @@ -2,14 +2,13 @@ layout: clqms-post.njk tags: clqms title: "Proposal: Test Definition Architecture Overhaul" -description: "Simplify database schema and improve query performance for test definitions" +description: "Remove magic numbers and enforce type safety using PHP Enums and Svelte stores" date: 2026-01-09 order: 1 --- # 🚀 Proposal: Test Definition Architecture Overhaul by Gemini 3 - **Target:** `testdef` Module **Objective:** Simplify database schema, improve query performance, and reduce code complexity. @@ -17,27 +16,28 @@ order: 1 ## 1. The Problem: "The Shredded Document" 🧩 -**Current Status:** +**Current Status:** Defining a single Lab Test currently requires joining 4-5 rigid tables: -* `testdefsite` (General Info) -* `testdeftech` (Technical Details) -* `testdefcal` (Calculations) -* `testdefgrp` (Grouping) +- `testdefsite` (General Info) +- `testdeftech` (Technical Details) +- `testdefcal` (Calculations) +- `testdefgrp` (Grouping) **Why it hurts:** -* **Complex Queries:** To get a full test definition, we write massive SQL joins. -* **Rigid Schema:** Adding a new technical attribute requires altering table schemas and updating multiple DAO files. -* **Maintenance Nightmare:** Logic is scattered. To understand a test, you have to look in five places. +- **Complex Queries:** To get a full test definition, we write massive SQL joins. +- **Rigid Schema:** Adding a new technical attribute requires altering table schemas and updating multiple DAO files. +- **Maintenance Nightmare:** Logic is scattered. To understand a test, you have to look in five places. --- ## 2. The Solution: JSON Configuration 📄 -**Strategy:** Treat a Test Definition as a **Document**. +**Strategy:** Treat a Test Definition as a **Document**. + We will consolidate the variable details (Technique, Calculations, Reference Ranges) into a structured `JSON` column within a single table. ### Schema Change -Old 5 tables $\rightarrow$ **1 Main Table**. +Old 5 tables → **1 Main Table**. ```sql CREATE TABLE LabTestDefinitions ( @@ -61,45 +61,73 @@ Instead of columns for every possible biological variable, we store a flexible d "specimen": "Serum", "result_type": "NUMERIC", "units": "mg/dL", - "formulas": { - "calculation": "primary_result * dilution_factor" - }, "reference_ranges": [ - { - "label": "Adult Male", - "sex": "M", - "min_age": 18, - "max_age": 99, - "min_val": 70, - "max_val": 100 - }, - { - "label": "Pediatric", - "max_age": 18, - "min_val": 60, - "max_val": 90 - } + { "sex": "M", "min": 70, "max": 100 }, + { "sex": "F", "min": 60, "max": 90 } ] } ``` --- -## 3. The Benefits 🏆 +## 3. How to Query (The Magic) 🪄 + +This is where the new design shines. No more joins. + +### A. Fetching a Test (The Usual Way) + +Just select the row. The application gets the full definition instantly. + +```sql +SELECT * FROM LabTestDefinitions WHERE code = 'GLUC'; +``` + +### B. Searching Inside JSON (The Cool Way) + +Need to find all tests that use "Serum"? Use the JSON arrow operator (`->>`). + +**MySQL / MariaDB:** +```sql +SELECT code, name +FROM LabTestDefinitions +WHERE configuration->>'$.specimen' = 'Serum'; +``` + +**PostgreSQL:** +```sql +SELECT code, name +FROM LabTestDefinitions +WHERE configuration->>'specimen' = 'Serum'; +``` + +### C. Performance Optimization 🏎️ + +If we search by "Technique" often, we don't index the JSON string. We add a Generated Column. + +```sql +ALTER TABLE LabTestDefinitions +ADD COLUMN technique_virtual VARCHAR(50) +GENERATED ALWAYS AS (configuration->>'$.technique') VIRTUAL; + +CREATE INDEX idx_technique ON LabTestDefinitions(technique_virtual); +``` + +**Result:** Querying the JSON is now as fast as a normal column. + +--- + +## 4. The Benefits 🏆 | Feature | Old Way (Relational) | New Way (JSON Document) | | :--- | :--- | :--- | | **Fetch Speed** | Slow (4+ Joins) | Instant (1 Row Select) | -| **Flexibility** | Requires ALTER TABLE | Edit JSON & Save | -| **Search** | Complex SQL | Fast JSON Indexing | +| **Flexibility** | Requires `ALTER TABLE` | Edit JSON & Save | +| **Search** | Complex SQL | Fast JSON Operators | | **Code Logic** | Mapping 5 SQL results | `json_decode()` → Object | --- -### Next Steps 🗒️ +## Next Steps -- [ ] Create migration for `LabTestDefinitions` table. -- [ ] Port 5 sample tests from the old structure to JSON format for verification. - ---- -_Last updated: 2026-01-09 08:40:21_ +1. Create migration for `LabTestDefinitions` table. +2. Port 5 sample tests from the old structure to JSON format for verification. \ No newline at end of file diff --git a/src/projects/clqms01/suggestion/002-valueset.md b/src/projects/clqms01/suggestion/002-valueset.md index f5f6442..7eb1573 100644 --- a/src/projects/clqms01/suggestion/002-valueset.md +++ b/src/projects/clqms01/suggestion/002-valueset.md @@ -7,40 +7,105 @@ date: 2026-01-09 order: 2 --- -# 🚀 Proposal: Valueset ("God Table") Replacement by Gemini 3 - +# 🚀 Proposal: Valueset ("God Table") Replacement **Target:** `valueset` / `valuesetdef` Tables -**Objective:** Remove "Magic Numbers," enforce Type Safety, and optimize Frontend performance. +**Objective:** Eliminate "Magic Numbers," enforce Type Safety, and optimize Frontend performance by moving system logic into the codebase. --- ## 1. The Problem: "Magic Number Soup" 🥣 **Current Status:** -We store disparate system logic (Gender, Test Status, Colors, Payment Types) in a single massive table called `valueset`. -* **Code relies on IDs:** `if ($status == 1045) ...` -* **Frontend Overload:** Frontend makes frequent DB calls just to populate simple dropdowns. -* **No Type Safety:** Nothing stops a developer from assigning a "Payment Status" ID to a "Gender" column. +We store disparate logic (Gender, Test Status, Specimen Types, Priority) in a single massive table called `valueset`. + +* **Code relies on IDs:** Developers must remember that `1045` means `VERIFIED`. `if ($status == 1045)` is unreadable. +* **Frontend Overload:** The frontend makes frequent, redundant database calls just to populate simple dropdowns. +* **No Type Safety:** Nothing prevents assigning a "Payment Status" ID to a "Gender" column. +* **Invisible History:** Database changes are hard to track. Who changed "Verified" to "Authorized"? When? Why? --- -## 2. The Solution: Enums & API Store 🛠️ +## 2. The Solution: Single Source of Truth (SSOT) 🛠️ -**Strategy:** Split "System Logic" from the Database. -Use **PHP 8.1 Native Enums** for business rules and serve them via a cached API to Svelte. +**Strategy:** Move "System Logic" from the Database into the Code. This creates a "Single Source of Truth." -### Step A: The Backend (PHP Enums) -We delete the rows from the database and define them in code where they belong. +### Step A: The Backend Implementation +#### Option 1: The "God File" (Simple & Legacy Friendly) +**File:** `application/libraries/Valuesets.php` + +This file holds every dropdown option in the system. Benefits include **Ctrl+Click** navigation and clear **Git History**. + +```php + 'Male', + 'F' => 'Female', + 'U' => 'Unknown' + ]; + + // 2. Test Status + const TEST_STATUS = [ + 'PENDING' => 'Waiting for Results', + 'IN_PROCESS' => 'Analyzing', + 'VERIFIED' => 'Verified & Signed', + 'REJECTED' => 'Sample Rejected' + ]; + + // 3. Sample Types + const SPECIMEN_TYPE = [ + 'SERUM' => 'Serum', + 'PLASMA' => 'Plasma', + 'URINE' => 'Urine', + 'WB' => 'Whole Blood' + ]; + + // 4. Urgency + const PRIORITY = [ + 'ROUTINE' => 'Routine', + 'CITO' => 'Cito (Urgent)', + 'STAT' => 'Immediate (Life Threatening)' + ]; + + /** + * Helper to get all constants as one big JSON object. + * Useful for sending everything to Svelte in one go. + */ + public static function getAll() { + return [ + 'gender' => self::mapForFrontend(self::GENDER), + 'test_status' => self::mapForFrontend(self::TEST_STATUS), + 'specimen_type' => self::mapForFrontend(self::SPECIMEN_TYPE), + 'priority' => self::mapForFrontend(self::PRIORITY), + ]; + } + + // Helper to format array like: [{value: 'M', label: 'Male'}, ...] + private static function mapForFrontend($array) { + $result = []; + foreach ($array as $key => $label) { + $result[] = ['value' => $key, 'label' => $label]; + } + return $result; + } +} +``` + +#### Option 2: PHP 8.1+ Enums (Modern Approach) **File:** `App/Enums/TestStatus.php` + ```php enum TestStatus: string { case PENDING = 'PENDING'; case VERIFIED = 'VERIFIED'; case REJECTED = 'REJECTED'; - // Helper for Frontend Labels public function label(): string { return match($this) { self::PENDING => 'Waiting for Results', @@ -51,39 +116,42 @@ enum TestStatus: string { } ``` -### Step B: The API Contract +--- -**GET `/api/config/valueset`** -Instead of 20 small network requests for 20 dropdowns, the Frontend requests the entire dictionary once on load. +## 3. The "Aha!" Moment (Usage) 🔌 -**Response:** -```json -{ - "test_status": [ - { "value": "PENDING", "label": "Waiting for Results" }, - { "value": "VERIFIED", "label": "Verified & Signed" } - ], - "gender": [ - { "value": "M", "label": "Male" }, - { "value": "F", "label": "Female" } - ] +### 1. In PHP Logic +No more querying the DB to check if a status is valid. Logic is now instant and readable. + +```php +// ❌ Old Way (Sick) 🤮 +// $status = $this->db->get_where('valuesetdef', ['id' => 505])->row(); +if ($status_id == 505) { ... } + +// ✅ New Way (Clean) 😎 +if ($input_status === 'VERIFIED') { + // We know exactly what this string means. + send_email_to_patient(); +} + +// Validation is instant +if (!array_key_exists($input_gender, Valuesets::GENDER)) { + die("Invalid Gender!"); } ``` -### Step C: The Frontend (Svelte Store) +### 2. In Svelte (Frontend - Svelte 5 Runes) +We use a global state (rune) to store this configuration. The frontend engineers no longer need to check database IDs; they just use the human-readable keys. -We use a Svelte Store to cache this data globally. No more SQL queries for dropdowns. - -**Component Usage:** ```svelte {% raw %} -Status: - - {#each $config.test_status as option} +Specimen Type: + + {#each config.valueset.specimen_type as option} {option.label} {/each} @@ -92,22 +160,24 @@ We use a Svelte Store to cache this data globally. No more SQL queries for dropd --- -## 3. The Benefits 🏆 +## 4. Why This is Better 🏆 -| Feature | Old Way (valueset Table) | New Way (Enums + Store) | +| Feature | Old Way (`valueset` Table) | New Way (The God File) | | :--- | :--- | :--- | -| **Performance** | DB Query per dropdown | Zero DB Hits (Cached) | -| **Code Quality** | `if ($id == 505)` | `if ($s == Status::PENDING)` | -| **Reliability** | IDs can change/break | Code is immutable | -| **Network** | "Chatty" (Many requests) | Efficient (One request) | +| **Performance** | DB Query per dropdown | **Millions of times faster** (Constant) | +| **Readability** | `if ($id == 505)` | `if ($status == 'REJECTED')` | +| **Navigation** | Search DB rows | **Ctrl+Click** in VS Code | +| **Git History** | Opaque/Invisible | **Transparent** (See who changed what) | +| **Reliability** | IDs can change/break | Constants are immutable | --- -### Next Steps 🗒️ +## 5. Next Steps 🗒️ -- [ ] Define `TestStatus` and `TestType` Enums in PHP. -- [ ] Create the `/api/config/valueset` endpoint. -- [ ] Update one Svelte form to use the new Store instead of an API fetch. +- [ ] Implement `Valuesets.php` in the libraries folder. +- [ ] Create a controller to expose `Valuesets::getAll()` as JSON. +- [ ] Update the Svelte `configStore` to fetch this dictionary on app load. +- [ ] Refactor the Patient Registration form to use the new constants. --- -_Last updated: 2026-01-09 08:40:21_ \ No newline at end of file +_Last updated: 2026-01-09_ diff --git a/src/projects/clqms01/suggestion/003-decision-001-002.md b/src/projects/clqms01/suggestion/003-decision-001-002.md new file mode 100644 index 0000000..3877814 --- /dev/null +++ b/src/projects/clqms01/suggestion/003-decision-001-002.md @@ -0,0 +1,61 @@ +--- +layout: clqms-post.njk +tags: clqms +title: "Backend Dev: Implementation Plan" +description: "Consolidated developer review for suggestions 001 and 002" +date: 2026-01-09 +order: 3 +--- + +# 🛠️ Backend Dev: Decision for 001 & 002 + +This document summarizes the technical decision and implementation plan based on the previous proposals: +- [001-testdef.md](./001-testdef.md) (Test Definition Architecture) +- [002-valueset.md](./002-valueset.md) (Valueset Replacement) + +--- + +## 1. Suggestion 001: Test Definition Architecture +**Action: DEFERRED** + +- We will skip the architectural overhaul of the `testdef` module for now. +- Current table structure remains unchanged to maintain stability. + +--- + +## 2. Suggestion 002: Valueset Implementation +**Action: ADOPTED (Code-First Migration)** + +We are moving forward with moving system constants from the database into the codebase, but with a focus on existing API compatibility. + +### Technical Requirements: +- **Maintain Endpoint integrity**: The API endpoint must remain functional without changes to the frontend. +- **Data Migration**: Move values (Gender, Status, etc.) from the `valueset` table into a centralized PHP constant or config file named `Valuesets.php`. +- **Controller Refactor**: Update the backend controller to serve the JSON response from the file rather than a database query. + +--- + +## 3. Translation Logic (Getting labels like "Female" from "F") + +When pulling data from the database, you can translate raw keys into readable labels instantly using the new class. + +### Example PHP Usage: +```php +// Direct mapping in your controller or view +$genderLabel = Valuesets::GENDER[$patient->gender] ?? 'Unknown'; + +// Or use a helper method in the Valuesets class +public static function getLabel($category, $key) { + return self::{$category}[$key] ?? $key; +} +``` + +--- + +## 4. Implementation Steps +1. Create `Valuesets.php` in the backend libraries folder. +2. Refactor `Valueset` controller to return data from `Valuesets` class. +3. Verify JSON output matches the current schema to ensure no frontend breaks. + +--- +_Last updated: 2026-01-09 12:47:00_ diff --git a/src/projects/clqms01/suggestion/audit-logging-plan.md b/src/projects/clqms01/suggestion/audit-logging-plan.md new file mode 100644 index 0000000..6d22fb6 --- /dev/null +++ b/src/projects/clqms01/suggestion/audit-logging-plan.md @@ -0,0 +1,482 @@ +--- +layout: clqms-post.njk +tags: clqms +title: "CLQMS: Audit Logging Architecture Plan" +description: "Comprehensive audit trail strategy for tracking changes across master data, patient records, and laboratory operations" +date: 2026-02-19 +order: 4 +--- + +# Audit Logging Architecture Plan for CLQMS + +> **Clinical Laboratory Quality Management System (CLQMS)** - A comprehensive audit trail strategy for tracking changes across master data, patient records, and laboratory operations. + +--- + +## Executive Summary + +This document outlines a unified audit logging architecture for CLQMS, designed to provide complete traceability of data changes while maintaining optimal performance and maintainability. The approach separates audit logs into three domain-specific tables, utilizing JSON for flexible value storage. + +--- + +## 1. Current State Analysis + +### Existing Audit Infrastructure + +| Aspect | Current Status | +|--------|---------------| +| **Database Tables** | 3 tables exist in migrations (patreglog, patvisitlog, specimenlog) | +| **Implementation** | Tables created but not actively used | +| **Structure** | Fixed column approach (FldName, FldValuePrev) | +| **Code Coverage** | No models or controllers implemented | +| **Application Logging** | Basic CodeIgniter file logging for debug/errors | + +### Pain Points Identified + +- ❌ **3 separate tables** with nearly identical schemas +- ❌ **Fixed column structure** - rigid and requires schema changes for new entities +- ❌ **No implementation** - audit tables exist but aren't populated +- ❌ **Maintenance overhead** - adding new entities requires new migrations + +--- + +## 2. Proposed Architecture + +### 2.1 Domain Separation + +We categorize audit logs by **data domain** and **access patterns**: + +| Table | Domain | Volume | Retention | Use Case | +|-------|--------|--------|-----------|----------| +| `master_audit_log` | Reference Data | Low | Permanent | Organizations, Users, ValueSets | +| `patient_audit_log` | Patient Records | Medium | 7 years | Demographics, Contacts, Insurance | +| `order_audit_log` | Operations | High | 2 years | Orders, Tests, Specimens, Results | + +### 2.2 Unified Table Structure + +#### Master Audit Log + +```sql +CREATE TABLE master_audit_log ( + id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + entity_type VARCHAR(50) NOT NULL, -- 'organization', 'user', 'valueset' + entity_id VARCHAR(36) NOT NULL, -- UUID or primary key + action ENUM('CREATE', 'UPDATE', 'DELETE', 'PATCH') NOT NULL, + + old_values JSON NULL, -- Complete snapshot before change + new_values JSON NULL, -- Complete snapshot after change + changed_fields JSON, -- Array of modified field names + + -- Context + user_id VARCHAR(36), + site_id VARCHAR(36), + ip_address VARCHAR(45), + user_agent VARCHAR(500), + app_version VARCHAR(20), + + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + + INDEX idx_entity (entity_type, entity_id), + INDEX idx_created (created_at), + INDEX idx_user (user_id, created_at) +) ENGINE=InnoDB; +``` + +#### Patient Audit Log + +```sql +CREATE TABLE patient_audit_log ( + id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + entity_type VARCHAR(50) NOT NULL, -- 'patient', 'contact', 'insurance' + entity_id VARCHAR(36) NOT NULL, + patient_id VARCHAR(36), -- Context FK for patient + + action ENUM('CREATE', 'UPDATE', 'DELETE', 'MERGE', 'UNMERGE') NOT NULL, + + old_values JSON NULL, + new_values JSON NULL, + changed_fields JSON, + reason TEXT, -- Why the change was made + + -- Context + user_id VARCHAR(36), + site_id VARCHAR(36), + ip_address VARCHAR(45), + session_id VARCHAR(100), + + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + + INDEX idx_entity (entity_type, entity_id), + INDEX idx_patient (patient_id, created_at), + INDEX idx_created (created_at), + INDEX idx_user (user_id, created_at) +) ENGINE=InnoDB; +``` + +#### Order/Test Audit Log + +```sql +CREATE TABLE order_audit_log ( + id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + entity_type VARCHAR(50) NOT NULL, -- 'order', 'test', 'specimen', 'result' + entity_id VARCHAR(36) NOT NULL, + + -- Context FKs + patient_id VARCHAR(36), + visit_id VARCHAR(36), + order_id VARCHAR(36), + + action ENUM('CREATE', 'UPDATE', 'DELETE', 'CANCEL', 'REORDER', 'COLLECT', 'RESULT') NOT NULL, + + old_values JSON NULL, + new_values JSON NULL, + changed_fields JSON, + status_transition VARCHAR(100), -- e.g., 'pending->collected' + + -- Context + user_id VARCHAR(36), + site_id VARCHAR(36), + device_id VARCHAR(36), -- Instrument/edge device + ip_address VARCHAR(45), + session_id VARCHAR(100), + + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + + INDEX idx_entity (entity_type, entity_id), + INDEX idx_order (order_id, created_at), + INDEX idx_patient (patient_id, created_at), + INDEX idx_created (created_at), + INDEX idx_user (user_id, created_at) +) ENGINE=InnoDB; +``` + +--- + +## 3. JSON Value Structure + +### Example Audit Entry + +```json +{ + "id": 15243, + "entity_type": "patient", + "entity_id": "PAT-2026-001234", + "action": "UPDATE", + + "old_values": { + "NameFirst": "John", + "NameLast": "Doe", + "Gender": "M", + "BirthDate": "1990-01-15", + "Phone": "+1-555-0100" + }, + + "new_values": { + "NameFirst": "Johnny", + "NameLast": "Doe-Smith", + "Gender": "M", + "BirthDate": "1990-01-15", + "Phone": "+1-555-0199" + }, + + "changed_fields": ["NameFirst", "NameLast", "Phone"], + + "user_id": "USR-001", + "site_id": "SITE-001", + "created_at": "2026-02-19T14:30:00Z" +} +``` + +### Benefits of JSON Approach + +✅ **Schema Evolution** - Add new fields without migrations +✅ **Complete Snapshots** - Reconstruct full record state at any point +✅ **Flexible Queries** - MySQL 8.0+ supports JSON indexing and extraction +✅ **Audit Integrity** - Store exactly what changed, no data loss + +--- + +## 4. Implementation Strategy + +### 4.1 Central Audit Service + +```php + $entityType, + 'entity_id' => $entityId, + 'action' => $action, + 'old_values' => $oldValues ? json_encode($oldValues) : null, + 'new_values' => $newValues ? json_encode($newValues) : null, + 'changed_fields' => json_encode($changedFields), + 'user_id' => auth()->id() ?? 'SYSTEM', + 'site_id' => session('site_id') ?? 'MAIN', + 'created_at' => date('Y-m-d H:i:s') + ]; + + // Route to appropriate table + $table = match($category) { + 'master' => 'master_audit_log', + 'patient' => 'patient_audit_log', + 'order' => 'order_audit_log', + default => throw new \InvalidArgumentException("Unknown category: $category") + }; + + // Async logging recommended for high-volume operations + self::dispatchAuditJob($table, $data); + } + + private static function calculateChangedFields(?array $old, ?array $new): array + { + if (!$old || !$new) return []; + + $changes = []; + $allKeys = array_unique(array_merge(array_keys($old), array_keys($new))); + + foreach ($allKeys as $key) { + if (($old[$key] ?? null) !== ($new[$key] ?? null)) { + $changes[] = $key; + } + } + + return $changes; + } +} +``` + +### 4.2 Model Integration + +```php +getPatientId(), + action: $action, + oldValues: $oldValues, + newValues: $newValues + ); + } + + // Override save method to auto-log + public function save($data): bool + { + $oldData = $this->find($data['PatientID'] ?? null); + + $result = parent::save($data); + + if ($result) { + $this->logAudit( + $oldData ? 'UPDATE' : 'CREATE', + $oldData?->toArray(), + $this->find($data['PatientID'])->toArray() + ); + } + + return $result; + } +} +``` + +--- + +## 5. Query Patterns & Performance + +### 5.1 Common Queries + +```sql +-- View entity history +SELECT * FROM patient_audit_log +WHERE entity_type = 'patient' +AND entity_id = 'PAT-2026-001234' +ORDER BY created_at DESC; + +-- User activity report +SELECT entity_type, action, COUNT(*) as count +FROM patient_audit_log +WHERE user_id = 'USR-001' +AND created_at > DATE_SUB(NOW(), INTERVAL 7 DAY) +GROUP BY entity_type, action; + +-- Find all changes to a specific field +SELECT * FROM order_audit_log +WHERE JSON_CONTAINS(changed_fields, '"result_value"') +AND patient_id = 'PAT-001' +AND created_at > '2026-01-01'; +``` + +### 5.2 Partitioning Strategy (Order/Test) + +For high-volume tables, implement monthly partitioning: + +```sql +CREATE TABLE order_audit_log ( + -- ... columns + created_at DATETIME DEFAULT CURRENT_TIMESTAMP +) ENGINE=InnoDB +PARTITION BY RANGE (YEAR(created_at) * 100 + MONTH(created_at)) ( + PARTITION p202601 VALUES LESS THAN (202602), + PARTITION p202602 VALUES LESS THAN (202603), + PARTITION p_future VALUES LESS THAN MAXVALUE +); +``` + +--- + +## 6. Soft Delete Handling + +Soft deletes ARE captured as audit entries with complete snapshots: + +```php +// When soft deleting a patient: +AuditService::log( + category: 'patient', + entityType: 'patient', + entityId: $patientId, + action: 'DELETE', + oldValues: $fullRecordBeforeDelete, // Complete last known state + newValues: null, + reason: 'Patient requested data removal' +); +``` + +This ensures: +- ✅ Full audit trail even for deleted records +- ✅ Compliance with "right to be forgotten" (GDPR) +- ✅ Ability to restore accidentally deleted records + +--- + +## 7. Migration Plan + +### Phase 1: Foundation (Week 1) +- [ ] Drop existing unused tables (patreglog, patvisitlog, specimenlog) +- [ ] Create new audit tables with JSON columns +- [ ] Create AuditService class +- [ ] Add database indexes + +### Phase 2: Core Implementation (Week 2) +- [ ] Integrate AuditService into Patient model +- [ ] Integrate AuditService into Order model +- [ ] Integrate AuditService into Master data models +- [ ] Add audit trail to authentication events + +### Phase 3: API & UI (Week 3) +- [ ] Create API endpoints for querying audit logs +- [ ] Build admin interface for audit review +- [ ] Add audit export functionality (CSV/PDF) + +### Phase 4: Optimization (Week 4) +- [ ] Implement async logging queue +- [ ] Add table partitioning for order_audit_log +- [ ] Set up retention policies and archiving +- [ ] Performance testing and tuning + +--- + +## 8. Retention & Archiving Strategy + +| Table | Retention Period | Archive Action | +|-------|---------------|----------------| +| `master_audit_log` | Permanent | None (keep forever) | +| `patient_audit_log` | 7 years | Move to cold storage after 7 years | +| `order_audit_log` | 2 years | Partition rotation: drop old partitions | + +### Automated Maintenance + +```sql +-- Monthly job: Archive old patient audit logs +INSERT INTO patient_audit_log_archive +SELECT * FROM patient_audit_log +WHERE created_at < DATE_SUB(NOW(), INTERVAL 7 YEAR); + +DELETE FROM patient_audit_log +WHERE created_at < DATE_SUB(NOW(), INTERVAL 7 YEAR); + +-- Monthly job: Drop old order partitions +ALTER TABLE order_audit_log DROP PARTITION p202501; +``` + +--- + +## 9. Questions for Stakeholders + +Before implementation, please confirm: + +1. **Retention Policy**: Are the proposed retention periods (master=forever, patient=7 years, order=2 years) compliant with your regulatory requirements? + +2. **Async vs Sync**: Should audit logging be synchronous (block on failure) or asynchronous (queue-based)? Recommended: async for order/test operations. + +3. **Archive Storage**: Where should archived audit logs be stored? Options: separate database, file storage (S3), or compressed tables. + +4. **User Access**: Which user roles need access to audit trails? Should users see their own audit history? + +5. **Compliance**: Do you need specific compliance features (e.g., HIPAA audit trail requirements, 21 CFR Part 11 for FDA)? + +--- + +## 10. Key Design Decisions Summary + +| Decision | Choice | Rationale | +|----------|--------|-----------| +| **Table Count** | 3 tables | Separates concerns, optimizes queries, different retention | +| **JSON vs Columns** | JSON for values | Flexible, handles schema changes, complete snapshots | +| **Full vs Diff** | Full snapshots | Easier to reconstruct history, no data loss | +| **Soft Deletes** | Captured in audit | Compliance and restore capability | +| **Partitioning** | Order table only | High volume, time-based queries | +| **Async Logging** | Recommended | Don't block user operations | + +--- + +## Conclusion + +This unified audit logging architecture provides: + +✅ **Complete traceability** across all data domains +✅ **Regulatory compliance** with proper retention +✅ **Performance optimization** through domain separation +✅ **Flexibility** via JSON value storage +✅ **Maintainability** with centralized service + +The approach balances audit integrity with system performance, ensuring CLQMS can scale while maintaining comprehensive audit trails. + +--- + +*Document Version: 1.0* +*Author: CLQMS Development Team* +*Date: February 19, 2026* diff --git a/src/projects/clqms01/suggestion/index.md b/src/projects/clqms01/suggestion/index.md index bb682ce..e817d5e 100644 --- a/src/projects/clqms01/suggestion/index.md +++ b/src/projects/clqms01/suggestion/index.md @@ -35,6 +35,14 @@ When documenting suggestions, please include: ## Suggestion List +### [High] Backend Dev: Implementation Plan +**Description:** Consolidated developer review for proposals 001 and 002. +[View Plan](./003-decision-001-002.md) + +### [High] Audit Logging Architecture Plan +**Description:** Comprehensive audit trail strategy for tracking changes across master data, patient records, and laboratory operations. +[View Plan](./audit-logging-plan.md) + ### [TBD] Add new suggestions here + - - + +
+ diff --git a/src/css/style.css b/src/css/style.css index 44487da..d3b172c 100644 --- a/src/css/style.css +++ b/src/css/style.css @@ -507,14 +507,6 @@ ORIGINAL CUSTOM COMPONENTS ======================================== */ - /* Gradient text effect */ - .gradient-text { - background: linear-gradient(to right, var(--color-primary), var(--color-secondary), var(--color-accent)); - -webkit-background-clip: text; - background-clip: text; - -webkit-text-fill-color: transparent; - } - /* Glass morphism card - uses theme-aware colors */ .glass-card { background-color: var(--color-base-200); @@ -524,25 +516,19 @@ box-shadow: 0 10px 40px -10px oklch(0 0 0 / 0.2); } - /* Hero section styles */ - .hero-gradient { - background: linear-gradient(135deg, var(--color-base-100), var(--color-base-200), var(--color-base-100)); - background-size: 400% 400%; - animation: gradient 15s ease infinite; + /* Hero section styles - solid background */ + .hero-solid { + background-color: var(--color-base-100); } - @keyframes gradient { - 0% { - background-position: 0% 50%; - } + /* Brand text styling */ + .brand-text { + color: var(--color-primary); + font-weight: 700; + } - 50% { - background-position: 100% 50%; - } - - 100% { - background-position: 0% 50%; - } + .brand-text:hover { + opacity: 0.8; } /* Project card hover effect - uses theme-aware colors */ @@ -600,9 +586,18 @@ border-left: 2px solid var(--color-primary); } - /* Section container */ + /* Section container - narrow for reading */ .section-container { - max-width: 80rem; + max-width: 48rem; + margin-left: auto; + margin-right: auto; + padding-left: 1rem; + padding-right: 1rem; + } + + /* Wider container for grids and layouts */ + .section-container-wide { + max-width: 72rem; margin-left: auto; margin-right: auto; padding-left: 1rem; @@ -610,14 +605,16 @@ } @media (min-width: 640px) { - .section-container { + .section-container, + .section-container-wide { padding-left: 1.5rem; padding-right: 1.5rem; } } @media (min-width: 1024px) { - .section-container { + .section-container, + .section-container-wide { padding-left: 2rem; padding-right: 2rem; } @@ -968,4 +965,27 @@ -webkit-box-orient: vertical; overflow: hidden; } + + /* Custom scrollbar for sidenav */ + .scrollbar-thin { + scrollbar-width: thin; + scrollbar-color: var(--color-base-300) transparent; + } + + .scrollbar-thin::-webkit-scrollbar { + width: 6px; + } + + .scrollbar-thin::-webkit-scrollbar-track { + background: transparent; + } + + .scrollbar-thin::-webkit-scrollbar-thumb { + background-color: var(--color-base-300); + border-radius: 3px; + } + + .scrollbar-thin::-webkit-scrollbar-thumb:hover { + background-color: var(--color-neutral); + } } \ No newline at end of file diff --git a/src/index.njk b/src/index.njk index 09df572..9f1a54b 100644 --- a/src/index.njk +++ b/src/index.njk @@ -5,32 +5,30 @@ description: Innovative projects and ideas --- - + - - + + - - - + + Development Team - + Building the - Future of Tech. + Future of Tech. - + We are a team of passionate developers, designers, and innovators crafting digital experiences that matter. - - - - - Meet the Team - - + + + + + Meet the Team + @@ -38,7 +36,7 @@ description: Innovative projects and ideas - + Portfolio Our Work @@ -85,7 +83,7 @@ description: Innovative projects and ideas {% if collections.clqms.length > 0 %} - + CLQMS Project Documentation diff --git a/src/projects/clqms01/index.njk b/src/projects/clqms01/index.njk index 869a343..1590974 100644 --- a/src/projects/clqms01/index.njk +++ b/src/projects/clqms01/index.njk @@ -6,129 +6,179 @@ date: 2025-12-01 order: 0 --- - - - - - Project Workspace - Clinical Laboratory Quality Management System - - A robust, mission-critical API suite designed to streamline modern laboratory operations through precision, scalability, - and modular design. - - - - - - - - - - - - 🏗️ - Architecture - - Core system design, authentication, and strategic pillars + + + + - - - - - - - # - Core Documentation - - - - {% for post in collections.clqms %} - {# Filter to show only root-level docs, excluding index and sub-directories #} - {% set is_suggestion = "/suggestion/" in post.url %} - {% set is_review = "/review/" in post.url %} - {% if post.url != page.url and not is_suggestion and not is_review and post.data.title %} - - - - {{ post.data.title }} - - {{ post.data.description | default('Technical reference for CLQMS implementation.') }} - - - - {{ post.content | readingTime }} - - + + + + + + + + Suggestions - {% endif %} - {% endfor %} - - - - - - - - Strategic Pillars - - - - 🎯 - Precision & Accuracy - - Strict validation for all laboratory parameters and multi-variant reference - ranges. + + {% for post in collections.clqms %} + {% if "/suggestion/" in post.url and post.data.title and post.url != "/projects/clqms01/suggestion/" %} + + {{ post.data.title }} + + {% endif %} + {% endfor %} + - - - ⚡ - Scalability - - Optimized database and API architecture for high-volume diagnostic environments. - - - - 📜 - Compliance - - Built-in audit trails and granular status history for full medical traceability. - - - - 🔗 - Interoperability - - Modular drivers designed for seamless LIS, HIS, and analyzer integration. + + + + + + + + Reviews + + + {% for post in collections.clqms %} + {% if "/review/" in post.url and post.data.title and post.url != "/projects/clqms01/review/" %} + + {{ post.data.title }} + + {% endif %} + {% endfor %} + - + + + + + + + + + Project Workspace + Clinical Laboratory Quality Management System + + A robust, mission-critical API suite designed to streamline modern laboratory operations through precision, scalability, + and modular design. + + + + + + + + # + Quick Access + + + + + 🏗️ + Core Docs + + Architecture, auth, and technical docs + + + + 💡 + Suggestions + + Future proposals and roadmap ideas + + + + 🔍 + Reviews + + Expert code reviews and audits + + + + + + + + # + Core Documentation + + + {% for post in collections.clqms %} + {% set is_suggestion = "/suggestion/" in post.url %} + {% set is_review = "/review/" in post.url %} + {% if post.url != page.url and not is_suggestion and not is_review and post.data.title %} + + + + {{ post.data.title }} + + {{ post.data.description | default('Technical reference for CLQMS implementation.') }} + + + {{ post.content | readingTime }} + + + {% endif %} + {% endfor %} + + + + + + Strategic Pillars + + + + 🎯 + Precision & Accuracy + + Strict validation for all laboratory parameters and multi-variant reference ranges. + + + + ⚡ + Scalability + + Optimized database and API architecture for high-volume diagnostic environments. + + + + 📜 + Compliance + + Built-in audit trails and granular status history for full medical traceability. + + + + 🔗 + Interoperability + + Modular drivers designed for seamless LIS, HIS, and analyzer integration. + + + + + - \ No newline at end of file + diff --git a/src/projects/clqms01/suggestion/001-testdef.md b/src/projects/clqms01/suggestion/001-testdef.md index 96c63f7..305175d 100644 --- a/src/projects/clqms01/suggestion/001-testdef.md +++ b/src/projects/clqms01/suggestion/001-testdef.md @@ -2,14 +2,13 @@ layout: clqms-post.njk tags: clqms title: "Proposal: Test Definition Architecture Overhaul" -description: "Simplify database schema and improve query performance for test definitions" +description: "Remove magic numbers and enforce type safety using PHP Enums and Svelte stores" date: 2026-01-09 order: 1 --- # 🚀 Proposal: Test Definition Architecture Overhaul by Gemini 3 - **Target:** `testdef` Module **Objective:** Simplify database schema, improve query performance, and reduce code complexity. @@ -17,27 +16,28 @@ order: 1 ## 1. The Problem: "The Shredded Document" 🧩 -**Current Status:** +**Current Status:** Defining a single Lab Test currently requires joining 4-5 rigid tables: -* `testdefsite` (General Info) -* `testdeftech` (Technical Details) -* `testdefcal` (Calculations) -* `testdefgrp` (Grouping) +- `testdefsite` (General Info) +- `testdeftech` (Technical Details) +- `testdefcal` (Calculations) +- `testdefgrp` (Grouping) **Why it hurts:** -* **Complex Queries:** To get a full test definition, we write massive SQL joins. -* **Rigid Schema:** Adding a new technical attribute requires altering table schemas and updating multiple DAO files. -* **Maintenance Nightmare:** Logic is scattered. To understand a test, you have to look in five places. +- **Complex Queries:** To get a full test definition, we write massive SQL joins. +- **Rigid Schema:** Adding a new technical attribute requires altering table schemas and updating multiple DAO files. +- **Maintenance Nightmare:** Logic is scattered. To understand a test, you have to look in five places. --- ## 2. The Solution: JSON Configuration 📄 -**Strategy:** Treat a Test Definition as a **Document**. +**Strategy:** Treat a Test Definition as a **Document**. + We will consolidate the variable details (Technique, Calculations, Reference Ranges) into a structured `JSON` column within a single table. ### Schema Change -Old 5 tables $\rightarrow$ **1 Main Table**. +Old 5 tables → **1 Main Table**. ```sql CREATE TABLE LabTestDefinitions ( @@ -61,45 +61,73 @@ Instead of columns for every possible biological variable, we store a flexible d "specimen": "Serum", "result_type": "NUMERIC", "units": "mg/dL", - "formulas": { - "calculation": "primary_result * dilution_factor" - }, "reference_ranges": [ - { - "label": "Adult Male", - "sex": "M", - "min_age": 18, - "max_age": 99, - "min_val": 70, - "max_val": 100 - }, - { - "label": "Pediatric", - "max_age": 18, - "min_val": 60, - "max_val": 90 - } + { "sex": "M", "min": 70, "max": 100 }, + { "sex": "F", "min": 60, "max": 90 } ] } ``` --- -## 3. The Benefits 🏆 +## 3. How to Query (The Magic) 🪄 + +This is where the new design shines. No more joins. + +### A. Fetching a Test (The Usual Way) + +Just select the row. The application gets the full definition instantly. + +```sql +SELECT * FROM LabTestDefinitions WHERE code = 'GLUC'; +``` + +### B. Searching Inside JSON (The Cool Way) + +Need to find all tests that use "Serum"? Use the JSON arrow operator (`->>`). + +**MySQL / MariaDB:** +```sql +SELECT code, name +FROM LabTestDefinitions +WHERE configuration->>'$.specimen' = 'Serum'; +``` + +**PostgreSQL:** +```sql +SELECT code, name +FROM LabTestDefinitions +WHERE configuration->>'specimen' = 'Serum'; +``` + +### C. Performance Optimization 🏎️ + +If we search by "Technique" often, we don't index the JSON string. We add a Generated Column. + +```sql +ALTER TABLE LabTestDefinitions +ADD COLUMN technique_virtual VARCHAR(50) +GENERATED ALWAYS AS (configuration->>'$.technique') VIRTUAL; + +CREATE INDEX idx_technique ON LabTestDefinitions(technique_virtual); +``` + +**Result:** Querying the JSON is now as fast as a normal column. + +--- + +## 4. The Benefits 🏆 | Feature | Old Way (Relational) | New Way (JSON Document) | | :--- | :--- | :--- | | **Fetch Speed** | Slow (4+ Joins) | Instant (1 Row Select) | -| **Flexibility** | Requires ALTER TABLE | Edit JSON & Save | -| **Search** | Complex SQL | Fast JSON Indexing | +| **Flexibility** | Requires `ALTER TABLE` | Edit JSON & Save | +| **Search** | Complex SQL | Fast JSON Operators | | **Code Logic** | Mapping 5 SQL results | `json_decode()` → Object | --- -### Next Steps 🗒️ +## Next Steps -- [ ] Create migration for `LabTestDefinitions` table. -- [ ] Port 5 sample tests from the old structure to JSON format for verification. - ---- -_Last updated: 2026-01-09 08:40:21_ +1. Create migration for `LabTestDefinitions` table. +2. Port 5 sample tests from the old structure to JSON format for verification. \ No newline at end of file diff --git a/src/projects/clqms01/suggestion/002-valueset.md b/src/projects/clqms01/suggestion/002-valueset.md index f5f6442..7eb1573 100644 --- a/src/projects/clqms01/suggestion/002-valueset.md +++ b/src/projects/clqms01/suggestion/002-valueset.md @@ -7,40 +7,105 @@ date: 2026-01-09 order: 2 --- -# 🚀 Proposal: Valueset ("God Table") Replacement by Gemini 3 - +# 🚀 Proposal: Valueset ("God Table") Replacement **Target:** `valueset` / `valuesetdef` Tables -**Objective:** Remove "Magic Numbers," enforce Type Safety, and optimize Frontend performance. +**Objective:** Eliminate "Magic Numbers," enforce Type Safety, and optimize Frontend performance by moving system logic into the codebase. --- ## 1. The Problem: "Magic Number Soup" 🥣 **Current Status:** -We store disparate system logic (Gender, Test Status, Colors, Payment Types) in a single massive table called `valueset`. -* **Code relies on IDs:** `if ($status == 1045) ...` -* **Frontend Overload:** Frontend makes frequent DB calls just to populate simple dropdowns. -* **No Type Safety:** Nothing stops a developer from assigning a "Payment Status" ID to a "Gender" column. +We store disparate logic (Gender, Test Status, Specimen Types, Priority) in a single massive table called `valueset`. + +* **Code relies on IDs:** Developers must remember that `1045` means `VERIFIED`. `if ($status == 1045)` is unreadable. +* **Frontend Overload:** The frontend makes frequent, redundant database calls just to populate simple dropdowns. +* **No Type Safety:** Nothing prevents assigning a "Payment Status" ID to a "Gender" column. +* **Invisible History:** Database changes are hard to track. Who changed "Verified" to "Authorized"? When? Why? --- -## 2. The Solution: Enums & API Store 🛠️ +## 2. The Solution: Single Source of Truth (SSOT) 🛠️ -**Strategy:** Split "System Logic" from the Database. -Use **PHP 8.1 Native Enums** for business rules and serve them via a cached API to Svelte. +**Strategy:** Move "System Logic" from the Database into the Code. This creates a "Single Source of Truth." -### Step A: The Backend (PHP Enums) -We delete the rows from the database and define them in code where they belong. +### Step A: The Backend Implementation +#### Option 1: The "God File" (Simple & Legacy Friendly) +**File:** `application/libraries/Valuesets.php` + +This file holds every dropdown option in the system. Benefits include **Ctrl+Click** navigation and clear **Git History**. + +```php + 'Male', + 'F' => 'Female', + 'U' => 'Unknown' + ]; + + // 2. Test Status + const TEST_STATUS = [ + 'PENDING' => 'Waiting for Results', + 'IN_PROCESS' => 'Analyzing', + 'VERIFIED' => 'Verified & Signed', + 'REJECTED' => 'Sample Rejected' + ]; + + // 3. Sample Types + const SPECIMEN_TYPE = [ + 'SERUM' => 'Serum', + 'PLASMA' => 'Plasma', + 'URINE' => 'Urine', + 'WB' => 'Whole Blood' + ]; + + // 4. Urgency + const PRIORITY = [ + 'ROUTINE' => 'Routine', + 'CITO' => 'Cito (Urgent)', + 'STAT' => 'Immediate (Life Threatening)' + ]; + + /** + * Helper to get all constants as one big JSON object. + * Useful for sending everything to Svelte in one go. + */ + public static function getAll() { + return [ + 'gender' => self::mapForFrontend(self::GENDER), + 'test_status' => self::mapForFrontend(self::TEST_STATUS), + 'specimen_type' => self::mapForFrontend(self::SPECIMEN_TYPE), + 'priority' => self::mapForFrontend(self::PRIORITY), + ]; + } + + // Helper to format array like: [{value: 'M', label: 'Male'}, ...] + private static function mapForFrontend($array) { + $result = []; + foreach ($array as $key => $label) { + $result[] = ['value' => $key, 'label' => $label]; + } + return $result; + } +} +``` + +#### Option 2: PHP 8.1+ Enums (Modern Approach) **File:** `App/Enums/TestStatus.php` + ```php enum TestStatus: string { case PENDING = 'PENDING'; case VERIFIED = 'VERIFIED'; case REJECTED = 'REJECTED'; - // Helper for Frontend Labels public function label(): string { return match($this) { self::PENDING => 'Waiting for Results', @@ -51,39 +116,42 @@ enum TestStatus: string { } ``` -### Step B: The API Contract +--- -**GET `/api/config/valueset`** -Instead of 20 small network requests for 20 dropdowns, the Frontend requests the entire dictionary once on load. +## 3. The "Aha!" Moment (Usage) 🔌 -**Response:** -```json -{ - "test_status": [ - { "value": "PENDING", "label": "Waiting for Results" }, - { "value": "VERIFIED", "label": "Verified & Signed" } - ], - "gender": [ - { "value": "M", "label": "Male" }, - { "value": "F", "label": "Female" } - ] +### 1. In PHP Logic +No more querying the DB to check if a status is valid. Logic is now instant and readable. + +```php +// ❌ Old Way (Sick) 🤮 +// $status = $this->db->get_where('valuesetdef', ['id' => 505])->row(); +if ($status_id == 505) { ... } + +// ✅ New Way (Clean) 😎 +if ($input_status === 'VERIFIED') { + // We know exactly what this string means. + send_email_to_patient(); +} + +// Validation is instant +if (!array_key_exists($input_gender, Valuesets::GENDER)) { + die("Invalid Gender!"); } ``` -### Step C: The Frontend (Svelte Store) +### 2. In Svelte (Frontend - Svelte 5 Runes) +We use a global state (rune) to store this configuration. The frontend engineers no longer need to check database IDs; they just use the human-readable keys. -We use a Svelte Store to cache this data globally. No more SQL queries for dropdowns. - -**Component Usage:** ```svelte {% raw %} -Status: - - {#each $config.test_status as option} +Specimen Type: + + {#each config.valueset.specimen_type as option} {option.label} {/each} @@ -92,22 +160,24 @@ We use a Svelte Store to cache this data globally. No more SQL queries for dropd --- -## 3. The Benefits 🏆 +## 4. Why This is Better 🏆 -| Feature | Old Way (valueset Table) | New Way (Enums + Store) | +| Feature | Old Way (`valueset` Table) | New Way (The God File) | | :--- | :--- | :--- | -| **Performance** | DB Query per dropdown | Zero DB Hits (Cached) | -| **Code Quality** | `if ($id == 505)` | `if ($s == Status::PENDING)` | -| **Reliability** | IDs can change/break | Code is immutable | -| **Network** | "Chatty" (Many requests) | Efficient (One request) | +| **Performance** | DB Query per dropdown | **Millions of times faster** (Constant) | +| **Readability** | `if ($id == 505)` | `if ($status == 'REJECTED')` | +| **Navigation** | Search DB rows | **Ctrl+Click** in VS Code | +| **Git History** | Opaque/Invisible | **Transparent** (See who changed what) | +| **Reliability** | IDs can change/break | Constants are immutable | --- -### Next Steps 🗒️ +## 5. Next Steps 🗒️ -- [ ] Define `TestStatus` and `TestType` Enums in PHP. -- [ ] Create the `/api/config/valueset` endpoint. -- [ ] Update one Svelte form to use the new Store instead of an API fetch. +- [ ] Implement `Valuesets.php` in the libraries folder. +- [ ] Create a controller to expose `Valuesets::getAll()` as JSON. +- [ ] Update the Svelte `configStore` to fetch this dictionary on app load. +- [ ] Refactor the Patient Registration form to use the new constants. --- -_Last updated: 2026-01-09 08:40:21_ \ No newline at end of file +_Last updated: 2026-01-09_ diff --git a/src/projects/clqms01/suggestion/003-decision-001-002.md b/src/projects/clqms01/suggestion/003-decision-001-002.md new file mode 100644 index 0000000..3877814 --- /dev/null +++ b/src/projects/clqms01/suggestion/003-decision-001-002.md @@ -0,0 +1,61 @@ +--- +layout: clqms-post.njk +tags: clqms +title: "Backend Dev: Implementation Plan" +description: "Consolidated developer review for suggestions 001 and 002" +date: 2026-01-09 +order: 3 +--- + +# 🛠️ Backend Dev: Decision for 001 & 002 + +This document summarizes the technical decision and implementation plan based on the previous proposals: +- [001-testdef.md](./001-testdef.md) (Test Definition Architecture) +- [002-valueset.md](./002-valueset.md) (Valueset Replacement) + +--- + +## 1. Suggestion 001: Test Definition Architecture +**Action: DEFERRED** + +- We will skip the architectural overhaul of the `testdef` module for now. +- Current table structure remains unchanged to maintain stability. + +--- + +## 2. Suggestion 002: Valueset Implementation +**Action: ADOPTED (Code-First Migration)** + +We are moving forward with moving system constants from the database into the codebase, but with a focus on existing API compatibility. + +### Technical Requirements: +- **Maintain Endpoint integrity**: The API endpoint must remain functional without changes to the frontend. +- **Data Migration**: Move values (Gender, Status, etc.) from the `valueset` table into a centralized PHP constant or config file named `Valuesets.php`. +- **Controller Refactor**: Update the backend controller to serve the JSON response from the file rather than a database query. + +--- + +## 3. Translation Logic (Getting labels like "Female" from "F") + +When pulling data from the database, you can translate raw keys into readable labels instantly using the new class. + +### Example PHP Usage: +```php +// Direct mapping in your controller or view +$genderLabel = Valuesets::GENDER[$patient->gender] ?? 'Unknown'; + +// Or use a helper method in the Valuesets class +public static function getLabel($category, $key) { + return self::{$category}[$key] ?? $key; +} +``` + +--- + +## 4. Implementation Steps +1. Create `Valuesets.php` in the backend libraries folder. +2. Refactor `Valueset` controller to return data from `Valuesets` class. +3. Verify JSON output matches the current schema to ensure no frontend breaks. + +--- +_Last updated: 2026-01-09 12:47:00_ diff --git a/src/projects/clqms01/suggestion/audit-logging-plan.md b/src/projects/clqms01/suggestion/audit-logging-plan.md new file mode 100644 index 0000000..6d22fb6 --- /dev/null +++ b/src/projects/clqms01/suggestion/audit-logging-plan.md @@ -0,0 +1,482 @@ +--- +layout: clqms-post.njk +tags: clqms +title: "CLQMS: Audit Logging Architecture Plan" +description: "Comprehensive audit trail strategy for tracking changes across master data, patient records, and laboratory operations" +date: 2026-02-19 +order: 4 +--- + +# Audit Logging Architecture Plan for CLQMS + +> **Clinical Laboratory Quality Management System (CLQMS)** - A comprehensive audit trail strategy for tracking changes across master data, patient records, and laboratory operations. + +--- + +## Executive Summary + +This document outlines a unified audit logging architecture for CLQMS, designed to provide complete traceability of data changes while maintaining optimal performance and maintainability. The approach separates audit logs into three domain-specific tables, utilizing JSON for flexible value storage. + +--- + +## 1. Current State Analysis + +### Existing Audit Infrastructure + +| Aspect | Current Status | +|--------|---------------| +| **Database Tables** | 3 tables exist in migrations (patreglog, patvisitlog, specimenlog) | +| **Implementation** | Tables created but not actively used | +| **Structure** | Fixed column approach (FldName, FldValuePrev) | +| **Code Coverage** | No models or controllers implemented | +| **Application Logging** | Basic CodeIgniter file logging for debug/errors | + +### Pain Points Identified + +- ❌ **3 separate tables** with nearly identical schemas +- ❌ **Fixed column structure** - rigid and requires schema changes for new entities +- ❌ **No implementation** - audit tables exist but aren't populated +- ❌ **Maintenance overhead** - adding new entities requires new migrations + +--- + +## 2. Proposed Architecture + +### 2.1 Domain Separation + +We categorize audit logs by **data domain** and **access patterns**: + +| Table | Domain | Volume | Retention | Use Case | +|-------|--------|--------|-----------|----------| +| `master_audit_log` | Reference Data | Low | Permanent | Organizations, Users, ValueSets | +| `patient_audit_log` | Patient Records | Medium | 7 years | Demographics, Contacts, Insurance | +| `order_audit_log` | Operations | High | 2 years | Orders, Tests, Specimens, Results | + +### 2.2 Unified Table Structure + +#### Master Audit Log + +```sql +CREATE TABLE master_audit_log ( + id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + entity_type VARCHAR(50) NOT NULL, -- 'organization', 'user', 'valueset' + entity_id VARCHAR(36) NOT NULL, -- UUID or primary key + action ENUM('CREATE', 'UPDATE', 'DELETE', 'PATCH') NOT NULL, + + old_values JSON NULL, -- Complete snapshot before change + new_values JSON NULL, -- Complete snapshot after change + changed_fields JSON, -- Array of modified field names + + -- Context + user_id VARCHAR(36), + site_id VARCHAR(36), + ip_address VARCHAR(45), + user_agent VARCHAR(500), + app_version VARCHAR(20), + + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + + INDEX idx_entity (entity_type, entity_id), + INDEX idx_created (created_at), + INDEX idx_user (user_id, created_at) +) ENGINE=InnoDB; +``` + +#### Patient Audit Log + +```sql +CREATE TABLE patient_audit_log ( + id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + entity_type VARCHAR(50) NOT NULL, -- 'patient', 'contact', 'insurance' + entity_id VARCHAR(36) NOT NULL, + patient_id VARCHAR(36), -- Context FK for patient + + action ENUM('CREATE', 'UPDATE', 'DELETE', 'MERGE', 'UNMERGE') NOT NULL, + + old_values JSON NULL, + new_values JSON NULL, + changed_fields JSON, + reason TEXT, -- Why the change was made + + -- Context + user_id VARCHAR(36), + site_id VARCHAR(36), + ip_address VARCHAR(45), + session_id VARCHAR(100), + + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + + INDEX idx_entity (entity_type, entity_id), + INDEX idx_patient (patient_id, created_at), + INDEX idx_created (created_at), + INDEX idx_user (user_id, created_at) +) ENGINE=InnoDB; +``` + +#### Order/Test Audit Log + +```sql +CREATE TABLE order_audit_log ( + id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + entity_type VARCHAR(50) NOT NULL, -- 'order', 'test', 'specimen', 'result' + entity_id VARCHAR(36) NOT NULL, + + -- Context FKs + patient_id VARCHAR(36), + visit_id VARCHAR(36), + order_id VARCHAR(36), + + action ENUM('CREATE', 'UPDATE', 'DELETE', 'CANCEL', 'REORDER', 'COLLECT', 'RESULT') NOT NULL, + + old_values JSON NULL, + new_values JSON NULL, + changed_fields JSON, + status_transition VARCHAR(100), -- e.g., 'pending->collected' + + -- Context + user_id VARCHAR(36), + site_id VARCHAR(36), + device_id VARCHAR(36), -- Instrument/edge device + ip_address VARCHAR(45), + session_id VARCHAR(100), + + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + + INDEX idx_entity (entity_type, entity_id), + INDEX idx_order (order_id, created_at), + INDEX idx_patient (patient_id, created_at), + INDEX idx_created (created_at), + INDEX idx_user (user_id, created_at) +) ENGINE=InnoDB; +``` + +--- + +## 3. JSON Value Structure + +### Example Audit Entry + +```json +{ + "id": 15243, + "entity_type": "patient", + "entity_id": "PAT-2026-001234", + "action": "UPDATE", + + "old_values": { + "NameFirst": "John", + "NameLast": "Doe", + "Gender": "M", + "BirthDate": "1990-01-15", + "Phone": "+1-555-0100" + }, + + "new_values": { + "NameFirst": "Johnny", + "NameLast": "Doe-Smith", + "Gender": "M", + "BirthDate": "1990-01-15", + "Phone": "+1-555-0199" + }, + + "changed_fields": ["NameFirst", "NameLast", "Phone"], + + "user_id": "USR-001", + "site_id": "SITE-001", + "created_at": "2026-02-19T14:30:00Z" +} +``` + +### Benefits of JSON Approach + +✅ **Schema Evolution** - Add new fields without migrations +✅ **Complete Snapshots** - Reconstruct full record state at any point +✅ **Flexible Queries** - MySQL 8.0+ supports JSON indexing and extraction +✅ **Audit Integrity** - Store exactly what changed, no data loss + +--- + +## 4. Implementation Strategy + +### 4.1 Central Audit Service + +```php + $entityType, + 'entity_id' => $entityId, + 'action' => $action, + 'old_values' => $oldValues ? json_encode($oldValues) : null, + 'new_values' => $newValues ? json_encode($newValues) : null, + 'changed_fields' => json_encode($changedFields), + 'user_id' => auth()->id() ?? 'SYSTEM', + 'site_id' => session('site_id') ?? 'MAIN', + 'created_at' => date('Y-m-d H:i:s') + ]; + + // Route to appropriate table + $table = match($category) { + 'master' => 'master_audit_log', + 'patient' => 'patient_audit_log', + 'order' => 'order_audit_log', + default => throw new \InvalidArgumentException("Unknown category: $category") + }; + + // Async logging recommended for high-volume operations + self::dispatchAuditJob($table, $data); + } + + private static function calculateChangedFields(?array $old, ?array $new): array + { + if (!$old || !$new) return []; + + $changes = []; + $allKeys = array_unique(array_merge(array_keys($old), array_keys($new))); + + foreach ($allKeys as $key) { + if (($old[$key] ?? null) !== ($new[$key] ?? null)) { + $changes[] = $key; + } + } + + return $changes; + } +} +``` + +### 4.2 Model Integration + +```php +getPatientId(), + action: $action, + oldValues: $oldValues, + newValues: $newValues + ); + } + + // Override save method to auto-log + public function save($data): bool + { + $oldData = $this->find($data['PatientID'] ?? null); + + $result = parent::save($data); + + if ($result) { + $this->logAudit( + $oldData ? 'UPDATE' : 'CREATE', + $oldData?->toArray(), + $this->find($data['PatientID'])->toArray() + ); + } + + return $result; + } +} +``` + +--- + +## 5. Query Patterns & Performance + +### 5.1 Common Queries + +```sql +-- View entity history +SELECT * FROM patient_audit_log +WHERE entity_type = 'patient' +AND entity_id = 'PAT-2026-001234' +ORDER BY created_at DESC; + +-- User activity report +SELECT entity_type, action, COUNT(*) as count +FROM patient_audit_log +WHERE user_id = 'USR-001' +AND created_at > DATE_SUB(NOW(), INTERVAL 7 DAY) +GROUP BY entity_type, action; + +-- Find all changes to a specific field +SELECT * FROM order_audit_log +WHERE JSON_CONTAINS(changed_fields, '"result_value"') +AND patient_id = 'PAT-001' +AND created_at > '2026-01-01'; +``` + +### 5.2 Partitioning Strategy (Order/Test) + +For high-volume tables, implement monthly partitioning: + +```sql +CREATE TABLE order_audit_log ( + -- ... columns + created_at DATETIME DEFAULT CURRENT_TIMESTAMP +) ENGINE=InnoDB +PARTITION BY RANGE (YEAR(created_at) * 100 + MONTH(created_at)) ( + PARTITION p202601 VALUES LESS THAN (202602), + PARTITION p202602 VALUES LESS THAN (202603), + PARTITION p_future VALUES LESS THAN MAXVALUE +); +``` + +--- + +## 6. Soft Delete Handling + +Soft deletes ARE captured as audit entries with complete snapshots: + +```php +// When soft deleting a patient: +AuditService::log( + category: 'patient', + entityType: 'patient', + entityId: $patientId, + action: 'DELETE', + oldValues: $fullRecordBeforeDelete, // Complete last known state + newValues: null, + reason: 'Patient requested data removal' +); +``` + +This ensures: +- ✅ Full audit trail even for deleted records +- ✅ Compliance with "right to be forgotten" (GDPR) +- ✅ Ability to restore accidentally deleted records + +--- + +## 7. Migration Plan + +### Phase 1: Foundation (Week 1) +- [ ] Drop existing unused tables (patreglog, patvisitlog, specimenlog) +- [ ] Create new audit tables with JSON columns +- [ ] Create AuditService class +- [ ] Add database indexes + +### Phase 2: Core Implementation (Week 2) +- [ ] Integrate AuditService into Patient model +- [ ] Integrate AuditService into Order model +- [ ] Integrate AuditService into Master data models +- [ ] Add audit trail to authentication events + +### Phase 3: API & UI (Week 3) +- [ ] Create API endpoints for querying audit logs +- [ ] Build admin interface for audit review +- [ ] Add audit export functionality (CSV/PDF) + +### Phase 4: Optimization (Week 4) +- [ ] Implement async logging queue +- [ ] Add table partitioning for order_audit_log +- [ ] Set up retention policies and archiving +- [ ] Performance testing and tuning + +--- + +## 8. Retention & Archiving Strategy + +| Table | Retention Period | Archive Action | +|-------|---------------|----------------| +| `master_audit_log` | Permanent | None (keep forever) | +| `patient_audit_log` | 7 years | Move to cold storage after 7 years | +| `order_audit_log` | 2 years | Partition rotation: drop old partitions | + +### Automated Maintenance + +```sql +-- Monthly job: Archive old patient audit logs +INSERT INTO patient_audit_log_archive +SELECT * FROM patient_audit_log +WHERE created_at < DATE_SUB(NOW(), INTERVAL 7 YEAR); + +DELETE FROM patient_audit_log +WHERE created_at < DATE_SUB(NOW(), INTERVAL 7 YEAR); + +-- Monthly job: Drop old order partitions +ALTER TABLE order_audit_log DROP PARTITION p202501; +``` + +--- + +## 9. Questions for Stakeholders + +Before implementation, please confirm: + +1. **Retention Policy**: Are the proposed retention periods (master=forever, patient=7 years, order=2 years) compliant with your regulatory requirements? + +2. **Async vs Sync**: Should audit logging be synchronous (block on failure) or asynchronous (queue-based)? Recommended: async for order/test operations. + +3. **Archive Storage**: Where should archived audit logs be stored? Options: separate database, file storage (S3), or compressed tables. + +4. **User Access**: Which user roles need access to audit trails? Should users see their own audit history? + +5. **Compliance**: Do you need specific compliance features (e.g., HIPAA audit trail requirements, 21 CFR Part 11 for FDA)? + +--- + +## 10. Key Design Decisions Summary + +| Decision | Choice | Rationale | +|----------|--------|-----------| +| **Table Count** | 3 tables | Separates concerns, optimizes queries, different retention | +| **JSON vs Columns** | JSON for values | Flexible, handles schema changes, complete snapshots | +| **Full vs Diff** | Full snapshots | Easier to reconstruct history, no data loss | +| **Soft Deletes** | Captured in audit | Compliance and restore capability | +| **Partitioning** | Order table only | High volume, time-based queries | +| **Async Logging** | Recommended | Don't block user operations | + +--- + +## Conclusion + +This unified audit logging architecture provides: + +✅ **Complete traceability** across all data domains +✅ **Regulatory compliance** with proper retention +✅ **Performance optimization** through domain separation +✅ **Flexibility** via JSON value storage +✅ **Maintainability** with centralized service + +The approach balances audit integrity with system performance, ensuring CLQMS can scale while maintaining comprehensive audit trails. + +--- + +*Document Version: 1.0* +*Author: CLQMS Development Team* +*Date: February 19, 2026* diff --git a/src/projects/clqms01/suggestion/index.md b/src/projects/clqms01/suggestion/index.md index bb682ce..e817d5e 100644 --- a/src/projects/clqms01/suggestion/index.md +++ b/src/projects/clqms01/suggestion/index.md @@ -35,6 +35,14 @@ When documenting suggestions, please include: ## Suggestion List +### [High] Backend Dev: Implementation Plan +**Description:** Consolidated developer review for proposals 001 and 002. +[View Plan](./003-decision-001-002.md) + +### [High] Audit Logging Architecture Plan +**Description:** Comprehensive audit trail strategy for tracking changes across master data, patient records, and laboratory operations. +[View Plan](./audit-logging-plan.md) + ### [TBD] Add new suggestions here + - - + +