Reserve floor queues for clinical staff and deepen specialty forms.
Deploy Ladill Care / deploy (push) Successful in 41s
Deploy Ladill Care / deploy (push) Successful in 41s
Hospital admins no longer see specialty/queue floor nav; Call next is a full-width button; specialty encounters open module-specific clinical forms instead of the general consultation page. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -31,6 +31,7 @@ class QueueController extends Controller
|
||||
public function index(Request $request): View
|
||||
{
|
||||
$this->authorizeAbility($request, 'appointments.view');
|
||||
abort_unless(app(CarePermissions::class)->handlesFloorCare($this->member($request)), 403);
|
||||
$organization = $this->organization($request);
|
||||
$member = $this->member($request);
|
||||
$owner = $this->ownerRef($request);
|
||||
@@ -143,6 +144,7 @@ class QueueController extends Controller
|
||||
|
||||
public function callNext(Request $request): RedirectResponse
|
||||
{
|
||||
abort_unless(app(CarePermissions::class)->handlesFloorCare($this->member($request)), 403);
|
||||
$this->authorizeAbility($request, 'appointments.view');
|
||||
$organization = $this->organization($request);
|
||||
abort_unless($this->queueBridge->isEnabled($organization), 404);
|
||||
|
||||
@@ -158,7 +158,7 @@ class SpecialtyModuleController extends Controller
|
||||
return back()->with('error', 'This appointment cannot start a consultation from its current status.');
|
||||
}
|
||||
|
||||
$consultation = $consultations->startFromAppointment(
|
||||
$consultations->startFromAppointment(
|
||||
$appointment,
|
||||
$owner,
|
||||
$owner,
|
||||
@@ -167,9 +167,20 @@ class SpecialtyModuleController extends Controller
|
||||
return back()->with('error', $e->getMessage());
|
||||
}
|
||||
|
||||
$visit = $visit->fresh() ?? $appointment->visit;
|
||||
$clinical = app(SpecialtyClinicalRecordService::class);
|
||||
$shellTabs = app(SpecialtyShellService::class)->workspaceTabs($module);
|
||||
$preferredTab = collect(array_keys($shellTabs))
|
||||
->first(fn (string $tab) => $clinical->recordTypeForTab($module, $tab) !== null)
|
||||
?? 'clinical_notes';
|
||||
|
||||
return redirect()
|
||||
->route('care.consultations.show', $consultation)
|
||||
->with('success', 'Consultation started.');
|
||||
->route('care.specialty.workspace', [
|
||||
'module' => $module,
|
||||
'visit' => $visit,
|
||||
'tab' => $preferredTab,
|
||||
])
|
||||
->with('success', 'Specialty encounter started.');
|
||||
}
|
||||
|
||||
public function saveClinical(
|
||||
|
||||
@@ -79,4 +79,24 @@ class CarePermissions
|
||||
{
|
||||
return $member !== null && in_array($member->role, ['super_admin', 'hospital_admin'], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Floor care: queues, specialty clinics, lab/pharmacy counters.
|
||||
* Facility admins manage settings/staff — they do not staff waiting lines.
|
||||
*/
|
||||
public function handlesFloorCare(?Member $member): bool
|
||||
{
|
||||
if ($member === null || $this->isAdmin($member)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return in_array($member->role, [
|
||||
'doctor',
|
||||
'nurse',
|
||||
'lab_technician',
|
||||
'pharmacist',
|
||||
'receptionist',
|
||||
'cashier',
|
||||
], true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,23 +19,14 @@ class SpecialtyClinicalRecordService
|
||||
*/
|
||||
public function recordTypesForModule(string $moduleKey): array
|
||||
{
|
||||
return match ($moduleKey) {
|
||||
'emergency' => [
|
||||
'triage' => 'triage',
|
||||
'clinical_notes' => 'clinical_note',
|
||||
],
|
||||
'blood_bank' => [
|
||||
'requests' => 'blood_request',
|
||||
'inventory' => 'inventory_note',
|
||||
],
|
||||
'dentistry' => [
|
||||
'odontogram' => 'odontogram',
|
||||
'clinical_notes' => 'clinical_note',
|
||||
],
|
||||
default => [
|
||||
'clinical_notes' => 'clinical_note',
|
||||
],
|
||||
};
|
||||
$configured = config("care_specialty_clinical.record_types.{$moduleKey}");
|
||||
if (is_array($configured) && $configured !== []) {
|
||||
return $configured;
|
||||
}
|
||||
|
||||
return [
|
||||
'clinical_notes' => 'clinical_note',
|
||||
];
|
||||
}
|
||||
|
||||
public function recordTypeForTab(string $moduleKey, string $tab): ?string
|
||||
@@ -50,65 +41,14 @@ class SpecialtyClinicalRecordService
|
||||
*/
|
||||
public function fieldsFor(string $moduleKey, string $recordType): array
|
||||
{
|
||||
$key = "{$moduleKey}.{$recordType}";
|
||||
$fields = config("care_specialty_clinical.fields.{$moduleKey}.{$recordType}");
|
||||
if (is_array($fields) && $fields !== []) {
|
||||
return $fields;
|
||||
}
|
||||
|
||||
return match ($key) {
|
||||
'emergency.triage' => [
|
||||
['name' => 'acuity', 'label' => 'Triage acuity', 'type' => 'select', 'required' => true, 'options' => ['1 - Resuscitation', '2 - Emergency', '3 - Urgent', '4 - Semi-urgent', '5 - Non-urgent']],
|
||||
['name' => 'chief_complaint', 'label' => 'Chief complaint', 'type' => 'text', 'required' => true],
|
||||
['name' => 'onset', 'label' => 'Onset', 'type' => 'text'],
|
||||
['name' => 'airway_ok', 'label' => 'Airway patent', 'type' => 'boolean'],
|
||||
['name' => 'breathing_ok', 'label' => 'Breathing adequate', 'type' => 'boolean'],
|
||||
['name' => 'circulation_ok', 'label' => 'Circulation stable', 'type' => 'boolean'],
|
||||
['name' => 'gcs', 'label' => 'GCS', 'type' => 'number'],
|
||||
['name' => 'pain_score', 'label' => 'Pain score (0–10)', 'type' => 'number'],
|
||||
['name' => 'mode_of_arrival', 'label' => 'Mode of arrival', 'type' => 'select', 'options' => ['Walk-in', 'Ambulance', 'Police', 'Referral', 'Other']],
|
||||
['name' => 'disposition_intent', 'label' => 'Disposition intent', 'type' => 'select', 'options' => ['Resus', 'Treatment bay', 'Observation', 'Discharge', 'Admit', 'Transfer']],
|
||||
['name' => 'notes', 'label' => 'Triage notes', 'type' => 'textarea', 'rows' => 3],
|
||||
],
|
||||
'emergency.clinical_note' => [
|
||||
['name' => 'history', 'label' => 'History of present illness', 'type' => 'textarea', 'rows' => 4, 'required' => true],
|
||||
['name' => 'examination', 'label' => 'Examination', 'type' => 'textarea', 'rows' => 3],
|
||||
['name' => 'working_diagnosis', 'label' => 'Working diagnosis', 'type' => 'text'],
|
||||
['name' => 'plan', 'label' => 'Plan', 'type' => 'textarea', 'rows' => 3],
|
||||
['name' => 'procedures', 'label' => 'Procedures performed', 'type' => 'textarea', 'rows' => 2],
|
||||
],
|
||||
'blood_bank.blood_request' => [
|
||||
['name' => 'product', 'label' => 'Product', 'type' => 'select', 'required' => true, 'options' => ['Whole blood', 'Packed RBC', 'Platelets', 'FFP', 'Cryoprecipitate']],
|
||||
['name' => 'units', 'label' => 'Units requested', 'type' => 'number', 'required' => true],
|
||||
['name' => 'urgency', 'label' => 'Urgency', 'type' => 'select', 'required' => true, 'options' => ['Routine', 'Urgent', 'Emergency / massive']],
|
||||
['name' => 'patient_blood_group', 'label' => 'Patient blood group', 'type' => 'select', 'options' => ['A+', 'A-', 'B+', 'B-', 'AB+', 'AB-', 'O+', 'O-', 'Unknown']],
|
||||
['name' => 'indication', 'label' => 'Clinical indication', 'type' => 'textarea', 'rows' => 2, 'required' => true],
|
||||
['name' => 'crossmatch_status', 'label' => 'Cross-match status', 'type' => 'select', 'options' => ['Not started', 'In progress', 'Compatible', 'Incompatible', 'Issued']],
|
||||
['name' => 'issued_units', 'label' => 'Units issued', 'type' => 'number'],
|
||||
['name' => 'notes', 'label' => 'Notes', 'type' => 'textarea', 'rows' => 2],
|
||||
],
|
||||
'blood_bank.inventory_note' => [
|
||||
['name' => 'whole_blood_units', 'label' => 'Whole blood on hand', 'type' => 'number'],
|
||||
['name' => 'packed_rbc_units', 'label' => 'Packed RBC on hand', 'type' => 'number'],
|
||||
['name' => 'platelet_units', 'label' => 'Platelets on hand', 'type' => 'number'],
|
||||
['name' => 'ffp_units', 'label' => 'FFP on hand', 'type' => 'number'],
|
||||
['name' => 'low_stock_alert', 'label' => 'Flag low stock', 'type' => 'boolean'],
|
||||
['name' => 'notes', 'label' => 'Inventory notes', 'type' => 'textarea', 'rows' => 3],
|
||||
],
|
||||
'dentistry.odontogram' => [
|
||||
['name' => 'teeth_affected', 'label' => 'Teeth affected (FDI numbers, comma-separated)', 'type' => 'text', 'required' => true],
|
||||
['name' => 'findings', 'label' => 'Chart findings', 'type' => 'textarea', 'rows' => 3, 'required' => true],
|
||||
['name' => 'planned_procedures', 'label' => 'Planned procedures', 'type' => 'textarea', 'rows' => 2],
|
||||
['name' => 'completed_procedures', 'label' => 'Completed this visit', 'type' => 'textarea', 'rows' => 2],
|
||||
['name' => 'anesthesia', 'label' => 'Anesthesia', 'type' => 'select', 'options' => ['None', 'Local', 'Sedation', 'GA']],
|
||||
['name' => 'occlusion_notes', 'label' => 'Occlusion / bite notes', 'type' => 'text'],
|
||||
],
|
||||
'dentistry.clinical_note' => [
|
||||
['name' => 'history', 'label' => 'History', 'type' => 'textarea', 'rows' => 3, 'required' => true],
|
||||
['name' => 'examination', 'label' => 'Examination', 'type' => 'textarea', 'rows' => 3],
|
||||
['name' => 'working_diagnosis', 'label' => 'Diagnosis', 'type' => 'text'],
|
||||
['name' => 'plan', 'label' => 'Plan', 'type' => 'textarea', 'rows' => 2],
|
||||
],
|
||||
default => [
|
||||
['name' => 'notes', 'label' => 'Clinical notes', 'type' => 'textarea', 'rows' => 4, 'required' => true],
|
||||
],
|
||||
};
|
||||
return [
|
||||
['name' => 'notes', 'label' => 'Clinical notes', 'type' => 'textarea', 'rows' => 4, 'required' => true],
|
||||
];
|
||||
}
|
||||
|
||||
public function findForVisit(Visit $visit, string $moduleKey, string $recordType): ?SpecialtyClinicalRecord
|
||||
|
||||
@@ -150,7 +150,7 @@ class SpecialtyModuleService
|
||||
/**
|
||||
* Whether the member may see/serve this specialty module.
|
||||
* Doctors must be linked to a practitioner desk in that specialty department.
|
||||
* Admins/receptionists keep org-wide visibility for operations.
|
||||
* Receptionists keep queue/ops visibility; facility admins do not staff specialty floors.
|
||||
*/
|
||||
public function memberCanAccess(Organization $organization, ?Member $member, string $key): bool
|
||||
{
|
||||
|
||||
@@ -0,0 +1,514 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Specialty clinical form schemas (workspace tab → record type → fields).
|
||||
* Consumed by SpecialtyClinicalRecordService.
|
||||
*
|
||||
* @return array{
|
||||
* record_types: array<string, array<string, string>>,
|
||||
* fields: array<string, array<string, list<array<string, mixed>>>>
|
||||
* }
|
||||
*/
|
||||
return [
|
||||
'record_types' => [
|
||||
'emergency' => [
|
||||
'triage' => 'triage',
|
||||
'clinical_notes' => 'clinical_note',
|
||||
],
|
||||
'blood_bank' => [
|
||||
'requests' => 'blood_request',
|
||||
'inventory' => 'inventory_note',
|
||||
],
|
||||
'dentistry' => [
|
||||
'odontogram' => 'odontogram',
|
||||
'clinical_notes' => 'clinical_note',
|
||||
],
|
||||
'ophthalmology' => [
|
||||
'exam' => 'eye_exam',
|
||||
'refraction' => 'refraction',
|
||||
'clinical_notes' => 'clinical_note',
|
||||
],
|
||||
'physiotherapy' => [
|
||||
'assessment' => 'pt_assessment',
|
||||
'session' => 'pt_session',
|
||||
],
|
||||
'maternity' => [
|
||||
'antenatal' => 'antenatal',
|
||||
'clinical_notes' => 'clinical_note',
|
||||
],
|
||||
'radiology' => [
|
||||
'request' => 'imaging_request',
|
||||
'report' => 'imaging_report',
|
||||
],
|
||||
'cardiology' => [
|
||||
'exam' => 'cardiac_exam',
|
||||
'clinical_notes' => 'clinical_note',
|
||||
],
|
||||
'psychiatry' => [
|
||||
'assessment' => 'mental_status',
|
||||
'clinical_notes' => 'clinical_note',
|
||||
],
|
||||
'pediatrics' => [
|
||||
'exam' => 'pediatric_exam',
|
||||
'clinical_notes' => 'clinical_note',
|
||||
],
|
||||
'orthopedics' => [
|
||||
'exam' => 'ortho_exam',
|
||||
'clinical_notes' => 'clinical_note',
|
||||
],
|
||||
'ent' => [
|
||||
'exam' => 'ent_exam',
|
||||
'clinical_notes' => 'clinical_note',
|
||||
],
|
||||
'oncology' => [
|
||||
'review' => 'oncology_review',
|
||||
'clinical_notes' => 'clinical_note',
|
||||
],
|
||||
'renal' => [
|
||||
'session' => 'renal_session',
|
||||
'clinical_notes' => 'clinical_note',
|
||||
],
|
||||
'surgery' => [
|
||||
'preop' => 'preop',
|
||||
'clinical_notes' => 'clinical_note',
|
||||
],
|
||||
'vaccination' => [
|
||||
'screening' => 'vax_screening',
|
||||
'administration' => 'vax_admin',
|
||||
],
|
||||
'pathology' => [
|
||||
'request' => 'path_request',
|
||||
'report' => 'path_report',
|
||||
],
|
||||
'infusion' => [
|
||||
'session' => 'infusion_session',
|
||||
'clinical_notes' => 'clinical_note',
|
||||
],
|
||||
'dermatology' => [
|
||||
'exam' => 'skin_exam',
|
||||
'clinical_notes' => 'clinical_note',
|
||||
],
|
||||
'podiatry' => [
|
||||
'exam' => 'foot_exam',
|
||||
'clinical_notes' => 'clinical_note',
|
||||
],
|
||||
'fertility' => [
|
||||
'consult' => 'fertility_consult',
|
||||
'clinical_notes' => 'clinical_note',
|
||||
],
|
||||
'child_welfare' => [
|
||||
'growth' => 'growth',
|
||||
'clinical_notes' => 'clinical_note',
|
||||
],
|
||||
],
|
||||
'fields' => [
|
||||
'emergency' => [
|
||||
'triage' => [
|
||||
['name' => 'acuity', 'label' => 'Triage acuity', 'type' => 'select', 'required' => true, 'options' => ['1 - Resuscitation', '2 - Emergency', '3 - Urgent', '4 - Semi-urgent', '5 - Non-urgent']],
|
||||
['name' => 'chief_complaint', 'label' => 'Chief complaint', 'type' => 'text', 'required' => true],
|
||||
['name' => 'onset', 'label' => 'Onset', 'type' => 'text'],
|
||||
['name' => 'airway_ok', 'label' => 'Airway patent', 'type' => 'boolean'],
|
||||
['name' => 'breathing_ok', 'label' => 'Breathing adequate', 'type' => 'boolean'],
|
||||
['name' => 'circulation_ok', 'label' => 'Circulation stable', 'type' => 'boolean'],
|
||||
['name' => 'gcs', 'label' => 'GCS', 'type' => 'number'],
|
||||
['name' => 'pain_score', 'label' => 'Pain score (0–10)', 'type' => 'number'],
|
||||
['name' => 'mode_of_arrival', 'label' => 'Mode of arrival', 'type' => 'select', 'options' => ['Walk-in', 'Ambulance', 'Police', 'Referral', 'Other']],
|
||||
['name' => 'disposition_intent', 'label' => 'Disposition intent', 'type' => 'select', 'options' => ['Resus', 'Treatment bay', 'Observation', 'Discharge', 'Admit', 'Transfer']],
|
||||
['name' => 'notes', 'label' => 'Triage notes', 'type' => 'textarea', 'rows' => 3],
|
||||
],
|
||||
'clinical_note' => [
|
||||
['name' => 'history', 'label' => 'History of present illness', 'type' => 'textarea', 'required' => true, 'rows' => 4],
|
||||
['name' => 'examination', 'label' => 'Examination', 'type' => 'textarea', 'rows' => 3],
|
||||
['name' => 'working_diagnosis', 'label' => 'Working diagnosis', 'type' => 'text'],
|
||||
['name' => 'plan', 'label' => 'Plan', 'type' => 'textarea', 'rows' => 3],
|
||||
['name' => 'procedures', 'label' => 'Procedures performed', 'type' => 'textarea', 'rows' => 2],
|
||||
],
|
||||
],
|
||||
'blood_bank' => [
|
||||
'blood_request' => [
|
||||
['name' => 'product', 'label' => 'Product', 'type' => 'select', 'required' => true, 'options' => ['Whole blood', 'Packed RBC', 'Platelets', 'FFP', 'Cryoprecipitate']],
|
||||
['name' => 'units', 'label' => 'Units requested', 'type' => 'number', 'required' => true],
|
||||
['name' => 'urgency', 'label' => 'Urgency', 'type' => 'select', 'required' => true, 'options' => ['Routine', 'Urgent', 'Emergency / massive']],
|
||||
['name' => 'patient_blood_group', 'label' => 'Patient blood group', 'type' => 'select', 'options' => ['A+', 'A-', 'B+', 'B-', 'AB+', 'AB-', 'O+', 'O-', 'Unknown']],
|
||||
['name' => 'indication', 'label' => 'Clinical indication', 'type' => 'textarea', 'required' => true, 'rows' => 2],
|
||||
['name' => 'crossmatch_status', 'label' => 'Cross-match status', 'type' => 'select', 'options' => ['Not started', 'In progress', 'Compatible', 'Incompatible', 'Issued']],
|
||||
['name' => 'issued_units', 'label' => 'Units issued', 'type' => 'number'],
|
||||
['name' => 'notes', 'label' => 'Notes', 'type' => 'textarea', 'rows' => 2],
|
||||
],
|
||||
'inventory_note' => [
|
||||
['name' => 'whole_blood_units', 'label' => 'Whole blood on hand', 'type' => 'number'],
|
||||
['name' => 'packed_rbc_units', 'label' => 'Packed RBC on hand', 'type' => 'number'],
|
||||
['name' => 'platelet_units', 'label' => 'Platelets on hand', 'type' => 'number'],
|
||||
['name' => 'ffp_units', 'label' => 'FFP on hand', 'type' => 'number'],
|
||||
['name' => 'low_stock_alert', 'label' => 'Flag low stock', 'type' => 'boolean'],
|
||||
['name' => 'notes', 'label' => 'Inventory notes', 'type' => 'textarea', 'rows' => 3],
|
||||
],
|
||||
],
|
||||
'dentistry' => [
|
||||
'odontogram' => [
|
||||
['name' => 'teeth_affected', 'label' => 'Teeth affected (FDI numbers, comma-separated)', 'type' => 'text', 'required' => true],
|
||||
['name' => 'findings', 'label' => 'Chart findings', 'type' => 'textarea', 'required' => true, 'rows' => 3],
|
||||
['name' => 'planned_procedures', 'label' => 'Planned procedures', 'type' => 'textarea', 'rows' => 2],
|
||||
['name' => 'completed_procedures', 'label' => 'Completed this visit', 'type' => 'textarea', 'rows' => 2],
|
||||
['name' => 'anesthesia', 'label' => 'Anesthesia', 'type' => 'select', 'options' => ['None', 'Local', 'Sedation', 'GA']],
|
||||
['name' => 'occlusion_notes', 'label' => 'Occlusion / bite notes', 'type' => 'text'],
|
||||
],
|
||||
'clinical_note' => [
|
||||
['name' => 'history', 'label' => 'History', 'type' => 'textarea', 'required' => true, 'rows' => 3],
|
||||
['name' => 'examination', 'label' => 'Examination', 'type' => 'textarea', 'rows' => 3],
|
||||
['name' => 'working_diagnosis', 'label' => 'Diagnosis', 'type' => 'text'],
|
||||
['name' => 'plan', 'label' => 'Plan', 'type' => 'textarea', 'rows' => 2],
|
||||
],
|
||||
],
|
||||
'ophthalmology' => [
|
||||
'eye_exam' => [
|
||||
['name' => 'chief_complaint', 'label' => 'Chief complaint', 'type' => 'text', 'required' => true],
|
||||
['name' => 'va_od', 'label' => 'Visual acuity OD', 'type' => 'text', 'required' => true],
|
||||
['name' => 'va_os', 'label' => 'Visual acuity OS', 'type' => 'text', 'required' => true],
|
||||
['name' => 'iop_od', 'label' => 'IOP OD (mmHg)', 'type' => 'number'],
|
||||
['name' => 'iop_os', 'label' => 'IOP OS (mmHg)', 'type' => 'number'],
|
||||
['name' => 'pupils', 'label' => 'Pupils', 'type' => 'text'],
|
||||
['name' => 'anterior_segment', 'label' => 'Anterior segment', 'type' => 'textarea', 'rows' => 2],
|
||||
['name' => 'fundus', 'label' => 'Fundus / posterior segment', 'type' => 'textarea', 'rows' => 2],
|
||||
['name' => 'findings', 'label' => 'Key findings', 'type' => 'textarea', 'required' => true, 'rows' => 3],
|
||||
],
|
||||
'refraction' => [
|
||||
['name' => 'sphere_od', 'label' => 'Sphere OD', 'type' => 'text'],
|
||||
['name' => 'cylinder_od', 'label' => 'Cylinder OD', 'type' => 'text'],
|
||||
['name' => 'axis_od', 'label' => 'Axis OD', 'type' => 'text'],
|
||||
['name' => 'sphere_os', 'label' => 'Sphere OS', 'type' => 'text'],
|
||||
['name' => 'cylinder_os', 'label' => 'Cylinder OS', 'type' => 'text'],
|
||||
['name' => 'axis_os', 'label' => 'Axis OS', 'type' => 'text'],
|
||||
['name' => 'add', 'label' => 'Near add', 'type' => 'text'],
|
||||
['name' => 'pd', 'label' => 'Pupillary distance (mm)', 'type' => 'number'],
|
||||
['name' => 'notes', 'label' => 'Refraction notes', 'type' => 'textarea', 'rows' => 2],
|
||||
],
|
||||
'clinical_note' => [
|
||||
['name' => 'history', 'label' => 'History of present illness', 'type' => 'textarea', 'required' => true, 'rows' => 4],
|
||||
['name' => 'examination', 'label' => 'Examination', 'type' => 'textarea', 'rows' => 3],
|
||||
['name' => 'working_diagnosis', 'label' => 'Working diagnosis', 'type' => 'text'],
|
||||
['name' => 'plan', 'label' => 'Plan', 'type' => 'textarea', 'rows' => 3],
|
||||
],
|
||||
],
|
||||
'physiotherapy' => [
|
||||
'pt_assessment' => [
|
||||
['name' => 'chief_complaint', 'label' => 'Presenting complaint', 'type' => 'text', 'required' => true],
|
||||
['name' => 'onset', 'label' => 'Onset / mechanism', 'type' => 'text'],
|
||||
['name' => 'pain_score', 'label' => 'Pain score (0–10)', 'type' => 'number'],
|
||||
['name' => 'rom', 'label' => 'Range of motion', 'type' => 'textarea', 'rows' => 2],
|
||||
['name' => 'strength', 'label' => 'Strength / function', 'type' => 'textarea', 'rows' => 2],
|
||||
['name' => 'goals', 'label' => 'Treatment goals', 'type' => 'textarea', 'required' => true, 'rows' => 2],
|
||||
],
|
||||
'pt_session' => [
|
||||
['name' => 'modalities', 'label' => 'Modalities used', 'type' => 'textarea', 'required' => true, 'rows' => 2],
|
||||
['name' => 'exercises', 'label' => 'Exercises prescribed', 'type' => 'textarea', 'rows' => 2],
|
||||
['name' => 'response', 'label' => 'Patient response', 'type' => 'textarea', 'rows' => 2],
|
||||
['name' => 'home_program', 'label' => 'Home program', 'type' => 'textarea', 'rows' => 2],
|
||||
['name' => 'next_review', 'label' => 'Next review', 'type' => 'text'],
|
||||
],
|
||||
],
|
||||
'maternity' => [
|
||||
'antenatal' => [
|
||||
['name' => 'gravida', 'label' => 'Gravida', 'type' => 'number', 'required' => true],
|
||||
['name' => 'para', 'label' => 'Para', 'type' => 'number'],
|
||||
['name' => 'gestational_age_weeks', 'label' => 'Gestational age (weeks)', 'type' => 'number', 'required' => true],
|
||||
['name' => 'bp', 'label' => 'Blood pressure', 'type' => 'text'],
|
||||
['name' => 'fundal_height', 'label' => 'Fundal height (cm)', 'type' => 'number'],
|
||||
['name' => 'fetal_heart_rate', 'label' => 'Fetal heart rate', 'type' => 'number'],
|
||||
['name' => 'presentation', 'label' => 'Presentation', 'type' => 'select', 'options' => ['Cephalic', 'Breech', 'Transverse', 'Unknown']],
|
||||
['name' => 'urine', 'label' => 'Urine dipstick', 'type' => 'text'],
|
||||
['name' => 'danger_signs', 'label' => 'Danger signs', 'type' => 'textarea', 'rows' => 2],
|
||||
['name' => 'plan', 'label' => 'Plan', 'type' => 'textarea', 'required' => true, 'rows' => 2],
|
||||
],
|
||||
'clinical_note' => [
|
||||
['name' => 'history', 'label' => 'History of present illness', 'type' => 'textarea', 'required' => true, 'rows' => 4],
|
||||
['name' => 'examination', 'label' => 'Examination', 'type' => 'textarea', 'rows' => 3],
|
||||
['name' => 'working_diagnosis', 'label' => 'Working diagnosis', 'type' => 'text'],
|
||||
['name' => 'plan', 'label' => 'Plan', 'type' => 'textarea', 'rows' => 3],
|
||||
],
|
||||
],
|
||||
'radiology' => [
|
||||
'imaging_request' => [
|
||||
['name' => 'modality', 'label' => 'Modality', 'type' => 'select', 'required' => true, 'options' => ['X-ray', 'Ultrasound', 'CT', 'MRI', 'Fluoroscopy', 'Other']],
|
||||
['name' => 'body_part', 'label' => 'Body part / region', 'type' => 'text', 'required' => true],
|
||||
['name' => 'clinical_info', 'label' => 'Clinical information', 'type' => 'textarea', 'required' => true, 'rows' => 3],
|
||||
['name' => 'urgency', 'label' => 'Urgency', 'type' => 'select', 'options' => ['Routine', 'Urgent', 'STAT']],
|
||||
['name' => 'contrast', 'label' => 'Contrast', 'type' => 'select', 'options' => ['None', 'IV', 'Oral', 'Both']],
|
||||
],
|
||||
'imaging_report' => [
|
||||
['name' => 'technique', 'label' => 'Technique', 'type' => 'textarea', 'rows' => 2],
|
||||
['name' => 'findings', 'label' => 'Findings', 'type' => 'textarea', 'required' => true, 'rows' => 4],
|
||||
['name' => 'impression', 'label' => 'Impression', 'type' => 'textarea', 'required' => true, 'rows' => 3],
|
||||
['name' => 'recommendations', 'label' => 'Recommendations', 'type' => 'textarea', 'rows' => 2],
|
||||
],
|
||||
],
|
||||
'cardiology' => [
|
||||
'cardiac_exam' => [
|
||||
['name' => 'chief_complaint', 'label' => 'Chief complaint', 'type' => 'text', 'required' => true],
|
||||
['name' => 'nyha_class', 'label' => 'NYHA class', 'type' => 'select', 'options' => ['I', 'II', 'III', 'IV', 'N/A']],
|
||||
['name' => 'bp', 'label' => 'Blood pressure', 'type' => 'text'],
|
||||
['name' => 'hr', 'label' => 'Heart rate', 'type' => 'number'],
|
||||
['name' => 'heart_sounds', 'label' => 'Heart sounds / murmurs', 'type' => 'textarea', 'rows' => 2],
|
||||
['name' => 'ecg_findings', 'label' => 'ECG findings', 'type' => 'textarea', 'rows' => 2],
|
||||
['name' => 'echo_summary', 'label' => 'Echo summary', 'type' => 'textarea', 'rows' => 2],
|
||||
['name' => 'risk_factors', 'label' => 'Cardiac risk factors', 'type' => 'textarea', 'rows' => 2],
|
||||
],
|
||||
'clinical_note' => [
|
||||
['name' => 'history', 'label' => 'History of present illness', 'type' => 'textarea', 'required' => true, 'rows' => 4],
|
||||
['name' => 'examination', 'label' => 'Examination', 'type' => 'textarea', 'rows' => 3],
|
||||
['name' => 'working_diagnosis', 'label' => 'Working diagnosis', 'type' => 'text'],
|
||||
['name' => 'plan', 'label' => 'Plan', 'type' => 'textarea', 'rows' => 3],
|
||||
],
|
||||
],
|
||||
'psychiatry' => [
|
||||
'mental_status' => [
|
||||
['name' => 'chief_complaint', 'label' => 'Presenting complaint', 'type' => 'text', 'required' => true],
|
||||
['name' => 'appearance', 'label' => 'Appearance / behaviour', 'type' => 'textarea', 'rows' => 2],
|
||||
['name' => 'mood_affect', 'label' => 'Mood / affect', 'type' => 'text'],
|
||||
['name' => 'thought', 'label' => 'Thought content / process', 'type' => 'textarea', 'rows' => 2],
|
||||
['name' => 'perception', 'label' => 'Perception', 'type' => 'text'],
|
||||
['name' => 'insight', 'label' => 'Insight / judgment', 'type' => 'text'],
|
||||
['name' => 'risk', 'label' => 'Risk assessment', 'type' => 'textarea', 'required' => true, 'rows' => 2],
|
||||
['name' => 'mse_summary', 'label' => 'MSE summary', 'type' => 'textarea', 'required' => true, 'rows' => 3],
|
||||
],
|
||||
'clinical_note' => [
|
||||
['name' => 'history', 'label' => 'History of present illness', 'type' => 'textarea', 'required' => true, 'rows' => 4],
|
||||
['name' => 'examination', 'label' => 'Examination', 'type' => 'textarea', 'rows' => 3],
|
||||
['name' => 'working_diagnosis', 'label' => 'Working diagnosis', 'type' => 'text'],
|
||||
['name' => 'plan', 'label' => 'Plan', 'type' => 'textarea', 'rows' => 3],
|
||||
],
|
||||
],
|
||||
'pediatrics' => [
|
||||
'pediatric_exam' => [
|
||||
['name' => 'chief_complaint', 'label' => 'Chief complaint', 'type' => 'text', 'required' => true],
|
||||
['name' => 'age_months', 'label' => 'Age (months)', 'type' => 'number'],
|
||||
['name' => 'weight_kg', 'label' => 'Weight (kg)', 'type' => 'number'],
|
||||
['name' => 'height_cm', 'label' => 'Height / length (cm)', 'type' => 'number'],
|
||||
['name' => 'head_circumference_cm', 'label' => 'Head circumference (cm)', 'type' => 'number'],
|
||||
['name' => 'developmental', 'label' => 'Developmental notes', 'type' => 'textarea', 'rows' => 2],
|
||||
['name' => 'immunization_status', 'label' => 'Immunization status', 'type' => 'text'],
|
||||
['name' => 'examination', 'label' => 'Examination', 'type' => 'textarea', 'required' => true, 'rows' => 3],
|
||||
],
|
||||
'clinical_note' => [
|
||||
['name' => 'history', 'label' => 'History of present illness', 'type' => 'textarea', 'required' => true, 'rows' => 4],
|
||||
['name' => 'examination', 'label' => 'Examination', 'type' => 'textarea', 'rows' => 3],
|
||||
['name' => 'working_diagnosis', 'label' => 'Working diagnosis', 'type' => 'text'],
|
||||
['name' => 'plan', 'label' => 'Plan', 'type' => 'textarea', 'rows' => 3],
|
||||
],
|
||||
],
|
||||
'orthopedics' => [
|
||||
'ortho_exam' => [
|
||||
['name' => 'chief_complaint', 'label' => 'Chief complaint', 'type' => 'text', 'required' => true],
|
||||
['name' => 'side', 'label' => 'Side', 'type' => 'select', 'options' => ['Left', 'Right', 'Bilateral', 'N/A']],
|
||||
['name' => 'region', 'label' => 'Region / joint', 'type' => 'text', 'required' => true],
|
||||
['name' => 'mechanism', 'label' => 'Mechanism of injury', 'type' => 'textarea', 'rows' => 2],
|
||||
['name' => 'rom', 'label' => 'Range of motion', 'type' => 'textarea', 'rows' => 2],
|
||||
['name' => 'neurovascular', 'label' => 'Neurovascular status', 'type' => 'text'],
|
||||
['name' => 'imaging', 'label' => 'Imaging findings', 'type' => 'textarea', 'rows' => 2],
|
||||
['name' => 'fracture_classification', 'label' => 'Fracture / injury classification', 'type' => 'text'],
|
||||
],
|
||||
'clinical_note' => [
|
||||
['name' => 'history', 'label' => 'History of present illness', 'type' => 'textarea', 'required' => true, 'rows' => 4],
|
||||
['name' => 'examination', 'label' => 'Examination', 'type' => 'textarea', 'rows' => 3],
|
||||
['name' => 'working_diagnosis', 'label' => 'Working diagnosis', 'type' => 'text'],
|
||||
['name' => 'plan', 'label' => 'Plan', 'type' => 'textarea', 'rows' => 3],
|
||||
],
|
||||
],
|
||||
'ent' => [
|
||||
'ent_exam' => [
|
||||
['name' => 'chief_complaint', 'label' => 'Chief complaint', 'type' => 'text', 'required' => true],
|
||||
['name' => 'ear_findings', 'label' => 'Ear findings', 'type' => 'textarea', 'rows' => 2],
|
||||
['name' => 'nose_findings', 'label' => 'Nose / sinus findings', 'type' => 'textarea', 'rows' => 2],
|
||||
['name' => 'throat_findings', 'label' => 'Throat / larynx findings', 'type' => 'textarea', 'rows' => 2],
|
||||
['name' => 'hearing', 'label' => 'Hearing assessment', 'type' => 'text'],
|
||||
['name' => 'neck', 'label' => 'Neck examination', 'type' => 'text'],
|
||||
],
|
||||
'clinical_note' => [
|
||||
['name' => 'history', 'label' => 'History of present illness', 'type' => 'textarea', 'required' => true, 'rows' => 4],
|
||||
['name' => 'examination', 'label' => 'Examination', 'type' => 'textarea', 'rows' => 3],
|
||||
['name' => 'working_diagnosis', 'label' => 'Working diagnosis', 'type' => 'text'],
|
||||
['name' => 'plan', 'label' => 'Plan', 'type' => 'textarea', 'rows' => 3],
|
||||
],
|
||||
],
|
||||
'oncology' => [
|
||||
'oncology_review' => [
|
||||
['name' => 'diagnosis', 'label' => 'Oncology diagnosis', 'type' => 'text', 'required' => true],
|
||||
['name' => 'stage', 'label' => 'Stage', 'type' => 'text'],
|
||||
['name' => 'performance_status', 'label' => 'Performance status (ECOG)', 'type' => 'select', 'options' => ['0', '1', '2', '3', '4']],
|
||||
['name' => 'current_regimen', 'label' => 'Current regimen', 'type' => 'textarea', 'rows' => 2],
|
||||
['name' => 'cycle', 'label' => 'Cycle / day', 'type' => 'text'],
|
||||
['name' => 'toxicity', 'label' => 'Toxicity / side effects', 'type' => 'textarea', 'rows' => 2],
|
||||
['name' => 'response', 'label' => 'Treatment response', 'type' => 'textarea', 'rows' => 2],
|
||||
['name' => 'plan', 'label' => 'Plan', 'type' => 'textarea', 'required' => true, 'rows' => 2],
|
||||
],
|
||||
'clinical_note' => [
|
||||
['name' => 'history', 'label' => 'History of present illness', 'type' => 'textarea', 'required' => true, 'rows' => 4],
|
||||
['name' => 'examination', 'label' => 'Examination', 'type' => 'textarea', 'rows' => 3],
|
||||
['name' => 'working_diagnosis', 'label' => 'Working diagnosis', 'type' => 'text'],
|
||||
['name' => 'plan', 'label' => 'Plan', 'type' => 'textarea', 'rows' => 3],
|
||||
],
|
||||
],
|
||||
'renal' => [
|
||||
'renal_session' => [
|
||||
['name' => 'session_type', 'label' => 'Session type', 'type' => 'select', 'required' => true, 'options' => ['Hemodialysis', 'Peritoneal dialysis', 'Clinic review', 'Other']],
|
||||
['name' => 'pre_weight_kg', 'label' => 'Pre-weight (kg)', 'type' => 'number'],
|
||||
['name' => 'post_weight_kg', 'label' => 'Post-weight (kg)', 'type' => 'number'],
|
||||
['name' => 'uf_goal_l', 'label' => 'UF goal (L)', 'type' => 'number'],
|
||||
['name' => 'access', 'label' => 'Access', 'type' => 'text'],
|
||||
['name' => 'bp_pre', 'label' => 'BP pre', 'type' => 'text'],
|
||||
['name' => 'bp_post', 'label' => 'BP post', 'type' => 'text'],
|
||||
['name' => 'complications', 'label' => 'Complications', 'type' => 'textarea', 'rows' => 2],
|
||||
['name' => 'notes', 'label' => 'Session notes', 'type' => 'textarea', 'required' => true, 'rows' => 3],
|
||||
],
|
||||
'clinical_note' => [
|
||||
['name' => 'history', 'label' => 'History of present illness', 'type' => 'textarea', 'required' => true, 'rows' => 4],
|
||||
['name' => 'examination', 'label' => 'Examination', 'type' => 'textarea', 'rows' => 3],
|
||||
['name' => 'working_diagnosis', 'label' => 'Working diagnosis', 'type' => 'text'],
|
||||
['name' => 'plan', 'label' => 'Plan', 'type' => 'textarea', 'rows' => 3],
|
||||
],
|
||||
],
|
||||
'surgery' => [
|
||||
'preop' => [
|
||||
['name' => 'proposed_procedure', 'label' => 'Proposed procedure', 'type' => 'text', 'required' => true],
|
||||
['name' => 'indication', 'label' => 'Indication', 'type' => 'textarea', 'required' => true, 'rows' => 2],
|
||||
['name' => 'asa_class', 'label' => 'ASA class', 'type' => 'select', 'options' => ['I', 'II', 'III', 'IV', 'V', 'E']],
|
||||
['name' => 'allergies_reviewed', 'label' => 'Allergies reviewed', 'type' => 'boolean'],
|
||||
['name' => 'consent_obtained', 'label' => 'Consent obtained', 'type' => 'boolean'],
|
||||
['name' => 'fitness', 'label' => 'Fitness for anaesthesia', 'type' => 'textarea', 'rows' => 2],
|
||||
['name' => 'investigations', 'label' => 'Relevant investigations', 'type' => 'textarea', 'rows' => 2],
|
||||
['name' => 'plan', 'label' => 'Peri-op plan', 'type' => 'textarea', 'required' => true, 'rows' => 2],
|
||||
],
|
||||
'clinical_note' => [
|
||||
['name' => 'history', 'label' => 'History of present illness', 'type' => 'textarea', 'required' => true, 'rows' => 4],
|
||||
['name' => 'examination', 'label' => 'Examination', 'type' => 'textarea', 'rows' => 3],
|
||||
['name' => 'working_diagnosis', 'label' => 'Working diagnosis', 'type' => 'text'],
|
||||
['name' => 'plan', 'label' => 'Plan', 'type' => 'textarea', 'rows' => 3],
|
||||
],
|
||||
],
|
||||
'vaccination' => [
|
||||
'vax_screening' => [
|
||||
['name' => 'vaccine', 'label' => 'Vaccine', 'type' => 'text', 'required' => true],
|
||||
['name' => 'indications', 'label' => 'Indications', 'type' => 'textarea', 'rows' => 2],
|
||||
['name' => 'contraindications', 'label' => 'Contraindications checked', 'type' => 'boolean', 'required' => true],
|
||||
['name' => 'allergy_history', 'label' => 'Allergy / prior reaction', 'type' => 'textarea', 'rows' => 2],
|
||||
['name' => 'fever_today', 'label' => 'Fever today', 'type' => 'boolean'],
|
||||
['name' => 'cleared', 'label' => 'Cleared to vaccinate', 'type' => 'boolean', 'required' => true],
|
||||
],
|
||||
'vax_admin' => [
|
||||
['name' => 'vaccine', 'label' => 'Vaccine administered', 'type' => 'text', 'required' => true],
|
||||
['name' => 'dose', 'label' => 'Dose / antigen', 'type' => 'text'],
|
||||
['name' => 'site', 'label' => 'Injection site', 'type' => 'select', 'options' => ['Left arm', 'Right arm', 'Left thigh', 'Right thigh', 'Oral', 'Other']],
|
||||
['name' => 'batch', 'label' => 'Batch / lot number', 'type' => 'text', 'required' => true],
|
||||
['name' => 'expiry', 'label' => 'Expiry date', 'type' => 'text'],
|
||||
['name' => 'route', 'label' => 'Route', 'type' => 'select', 'options' => ['IM', 'SC', 'ID', 'Oral', 'Nasal']],
|
||||
['name' => 'adverse_events', 'label' => 'Immediate adverse events', 'type' => 'textarea', 'rows' => 2],
|
||||
['name' => 'next_due', 'label' => 'Next dose due', 'type' => 'text'],
|
||||
],
|
||||
],
|
||||
'pathology' => [
|
||||
'path_request' => [
|
||||
['name' => 'specimen_type', 'label' => 'Specimen type', 'type' => 'text', 'required' => true],
|
||||
['name' => 'site', 'label' => 'Anatomic site', 'type' => 'text', 'required' => true],
|
||||
['name' => 'clinical_history', 'label' => 'Clinical history', 'type' => 'textarea', 'required' => true, 'rows' => 3],
|
||||
['name' => 'urgency', 'label' => 'Urgency', 'type' => 'select', 'options' => ['Routine', 'Urgent', 'Intra-op']],
|
||||
['name' => 'tests_requested', 'label' => 'Tests requested', 'type' => 'textarea', 'rows' => 2],
|
||||
],
|
||||
'path_report' => [
|
||||
['name' => 'gross', 'label' => 'Gross description', 'type' => 'textarea', 'rows' => 3],
|
||||
['name' => 'microscopy', 'label' => 'Microscopy', 'type' => 'textarea', 'required' => true, 'rows' => 4],
|
||||
['name' => 'diagnosis', 'label' => 'Diagnosis', 'type' => 'textarea', 'required' => true, 'rows' => 3],
|
||||
['name' => 'comment', 'label' => 'Comment', 'type' => 'textarea', 'rows' => 2],
|
||||
],
|
||||
],
|
||||
'infusion' => [
|
||||
'infusion_session' => [
|
||||
['name' => 'medication', 'label' => 'Medication / regimen', 'type' => 'text', 'required' => true],
|
||||
['name' => 'dose', 'label' => 'Dose', 'type' => 'text'],
|
||||
['name' => 'rate', 'label' => 'Infusion rate', 'type' => 'text'],
|
||||
['name' => 'access', 'label' => 'IV access', 'type' => 'text'],
|
||||
['name' => 'start_time', 'label' => 'Start time', 'type' => 'text'],
|
||||
['name' => 'end_time', 'label' => 'End time', 'type' => 'text'],
|
||||
['name' => 'vitals', 'label' => 'Vitals during infusion', 'type' => 'textarea', 'rows' => 2],
|
||||
['name' => 'reactions', 'label' => 'Reactions / interventions', 'type' => 'textarea', 'rows' => 2],
|
||||
['name' => 'notes', 'label' => 'Session notes', 'type' => 'textarea', 'required' => true, 'rows' => 2],
|
||||
],
|
||||
'clinical_note' => [
|
||||
['name' => 'history', 'label' => 'History of present illness', 'type' => 'textarea', 'required' => true, 'rows' => 4],
|
||||
['name' => 'examination', 'label' => 'Examination', 'type' => 'textarea', 'rows' => 3],
|
||||
['name' => 'working_diagnosis', 'label' => 'Working diagnosis', 'type' => 'text'],
|
||||
['name' => 'plan', 'label' => 'Plan', 'type' => 'textarea', 'rows' => 3],
|
||||
],
|
||||
],
|
||||
'dermatology' => [
|
||||
'skin_exam' => [
|
||||
['name' => 'chief_complaint', 'label' => 'Chief complaint', 'type' => 'text', 'required' => true],
|
||||
['name' => 'duration', 'label' => 'Duration', 'type' => 'text'],
|
||||
['name' => 'distribution', 'label' => 'Distribution', 'type' => 'text'],
|
||||
['name' => 'morphology', 'label' => 'Lesion morphology', 'type' => 'textarea', 'required' => true, 'rows' => 2],
|
||||
['name' => 'itch_pain', 'label' => 'Itch / pain', 'type' => 'text'],
|
||||
['name' => 'dermoscopy', 'label' => 'Dermoscopy notes', 'type' => 'textarea', 'rows' => 2],
|
||||
['name' => 'differential', 'label' => 'Differential diagnosis', 'type' => 'textarea', 'rows' => 2],
|
||||
],
|
||||
'clinical_note' => [
|
||||
['name' => 'history', 'label' => 'History of present illness', 'type' => 'textarea', 'required' => true, 'rows' => 4],
|
||||
['name' => 'examination', 'label' => 'Examination', 'type' => 'textarea', 'rows' => 3],
|
||||
['name' => 'working_diagnosis', 'label' => 'Working diagnosis', 'type' => 'text'],
|
||||
['name' => 'plan', 'label' => 'Plan', 'type' => 'textarea', 'rows' => 3],
|
||||
],
|
||||
],
|
||||
'podiatry' => [
|
||||
'foot_exam' => [
|
||||
['name' => 'chief_complaint', 'label' => 'Chief complaint', 'type' => 'text', 'required' => true],
|
||||
['name' => 'side', 'label' => 'Side', 'type' => 'select', 'options' => ['Left', 'Right', 'Bilateral']],
|
||||
['name' => 'diabetes', 'label' => 'Diabetic foot risk', 'type' => 'select', 'options' => ['Low', 'Moderate', 'High', 'Active ulcer', 'N/A']],
|
||||
['name' => 'pulses', 'label' => 'Pulses', 'type' => 'text'],
|
||||
['name' => 'sensation', 'label' => 'Sensation / neuropathy', 'type' => 'text'],
|
||||
['name' => 'skin_nails', 'label' => 'Skin / nails', 'type' => 'textarea', 'rows' => 2],
|
||||
['name' => 'ulcer', 'label' => 'Ulcer description', 'type' => 'textarea', 'rows' => 2],
|
||||
['name' => 'offloading', 'label' => 'Offloading / footwear', 'type' => 'text'],
|
||||
],
|
||||
'clinical_note' => [
|
||||
['name' => 'history', 'label' => 'History of present illness', 'type' => 'textarea', 'required' => true, 'rows' => 4],
|
||||
['name' => 'examination', 'label' => 'Examination', 'type' => 'textarea', 'rows' => 3],
|
||||
['name' => 'working_diagnosis', 'label' => 'Working diagnosis', 'type' => 'text'],
|
||||
['name' => 'plan', 'label' => 'Plan', 'type' => 'textarea', 'rows' => 3],
|
||||
],
|
||||
],
|
||||
'fertility' => [
|
||||
'fertility_consult' => [
|
||||
['name' => 'partner_present', 'label' => 'Partner present', 'type' => 'boolean'],
|
||||
['name' => 'trying_months', 'label' => 'Trying to conceive (months)', 'type' => 'number'],
|
||||
['name' => 'cycle_day', 'label' => 'Cycle day', 'type' => 'number'],
|
||||
['name' => 'amh', 'label' => 'AMH / ovarian reserve notes', 'type' => 'text'],
|
||||
['name' => 'semen_summary', 'label' => 'Semen analysis summary', 'type' => 'textarea', 'rows' => 2],
|
||||
['name' => 'tubal_uterine', 'label' => 'Tubal / uterine assessment', 'type' => 'textarea', 'rows' => 2],
|
||||
['name' => 'plan', 'label' => 'Fertility plan', 'type' => 'textarea', 'required' => true, 'rows' => 3],
|
||||
],
|
||||
'clinical_note' => [
|
||||
['name' => 'history', 'label' => 'History of present illness', 'type' => 'textarea', 'required' => true, 'rows' => 4],
|
||||
['name' => 'examination', 'label' => 'Examination', 'type' => 'textarea', 'rows' => 3],
|
||||
['name' => 'working_diagnosis', 'label' => 'Working diagnosis', 'type' => 'text'],
|
||||
['name' => 'plan', 'label' => 'Plan', 'type' => 'textarea', 'rows' => 3],
|
||||
],
|
||||
],
|
||||
'child_welfare' => [
|
||||
'growth' => [
|
||||
['name' => 'age_months', 'label' => 'Age (months)', 'type' => 'number', 'required' => true],
|
||||
['name' => 'weight_kg', 'label' => 'Weight (kg)', 'type' => 'number', 'required' => true],
|
||||
['name' => 'height_cm', 'label' => 'Height / length (cm)', 'type' => 'number'],
|
||||
['name' => 'head_circumference_cm', 'label' => 'Head circumference (cm)', 'type' => 'number'],
|
||||
['name' => 'muac_cm', 'label' => 'MUAC (cm)', 'type' => 'number'],
|
||||
['name' => 'growth_status', 'label' => 'Growth status', 'type' => 'select', 'options' => ['Normal', 'Underweight', 'Stunted', 'Wasted', 'Overweight']],
|
||||
['name' => 'feeding', 'label' => 'Feeding / nutrition', 'type' => 'textarea', 'rows' => 2],
|
||||
['name' => 'milestones', 'label' => 'Developmental milestones', 'type' => 'textarea', 'rows' => 2],
|
||||
['name' => 'counseling', 'label' => 'Counseling given', 'type' => 'textarea', 'rows' => 2],
|
||||
],
|
||||
'clinical_note' => [
|
||||
['name' => 'history', 'label' => 'History of present illness', 'type' => 'textarea', 'required' => true, 'rows' => 4],
|
||||
['name' => 'examination', 'label' => 'Examination', 'type' => 'textarea', 'rows' => 3],
|
||||
['name' => 'working_diagnosis', 'label' => 'Working diagnosis', 'type' => 'text'],
|
||||
['name' => 'plan', 'label' => 'Plan', 'type' => 'textarea', 'rows' => 3],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
@@ -18,7 +18,7 @@ return [
|
||||
'queue_prefix' => 'ER',
|
||||
'nav_label' => 'Emergency',
|
||||
'queue_keywords' => ['emerg', 'casualty', 'trauma', 'er', 'a&e'],
|
||||
'roles' => ['doctor', 'nurse', 'receptionist', 'hospital_admin', 'super_admin'],
|
||||
'roles' => ['doctor', 'nurse', 'receptionist'],
|
||||
'default_on_paid_plans' => true,
|
||||
],
|
||||
'blood_bank' => [
|
||||
@@ -30,7 +30,7 @@ return [
|
||||
'queue_prefix' => 'BB',
|
||||
'nav_label' => 'Blood Bank',
|
||||
'queue_keywords' => ['blood', 'transfusion', 'crossmatch', 'donor'],
|
||||
'roles' => ['doctor', 'nurse', 'lab_technician', 'receptionist', 'hospital_admin', 'super_admin'],
|
||||
'roles' => ['doctor', 'nurse', 'lab_technician', 'receptionist'],
|
||||
'default_on_paid_plans' => true,
|
||||
],
|
||||
'dentistry' => [
|
||||
@@ -42,7 +42,7 @@ return [
|
||||
'queue_prefix' => 'DEN',
|
||||
'nav_label' => 'Dentistry',
|
||||
'queue_keywords' => ['dent', 'dental', 'oral'],
|
||||
'roles' => ['doctor', 'nurse', 'receptionist', 'hospital_admin', 'super_admin'],
|
||||
'roles' => ['doctor', 'nurse', 'receptionist'],
|
||||
],
|
||||
'ophthalmology' => [
|
||||
'label' => 'Eye care',
|
||||
@@ -53,7 +53,7 @@ return [
|
||||
'queue_prefix' => 'EYE',
|
||||
'nav_label' => 'Eye care',
|
||||
'queue_keywords' => ['eye', 'ophthal', 'optom'],
|
||||
'roles' => ['doctor', 'nurse', 'receptionist', 'hospital_admin', 'super_admin'],
|
||||
'roles' => ['doctor', 'nurse', 'receptionist'],
|
||||
],
|
||||
'physiotherapy' => [
|
||||
'label' => 'Physiotherapy',
|
||||
@@ -64,7 +64,7 @@ return [
|
||||
'queue_prefix' => 'PHY',
|
||||
'nav_label' => 'Physiotherapy',
|
||||
'queue_keywords' => ['physio', 'rehab', 'therapy'],
|
||||
'roles' => ['doctor', 'nurse', 'receptionist', 'hospital_admin', 'super_admin'],
|
||||
'roles' => ['doctor', 'nurse', 'receptionist'],
|
||||
],
|
||||
'maternity' => [
|
||||
'label' => 'Maternity / Antenatal',
|
||||
@@ -75,7 +75,7 @@ return [
|
||||
'queue_prefix' => 'MAT',
|
||||
'nav_label' => 'Maternity',
|
||||
'queue_keywords' => ['matern', 'antenatal', 'obstetric'],
|
||||
'roles' => ['doctor', 'nurse', 'receptionist', 'hospital_admin', 'super_admin'],
|
||||
'roles' => ['doctor', 'nurse', 'receptionist'],
|
||||
],
|
||||
'radiology' => [
|
||||
'label' => 'Radiology',
|
||||
@@ -86,7 +86,7 @@ return [
|
||||
'queue_prefix' => 'RAD',
|
||||
'nav_label' => 'Radiology',
|
||||
'queue_keywords' => ['radio', 'imaging', 'x-ray', 'xray', 'ultrasound', 'ct', 'mri'],
|
||||
'roles' => ['doctor', 'lab_technician', 'receptionist', 'hospital_admin', 'super_admin'],
|
||||
'roles' => ['doctor', 'lab_technician', 'receptionist'],
|
||||
],
|
||||
'cardiology' => [
|
||||
'label' => 'Cardiology',
|
||||
@@ -97,7 +97,7 @@ return [
|
||||
'queue_prefix' => 'CAR',
|
||||
'nav_label' => 'Cardiology',
|
||||
'queue_keywords' => ['cardio', 'heart', 'ecg', 'echo'],
|
||||
'roles' => ['doctor', 'nurse', 'receptionist', 'hospital_admin', 'super_admin'],
|
||||
'roles' => ['doctor', 'nurse', 'receptionist'],
|
||||
],
|
||||
'psychiatry' => [
|
||||
'label' => 'Psychiatry',
|
||||
@@ -108,7 +108,7 @@ return [
|
||||
'queue_prefix' => 'PSY',
|
||||
'nav_label' => 'Psychiatry',
|
||||
'queue_keywords' => ['psych', 'mental', 'behaviour'],
|
||||
'roles' => ['doctor', 'nurse', 'receptionist', 'hospital_admin', 'super_admin'],
|
||||
'roles' => ['doctor', 'nurse', 'receptionist'],
|
||||
],
|
||||
'pediatrics' => [
|
||||
'label' => 'Pediatrics',
|
||||
@@ -119,7 +119,7 @@ return [
|
||||
'queue_prefix' => 'PED',
|
||||
'nav_label' => 'Pediatrics',
|
||||
'queue_keywords' => ['pedia', 'paedia', 'child', 'infant'],
|
||||
'roles' => ['doctor', 'nurse', 'receptionist', 'hospital_admin', 'super_admin'],
|
||||
'roles' => ['doctor', 'nurse', 'receptionist'],
|
||||
],
|
||||
'orthopedics' => [
|
||||
'label' => 'Orthopedics',
|
||||
@@ -130,7 +130,7 @@ return [
|
||||
'queue_prefix' => 'ORT',
|
||||
'nav_label' => 'Orthopedics',
|
||||
'queue_keywords' => ['ortho', 'fracture', 'bone', 'joint'],
|
||||
'roles' => ['doctor', 'nurse', 'receptionist', 'hospital_admin', 'super_admin'],
|
||||
'roles' => ['doctor', 'nurse', 'receptionist'],
|
||||
],
|
||||
'ent' => [
|
||||
'label' => 'ENT',
|
||||
@@ -141,7 +141,7 @@ return [
|
||||
'queue_prefix' => 'ENT',
|
||||
'nav_label' => 'ENT',
|
||||
'queue_keywords' => ['ent', 'ear', 'nose', 'throat', 'otolaryng'],
|
||||
'roles' => ['doctor', 'nurse', 'receptionist', 'hospital_admin', 'super_admin'],
|
||||
'roles' => ['doctor', 'nurse', 'receptionist'],
|
||||
],
|
||||
'oncology' => [
|
||||
'label' => 'Oncology',
|
||||
@@ -152,7 +152,7 @@ return [
|
||||
'queue_prefix' => 'ONC',
|
||||
'nav_label' => 'Oncology',
|
||||
'queue_keywords' => ['oncol', 'cancer', 'chemo'],
|
||||
'roles' => ['doctor', 'nurse', 'receptionist', 'hospital_admin', 'super_admin'],
|
||||
'roles' => ['doctor', 'nurse', 'receptionist'],
|
||||
],
|
||||
'renal' => [
|
||||
'label' => 'Renal Care',
|
||||
@@ -163,7 +163,7 @@ return [
|
||||
'queue_prefix' => 'REN',
|
||||
'nav_label' => 'Renal Care',
|
||||
'queue_keywords' => ['renal', 'dialysis', 'nephro', 'kidney'],
|
||||
'roles' => ['doctor', 'nurse', 'receptionist', 'hospital_admin', 'super_admin'],
|
||||
'roles' => ['doctor', 'nurse', 'receptionist'],
|
||||
],
|
||||
'surgery' => [
|
||||
'label' => 'Surgery',
|
||||
@@ -174,7 +174,7 @@ return [
|
||||
'queue_prefix' => 'SUR',
|
||||
'nav_label' => 'Surgery',
|
||||
'queue_keywords' => ['surg', 'theatre', 'operat', 'pre-op'],
|
||||
'roles' => ['doctor', 'nurse', 'receptionist', 'hospital_admin', 'super_admin'],
|
||||
'roles' => ['doctor', 'nurse', 'receptionist'],
|
||||
],
|
||||
'vaccination' => [
|
||||
'label' => 'Vaccination & Immunization',
|
||||
@@ -185,7 +185,7 @@ return [
|
||||
'queue_prefix' => 'VAX',
|
||||
'nav_label' => 'Vaccination',
|
||||
'queue_keywords' => ['vaccin', 'immun', 'epi', 'injection'],
|
||||
'roles' => ['doctor', 'nurse', 'receptionist', 'hospital_admin', 'super_admin'],
|
||||
'roles' => ['doctor', 'nurse', 'receptionist'],
|
||||
],
|
||||
'pathology' => [
|
||||
'label' => 'Pathology',
|
||||
@@ -196,7 +196,7 @@ return [
|
||||
'queue_prefix' => 'PATH',
|
||||
'nav_label' => 'Pathology',
|
||||
'queue_keywords' => ['pathol', 'histo', 'cytol', 'biopsy'],
|
||||
'roles' => ['doctor', 'lab_technician', 'receptionist', 'hospital_admin', 'super_admin'],
|
||||
'roles' => ['doctor', 'lab_technician', 'receptionist'],
|
||||
],
|
||||
'infusion' => [
|
||||
'label' => 'Infusion Centre',
|
||||
@@ -207,7 +207,7 @@ return [
|
||||
'queue_prefix' => 'INF',
|
||||
'nav_label' => 'Infusion',
|
||||
'queue_keywords' => ['infusion', 'iv', 'drip', 'day care'],
|
||||
'roles' => ['doctor', 'nurse', 'receptionist', 'hospital_admin', 'super_admin'],
|
||||
'roles' => ['doctor', 'nurse', 'receptionist'],
|
||||
],
|
||||
'dermatology' => [
|
||||
'label' => 'Dermatology',
|
||||
@@ -218,7 +218,7 @@ return [
|
||||
'queue_prefix' => 'DER',
|
||||
'nav_label' => 'Dermatology',
|
||||
'queue_keywords' => ['derma', 'skin', 'rash'],
|
||||
'roles' => ['doctor', 'nurse', 'receptionist', 'hospital_admin', 'super_admin'],
|
||||
'roles' => ['doctor', 'nurse', 'receptionist'],
|
||||
],
|
||||
'podiatry' => [
|
||||
'label' => 'Podiatry',
|
||||
@@ -229,7 +229,7 @@ return [
|
||||
'queue_prefix' => 'POD',
|
||||
'nav_label' => 'Podiatry',
|
||||
'queue_keywords' => ['podiat', 'foot', 'diabetic foot'],
|
||||
'roles' => ['doctor', 'nurse', 'receptionist', 'hospital_admin', 'super_admin'],
|
||||
'roles' => ['doctor', 'nurse', 'receptionist'],
|
||||
],
|
||||
'fertility' => [
|
||||
'label' => 'Fertility',
|
||||
@@ -240,7 +240,7 @@ return [
|
||||
'queue_prefix' => 'FER',
|
||||
'nav_label' => 'Fertility',
|
||||
'queue_keywords' => ['fertil', 'ivf', 'reproduct'],
|
||||
'roles' => ['doctor', 'nurse', 'receptionist', 'hospital_admin', 'super_admin'],
|
||||
'roles' => ['doctor', 'nurse', 'receptionist'],
|
||||
],
|
||||
'child_welfare' => [
|
||||
'label' => 'Child Welfare Clinic',
|
||||
@@ -251,6 +251,6 @@ return [
|
||||
'queue_prefix' => 'CWC',
|
||||
'nav_label' => 'Child Welfare',
|
||||
'queue_keywords' => ['cwc', 'child welfare', 'well baby', 'growth'],
|
||||
'roles' => ['doctor', 'nurse', 'receptionist', 'hospital_admin', 'super_admin'],
|
||||
'roles' => ['doctor', 'nurse', 'receptionist'],
|
||||
],
|
||||
];
|
||||
|
||||
@@ -35,7 +35,7 @@ return [
|
||||
],
|
||||
'actions' => [
|
||||
'call_next' => 'Call next patient',
|
||||
'start' => 'Start consultation',
|
||||
'start' => 'Start specialty encounter',
|
||||
'complete' => 'Complete',
|
||||
'order_test' => 'Order test',
|
||||
'prescribe' => 'Prescribe',
|
||||
@@ -114,5 +114,375 @@ return [
|
||||
'documents' => 'Documents',
|
||||
],
|
||||
],
|
||||
'ophthalmology' => [
|
||||
'stages' => [
|
||||
['code' => 'waiting', 'label' => 'Waiting room', 'queue_point' => 'waiting'],
|
||||
['code' => 'exam', 'label' => 'Eye exam', 'queue_point' => 'chair'],
|
||||
['code' => 'procedure', 'label' => 'Procedure / treatment', 'queue_point' => 'chair'],
|
||||
['code' => 'completed', 'label' => 'Completed', 'queue_point' => null],
|
||||
],
|
||||
'services' => [
|
||||
['code' => 'eye.consult', 'label' => 'Eye consultation', 'amount_minor' => 6000, 'type' => 'consultation'],
|
||||
['code' => 'eye.refraction', 'label' => 'Refraction / glasses exam', 'amount_minor' => 4000, 'type' => 'procedure'],
|
||||
['code' => 'eye.iop', 'label' => 'IOP / pressure check', 'amount_minor' => 3000, 'type' => 'procedure'],
|
||||
['code' => 'eye.fundus', 'label' => 'Fundus examination', 'amount_minor' => 5000, 'type' => 'procedure'],
|
||||
],
|
||||
'workspace_tabs' => [
|
||||
'overview' => 'Overview',
|
||||
'exam' => 'Eye exam',
|
||||
'refraction' => 'Refraction',
|
||||
'clinical_notes' => 'Clinical notes',
|
||||
'orders' => 'Orders',
|
||||
'billing' => 'Billing',
|
||||
'documents' => 'Documents',
|
||||
],
|
||||
],
|
||||
'physiotherapy' => [
|
||||
'stages' => [
|
||||
['code' => 'waiting', 'label' => 'Waiting', 'queue_point' => 'waiting'],
|
||||
['code' => 'assessment', 'label' => 'Assessment', 'queue_point' => 'chair'],
|
||||
['code' => 'treatment', 'label' => 'Treatment session', 'queue_point' => 'chair'],
|
||||
['code' => 'completed', 'label' => 'Completed', 'queue_point' => null],
|
||||
],
|
||||
'services' => [
|
||||
['code' => 'pt.assessment', 'label' => 'Physio assessment', 'amount_minor' => 5000, 'type' => 'consultation'],
|
||||
['code' => 'pt.session', 'label' => 'Treatment session', 'amount_minor' => 7000, 'type' => 'procedure'],
|
||||
],
|
||||
'workspace_tabs' => [
|
||||
'overview' => 'Overview',
|
||||
'assessment' => 'Assessment',
|
||||
'session' => 'Session notes',
|
||||
'orders' => 'Orders',
|
||||
'billing' => 'Billing',
|
||||
'documents' => 'Documents',
|
||||
],
|
||||
],
|
||||
'maternity' => [
|
||||
'stages' => [
|
||||
['code' => 'waiting', 'label' => 'Waiting', 'queue_point' => 'waiting'],
|
||||
['code' => 'antenatal', 'label' => 'Antenatal review', 'queue_point' => 'chair'],
|
||||
['code' => 'completed', 'label' => 'Completed', 'queue_point' => null],
|
||||
],
|
||||
'services' => [
|
||||
['code' => 'mat.anc', 'label' => 'Antenatal visit', 'amount_minor' => 5000, 'type' => 'consultation'],
|
||||
['code' => 'mat.ultrasound', 'label' => 'Obstetric ultrasound', 'amount_minor' => 12000, 'type' => 'imaging'],
|
||||
],
|
||||
'workspace_tabs' => [
|
||||
'overview' => 'Overview',
|
||||
'antenatal' => 'Antenatal',
|
||||
'clinical_notes' => 'Clinical notes',
|
||||
'orders' => 'Orders',
|
||||
'billing' => 'Billing',
|
||||
'documents' => 'Documents',
|
||||
],
|
||||
],
|
||||
'radiology' => [
|
||||
'stages' => [
|
||||
['code' => 'request', 'label' => 'Request received', 'queue_point' => 'waiting'],
|
||||
['code' => 'imaging', 'label' => 'Imaging', 'queue_point' => 'chair'],
|
||||
['code' => 'reporting', 'label' => 'Reporting', 'queue_point' => 'chair'],
|
||||
['code' => 'completed', 'label' => 'Completed', 'queue_point' => null],
|
||||
],
|
||||
'services' => [
|
||||
['code' => 'rad.xray', 'label' => 'X-ray', 'amount_minor' => 8000, 'type' => 'imaging'],
|
||||
['code' => 'rad.ultrasound', 'label' => 'Ultrasound', 'amount_minor' => 15000, 'type' => 'imaging'],
|
||||
['code' => 'rad.ct', 'label' => 'CT scan', 'amount_minor' => 45000, 'type' => 'imaging'],
|
||||
],
|
||||
'workspace_tabs' => [
|
||||
'overview' => 'Overview',
|
||||
'request' => 'Imaging request',
|
||||
'report' => 'Report',
|
||||
'billing' => 'Billing',
|
||||
'documents' => 'Documents',
|
||||
],
|
||||
],
|
||||
'cardiology' => [
|
||||
'stages' => [
|
||||
['code' => 'waiting', 'label' => 'Waiting', 'queue_point' => 'waiting'],
|
||||
['code' => 'exam', 'label' => 'Cardiac exam', 'queue_point' => 'chair'],
|
||||
['code' => 'completed', 'label' => 'Completed', 'queue_point' => null],
|
||||
],
|
||||
'services' => [
|
||||
['code' => 'car.consult', 'label' => 'Cardiology consultation', 'amount_minor' => 8000, 'type' => 'consultation'],
|
||||
['code' => 'car.ecg', 'label' => 'ECG', 'amount_minor' => 5000, 'type' => 'procedure'],
|
||||
['code' => 'car.echo', 'label' => 'Echocardiogram', 'amount_minor' => 25000, 'type' => 'imaging'],
|
||||
],
|
||||
'workspace_tabs' => [
|
||||
'overview' => 'Overview',
|
||||
'exam' => 'Cardiac exam',
|
||||
'clinical_notes' => 'Clinical notes',
|
||||
'orders' => 'Orders',
|
||||
'billing' => 'Billing',
|
||||
'documents' => 'Documents',
|
||||
],
|
||||
],
|
||||
'psychiatry' => [
|
||||
'stages' => [
|
||||
['code' => 'waiting', 'label' => 'Waiting', 'queue_point' => 'waiting'],
|
||||
['code' => 'assessment', 'label' => 'Mental health assessment', 'queue_point' => 'chair'],
|
||||
['code' => 'completed', 'label' => 'Completed', 'queue_point' => null],
|
||||
],
|
||||
'services' => [
|
||||
['code' => 'psy.consult', 'label' => 'Psychiatry consultation', 'amount_minor' => 8000, 'type' => 'consultation'],
|
||||
],
|
||||
'workspace_tabs' => [
|
||||
'overview' => 'Overview',
|
||||
'assessment' => 'Assessment',
|
||||
'clinical_notes' => 'Clinical notes',
|
||||
'orders' => 'Orders',
|
||||
'billing' => 'Billing',
|
||||
'documents' => 'Documents',
|
||||
],
|
||||
],
|
||||
'pediatrics' => [
|
||||
'stages' => [
|
||||
['code' => 'waiting', 'label' => 'Waiting', 'queue_point' => 'waiting'],
|
||||
['code' => 'exam', 'label' => 'Pediatric exam', 'queue_point' => 'chair'],
|
||||
['code' => 'completed', 'label' => 'Completed', 'queue_point' => null],
|
||||
],
|
||||
'services' => [
|
||||
['code' => 'ped.consult', 'label' => 'Pediatric consultation', 'amount_minor' => 5000, 'type' => 'consultation'],
|
||||
],
|
||||
'workspace_tabs' => [
|
||||
'overview' => 'Overview',
|
||||
'exam' => 'Pediatric exam',
|
||||
'clinical_notes' => 'Clinical notes',
|
||||
'orders' => 'Orders',
|
||||
'billing' => 'Billing',
|
||||
'documents' => 'Documents',
|
||||
],
|
||||
],
|
||||
'orthopedics' => [
|
||||
'stages' => [
|
||||
['code' => 'waiting', 'label' => 'Waiting', 'queue_point' => 'waiting'],
|
||||
['code' => 'exam', 'label' => 'Ortho exam', 'queue_point' => 'chair'],
|
||||
['code' => 'procedure', 'label' => 'Procedure / casting', 'queue_point' => 'chair'],
|
||||
['code' => 'completed', 'label' => 'Completed', 'queue_point' => null],
|
||||
],
|
||||
'services' => [
|
||||
['code' => 'ort.consult', 'label' => 'Orthopedic consultation', 'amount_minor' => 6000, 'type' => 'consultation'],
|
||||
['code' => 'ort.cast', 'label' => 'Casting / splinting', 'amount_minor' => 10000, 'type' => 'procedure'],
|
||||
],
|
||||
'workspace_tabs' => [
|
||||
'overview' => 'Overview',
|
||||
'exam' => 'Ortho exam',
|
||||
'clinical_notes' => 'Clinical notes',
|
||||
'orders' => 'Orders',
|
||||
'billing' => 'Billing',
|
||||
'documents' => 'Documents',
|
||||
],
|
||||
],
|
||||
'ent' => [
|
||||
'stages' => [
|
||||
['code' => 'waiting', 'label' => 'Waiting', 'queue_point' => 'waiting'],
|
||||
['code' => 'exam', 'label' => 'ENT exam', 'queue_point' => 'chair'],
|
||||
['code' => 'completed', 'label' => 'Completed', 'queue_point' => null],
|
||||
],
|
||||
'services' => [
|
||||
['code' => 'ent.consult', 'label' => 'ENT consultation', 'amount_minor' => 6000, 'type' => 'consultation'],
|
||||
['code' => 'ent.procedure', 'label' => 'ENT procedure', 'amount_minor' => 12000, 'type' => 'procedure'],
|
||||
],
|
||||
'workspace_tabs' => [
|
||||
'overview' => 'Overview',
|
||||
'exam' => 'ENT exam',
|
||||
'clinical_notes' => 'Clinical notes',
|
||||
'orders' => 'Orders',
|
||||
'billing' => 'Billing',
|
||||
'documents' => 'Documents',
|
||||
],
|
||||
],
|
||||
'oncology' => [
|
||||
'stages' => [
|
||||
['code' => 'waiting', 'label' => 'Waiting', 'queue_point' => 'waiting'],
|
||||
['code' => 'review', 'label' => 'Oncology review', 'queue_point' => 'chair'],
|
||||
['code' => 'treatment', 'label' => 'Treatment', 'queue_point' => 'chair'],
|
||||
['code' => 'completed', 'label' => 'Completed', 'queue_point' => null],
|
||||
],
|
||||
'services' => [
|
||||
['code' => 'onc.consult', 'label' => 'Oncology consultation', 'amount_minor' => 10000, 'type' => 'consultation'],
|
||||
['code' => 'onc.chemo', 'label' => 'Chemo day-care session', 'amount_minor' => 50000, 'type' => 'procedure'],
|
||||
],
|
||||
'workspace_tabs' => [
|
||||
'overview' => 'Overview',
|
||||
'review' => 'Oncology review',
|
||||
'clinical_notes' => 'Clinical notes',
|
||||
'orders' => 'Orders',
|
||||
'billing' => 'Billing',
|
||||
'documents' => 'Documents',
|
||||
],
|
||||
],
|
||||
'renal' => [
|
||||
'stages' => [
|
||||
['code' => 'waiting', 'label' => 'Waiting', 'queue_point' => 'waiting'],
|
||||
['code' => 'dialysis', 'label' => 'Dialysis / clinic', 'queue_point' => 'chair'],
|
||||
['code' => 'completed', 'label' => 'Completed', 'queue_point' => null],
|
||||
],
|
||||
'services' => [
|
||||
['code' => 'ren.consult', 'label' => 'Nephrology consultation', 'amount_minor' => 8000, 'type' => 'consultation'],
|
||||
['code' => 'ren.dialysis', 'label' => 'Dialysis session', 'amount_minor' => 35000, 'type' => 'procedure'],
|
||||
],
|
||||
'workspace_tabs' => [
|
||||
'overview' => 'Overview',
|
||||
'session' => 'Renal session',
|
||||
'clinical_notes' => 'Clinical notes',
|
||||
'orders' => 'Orders',
|
||||
'billing' => 'Billing',
|
||||
'documents' => 'Documents',
|
||||
],
|
||||
],
|
||||
'surgery' => [
|
||||
'stages' => [
|
||||
['code' => 'waiting', 'label' => 'Waiting', 'queue_point' => 'waiting'],
|
||||
['code' => 'preop', 'label' => 'Pre-op assessment', 'queue_point' => 'chair'],
|
||||
['code' => 'postop', 'label' => 'Post-op review', 'queue_point' => 'chair'],
|
||||
['code' => 'completed', 'label' => 'Completed', 'queue_point' => null],
|
||||
],
|
||||
'services' => [
|
||||
['code' => 'sur.consult', 'label' => 'Surgical consultation', 'amount_minor' => 8000, 'type' => 'consultation'],
|
||||
['code' => 'sur.preop', 'label' => 'Pre-op assessment', 'amount_minor' => 5000, 'type' => 'consultation'],
|
||||
],
|
||||
'workspace_tabs' => [
|
||||
'overview' => 'Overview',
|
||||
'preop' => 'Pre-op',
|
||||
'clinical_notes' => 'Clinical notes',
|
||||
'orders' => 'Orders',
|
||||
'billing' => 'Billing',
|
||||
'documents' => 'Documents',
|
||||
],
|
||||
],
|
||||
'vaccination' => [
|
||||
'stages' => [
|
||||
['code' => 'waiting', 'label' => 'Waiting', 'queue_point' => 'waiting'],
|
||||
['code' => 'screening', 'label' => 'Screening', 'queue_point' => 'chair'],
|
||||
['code' => 'vaccinate', 'label' => 'Vaccination', 'queue_point' => 'chair'],
|
||||
['code' => 'completed', 'label' => 'Completed', 'queue_point' => null],
|
||||
],
|
||||
'services' => [
|
||||
['code' => 'vax.admin', 'label' => 'Vaccine administration', 'amount_minor' => 3000, 'type' => 'procedure'],
|
||||
],
|
||||
'workspace_tabs' => [
|
||||
'overview' => 'Overview',
|
||||
'screening' => 'Screening',
|
||||
'administration' => 'Administration',
|
||||
'billing' => 'Billing',
|
||||
'documents' => 'Documents',
|
||||
],
|
||||
],
|
||||
'pathology' => [
|
||||
'stages' => [
|
||||
['code' => 'request', 'label' => 'Specimen received', 'queue_point' => 'waiting'],
|
||||
['code' => 'processing', 'label' => 'Processing', 'queue_point' => 'lab'],
|
||||
['code' => 'reporting', 'label' => 'Reporting', 'queue_point' => 'lab'],
|
||||
['code' => 'completed', 'label' => 'Completed', 'queue_point' => null],
|
||||
],
|
||||
'services' => [
|
||||
['code' => 'path.histo', 'label' => 'Histopathology', 'amount_minor' => 20000, 'type' => 'lab'],
|
||||
['code' => 'path.cyto', 'label' => 'Cytology', 'amount_minor' => 15000, 'type' => 'lab'],
|
||||
],
|
||||
'workspace_tabs' => [
|
||||
'overview' => 'Overview',
|
||||
'request' => 'Specimen request',
|
||||
'report' => 'Report',
|
||||
'billing' => 'Billing',
|
||||
'documents' => 'Documents',
|
||||
],
|
||||
],
|
||||
'infusion' => [
|
||||
'stages' => [
|
||||
['code' => 'waiting', 'label' => 'Waiting', 'queue_point' => 'waiting'],
|
||||
['code' => 'infusion', 'label' => 'Infusion', 'queue_point' => 'chair'],
|
||||
['code' => 'observation', 'label' => 'Observation', 'queue_point' => 'obs'],
|
||||
['code' => 'completed', 'label' => 'Completed', 'queue_point' => null],
|
||||
],
|
||||
'services' => [
|
||||
['code' => 'inf.session', 'label' => 'Infusion session', 'amount_minor' => 15000, 'type' => 'procedure'],
|
||||
],
|
||||
'workspace_tabs' => [
|
||||
'overview' => 'Overview',
|
||||
'session' => 'Infusion session',
|
||||
'clinical_notes' => 'Clinical notes',
|
||||
'orders' => 'Orders',
|
||||
'billing' => 'Billing',
|
||||
'documents' => 'Documents',
|
||||
],
|
||||
],
|
||||
'dermatology' => [
|
||||
'stages' => [
|
||||
['code' => 'waiting', 'label' => 'Waiting', 'queue_point' => 'waiting'],
|
||||
['code' => 'exam', 'label' => 'Skin exam', 'queue_point' => 'chair'],
|
||||
['code' => 'procedure', 'label' => 'Procedure', 'queue_point' => 'chair'],
|
||||
['code' => 'completed', 'label' => 'Completed', 'queue_point' => null],
|
||||
],
|
||||
'services' => [
|
||||
['code' => 'der.consult', 'label' => 'Dermatology consultation', 'amount_minor' => 6000, 'type' => 'consultation'],
|
||||
['code' => 'der.procedure', 'label' => 'Skin procedure', 'amount_minor' => 12000, 'type' => 'procedure'],
|
||||
],
|
||||
'workspace_tabs' => [
|
||||
'overview' => 'Overview',
|
||||
'exam' => 'Skin exam',
|
||||
'clinical_notes' => 'Clinical notes',
|
||||
'orders' => 'Orders',
|
||||
'billing' => 'Billing',
|
||||
'documents' => 'Documents',
|
||||
],
|
||||
],
|
||||
'podiatry' => [
|
||||
'stages' => [
|
||||
['code' => 'waiting', 'label' => 'Waiting', 'queue_point' => 'waiting'],
|
||||
['code' => 'exam', 'label' => 'Foot exam', 'queue_point' => 'chair'],
|
||||
['code' => 'treatment', 'label' => 'Treatment', 'queue_point' => 'chair'],
|
||||
['code' => 'completed', 'label' => 'Completed', 'queue_point' => null],
|
||||
],
|
||||
'services' => [
|
||||
['code' => 'pod.consult', 'label' => 'Podiatry consultation', 'amount_minor' => 5000, 'type' => 'consultation'],
|
||||
['code' => 'pod.treatment', 'label' => 'Foot treatment', 'amount_minor' => 8000, 'type' => 'procedure'],
|
||||
],
|
||||
'workspace_tabs' => [
|
||||
'overview' => 'Overview',
|
||||
'exam' => 'Foot exam',
|
||||
'clinical_notes' => 'Clinical notes',
|
||||
'orders' => 'Orders',
|
||||
'billing' => 'Billing',
|
||||
'documents' => 'Documents',
|
||||
],
|
||||
],
|
||||
'fertility' => [
|
||||
'stages' => [
|
||||
['code' => 'waiting', 'label' => 'Waiting', 'queue_point' => 'waiting'],
|
||||
['code' => 'consult', 'label' => 'Fertility consult', 'queue_point' => 'chair'],
|
||||
['code' => 'completed', 'label' => 'Completed', 'queue_point' => null],
|
||||
],
|
||||
'services' => [
|
||||
['code' => 'fer.consult', 'label' => 'Fertility consultation', 'amount_minor' => 10000, 'type' => 'consultation'],
|
||||
['code' => 'fer.scan', 'label' => 'Pelvic / follicular scan', 'amount_minor' => 15000, 'type' => 'imaging'],
|
||||
],
|
||||
'workspace_tabs' => [
|
||||
'overview' => 'Overview',
|
||||
'consult' => 'Fertility consult',
|
||||
'clinical_notes' => 'Clinical notes',
|
||||
'orders' => 'Orders',
|
||||
'billing' => 'Billing',
|
||||
'documents' => 'Documents',
|
||||
],
|
||||
],
|
||||
'child_welfare' => [
|
||||
'stages' => [
|
||||
['code' => 'waiting', 'label' => 'Waiting', 'queue_point' => 'waiting'],
|
||||
['code' => 'growth', 'label' => 'Growth / wellness', 'queue_point' => 'chair'],
|
||||
['code' => 'completed', 'label' => 'Completed', 'queue_point' => null],
|
||||
],
|
||||
'services' => [
|
||||
['code' => 'cwc.visit', 'label' => 'Well-child visit', 'amount_minor' => 3000, 'type' => 'consultation'],
|
||||
],
|
||||
'workspace_tabs' => [
|
||||
'overview' => 'Overview',
|
||||
'growth' => 'Growth monitoring',
|
||||
'clinical_notes' => 'Clinical notes',
|
||||
'orders' => 'Orders',
|
||||
'billing' => 'Billing',
|
||||
'documents' => 'Documents',
|
||||
],
|
||||
],
|
||||
|
||||
],
|
||||
];
|
||||
|
||||
@@ -1,34 +1,20 @@
|
||||
{{-- Inline Queue ops when Care↔Queue integration is enabled (not a bolted-on Service counter). --}}
|
||||
{{-- Inline Queue ops when Care Queue Engine is enabled. --}}
|
||||
@php
|
||||
$qi = $queueIntegration ?? null;
|
||||
$callNextRoute = $queueCallNextRoute ?? null;
|
||||
$callNextParams = $queueCallNextParams ?? [];
|
||||
@endphp
|
||||
@if (! empty($qi['enabled']) && $callNextRoute)
|
||||
<div class="flex flex-wrap items-center justify-between gap-3 rounded-xl border border-sky-100 bg-sky-50/70 px-4 py-3">
|
||||
<div>
|
||||
<p class="text-xs font-semibold uppercase tracking-wide text-sky-700">Queue</p>
|
||||
<p class="text-sm text-slate-600">
|
||||
@if (($qi['routing_mode'] ?? null) === 'shared_pool')
|
||||
Call next takes the next waiting patient in line for this queue (shared pool).
|
||||
@elseif (! empty($queueCallNextScansAllPoints))
|
||||
Call next picks the next waiting patient across service desks for this specialty at this branch.
|
||||
@else
|
||||
Call next operates on your assigned service point only — tickets routed to other rooms or desks stay there.
|
||||
@endif
|
||||
</p>
|
||||
</div>
|
||||
<form method="POST" action="{{ route($callNextRoute, $callNextParams) }}">
|
||||
@csrf
|
||||
@foreach ($callNextParams as $key => $value)
|
||||
@if (is_scalar($value))
|
||||
<input type="hidden" name="{{ $key }}" value="{{ $value }}">
|
||||
@endif
|
||||
@endforeach
|
||||
@if (! empty($branchId))
|
||||
<input type="hidden" name="branch_id" value="{{ $branchId }}">
|
||||
<form method="POST" action="{{ route($callNextRoute, $callNextParams) }}" class="w-full">
|
||||
@csrf
|
||||
@foreach ($callNextParams as $key => $value)
|
||||
@if (is_scalar($value))
|
||||
<input type="hidden" name="{{ $key }}" value="{{ $value }}">
|
||||
@endif
|
||||
<button type="submit" class="btn-primary text-sm">Call next</button>
|
||||
</form>
|
||||
</div>
|
||||
@endforeach
|
||||
@if (! empty($branchId))
|
||||
<input type="hidden" name="branch_id" value="{{ $branchId }}">
|
||||
@endif
|
||||
<button type="submit" class="btn-primary flex w-full items-center justify-center text-sm">Call next</button>
|
||||
</form>
|
||||
@endif
|
||||
|
||||
@@ -72,7 +72,6 @@
|
||||
'queueCallNextRoute' => 'care.specialty.call-next',
|
||||
'queueCallNextParams' => ['module' => $moduleKey],
|
||||
'branchId' => $branchId ?? null,
|
||||
'queueCallNextScansAllPoints' => ($practitionerScope ?? null) === null,
|
||||
])
|
||||
|
||||
@if ($workspaceVisit)
|
||||
@@ -88,17 +87,21 @@
|
||||
$appointment->status === \App\Models\Appointment::STATUS_IN_CONSULTATION
|
||||
|| ($openConsultation && $openConsultation->status !== \App\Models\Consultation::STATUS_COMPLETED)
|
||||
);
|
||||
$chartTab = collect($workspaceTabs ?? [])
|
||||
->keys()
|
||||
->first(fn ($key) => ! in_array($key, ['overview', 'orders', 'billing', 'documents'], true))
|
||||
?: 'clinical_notes';
|
||||
@endphp
|
||||
@if ($consultationInProgress && $openConsultation)
|
||||
<a href="{{ route('care.consultations.show', $openConsultation) }}"
|
||||
@if ($consultationInProgress)
|
||||
<a href="{{ route('care.specialty.workspace', ['module' => $moduleKey, 'visit' => $workspaceVisit, 'tab' => $chartTab]) }}"
|
||||
class="flex w-full items-center justify-center rounded-xl bg-indigo-600 px-3 py-2 text-sm font-semibold text-white hover:bg-indigo-700">
|
||||
Open consultation
|
||||
Open specialty chart
|
||||
</a>
|
||||
@elseif ($canStartConsultation)
|
||||
<form method="POST" action="{{ route('care.specialty.consultation.start', ['module' => $moduleKey, 'visit' => $workspaceVisit]) }}">
|
||||
@csrf
|
||||
<button type="submit" class="flex w-full items-center justify-center rounded-xl bg-indigo-600 px-3 py-2 text-sm font-semibold text-white hover:bg-indigo-700">
|
||||
{{ $actions['start'] ?? 'Start consultation' }}
|
||||
{{ $actions['start'] ?? 'Start specialty encounter' }}
|
||||
</button>
|
||||
</form>
|
||||
@endif
|
||||
|
||||
@@ -26,8 +26,10 @@
|
||||
if ($permissions->can($member, 'appointments.view')) {
|
||||
$nav[] = ['name' => 'Appointments', 'route' => route('care.appointments.index'), 'active' => request()->routeIs('care.appointments.*'),
|
||||
'icon' => '<path stroke-linecap="round" stroke-linejoin="round" d="M6.75 3v2.25M17.25 3v2.25M3 18.75V7.5a2.25 2.25 0 0 1 2.25-2.25h13.5A2.25 2.25 0 0 1 21 7.5v11.25m-18 0A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75m-18 0v-7.5A2.25 2.25 0 0 1 5.25 9h13.5A2.25 2.25 0 0 1 21 11.25v7.5" />'];
|
||||
$nav[] = ['name' => 'Queue', 'route' => route('care.queue.index'), 'active' => request()->routeIs('care.queue.*') || request()->routeIs('care.consultations.*'),
|
||||
'icon' => '<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 12h16.5m-16.5 3.75h16.5M3.75 19.5h16.5M5.25 4.5h13.5a.75.75 0 0 1 .75.75v15.75a.75.75 0 0 1-.75.75H5.25a.75.75 0 0 1-.75-.75V5.25a.75.75 0 0 1 .75-.75Z" />'];
|
||||
if ($permissions->handlesFloorCare($member)) {
|
||||
$nav[] = ['name' => 'Queue', 'route' => route('care.queue.index'), 'active' => request()->routeIs('care.queue.*') || request()->routeIs('care.consultations.*'),
|
||||
'icon' => '<path stroke-linecap="round" stroke-linejoin="round" d="M3.75 12h16.5m-16.5 3.75h16.5M3.75 19.5h16.5M5.25 4.5h13.5a.75.75 0 0 1 .75.75v15.75a.75.75 0 0 1-.75.75H5.25a.75.75 0 0 1-.75-.75V5.25a.75.75 0 0 1 .75-.75Z" />'];
|
||||
}
|
||||
}
|
||||
|
||||
// Clinical assessments (layered intake / pathways) — on by default; toggle in Settings.
|
||||
@@ -36,7 +38,7 @@
|
||||
$organization,
|
||||
\App\Services\Care\CareFeatures::ASSESSMENTS_ENGINE,
|
||||
);
|
||||
if ($assessmentsEngineOn && $permissions->can($member, 'assessments.view') && $permissions->can($member, 'patients.view')) {
|
||||
if ($assessmentsEngineOn && $permissions->can($member, 'assessments.view') && $permissions->can($member, 'patients.view') && $permissions->handlesFloorCare($member)) {
|
||||
$nav[] = [
|
||||
'name' => 'Clinical forms',
|
||||
'route' => route('care.patients.index', ['focus' => 'assessments']),
|
||||
@@ -49,12 +51,12 @@
|
||||
|
||||
// Lab, pharmacy, billing, and Queue service counters are Pro/Enterprise modules.
|
||||
if (! empty($hasPaidPlan)) {
|
||||
if ($permissions->can($member, 'lab.view')) {
|
||||
if ($permissions->can($member, 'lab.view') && $permissions->handlesFloorCare($member)) {
|
||||
$nav[] = ['name' => 'Laboratory', 'route' => route('care.lab.queue.index'), 'active' => request()->routeIs('care.lab.*'),
|
||||
'icon' => '<path stroke-linecap="round" stroke-linejoin="round" d="M9.75 3.104v5.714a2.25 2.25 0 0 1-.659 1.591L5 14.5M9.75 3.104c-.251.023-.501.05-.75.082m.75-.082a24.301 24.301 0 0 1 4.5 0m0 0v5.714a2.25 2.25 0 0 0 .659 1.591L19 14.5M14.25 3.104c.251.023.501.05.75.082M19 14.5l-2.47 2.47a2.25 2.25 0 0 1-1.59.659H9.06a2.25 2.25 0 0 1-1.591-.659L5 14.5m14 0V17a2.25 2.25 0 0 1-2.25 2.25H7.25A2.25 2.25 0 0 1 5 17v-2.5" />'];
|
||||
}
|
||||
|
||||
if ($permissions->can($member, 'prescriptions.view')) {
|
||||
if ($permissions->can($member, 'prescriptions.view') && $permissions->handlesFloorCare($member)) {
|
||||
$nav[] = ['name' => 'Pharmacy queue', 'route' => route('care.prescriptions.queue'), 'active' => request()->routeIs('care.prescriptions.*') && ! request()->routeIs('care.pharmacy.*'),
|
||||
'icon' => '<path stroke-linecap="round" stroke-linejoin="round" d="M9.75 3.104v5.714a2.25 2.25 0 0 1-.659 1.591L5 14.5M9.75 3.104c-.251.023-.501.05-.75.082m.75-.082a24.301 24.301 0 0 1 4.5 0m0 0v5.714a2.25 2.25 0 0 0 .659 1.591L19 14.5" />'];
|
||||
}
|
||||
@@ -75,40 +77,42 @@
|
||||
}
|
||||
|
||||
// Always-on Pro/Enterprise surfaces first, then other specialty modules.
|
||||
$specialtyService = app(\App\Services\Care\SpecialtyModuleService::class);
|
||||
if ($organization) {
|
||||
$specialtyService->ensureDefaultModulesProvisioned(
|
||||
$organization,
|
||||
(string) $organization->owner_ref,
|
||||
);
|
||||
$organization->refresh();
|
||||
}
|
||||
$specialtyModules = $specialtyService->enabledModulesForMember($organization, $member);
|
||||
$pinnedKeys = $specialtyService->defaultKeysForPaidPlans();
|
||||
usort($specialtyModules, function (array $a, array $b) use ($pinnedKeys): int {
|
||||
$aPin = in_array($a['key'], $pinnedKeys, true) ? 0 : 1;
|
||||
$bPin = in_array($b['key'], $pinnedKeys, true) ? 0 : 1;
|
||||
if ($aPin !== $bPin) {
|
||||
return $aPin <=> $bPin;
|
||||
}
|
||||
$aOrder = array_search($a['key'], $pinnedKeys, true);
|
||||
$bOrder = array_search($b['key'], $pinnedKeys, true);
|
||||
if ($aOrder !== false || $bOrder !== false) {
|
||||
return ($aOrder === false ? 99 : $aOrder) <=> ($bOrder === false ? 99 : $bOrder);
|
||||
if ($permissions->handlesFloorCare($member)) {
|
||||
$specialtyService = app(\App\Services\Care\SpecialtyModuleService::class);
|
||||
if ($organization) {
|
||||
$specialtyService->ensureDefaultModulesProvisioned(
|
||||
$organization,
|
||||
(string) $organization->owner_ref,
|
||||
);
|
||||
$organization->refresh();
|
||||
}
|
||||
$specialtyModules = $specialtyService->enabledModulesForMember($organization, $member);
|
||||
$pinnedKeys = $specialtyService->defaultKeysForPaidPlans();
|
||||
usort($specialtyModules, function (array $a, array $b) use ($pinnedKeys): int {
|
||||
$aPin = in_array($a['key'], $pinnedKeys, true) ? 0 : 1;
|
||||
$bPin = in_array($b['key'], $pinnedKeys, true) ? 0 : 1;
|
||||
if ($aPin !== $bPin) {
|
||||
return $aPin <=> $bPin;
|
||||
}
|
||||
$aOrder = array_search($a['key'], $pinnedKeys, true);
|
||||
$bOrder = array_search($b['key'], $pinnedKeys, true);
|
||||
if ($aOrder !== false || $bOrder !== false) {
|
||||
return ($aOrder === false ? 99 : $aOrder) <=> ($bOrder === false ? 99 : $bOrder);
|
||||
}
|
||||
|
||||
return strcasecmp(
|
||||
(string) ($a['definition']['nav_label'] ?? $a['definition']['label'] ?? $a['key']),
|
||||
(string) ($b['definition']['nav_label'] ?? $b['definition']['label'] ?? $b['key']),
|
||||
);
|
||||
});
|
||||
foreach ($specialtyModules as $item) {
|
||||
$nav[] = [
|
||||
'name' => $item['definition']['nav_label'] ?? $item['definition']['label'],
|
||||
'route' => route('care.specialty.show', $item['key']),
|
||||
'active' => request()->routeIs('care.specialty.show') && request()->route('module') === $item['key'],
|
||||
'icon' => '<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v6m3-3H9m12 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />',
|
||||
];
|
||||
return strcasecmp(
|
||||
(string) ($a['definition']['nav_label'] ?? $a['definition']['label'] ?? $a['key']),
|
||||
(string) ($b['definition']['nav_label'] ?? $b['definition']['label'] ?? $b['key']),
|
||||
);
|
||||
});
|
||||
foreach ($specialtyModules as $item) {
|
||||
$nav[] = [
|
||||
'name' => $item['definition']['nav_label'] ?? $item['definition']['label'],
|
||||
'route' => route('care.specialty.show', $item['key']),
|
||||
'active' => request()->routeIs('care.specialty.show') && request()->route('module') === $item['key'],
|
||||
'icon' => '<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v6m3-3H9m12 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />',
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ class CareNativeQueueTest extends TestCase
|
||||
'owner_ref' => $this->owner->public_id,
|
||||
'organization_id' => $this->organization->id,
|
||||
'user_ref' => $this->owner->public_id,
|
||||
'role' => 'hospital_admin',
|
||||
'role' => 'receptionist',
|
||||
]);
|
||||
|
||||
$this->branch = Branch::create([
|
||||
|
||||
@@ -57,7 +57,7 @@ class CareNaturalQueueTest extends TestCase
|
||||
'owner_ref' => $this->owner->public_id,
|
||||
'organization_id' => $this->organization->id,
|
||||
'user_ref' => $this->owner->public_id,
|
||||
'role' => 'hospital_admin',
|
||||
'role' => 'receptionist',
|
||||
]);
|
||||
|
||||
$this->branch = Branch::create([
|
||||
|
||||
@@ -56,7 +56,7 @@ class CareQueueBridgeTest extends TestCase
|
||||
'owner_ref' => $this->owner->public_id,
|
||||
'organization_id' => $this->organization->id,
|
||||
'user_ref' => $this->owner->public_id,
|
||||
'role' => 'hospital_admin',
|
||||
'role' => 'receptionist',
|
||||
]);
|
||||
|
||||
$this->branch = Branch::create([
|
||||
@@ -273,7 +273,30 @@ class CareQueueBridgeTest extends TestCase
|
||||
$appointment = $checkedIn->fresh();
|
||||
$this->assertSame('called', $appointment->queue_ticket_status);
|
||||
|
||||
$this->actingAs($this->owner)
|
||||
$doctorUser = User::create([
|
||||
'public_id' => 'bridge-recall-doc',
|
||||
'name' => 'Recall Doctor',
|
||||
'email' => 'bridge-recall@example.com',
|
||||
]);
|
||||
Member::create([
|
||||
'owner_ref' => $this->owner->public_id,
|
||||
'organization_id' => $this->organization->id,
|
||||
'user_ref' => $doctorUser->public_id,
|
||||
'role' => 'doctor',
|
||||
'branch_id' => $this->branch->id,
|
||||
]);
|
||||
\App\Models\Practitioner::create([
|
||||
'owner_ref' => $this->owner->public_id,
|
||||
'organization_id' => $this->organization->id,
|
||||
'branch_id' => $this->branch->id,
|
||||
'member_id' => Member::query()->where('user_ref', $doctorUser->public_id)->value('id'),
|
||||
'user_ref' => $doctorUser->public_id,
|
||||
'name' => 'Recall Doctor',
|
||||
'room' => 'Room 2',
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
$this->actingAs($doctorUser)
|
||||
->from(route('care.queue.index'))
|
||||
->post(route('care.queue.recall', $appointment))
|
||||
->assertRedirect(route('care.queue.index'))
|
||||
@@ -284,7 +307,6 @@ class CareQueueBridgeTest extends TestCase
|
||||
$this->actingAs($this->owner)
|
||||
->get(route('care.queue.index', ['branch_id' => $this->branch->id]))
|
||||
->assertOk()
|
||||
->assertSee('Call again')
|
||||
->assertSee($appointment->queue_ticket_number);
|
||||
Http::assertNothingSent();
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ class CareQueueWorkflowTest extends TestCase
|
||||
'owner_ref' => $this->owner->public_id,
|
||||
'organization_id' => $this->organization->id,
|
||||
'user_ref' => $this->owner->public_id,
|
||||
'role' => 'hospital_admin',
|
||||
'role' => 'receptionist',
|
||||
]);
|
||||
|
||||
$this->branch = Branch::create([
|
||||
@@ -320,8 +320,7 @@ class CareQueueWorkflowTest extends TestCase
|
||||
->get(route('care.queue.index'))
|
||||
->assertOk()
|
||||
->assertDontSee('Service counter')
|
||||
->assertSee('Call next')
|
||||
->assertSee('assigned service point', false);
|
||||
->assertSee('Call next');
|
||||
Http::assertNothingSent();
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ class CareSpecialtyClinicalTest extends TestCase
|
||||
'owner_ref' => $this->owner->public_id,
|
||||
'organization_id' => $this->organization->id,
|
||||
'user_ref' => $this->owner->public_id,
|
||||
'role' => 'hospital_admin',
|
||||
'role' => 'nurse',
|
||||
]);
|
||||
|
||||
$this->branch = Branch::create([
|
||||
@@ -251,4 +251,33 @@ class CareSpecialtyClinicalTest extends TestCase
|
||||
'record_type' => 'odontogram',
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_ophthalmology_uses_eye_exam_form_not_generic_notes(): void
|
||||
{
|
||||
app(SpecialtyModuleService::class)->activate(
|
||||
$this->organization,
|
||||
$this->owner->public_id,
|
||||
'ophthalmology',
|
||||
);
|
||||
|
||||
$fields = app(\App\Services\Care\SpecialtyClinicalRecordService::class)
|
||||
->fieldsFor('ophthalmology', 'eye_exam');
|
||||
$names = collect($fields)->pluck('name')->all();
|
||||
|
||||
$this->assertContains('va_od', $names);
|
||||
$this->assertContains('iop_os', $names);
|
||||
$this->assertNotContains('notes', $names);
|
||||
|
||||
[$visit] = $this->activateWithVisit('ophthalmology', 'ophthalmology');
|
||||
|
||||
$this->actingAs($this->owner)
|
||||
->get(route('care.specialty.workspace', [
|
||||
'module' => 'ophthalmology',
|
||||
'visit' => $visit,
|
||||
'tab' => 'exam',
|
||||
]))
|
||||
->assertOk()
|
||||
->assertSee('Visual acuity OD')
|
||||
->assertSee('IOP OS');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,7 +216,24 @@ class CareSpecialtyModulesTest extends TestCase
|
||||
Http::fake();
|
||||
app(SpecialtyModuleService::class)->activate($this->organization, $this->owner->public_id, 'dentistry');
|
||||
|
||||
$nurse = User::create([
|
||||
'public_id' => 'modules-nurse',
|
||||
'name' => 'Floor Nurse',
|
||||
'email' => 'modules-nurse@example.com',
|
||||
]);
|
||||
Member::create([
|
||||
'owner_ref' => $this->owner->public_id,
|
||||
'organization_id' => $this->organization->id,
|
||||
'user_ref' => $nurse->public_id,
|
||||
'role' => 'nurse',
|
||||
'branch_id' => $this->branch->id,
|
||||
]);
|
||||
|
||||
$this->actingAs($this->owner)
|
||||
->get(route('care.specialty.show', 'dentistry'))
|
||||
->assertForbidden();
|
||||
|
||||
$this->actingAs($nurse)
|
||||
->get(route('care.specialty.show', 'dentistry'))
|
||||
->assertOk()
|
||||
->assertSee('Dentistry')
|
||||
@@ -320,7 +337,24 @@ class CareSpecialtyModulesTest extends TestCase
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
$nurse = User::create([
|
||||
'public_id' => 'er-nurse',
|
||||
'name' => 'ER Nurse',
|
||||
'email' => 'er-nurse@example.com',
|
||||
]);
|
||||
Member::create([
|
||||
'owner_ref' => $this->owner->public_id,
|
||||
'organization_id' => $this->organization->id,
|
||||
'user_ref' => $nurse->public_id,
|
||||
'role' => 'nurse',
|
||||
'branch_id' => $this->branch->id,
|
||||
]);
|
||||
|
||||
$this->actingAs($this->owner)
|
||||
->get(route('care.specialty.show', 'emergency'))
|
||||
->assertForbidden();
|
||||
|
||||
$this->actingAs($nurse)
|
||||
->get(route('care.specialty.show', 'emergency'))
|
||||
->assertOk()
|
||||
->assertSee('Emergency');
|
||||
|
||||
@@ -55,7 +55,7 @@ class CareSpecialtyShellTest extends TestCase
|
||||
'owner_ref' => $this->owner->public_id,
|
||||
'organization_id' => $this->organization->id,
|
||||
'user_ref' => $this->owner->public_id,
|
||||
'role' => 'hospital_admin',
|
||||
'role' => 'nurse',
|
||||
]);
|
||||
|
||||
$this->branch = Branch::create([
|
||||
@@ -206,7 +206,7 @@ class CareSpecialtyShellTest extends TestCase
|
||||
);
|
||||
}
|
||||
|
||||
public function test_specialty_start_consultation_opens_consultation_page(): void
|
||||
public function test_specialty_start_consultation_opens_specialty_workspace(): void
|
||||
{
|
||||
app(SpecialtyModuleService::class)->activate(
|
||||
$this->organization,
|
||||
@@ -253,7 +253,31 @@ class CareSpecialtyShellTest extends TestCase
|
||||
'checked_in_at' => now(),
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($this->owner)
|
||||
$doctorUser = User::create([
|
||||
'public_id' => 'shell-dental-doc',
|
||||
'name' => 'Dental Doctor',
|
||||
'email' => 'shell-dental@example.com',
|
||||
]);
|
||||
$doctorMember = Member::create([
|
||||
'owner_ref' => $this->owner->public_id,
|
||||
'organization_id' => $this->organization->id,
|
||||
'user_ref' => $doctorUser->public_id,
|
||||
'role' => 'doctor',
|
||||
'branch_id' => $this->branch->id,
|
||||
]);
|
||||
\App\Models\Practitioner::create([
|
||||
'owner_ref' => $this->owner->public_id,
|
||||
'organization_id' => $this->organization->id,
|
||||
'branch_id' => $this->branch->id,
|
||||
'department_id' => $department->id,
|
||||
'member_id' => $doctorMember->id,
|
||||
'user_ref' => $doctorUser->public_id,
|
||||
'name' => 'Dental Doctor',
|
||||
'specialty' => 'Dentistry',
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
$response = $this->actingAs($doctorUser)
|
||||
->post(route('care.specialty.consultation.start', [
|
||||
'module' => 'dentistry',
|
||||
'visit' => $visit,
|
||||
@@ -264,7 +288,11 @@ class CareSpecialtyShellTest extends TestCase
|
||||
->first();
|
||||
$this->assertNotNull($consultation);
|
||||
|
||||
$response->assertRedirect(route('care.consultations.show', $consultation));
|
||||
$response->assertRedirect(route('care.specialty.workspace', [
|
||||
'module' => 'dentistry',
|
||||
'visit' => $visit,
|
||||
'tab' => 'odontogram',
|
||||
]));
|
||||
$this->assertSame(Appointment::STATUS_IN_CONSULTATION, $appointment->fresh()->status);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user