Hide specialty mutate UI when staff lack manage access.
Deploy Ladill Care / deploy (push) Successful in 36s
Deploy Ladill Care / deploy (push) Successful in 36s
View/refer users keep read surfaces and refer actions; zero-access modules stay out of nav via existing enabledModulesForMember filtering. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -563,6 +563,7 @@ class SpecialtyModuleController extends Controller
|
||||
abort_unless($modules->memberCanAccess($organization, $member, $module), 403);
|
||||
|
||||
$canManageSpecialty = $modules->memberCanManage($organization, $member, $module);
|
||||
$canViewSpecialty = $modules->memberCanView($organization, $member, $module);
|
||||
$canReferSpecialty = $modules->memberCanRefer($organization, $member, $module);
|
||||
$specialtyAccessLevel = $modules->memberAccessLevel($organization, $member, $module);
|
||||
|
||||
@@ -881,6 +882,7 @@ class SpecialtyModuleController extends Controller
|
||||
'canConsult' => $canConsult,
|
||||
'canManageQueue' => $canManageQueue,
|
||||
'canManageSpecialty' => $canManageSpecialty,
|
||||
'canViewSpecialty' => $canViewSpecialty,
|
||||
'canReferSpecialty' => $canReferSpecialty,
|
||||
'specialtyAccessLevel' => $specialtyAccessLevel,
|
||||
'queueIntegration' => $branchId
|
||||
|
||||
@@ -1,48 +1,52 @@
|
||||
@php $canManage = $canManageSpecialty ?? true; @endphp
|
||||
|
||||
<section class="rounded-2xl border border-slate-200 bg-white p-5">
|
||||
<h3 class="text-sm font-semibold text-slate-900">Dental imaging</h3>
|
||||
<p class="mt-0.5 text-xs text-slate-500">Upload PA, bitewing, OPG, or clinical photos linked to teeth.</p>
|
||||
|
||||
<form method="POST" action="{{ route('care.specialty.dentistry.images', $workspaceVisit) }}" enctype="multipart/form-data" class="mt-4 space-y-3">
|
||||
@csrf
|
||||
<div class="grid gap-3 sm:grid-cols-2">
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">File</label>
|
||||
<input type="file" name="attachment" required accept=".pdf,.jpg,.jpeg,.png,.webp" class="mt-1 block w-full text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Modality</label>
|
||||
<select name="modality" required class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
@foreach ($dentalModalities as $value => $label)
|
||||
<option value="{{ $value }}">{{ $label }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="sm:col-span-2">
|
||||
<label class="block text-xs font-medium text-slate-600">Caption</label>
|
||||
<input type="text" name="caption" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
</div>
|
||||
<div class="sm:col-span-2">
|
||||
<p class="text-xs font-medium text-slate-600">Teeth</p>
|
||||
<div class="mt-2 flex max-h-28 flex-wrap gap-2 overflow-y-auto">
|
||||
@foreach (array_merge($dentalRows['upper'] ?? [], $dentalRows['lower'] ?? []) as $code)
|
||||
<label class="inline-flex items-center gap-1 rounded border border-slate-200 px-1.5 py-0.5 text-xs">
|
||||
<input type="checkbox" name="tooth_codes[]" value="{{ $code }}" class="rounded border-slate-300 text-indigo-600">
|
||||
{{ $code }}
|
||||
</label>
|
||||
@endforeach
|
||||
@if ($canManage)
|
||||
<form method="POST" action="{{ route('care.specialty.dentistry.images', $workspaceVisit) }}" enctype="multipart/form-data" class="mt-4 space-y-3">
|
||||
@csrf
|
||||
<div class="grid gap-3 sm:grid-cols-2">
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">File</label>
|
||||
<input type="file" name="attachment" required accept=".pdf,.jpg,.jpeg,.png,.webp" class="mt-1 block w-full text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Modality</label>
|
||||
<select name="modality" required class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
@foreach ($dentalModalities as $value => $label)
|
||||
<option value="{{ $value }}">{{ $label }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="sm:col-span-2">
|
||||
<label class="block text-xs font-medium text-slate-600">Caption</label>
|
||||
<input type="text" name="caption" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
</div>
|
||||
<div class="sm:col-span-2">
|
||||
<p class="text-xs font-medium text-slate-600">Teeth</p>
|
||||
<div class="mt-2 flex max-h-28 flex-wrap gap-2 overflow-y-auto">
|
||||
@foreach (array_merge($dentalRows['upper'] ?? [], $dentalRows['lower'] ?? []) as $code)
|
||||
<label class="inline-flex items-center gap-1 rounded border border-slate-200 px-1.5 py-0.5 text-xs">
|
||||
<input type="checkbox" name="tooth_codes[]" value="{{ $code }}" class="rounded border-slate-300 text-indigo-600">
|
||||
{{ $code }}
|
||||
</label>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
<div class="sm:col-span-2">
|
||||
<label class="inline-flex items-center gap-2 text-sm text-slate-700">
|
||||
<input type="checkbox" name="add_fee" value="1" class="rounded border-slate-300 text-indigo-600" checked>
|
||||
Add imaging fee to bill
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sm:col-span-2">
|
||||
<label class="inline-flex items-center gap-2 text-sm text-slate-700">
|
||||
<input type="checkbox" name="add_fee" value="1" class="rounded border-slate-300 text-indigo-600" checked>
|
||||
Add imaging fee to bill
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn-primary">Upload image</button>
|
||||
</form>
|
||||
<button type="submit" class="btn-primary">Upload image</button>
|
||||
</form>
|
||||
@endif
|
||||
|
||||
<div class="mt-6 grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
|
||||
<div @class(['mt-6 grid gap-3 sm:grid-cols-2 lg:grid-cols-3' => true, 'mt-4' => ! $canManage])>
|
||||
@forelse ($dentalImages as $image)
|
||||
@php
|
||||
$url = $image->attachment?->file_path
|
||||
@@ -71,10 +75,12 @@
|
||||
<p class="mt-0.5 text-slate-600">{{ $image->caption }}</p>
|
||||
@endif
|
||||
</div>
|
||||
<form method="POST" action="{{ route('care.specialty.dentistry.images.void', [$workspaceVisit, $image]) }}" onsubmit="return confirm('Void this image?')">
|
||||
@csrf
|
||||
<button type="submit" class="text-rose-600 hover:underline">Void</button>
|
||||
</form>
|
||||
@if ($canManage)
|
||||
<form method="POST" action="{{ route('care.specialty.dentistry.images.void', [$workspaceVisit, $image]) }}" onsubmit="return confirm('Void this image?')">
|
||||
@csrf
|
||||
<button type="submit" class="text-rose-600 hover:underline">Void</button>
|
||||
</form>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
@php $canManage = $canManageSpecialty ?? true; @endphp
|
||||
|
||||
<section class="rounded-2xl border border-slate-200 bg-white p-5">
|
||||
<h3 class="text-sm font-semibold text-slate-900">Lab cases</h3>
|
||||
<p class="mt-0.5 text-xs text-slate-500">Track crowns, bridges, dentures, and other lab work.</p>
|
||||
@@ -17,7 +19,7 @@
|
||||
@if ($case->due_at) · due {{ $case->due_at->format('d M Y') }} @endif
|
||||
</p>
|
||||
</div>
|
||||
@if ($case->status !== 'cancelled' && $case->status !== 'fitted')
|
||||
@if ($canManage && $case->status !== 'cancelled' && $case->status !== 'fitted')
|
||||
<form method="POST" action="{{ route('care.specialty.dentistry.lab-cases.status', [$workspaceVisit, $case]) }}" class="flex gap-1">
|
||||
@csrf
|
||||
<select name="status" class="rounded-lg border border-slate-200 px-2 py-1 text-xs">
|
||||
@@ -35,49 +37,51 @@
|
||||
@endforelse
|
||||
</ul>
|
||||
|
||||
<form method="POST" action="{{ route('care.specialty.dentistry.lab-cases', $workspaceVisit) }}" class="mt-6 space-y-3 border-t border-slate-100 pt-4">
|
||||
@csrf
|
||||
<h4 class="text-sm font-semibold text-slate-900">New lab case</h4>
|
||||
<div class="grid gap-3 sm:grid-cols-2">
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Type</label>
|
||||
<select name="case_type" required class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
@foreach ($dentalLabCaseTypes ?? [] as $value => $label)
|
||||
<option value="{{ $value }}">{{ $label }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Lab name</label>
|
||||
<input type="text" name="lab_name" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Due date</label>
|
||||
<input type="date" name="due_at" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Status</label>
|
||||
<select name="status" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
<option value="draft">Draft</option>
|
||||
<option value="ordered">Ordered</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="sm:col-span-2">
|
||||
<p class="text-xs font-medium text-slate-600">Teeth</p>
|
||||
<div class="mt-2 flex max-h-24 flex-wrap gap-2 overflow-y-auto">
|
||||
@foreach (array_merge($dentalRows['upper'] ?? [], $dentalRows['lower'] ?? []) as $code)
|
||||
<label class="inline-flex items-center gap-1 rounded border border-slate-200 px-1.5 py-0.5 text-xs">
|
||||
<input type="checkbox" name="tooth_codes[]" value="{{ $code }}" class="rounded border-slate-300 text-indigo-600">
|
||||
{{ $code }}
|
||||
</label>
|
||||
@endforeach
|
||||
@if ($canManage)
|
||||
<form method="POST" action="{{ route('care.specialty.dentistry.lab-cases', $workspaceVisit) }}" class="mt-6 space-y-3 border-t border-slate-100 pt-4">
|
||||
@csrf
|
||||
<h4 class="text-sm font-semibold text-slate-900">New lab case</h4>
|
||||
<div class="grid gap-3 sm:grid-cols-2">
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Type</label>
|
||||
<select name="case_type" required class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
@foreach ($dentalLabCaseTypes ?? [] as $value => $label)
|
||||
<option value="{{ $value }}">{{ $label }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Lab name</label>
|
||||
<input type="text" name="lab_name" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Due date</label>
|
||||
<input type="date" name="due_at" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Status</label>
|
||||
<select name="status" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
<option value="draft">Draft</option>
|
||||
<option value="ordered">Ordered</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="sm:col-span-2">
|
||||
<p class="text-xs font-medium text-slate-600">Teeth</p>
|
||||
<div class="mt-2 flex max-h-24 flex-wrap gap-2 overflow-y-auto">
|
||||
@foreach (array_merge($dentalRows['upper'] ?? [], $dentalRows['lower'] ?? []) as $code)
|
||||
<label class="inline-flex items-center gap-1 rounded border border-slate-200 px-1.5 py-0.5 text-xs">
|
||||
<input type="checkbox" name="tooth_codes[]" value="{{ $code }}" class="rounded border-slate-300 text-indigo-600">
|
||||
{{ $code }}
|
||||
</label>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
<div class="sm:col-span-2">
|
||||
<label class="block text-xs font-medium text-slate-600">Notes</label>
|
||||
<input type="text" name="notes" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
</div>
|
||||
</div>
|
||||
<div class="sm:col-span-2">
|
||||
<label class="block text-xs font-medium text-slate-600">Notes</label>
|
||||
<input type="text" name="notes" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn-primary">Create lab case</button>
|
||||
</form>
|
||||
<button type="submit" class="btn-primary">Create lab case</button>
|
||||
</form>
|
||||
@endif
|
||||
</section>
|
||||
|
||||
@@ -1,25 +1,48 @@
|
||||
@php $canManage = $canManageSpecialty ?? true; @endphp
|
||||
|
||||
<section class="rounded-2xl border border-slate-200 bg-white p-5">
|
||||
<h3 class="text-sm font-semibold text-slate-900">Dental visit notes</h3>
|
||||
<p class="mt-0.5 text-xs text-slate-500">Short clinical notes for this visit — not a GP SOAP form.</p>
|
||||
|
||||
<form method="POST" action="{{ route('care.specialty.dentistry.notes', $workspaceVisit) }}" class="mt-4 space-y-3">
|
||||
@csrf
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Chief complaint</label>
|
||||
<textarea name="chief_complaint" rows="2" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">{{ old('chief_complaint', $dentalVisitNote?->chief_complaint) }}</textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Soft tissue</label>
|
||||
<textarea name="soft_tissue" rows="2" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">{{ old('soft_tissue', $dentalVisitNote?->soft_tissue) }}</textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Occlusion / bite</label>
|
||||
<textarea name="occlusion" rows="2" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">{{ old('occlusion', $dentalVisitNote?->occlusion) }}</textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Other notes</label>
|
||||
<textarea name="notes" rows="3" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">{{ old('notes', $dentalVisitNote?->notes) }}</textarea>
|
||||
</div>
|
||||
<button type="submit" class="btn-primary">Save notes</button>
|
||||
</form>
|
||||
@if ($canManage)
|
||||
<form method="POST" action="{{ route('care.specialty.dentistry.notes', $workspaceVisit) }}" class="mt-4 space-y-3">
|
||||
@csrf
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Chief complaint</label>
|
||||
<textarea name="chief_complaint" rows="2" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">{{ old('chief_complaint', $dentalVisitNote?->chief_complaint) }}</textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Soft tissue</label>
|
||||
<textarea name="soft_tissue" rows="2" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">{{ old('soft_tissue', $dentalVisitNote?->soft_tissue) }}</textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Occlusion / bite</label>
|
||||
<textarea name="occlusion" rows="2" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">{{ old('occlusion', $dentalVisitNote?->occlusion) }}</textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Other notes</label>
|
||||
<textarea name="notes" rows="3" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">{{ old('notes', $dentalVisitNote?->notes) }}</textarea>
|
||||
</div>
|
||||
<button type="submit" class="btn-primary">Save notes</button>
|
||||
</form>
|
||||
@else
|
||||
<dl class="mt-4 grid gap-3 text-sm">
|
||||
<div>
|
||||
<dt class="text-slate-500">Chief complaint</dt>
|
||||
<dd class="mt-0.5 font-medium text-slate-900 whitespace-pre-wrap">{{ $dentalVisitNote?->chief_complaint ?: '—' }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-slate-500">Soft tissue</dt>
|
||||
<dd class="mt-0.5 font-medium text-slate-900 whitespace-pre-wrap">{{ $dentalVisitNote?->soft_tissue ?: '—' }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-slate-500">Occlusion / bite</dt>
|
||||
<dd class="mt-0.5 font-medium text-slate-900 whitespace-pre-wrap">{{ $dentalVisitNote?->occlusion ?: '—' }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-slate-500">Other notes</dt>
|
||||
<dd class="mt-0.5 font-medium text-slate-900 whitespace-pre-wrap">{{ $dentalVisitNote?->notes ?: '—' }}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
@endif
|
||||
</section>
|
||||
|
||||
@@ -3,24 +3,34 @@
|
||||
$selected = old('tooth_code', request('tooth', ($dentalRows['upper'][0] ?? '16')));
|
||||
$selectedData = $teethMap[$selected] ?? ['status' => 'present', 'surfaces' => [], 'conditions' => [], 'notes' => null];
|
||||
$dentition = $dentalChart->dentition ?? 'adult';
|
||||
$canManage = $canManageSpecialty ?? true;
|
||||
@endphp
|
||||
|
||||
<section class="rounded-2xl border border-slate-200 bg-white p-5">
|
||||
<div class="flex flex-wrap items-start justify-between gap-3">
|
||||
<div>
|
||||
<h3 class="text-sm font-semibold text-slate-900">Odontogram (FDI)</h3>
|
||||
<p class="mt-0.5 text-xs text-slate-500">Click a tooth to chart status, surfaces, and conditions.</p>
|
||||
<p class="mt-0.5 text-xs text-slate-500">
|
||||
{{ $canManage ? 'Click a tooth to chart status, surfaces, and conditions.' : 'View tooth chart status, surfaces, and conditions.' }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex flex-wrap items-center gap-3">
|
||||
<form method="POST" action="{{ route('care.specialty.dentistry.dentition', $workspaceVisit) }}" class="flex items-center gap-2">
|
||||
@csrf
|
||||
<label class="text-xs font-medium text-slate-600">Dentition</label>
|
||||
<select name="dentition" onchange="this.form.submit()" class="rounded-lg border border-slate-200 px-2 py-1.5 text-sm">
|
||||
@foreach ($dentalDentitions ?? [] as $value => $label)
|
||||
<option value="{{ $value }}" @selected($dentition === $value)>{{ $label }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</form>
|
||||
@if ($canManage)
|
||||
<form method="POST" action="{{ route('care.specialty.dentistry.dentition', $workspaceVisit) }}" class="flex items-center gap-2">
|
||||
@csrf
|
||||
<label class="text-xs font-medium text-slate-600">Dentition</label>
|
||||
<select name="dentition" onchange="this.form.submit()" class="rounded-lg border border-slate-200 px-2 py-1.5 text-sm">
|
||||
@foreach ($dentalDentitions ?? [] as $value => $label)
|
||||
<option value="{{ $value }}" @selected($dentition === $value)>{{ $label }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</form>
|
||||
@else
|
||||
<p class="text-xs text-slate-600">
|
||||
Dentition:
|
||||
<span class="font-medium text-slate-900">{{ ($dentalDentitions ?? [])[$dentition] ?? $dentition }}</span>
|
||||
</p>
|
||||
@endif
|
||||
<a href="{{ route('care.specialty.dentistry.print', $workspaceVisit) }}" target="_blank" class="text-sm font-medium text-indigo-600">Print</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -60,51 +70,84 @@
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
<form method="POST" action="{{ route('care.specialty.dentistry.tooth', $workspaceVisit) }}" class="mt-6 grid gap-4 border-t border-slate-100 pt-4 sm:grid-cols-2">
|
||||
@csrf
|
||||
<input type="hidden" name="tooth_code" value="{{ $selected }}">
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Selected tooth</label>
|
||||
<p class="mt-1 text-lg font-semibold tabular-nums text-slate-900">{{ $selected }}</p>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Status</label>
|
||||
<select name="status" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
@foreach ($dentalStatuses as $value => $label)
|
||||
<option value="{{ $value }}" @selected(($selectedData['status'] ?? 'present') === $value)>{{ $label }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="sm:col-span-2">
|
||||
<p class="text-xs font-medium text-slate-600">Surfaces</p>
|
||||
<div class="mt-2 flex flex-wrap gap-3">
|
||||
@foreach ($dentalSurfaces as $surface)
|
||||
<label class="inline-flex items-center gap-1.5 text-sm text-slate-700">
|
||||
<input type="checkbox" name="surfaces[]" value="{{ $surface }}" class="rounded border-slate-300 text-indigo-600"
|
||||
@checked(in_array($surface, $selectedData['surfaces'] ?? [], true))>
|
||||
{{ $surface }}
|
||||
</label>
|
||||
@endforeach
|
||||
@if ($canManage)
|
||||
<form method="POST" action="{{ route('care.specialty.dentistry.tooth', $workspaceVisit) }}" class="mt-6 grid gap-4 border-t border-slate-100 pt-4 sm:grid-cols-2">
|
||||
@csrf
|
||||
<input type="hidden" name="tooth_code" value="{{ $selected }}">
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Selected tooth</label>
|
||||
<p class="mt-1 text-lg font-semibold tabular-nums text-slate-900">{{ $selected }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sm:col-span-2">
|
||||
<p class="text-xs font-medium text-slate-600">Conditions</p>
|
||||
<div class="mt-2 flex flex-wrap gap-3">
|
||||
@foreach ($dentalConditions as $value => $label)
|
||||
<label class="inline-flex items-center gap-1.5 text-sm text-slate-700">
|
||||
<input type="checkbox" name="conditions[]" value="{{ $value }}" class="rounded border-slate-300 text-indigo-600"
|
||||
@checked(in_array($value, $selectedData['conditions'] ?? [], true))>
|
||||
{{ $label }}
|
||||
</label>
|
||||
@endforeach
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Status</label>
|
||||
<select name="status" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
@foreach ($dentalStatuses as $value => $label)
|
||||
<option value="{{ $value }}" @selected(($selectedData['status'] ?? 'present') === $value)>{{ $label }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sm:col-span-2">
|
||||
<label class="block text-xs font-medium text-slate-600">Notes</label>
|
||||
<textarea name="notes" rows="2" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">{{ $selectedData['notes'] ?? '' }}</textarea>
|
||||
</div>
|
||||
<div class="sm:col-span-2">
|
||||
<button type="submit" class="btn-primary">Save tooth</button>
|
||||
</div>
|
||||
</form>
|
||||
<div class="sm:col-span-2">
|
||||
<p class="text-xs font-medium text-slate-600">Surfaces</p>
|
||||
<div class="mt-2 flex flex-wrap gap-3">
|
||||
@foreach ($dentalSurfaces as $surface)
|
||||
<label class="inline-flex items-center gap-1.5 text-sm text-slate-700">
|
||||
<input type="checkbox" name="surfaces[]" value="{{ $surface }}" class="rounded border-slate-300 text-indigo-600"
|
||||
@checked(in_array($surface, $selectedData['surfaces'] ?? [], true))>
|
||||
{{ $surface }}
|
||||
</label>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
<div class="sm:col-span-2">
|
||||
<p class="text-xs font-medium text-slate-600">Conditions</p>
|
||||
<div class="mt-2 flex flex-wrap gap-3">
|
||||
@foreach ($dentalConditions as $value => $label)
|
||||
<label class="inline-flex items-center gap-1.5 text-sm text-slate-700">
|
||||
<input type="checkbox" name="conditions[]" value="{{ $value }}" class="rounded border-slate-300 text-indigo-600"
|
||||
@checked(in_array($value, $selectedData['conditions'] ?? [], true))>
|
||||
{{ $label }}
|
||||
</label>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
<div class="sm:col-span-2">
|
||||
<label class="block text-xs font-medium text-slate-600">Notes</label>
|
||||
<textarea name="notes" rows="2" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">{{ $selectedData['notes'] ?? '' }}</textarea>
|
||||
</div>
|
||||
<div class="sm:col-span-2">
|
||||
<button type="submit" class="btn-primary">Save tooth</button>
|
||||
</div>
|
||||
</form>
|
||||
@else
|
||||
<dl class="mt-6 grid gap-3 border-t border-slate-100 pt-4 text-sm sm:grid-cols-2">
|
||||
<div>
|
||||
<dt class="text-slate-500">Selected tooth</dt>
|
||||
<dd class="mt-0.5 text-lg font-semibold tabular-nums text-slate-900">{{ $selected }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-slate-500">Status</dt>
|
||||
<dd class="mt-0.5 font-medium text-slate-900">{{ ($dentalStatuses ?? [])[$selectedData['status'] ?? 'present'] ?? ($selectedData['status'] ?? '—') }}</dd>
|
||||
</div>
|
||||
<div class="sm:col-span-2">
|
||||
<dt class="text-slate-500">Surfaces</dt>
|
||||
<dd class="mt-0.5 font-medium text-slate-900">{{ ! empty($selectedData['surfaces']) ? implode(', ', $selectedData['surfaces']) : '—' }}</dd>
|
||||
</div>
|
||||
<div class="sm:col-span-2">
|
||||
<dt class="text-slate-500">Conditions</dt>
|
||||
<dd class="mt-0.5 font-medium text-slate-900">
|
||||
@php
|
||||
$conditionLabels = collect($selectedData['conditions'] ?? [])
|
||||
->map(fn ($c) => ($dentalConditions ?? [])[$c] ?? $c)
|
||||
->filter()
|
||||
->all();
|
||||
@endphp
|
||||
{{ $conditionLabels !== [] ? implode(', ', $conditionLabels) : '—' }}
|
||||
</dd>
|
||||
</div>
|
||||
<div class="sm:col-span-2">
|
||||
<dt class="text-slate-500">Notes</dt>
|
||||
<dd class="mt-0.5 font-medium text-slate-900 whitespace-pre-wrap">{{ $selectedData['notes'] ?: '—' }}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
@endif
|
||||
</section>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
$plan = $dentalPlan;
|
||||
$openItems = $plan?->items?->whereIn('status', ['proposed', 'accepted']) ?? collect();
|
||||
$currentStage = $workspaceVisit->specialty_stage;
|
||||
$canManage = $canManageSpecialty ?? true;
|
||||
$stageFlow = [
|
||||
'waiting' => ['next' => 'chair', 'label' => 'Seat at chair'],
|
||||
'chair' => ['next' => 'procedure', 'label' => 'Start procedure'],
|
||||
@@ -26,20 +27,33 @@
|
||||
</p>
|
||||
<div class="mt-3 flex flex-wrap gap-2">
|
||||
@foreach (['waiting', 'chair', 'procedure', 'recovery', 'completed'] as $stageCode)
|
||||
<form method="POST" action="{{ route('care.specialty.dentistry.stage', $workspaceVisit) }}">
|
||||
@csrf
|
||||
<input type="hidden" name="stage" value="{{ $stageCode }}">
|
||||
<button type="submit"
|
||||
@if ($canManage)
|
||||
<form method="POST" action="{{ route('care.specialty.dentistry.stage', $workspaceVisit) }}">
|
||||
@csrf
|
||||
<input type="hidden" name="stage" value="{{ $stageCode }}">
|
||||
<button type="submit"
|
||||
@class([
|
||||
'rounded-lg px-2.5 py-1 text-xs font-semibold',
|
||||
'bg-indigo-600 text-white' => $currentStage === $stageCode,
|
||||
'border border-slate-200 bg-white text-slate-700 hover:bg-slate-50' => $currentStage !== $stageCode,
|
||||
])>
|
||||
{{ ucfirst($stageCode) }}
|
||||
</button>
|
||||
</form>
|
||||
@else
|
||||
<span
|
||||
@class([
|
||||
'rounded-lg px-2.5 py-1 text-xs font-semibold',
|
||||
'bg-indigo-600 text-white' => $currentStage === $stageCode,
|
||||
'border border-slate-200 bg-white text-slate-700 hover:bg-slate-50' => $currentStage !== $stageCode,
|
||||
])>
|
||||
'border border-slate-200 bg-white text-slate-400' => $currentStage !== $stageCode,
|
||||
])
|
||||
@if ($currentStage !== $stageCode) aria-disabled="true" title="View-only access" @endif
|
||||
>
|
||||
{{ ucfirst($stageCode) }}
|
||||
</button>
|
||||
</form>
|
||||
</span>
|
||||
@endif
|
||||
@endforeach
|
||||
@if ($currentStage && isset($stageFlow[$currentStage]))
|
||||
@if ($canManage && $currentStage && isset($stageFlow[$currentStage]))
|
||||
<form method="POST" action="{{ route('care.specialty.dentistry.stage', $workspaceVisit) }}">
|
||||
@csrf
|
||||
<input type="hidden" name="stage" value="{{ $stageFlow[$currentStage]['next'] }}">
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
}
|
||||
$teeth = array_merge($dentalRows['upper'] ?? [], $dentalRows['lower'] ?? []);
|
||||
$probeTeeth = array_slice($teeth, 0, 8);
|
||||
$canManage = $canManageSpecialty ?? true;
|
||||
@endphp
|
||||
|
||||
<section class="rounded-2xl border border-slate-200 bg-white p-5">
|
||||
@@ -16,9 +17,58 @@
|
||||
<p class="mt-3 text-xs text-slate-500">Latest exam {{ $exam->exam_date?->format('d M Y') }} · {{ $exam->sites->count() }} sites</p>
|
||||
@endif
|
||||
|
||||
<form method="POST" action="{{ route('care.specialty.dentistry.perio', $workspaceVisit) }}" class="mt-4 space-y-4">
|
||||
@csrf
|
||||
<div class="overflow-x-auto">
|
||||
@if ($canManage)
|
||||
<form method="POST" action="{{ route('care.specialty.dentistry.perio', $workspaceVisit) }}" class="mt-4 space-y-4">
|
||||
@csrf
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full text-left text-xs">
|
||||
<thead>
|
||||
<tr class="border-b border-slate-200 text-slate-500">
|
||||
<th class="py-2 pr-2">Tooth</th>
|
||||
@foreach ($dentalPerioSurfaces ?? [] as $surface)
|
||||
<th class="px-1 py-2">{{ $surface }} mm</th>
|
||||
<th class="px-1 py-2">B</th>
|
||||
<th class="px-1 py-2">P</th>
|
||||
@endforeach
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach ($probeTeeth as $idx => $tooth)
|
||||
<tr class="border-b border-slate-100">
|
||||
<td class="py-2 pr-2 font-semibold tabular-nums">{{ $tooth }}</td>
|
||||
@foreach ($dentalPerioSurfaces ?? [] as $sIdx => $surface)
|
||||
@php
|
||||
$existing = $siteMap[$tooth][$surface] ?? null;
|
||||
@endphp
|
||||
<input type="hidden" name="sites[{{ $idx }}_{{ $sIdx }}][tooth_code]" value="{{ $tooth }}">
|
||||
<input type="hidden" name="sites[{{ $idx }}_{{ $sIdx }}][surface]" value="{{ $surface }}">
|
||||
<td class="px-1 py-1">
|
||||
<input type="number" min="0" max="15" name="sites[{{ $idx }}_{{ $sIdx }}][probing_mm]"
|
||||
value="{{ $existing?->probing_mm }}"
|
||||
class="w-12 rounded border border-slate-200 px-1 py-1 tabular-nums">
|
||||
</td>
|
||||
<td class="px-1 py-1">
|
||||
<input type="checkbox" name="sites[{{ $idx }}_{{ $sIdx }}][bleeding]" value="1" class="rounded border-slate-300"
|
||||
@checked($existing?->bleeding)>
|
||||
</td>
|
||||
<td class="px-1 py-1">
|
||||
<input type="checkbox" name="sites[{{ $idx }}_{{ $sIdx }}][plaque]" value="1" class="rounded border-slate-300"
|
||||
@checked($existing?->plaque)>
|
||||
</td>
|
||||
@endforeach
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Notes</label>
|
||||
<textarea name="notes" rows="2" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">{{ $exam?->notes }}</textarea>
|
||||
</div>
|
||||
<button type="submit" class="btn-primary">Save perio exam</button>
|
||||
</form>
|
||||
@else
|
||||
<div class="mt-4 overflow-x-auto">
|
||||
<table class="min-w-full text-left text-xs">
|
||||
<thead>
|
||||
<tr class="border-b border-slate-200 text-slate-500">
|
||||
@@ -31,39 +81,26 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach ($probeTeeth as $idx => $tooth)
|
||||
@forelse ($probeTeeth as $tooth)
|
||||
<tr class="border-b border-slate-100">
|
||||
<td class="py-2 pr-2 font-semibold tabular-nums">{{ $tooth }}</td>
|
||||
@foreach ($dentalPerioSurfaces ?? [] as $sIdx => $surface)
|
||||
@php
|
||||
$existing = $siteMap[$tooth][$surface] ?? null;
|
||||
$base = "sites.{$idx}_{$sIdx}";
|
||||
@endphp
|
||||
<input type="hidden" name="sites[{{ $idx }}_{{ $sIdx }}][tooth_code]" value="{{ $tooth }}">
|
||||
<input type="hidden" name="sites[{{ $idx }}_{{ $sIdx }}][surface]" value="{{ $surface }}">
|
||||
<td class="px-1 py-1">
|
||||
<input type="number" min="0" max="15" name="sites[{{ $idx }}_{{ $sIdx }}][probing_mm]"
|
||||
value="{{ $existing?->probing_mm }}"
|
||||
class="w-12 rounded border border-slate-200 px-1 py-1 tabular-nums">
|
||||
</td>
|
||||
<td class="px-1 py-1">
|
||||
<input type="checkbox" name="sites[{{ $idx }}_{{ $sIdx }}][bleeding]" value="1" class="rounded border-slate-300"
|
||||
@checked($existing?->bleeding)>
|
||||
</td>
|
||||
<td class="px-1 py-1">
|
||||
<input type="checkbox" name="sites[{{ $idx }}_{{ $sIdx }}][plaque]" value="1" class="rounded border-slate-300"
|
||||
@checked($existing?->plaque)>
|
||||
</td>
|
||||
@foreach ($dentalPerioSurfaces ?? [] as $surface)
|
||||
@php $existing = $siteMap[$tooth][$surface] ?? null; @endphp
|
||||
<td class="px-1 py-1 tabular-nums">{{ $existing?->probing_mm ?? '—' }}</td>
|
||||
<td class="px-1 py-1">{{ $existing?->bleeding ? '✓' : '—' }}</td>
|
||||
<td class="px-1 py-1">{{ $existing?->plaque ? '✓' : '—' }}</td>
|
||||
@endforeach
|
||||
</tr>
|
||||
@endforeach
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="{{ 1 + count($dentalPerioSurfaces ?? []) * 3 }}" class="py-3 text-slate-500">No perio exam yet.</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Notes</label>
|
||||
<textarea name="notes" rows="2" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">{{ $exam?->notes }}</textarea>
|
||||
</div>
|
||||
<button type="submit" class="btn-primary">Save perio exam</button>
|
||||
</form>
|
||||
@if ($exam?->notes)
|
||||
<p class="mt-3 text-sm text-slate-700 whitespace-pre-wrap">{{ $exam->notes }}</p>
|
||||
@endif
|
||||
@endif
|
||||
</section>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
@php
|
||||
$plan = $dentalPlan;
|
||||
$history = ($dentalPlanHistory ?? collect())->filter(fn ($p) => ! $plan || $p->id !== $plan->id);
|
||||
$canManage = $canManageSpecialty ?? true;
|
||||
@endphp
|
||||
|
||||
<section class="rounded-2xl border border-slate-200 bg-white p-5">
|
||||
@@ -16,7 +17,7 @@
|
||||
@endif
|
||||
</p>
|
||||
</div>
|
||||
@if ($plan)
|
||||
@if ($canManage && $plan)
|
||||
<div class="flex flex-wrap gap-2">
|
||||
@if ($plan->status === 'draft')
|
||||
<form method="POST" action="{{ route('care.specialty.dentistry.plan.accept', $workspaceVisit) }}">
|
||||
@@ -53,14 +54,14 @@
|
||||
</p>
|
||||
<p class="text-xs text-slate-500">Priority {{ $item->priority }} · {{ ucfirst($item->status) }} · {{ number_format($item->estimate_minor / 100, 2) }}</p>
|
||||
</div>
|
||||
@if (! in_array($item->status, ['done', 'cancelled'], true))
|
||||
@if ($canManage && ! in_array($item->status, ['done', 'cancelled'], true))
|
||||
<form method="POST" action="{{ route('care.specialty.dentistry.plan-items.cancel', [$workspaceVisit, $item]) }}" onsubmit="return confirm('Cancel this item?')">
|
||||
@csrf
|
||||
<button type="submit" class="text-xs font-medium text-rose-600">Cancel item</button>
|
||||
</form>
|
||||
@endif
|
||||
</div>
|
||||
@if (! in_array($item->status, ['done', 'cancelled'], true))
|
||||
@if ($canManage && ! in_array($item->status, ['done', 'cancelled'], true))
|
||||
<form method="POST" action="{{ route('care.specialty.dentistry.plan-items.update', [$workspaceVisit, $item]) }}" class="mt-3 grid gap-2 border-t border-slate-200/70 pt-3 sm:grid-cols-4">
|
||||
@csrf
|
||||
<select name="procedure_code" class="rounded-lg border border-slate-200 px-2 py-1.5 text-xs">
|
||||
@@ -84,7 +85,7 @@
|
||||
@endforelse
|
||||
</ul>
|
||||
|
||||
@if (! $plan || ! in_array($plan->status, ['completed', 'rejected', 'cancelled'], true))
|
||||
@if ($canManage && (! $plan || ! in_array($plan->status, ['completed', 'rejected', 'cancelled'], true)))
|
||||
<form method="POST" action="{{ route('care.specialty.dentistry.plan-items', $workspaceVisit) }}" class="mt-6 space-y-3 border-t border-slate-100 pt-4">
|
||||
@csrf
|
||||
<h4 class="text-sm font-semibold text-slate-900">Add item</h4>
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
@php $canManage = $canManageSpecialty ?? true; @endphp
|
||||
|
||||
<section class="rounded-2xl border border-slate-200 bg-white p-5">
|
||||
<h3 class="text-sm font-semibold text-slate-900">Recalls</h3>
|
||||
<p class="mt-0.5 text-xs text-slate-500">Schedule follow-ups and mark them complete when the patient returns.</p>
|
||||
@@ -11,7 +13,7 @@
|
||||
Due {{ $recall->due_on?->format('d M Y') }} · {{ ucfirst($recall->status) }}
|
||||
</p>
|
||||
</div>
|
||||
@if (in_array($recall->status, ['scheduled', 'due'], true))
|
||||
@if ($canManage && in_array($recall->status, ['scheduled', 'due'], true))
|
||||
<div class="flex gap-2">
|
||||
<form method="POST" action="{{ route('care.specialty.dentistry.recalls.complete', [$workspaceVisit, $recall]) }}">
|
||||
@csrf
|
||||
@@ -29,24 +31,26 @@
|
||||
@endforelse
|
||||
</ul>
|
||||
|
||||
<form method="POST" action="{{ route('care.specialty.dentistry.recalls', $workspaceVisit) }}" class="mt-6 space-y-3 border-t border-slate-100 pt-4">
|
||||
@csrf
|
||||
<h4 class="text-sm font-semibold text-slate-900">Schedule recall</h4>
|
||||
<div class="grid gap-3 sm:grid-cols-2">
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Due date</label>
|
||||
<input type="date" name="due_on" required class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm"
|
||||
value="{{ now()->addMonths(6)->toDateString() }}">
|
||||
@if ($canManage)
|
||||
<form method="POST" action="{{ route('care.specialty.dentistry.recalls', $workspaceVisit) }}" class="mt-6 space-y-3 border-t border-slate-100 pt-4">
|
||||
@csrf
|
||||
<h4 class="text-sm font-semibold text-slate-900">Schedule recall</h4>
|
||||
<div class="grid gap-3 sm:grid-cols-2">
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Due date</label>
|
||||
<input type="date" name="due_on" required class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm"
|
||||
value="{{ now()->addMonths(6)->toDateString() }}">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Reason</label>
|
||||
<input type="text" name="reason" placeholder="e.g. 6-month check" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
</div>
|
||||
<div class="sm:col-span-2">
|
||||
<label class="block text-xs font-medium text-slate-600">Notes</label>
|
||||
<input type="text" name="notes" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Reason</label>
|
||||
<input type="text" name="reason" placeholder="e.g. 6-month check" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
</div>
|
||||
<div class="sm:col-span-2">
|
||||
<label class="block text-xs font-medium text-slate-600">Notes</label>
|
||||
<input type="text" name="notes" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn-primary">Schedule recall</button>
|
||||
</form>
|
||||
<button type="submit" class="btn-primary">Schedule recall</button>
|
||||
</form>
|
||||
@endif
|
||||
</section>
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
@php $canManage = $canManageSpecialty ?? true; @endphp
|
||||
|
||||
<section class="rounded-2xl border border-slate-200 bg-white p-5">
|
||||
<h3 class="text-sm font-semibold text-slate-900">Treat today</h3>
|
||||
<p class="mt-0.5 text-xs text-slate-500">Record a completed procedure. It posts to the visit bill automatically.</p>
|
||||
<p class="mt-0.5 text-xs text-slate-500">
|
||||
{{ $canManage ? 'Record a completed procedure. It posts to the visit bill automatically.' : 'Completed procedures for this visit.' }}
|
||||
</p>
|
||||
|
||||
@if ($dentalPlan?->items)
|
||||
@if ($canManage && $dentalPlan?->items)
|
||||
<div class="mt-4">
|
||||
<p class="text-xs font-semibold uppercase tracking-wide text-slate-500">From plan</p>
|
||||
<ul class="mt-2 space-y-2">
|
||||
@@ -27,59 +31,73 @@
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@elseif (! $canManage && $dentalPlan?->items?->whereIn('status', ['proposed', 'accepted'])->isNotEmpty())
|
||||
<div class="mt-4">
|
||||
<p class="text-xs font-semibold uppercase tracking-wide text-slate-500">Open plan items</p>
|
||||
<ul class="mt-2 space-y-2 text-sm">
|
||||
@foreach ($dentalPlan->items->whereIn('status', ['proposed', 'accepted']) as $item)
|
||||
<li class="rounded-xl border border-slate-100 bg-slate-50 px-3 py-2">
|
||||
<p class="font-medium text-slate-800">{{ $item->label }} @if($item->tooth_code)<span class="text-slate-500">· {{ $item->tooth_code }}</span>@endif</p>
|
||||
<p class="text-xs text-slate-500">{{ number_format($item->estimate_minor / 100, 2) }}</p>
|
||||
</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<form method="POST" action="{{ route('care.specialty.dentistry.procedures', $workspaceVisit) }}" class="mt-6 space-y-3 border-t border-slate-100 pt-4">
|
||||
@csrf
|
||||
<h4 class="text-sm font-semibold text-slate-900">Ad-hoc procedure</h4>
|
||||
<input type="hidden" name="bill" value="1">
|
||||
<div class="grid gap-3 sm:grid-cols-2">
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Procedure</label>
|
||||
<select name="procedure_code" required class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
<option value="">Select…</option>
|
||||
@foreach ($dentalCatalog as $service)
|
||||
<option value="{{ $service['code'] }}">{{ $service['label'] }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Tooth</label>
|
||||
<select name="tooth_code" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
<option value="">—</option>
|
||||
@foreach (array_merge($dentalRows['upper'] ?? [], $dentalRows['lower'] ?? []) as $code)
|
||||
<option value="{{ $code }}">{{ $code }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Anesthesia</label>
|
||||
<select name="anesthesia" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
<option value="">—</option>
|
||||
<option value="None">None</option>
|
||||
<option value="Local">Local</option>
|
||||
<option value="Sedation">Sedation</option>
|
||||
<option value="GA">GA</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="sm:col-span-2">
|
||||
<p class="text-xs font-medium text-slate-600">Surfaces</p>
|
||||
<div class="mt-2 flex flex-wrap gap-3">
|
||||
@foreach ($dentalSurfaces as $surface)
|
||||
<label class="inline-flex items-center gap-1.5 text-sm">
|
||||
<input type="checkbox" name="surfaces[]" value="{{ $surface }}" class="rounded border-slate-300 text-indigo-600">
|
||||
{{ $surface }}
|
||||
</label>
|
||||
@endforeach
|
||||
@if ($canManage)
|
||||
<form method="POST" action="{{ route('care.specialty.dentistry.procedures', $workspaceVisit) }}" class="mt-6 space-y-3 border-t border-slate-100 pt-4">
|
||||
@csrf
|
||||
<h4 class="text-sm font-semibold text-slate-900">Ad-hoc procedure</h4>
|
||||
<input type="hidden" name="bill" value="1">
|
||||
<div class="grid gap-3 sm:grid-cols-2">
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Procedure</label>
|
||||
<select name="procedure_code" required class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
<option value="">Select…</option>
|
||||
@foreach ($dentalCatalog as $service)
|
||||
<option value="{{ $service['code'] }}">{{ $service['label'] }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Tooth</label>
|
||||
<select name="tooth_code" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
<option value="">—</option>
|
||||
@foreach (array_merge($dentalRows['upper'] ?? [], $dentalRows['lower'] ?? []) as $code)
|
||||
<option value="{{ $code }}">{{ $code }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Anesthesia</label>
|
||||
<select name="anesthesia" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
<option value="">—</option>
|
||||
<option value="None">None</option>
|
||||
<option value="Local">Local</option>
|
||||
<option value="Sedation">Sedation</option>
|
||||
<option value="GA">GA</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="sm:col-span-2">
|
||||
<p class="text-xs font-medium text-slate-600">Surfaces</p>
|
||||
<div class="mt-2 flex flex-wrap gap-3">
|
||||
@foreach ($dentalSurfaces as $surface)
|
||||
<label class="inline-flex items-center gap-1.5 text-sm">
|
||||
<input type="checkbox" name="surfaces[]" value="{{ $surface }}" class="rounded border-slate-300 text-indigo-600">
|
||||
{{ $surface }}
|
||||
</label>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
<div class="sm:col-span-2">
|
||||
<label class="block text-xs font-medium text-slate-600">Notes</label>
|
||||
<input type="text" name="notes" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
</div>
|
||||
</div>
|
||||
<div class="sm:col-span-2">
|
||||
<label class="block text-xs font-medium text-slate-600">Notes</label>
|
||||
<input type="text" name="notes" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn-primary">Complete & bill</button>
|
||||
</form>
|
||||
<button type="submit" class="btn-primary">Complete & bill</button>
|
||||
</form>
|
||||
@endif
|
||||
|
||||
<div class="mt-6 border-t border-slate-100 pt-4">
|
||||
<p class="text-xs font-semibold uppercase tracking-wide text-slate-500">Completed this visit</p>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
@php
|
||||
$record = $emergencyDisposition;
|
||||
$payload = old('payload', $record?->payload ?? []);
|
||||
$canManage = $canManageSpecialty ?? true;
|
||||
@endphp
|
||||
|
||||
<section class="rounded-2xl border border-slate-200 bg-white p-5">
|
||||
@@ -12,57 +13,94 @@
|
||||
<a href="{{ route('care.specialty.emergency.print', $workspaceVisit) }}" target="_blank" class="text-sm font-medium text-indigo-600">Print summary</a>
|
||||
</div>
|
||||
|
||||
<form method="POST" action="{{ route('care.specialty.emergency.disposition', $workspaceVisit) }}" class="mt-4 space-y-3">
|
||||
@csrf
|
||||
<input type="hidden" name="tab" value="disposition">
|
||||
@if ($canManage)
|
||||
<form method="POST" action="{{ route('care.specialty.emergency.disposition', $workspaceVisit) }}" class="mt-4 space-y-3">
|
||||
@csrf
|
||||
<input type="hidden" name="tab" value="disposition">
|
||||
|
||||
<div class="grid gap-3 sm:grid-cols-2">
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Final disposition</label>
|
||||
<select name="payload[disposition]" required class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
<option value="">Select…</option>
|
||||
@foreach (['Discharge home', 'Admit to ward', 'Admit to ICU', 'Transfer to facility', 'Left against advice', 'Died', 'Absconded'] as $opt)
|
||||
<option value="{{ $opt }}" @selected(($payload['disposition'] ?? '') === $opt)>{{ $opt }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@error('payload.disposition')<p class="mt-1 text-sm text-red-600">{{ $message }}</p>@enderror
|
||||
<div class="grid gap-3 sm:grid-cols-2">
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Final disposition</label>
|
||||
<select name="payload[disposition]" required class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
<option value="">Select…</option>
|
||||
@foreach (['Discharge home', 'Admit to ward', 'Admit to ICU', 'Transfer to facility', 'Left against advice', 'Died', 'Absconded'] as $opt)
|
||||
<option value="{{ $opt }}" @selected(($payload['disposition'] ?? '') === $opt)>{{ $opt }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@error('payload.disposition')<p class="mt-1 text-sm text-red-600">{{ $message }}</p>@enderror
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Condition</label>
|
||||
<select name="payload[condition]" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
<option value="">Select…</option>
|
||||
@foreach (['Stable', 'Improving', 'Unstable', 'Critical', 'Deceased'] as $opt)
|
||||
<option value="{{ $opt }}" @selected(($payload['condition'] ?? '') === $opt)>{{ $opt }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Destination</label>
|
||||
<input type="text" name="payload[destination]" value="{{ $payload['destination'] ?? '' }}" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Disposition time</label>
|
||||
<input type="text" name="payload[time]" value="{{ $payload['time'] ?? '' }}" placeholder="e.g. 14:30" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
</div>
|
||||
<div class="sm:col-span-2">
|
||||
<label class="block text-xs font-medium text-slate-600">Diagnosis</label>
|
||||
<input type="text" name="payload[diagnosis]" value="{{ $payload['diagnosis'] ?? '' }}" required class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
@error('payload.diagnosis')<p class="mt-1 text-sm text-red-600">{{ $message }}</p>@enderror
|
||||
</div>
|
||||
<div class="sm:col-span-2">
|
||||
<label class="block text-xs font-medium text-slate-600">Advice / follow-up</label>
|
||||
<textarea name="payload[advice]" rows="3" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">{{ $payload['advice'] ?? '' }}</textarea>
|
||||
</div>
|
||||
<div class="sm:col-span-2">
|
||||
<label class="block text-xs font-medium text-slate-600">Medications on discharge</label>
|
||||
<textarea name="payload[medications]" rows="2" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">{{ $payload['medications'] ?? '' }}</textarea>
|
||||
</div>
|
||||
<div class="sm:col-span-2">
|
||||
<label class="block text-xs font-medium text-slate-600">Notes</label>
|
||||
<textarea name="payload[notes]" rows="2" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">{{ $payload['notes'] ?? '' }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Condition</label>
|
||||
<select name="payload[condition]" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
<option value="">Select…</option>
|
||||
@foreach (['Stable', 'Improving', 'Unstable', 'Critical', 'Deceased'] as $opt)
|
||||
<option value="{{ $opt }}" @selected(($payload['condition'] ?? '') === $opt)>{{ $opt }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Destination</label>
|
||||
<input type="text" name="payload[destination]" value="{{ $payload['destination'] ?? '' }}" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Disposition time</label>
|
||||
<input type="text" name="payload[time]" value="{{ $payload['time'] ?? '' }}" placeholder="e.g. 14:30" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
</div>
|
||||
<div class="sm:col-span-2">
|
||||
<label class="block text-xs font-medium text-slate-600">Diagnosis</label>
|
||||
<input type="text" name="payload[diagnosis]" value="{{ $payload['diagnosis'] ?? '' }}" required class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
@error('payload.diagnosis')<p class="mt-1 text-sm text-red-600">{{ $message }}</p>@enderror
|
||||
</div>
|
||||
<div class="sm:col-span-2">
|
||||
<label class="block text-xs font-medium text-slate-600">Advice / follow-up</label>
|
||||
<textarea name="payload[advice]" rows="3" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">{{ $payload['advice'] ?? '' }}</textarea>
|
||||
</div>
|
||||
<div class="sm:col-span-2">
|
||||
<label class="block text-xs font-medium text-slate-600">Medications on discharge</label>
|
||||
<textarea name="payload[medications]" rows="2" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">{{ $payload['medications'] ?? '' }}</textarea>
|
||||
</div>
|
||||
<div class="sm:col-span-2">
|
||||
<label class="block text-xs font-medium text-slate-600">Notes</label>
|
||||
<textarea name="payload[notes]" rows="2" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">{{ $payload['notes'] ?? '' }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn-primary">Save disposition & complete visit</button>
|
||||
</form>
|
||||
<button type="submit" class="btn-primary">Save disposition & complete visit</button>
|
||||
</form>
|
||||
@else
|
||||
<dl class="mt-4 grid gap-3 text-sm sm:grid-cols-2">
|
||||
<div>
|
||||
<dt class="text-slate-500">Final disposition</dt>
|
||||
<dd class="mt-0.5 font-medium text-slate-900">{{ $payload['disposition'] ?? '—' }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-slate-500">Condition</dt>
|
||||
<dd class="mt-0.5 font-medium text-slate-900">{{ $payload['condition'] ?? '—' }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-slate-500">Destination</dt>
|
||||
<dd class="mt-0.5 font-medium text-slate-900">{{ $payload['destination'] ?? '—' }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-slate-500">Disposition time</dt>
|
||||
<dd class="mt-0.5 font-medium text-slate-900">{{ $payload['time'] ?? '—' }}</dd>
|
||||
</div>
|
||||
<div class="sm:col-span-2">
|
||||
<dt class="text-slate-500">Diagnosis</dt>
|
||||
<dd class="mt-0.5 font-medium text-slate-900">{{ $payload['diagnosis'] ?? '—' }}</dd>
|
||||
</div>
|
||||
<div class="sm:col-span-2">
|
||||
<dt class="text-slate-500">Advice / follow-up</dt>
|
||||
<dd class="mt-0.5 font-medium text-slate-900 whitespace-pre-wrap">{{ $payload['advice'] ?? '—' }}</dd>
|
||||
</div>
|
||||
<div class="sm:col-span-2">
|
||||
<dt class="text-slate-500">Medications on discharge</dt>
|
||||
<dd class="mt-0.5 font-medium text-slate-900 whitespace-pre-wrap">{{ $payload['medications'] ?? '—' }}</dd>
|
||||
</div>
|
||||
<div class="sm:col-span-2">
|
||||
<dt class="text-slate-500">Notes</dt>
|
||||
<dd class="mt-0.5 font-medium text-slate-900 whitespace-pre-wrap">{{ $payload['notes'] ?? '—' }}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
@endif
|
||||
</section>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
$currentStage = $workspaceVisit->specialty_stage;
|
||||
$stageFlow = $emergencyStageFlow ?? [];
|
||||
$latest = $emergencyLatestVitals;
|
||||
$canManage = $canManageSpecialty ?? true;
|
||||
@endphp
|
||||
|
||||
<section class="rounded-2xl border border-slate-200 bg-white p-5">
|
||||
@@ -55,20 +56,33 @@
|
||||
</p>
|
||||
<div class="mt-3 flex flex-wrap gap-2">
|
||||
@foreach (($emergencyStageCodes ?: ['arrival', 'resus', 'treatment', 'observation', 'disposition']) as $stageCode)
|
||||
<form method="POST" action="{{ route('care.specialty.emergency.stage', $workspaceVisit) }}">
|
||||
@csrf
|
||||
<input type="hidden" name="stage" value="{{ $stageCode }}">
|
||||
<button type="submit"
|
||||
@if ($canManage)
|
||||
<form method="POST" action="{{ route('care.specialty.emergency.stage', $workspaceVisit) }}">
|
||||
@csrf
|
||||
<input type="hidden" name="stage" value="{{ $stageCode }}">
|
||||
<button type="submit"
|
||||
@class([
|
||||
'rounded-lg px-2.5 py-1 text-xs font-semibold',
|
||||
'bg-indigo-600 text-white' => $currentStage === $stageCode,
|
||||
'border border-slate-200 bg-white text-slate-700 hover:bg-slate-50' => $currentStage !== $stageCode,
|
||||
])>
|
||||
{{ ucfirst(str_replace('_', ' ', $stageCode)) }}
|
||||
</button>
|
||||
</form>
|
||||
@else
|
||||
<span
|
||||
@class([
|
||||
'rounded-lg px-2.5 py-1 text-xs font-semibold',
|
||||
'bg-indigo-600 text-white' => $currentStage === $stageCode,
|
||||
'border border-slate-200 bg-white text-slate-700 hover:bg-slate-50' => $currentStage !== $stageCode,
|
||||
])>
|
||||
'border border-slate-200 bg-white text-slate-400' => $currentStage !== $stageCode,
|
||||
])
|
||||
@if ($currentStage !== $stageCode) aria-disabled="true" title="View-only access" @endif
|
||||
>
|
||||
{{ ucfirst(str_replace('_', ' ', $stageCode)) }}
|
||||
</button>
|
||||
</form>
|
||||
</span>
|
||||
@endif
|
||||
@endforeach
|
||||
@if ($currentStage && isset($stageFlow[$currentStage]) && $stageFlow[$currentStage]['next'] !== $currentStage)
|
||||
@if ($canManage && $currentStage && isset($stageFlow[$currentStage]) && $stageFlow[$currentStage]['next'] !== $currentStage)
|
||||
<form method="POST" action="{{ route('care.specialty.emergency.stage', $workspaceVisit) }}">
|
||||
@csrf
|
||||
<input type="hidden" name="stage" value="{{ $stageFlow[$currentStage]['next'] }}">
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
@php $canManage = $canManageSpecialty ?? true; @endphp
|
||||
|
||||
<section class="rounded-2xl border border-slate-200 bg-white p-5">
|
||||
<div class="flex flex-wrap items-start justify-between gap-3">
|
||||
<div>
|
||||
@@ -6,46 +8,48 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form method="POST" action="{{ route('care.specialty.emergency.vitals', $workspaceVisit) }}" class="mt-4 grid gap-3 sm:grid-cols-4">
|
||||
@csrf
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">BP systolic</label>
|
||||
<input type="number" name="bp_systolic" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">BP diastolic</label>
|
||||
<input type="number" name="bp_diastolic" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Pulse</label>
|
||||
<input type="number" name="pulse" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">SpO₂ %</label>
|
||||
<input type="number" name="spo2" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Temperature °C</label>
|
||||
<input type="number" step="0.1" name="temperature" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Respiratory rate</label>
|
||||
<input type="number" name="respiratory_rate" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Weight kg</label>
|
||||
<input type="number" step="0.1" name="weight_kg" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Height cm</label>
|
||||
<input type="number" step="0.1" name="height_cm" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
</div>
|
||||
<div class="sm:col-span-4">
|
||||
<button type="submit" class="btn-primary">Save vitals</button>
|
||||
</div>
|
||||
</form>
|
||||
@if ($canManage)
|
||||
<form method="POST" action="{{ route('care.specialty.emergency.vitals', $workspaceVisit) }}" class="mt-4 grid gap-3 sm:grid-cols-4">
|
||||
@csrf
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">BP systolic</label>
|
||||
<input type="number" name="bp_systolic" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">BP diastolic</label>
|
||||
<input type="number" name="bp_diastolic" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Pulse</label>
|
||||
<input type="number" name="pulse" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">SpO₂ %</label>
|
||||
<input type="number" name="spo2" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Temperature °C</label>
|
||||
<input type="number" step="0.1" name="temperature" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Respiratory rate</label>
|
||||
<input type="number" name="respiratory_rate" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Weight kg</label>
|
||||
<input type="number" step="0.1" name="weight_kg" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-slate-600">Height cm</label>
|
||||
<input type="number" step="0.1" name="height_cm" class="mt-1 w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
</div>
|
||||
<div class="sm:col-span-4">
|
||||
<button type="submit" class="btn-primary">Save vitals</button>
|
||||
</div>
|
||||
</form>
|
||||
@endif
|
||||
|
||||
<div class="mt-6 border-t border-slate-100 pt-4">
|
||||
<div @class(['mt-6 border-t border-slate-100 pt-4' => $canManage, 'mt-4' => ! $canManage])>
|
||||
<p class="text-xs font-semibold uppercase tracking-wide text-slate-500">History</p>
|
||||
<ul class="mt-2 divide-y divide-slate-100 text-sm">
|
||||
@forelse ($emergencyVitals as $row)
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
$currentStage = $workspaceVisit?->specialty_stage;
|
||||
@endphp
|
||||
|
||||
@if ($onWorkspace)
|
||||
@if ($onWorkspace && ($canManageSpecialty ?? true))
|
||||
@include('care.partials.queue-ops', [
|
||||
'queueIntegration' => $queueIntegration ?? null,
|
||||
'queueCallNextRoute' => 'care.specialty.call-next',
|
||||
@@ -50,7 +50,7 @@
|
||||
])
|
||||
@endif
|
||||
|
||||
@if ($ticketCalled && $appointment)
|
||||
@if (($canManageSpecialty ?? true) && $ticketCalled && $appointment)
|
||||
<form method="POST" action="{{ route('care.queue.recall', $appointment) }}" class="w-full" @click.stop>
|
||||
@csrf
|
||||
<button type="submit" class="{{ $btnAccent }}">Call again</button>
|
||||
|
||||
@@ -42,22 +42,26 @@
|
||||
@include('care.specialty.emergency.workspace-'.$activeTab)
|
||||
@elseif ($activeTab === 'billing')
|
||||
<section class="rounded-2xl border border-slate-200 bg-white p-5">
|
||||
<h3 class="text-sm font-semibold text-slate-900">Add specialty service</h3>
|
||||
<form method="POST" action="{{ route('care.specialty.services.add', ['module' => $moduleKey, 'visit' => $workspaceVisit]) }}" class="mt-3 space-y-3">
|
||||
@csrf
|
||||
<select name="service_code" required class="w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
<option value="">Select service…</option>
|
||||
@foreach ($services as $service)
|
||||
<option value="{{ $service['code'] }}">
|
||||
{{ $service['label'] }} — {{ $currency ?? 'GHS' }} {{ number_format(((int) $service['amount_minor']) / 100, 2) }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<button type="submit" class="btn-primary">{{ $actions['invoice'] ?? 'Add to invoice' }}</button>
|
||||
</form>
|
||||
<h3 class="text-sm font-semibold text-slate-900">
|
||||
{{ ($canManageSpecialty ?? true) ? 'Add specialty service' : 'Visit billing' }}
|
||||
</h3>
|
||||
@if ($canManageSpecialty ?? true)
|
||||
<form method="POST" action="{{ route('care.specialty.services.add', ['module' => $moduleKey, 'visit' => $workspaceVisit]) }}" class="mt-3 space-y-3">
|
||||
@csrf
|
||||
<select name="service_code" required class="w-full rounded-xl border border-slate-200 px-3 py-2 text-sm">
|
||||
<option value="">Select service…</option>
|
||||
@foreach ($services as $service)
|
||||
<option value="{{ $service['code'] }}">
|
||||
{{ $service['label'] }} — {{ $currency ?? 'GHS' }} {{ number_format(((int) $service['amount_minor']) / 100, 2) }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
<button type="submit" class="btn-primary">{{ $actions['invoice'] ?? 'Add to invoice' }}</button>
|
||||
</form>
|
||||
@endif
|
||||
|
||||
@if ($workspaceVisit->bill)
|
||||
<div class="mt-6 border-t border-slate-100 pt-4">
|
||||
<div @class(['mt-6 border-t border-slate-100 pt-4' => ($canManageSpecialty ?? true), 'mt-3' => ! ($canManageSpecialty ?? true)])>
|
||||
<p class="text-xs font-semibold uppercase tracking-wide text-slate-500">Current invoice {{ $workspaceVisit->bill->invoice_number }}</p>
|
||||
<ul class="mt-2 space-y-1 text-sm">
|
||||
@forelse ($workspaceVisit->bill->lineItems as $line)
|
||||
@@ -71,6 +75,8 @@
|
||||
</ul>
|
||||
<a href="{{ route('care.bills.show', $workspaceVisit->bill) }}" class="mt-3 inline-flex text-sm font-medium text-indigo-600">Open bill</a>
|
||||
</div>
|
||||
@elseif (! ($canManageSpecialty ?? true))
|
||||
<p class="mt-3 text-sm text-slate-500">No invoice on this visit yet.</p>
|
||||
@endif
|
||||
</section>
|
||||
@elseif (! empty($clinicalFields))
|
||||
@@ -82,11 +88,13 @@
|
||||
<h3 class="text-sm font-semibold text-slate-900">Documents</h3>
|
||||
<p class="mt-1 text-xs text-slate-500">Specialty documents attach to the patient chart for this episode.</p>
|
||||
|
||||
<form method="POST" action="{{ route('care.specialty.documents.upload', ['module' => $moduleKey, 'visit' => $workspaceVisit]) }}" enctype="multipart/form-data" class="mt-4 space-y-3">
|
||||
@csrf
|
||||
<input type="file" name="attachment" required accept=".pdf,.jpg,.jpeg,.png,.webp" class="block w-full text-sm">
|
||||
<button type="submit" class="btn-primary">Upload document</button>
|
||||
</form>
|
||||
@if ($canManageSpecialty ?? true)
|
||||
<form method="POST" action="{{ route('care.specialty.documents.upload', ['module' => $moduleKey, 'visit' => $workspaceVisit]) }}" enctype="multipart/form-data" class="mt-4 space-y-3">
|
||||
@csrf
|
||||
<input type="file" name="attachment" required accept=".pdf,.jpg,.jpeg,.png,.webp" class="block w-full text-sm">
|
||||
<button type="submit" class="btn-primary">Upload document</button>
|
||||
</form>
|
||||
@endif
|
||||
|
||||
<ul class="mt-4 space-y-2 text-sm">
|
||||
@forelse ($visitDocuments ?? [] as $doc)
|
||||
|
||||
@@ -116,7 +116,9 @@
|
||||
|
||||
<div class="flex flex-wrap gap-3 text-sm">
|
||||
<a href="{{ route('care.specialty.history', $moduleKey) }}" class="rounded-lg border border-slate-200 bg-white px-3 py-1.5 font-medium text-slate-700 hover:bg-slate-50">History</a>
|
||||
<a href="{{ route('care.specialty.billing', $moduleKey) }}" class="rounded-lg border border-slate-200 bg-white px-3 py-1.5 font-medium text-slate-700 hover:bg-slate-50">Billing</a>
|
||||
@if ($canManageSpecialty ?? true)
|
||||
<a href="{{ route('care.specialty.billing', $moduleKey) }}" class="rounded-lg border border-slate-200 bg-white px-3 py-1.5 font-medium text-slate-700 hover:bg-slate-50">Billing</a>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
@include('care.partials.queue-board', [
|
||||
@@ -128,7 +130,7 @@
|
||||
'canConsult' => $canConsult ?? false,
|
||||
'lockToPractitioner' => $lockToPractitioner ?? false,
|
||||
'showFilters' => true,
|
||||
'queueCallNextRoute' => 'care.specialty.call-next',
|
||||
'queueCallNextRoute' => ($canManageSpecialty ?? true) ? 'care.specialty.call-next' : null,
|
||||
'queueCallNextParams' => ['module' => $moduleKey],
|
||||
'startRouteName' => 'care.queue.start',
|
||||
'inCareLabel' => 'In care',
|
||||
|
||||
@@ -235,9 +235,18 @@ class CareSpecialtyAccessTest extends TestCase
|
||||
]);
|
||||
|
||||
$this->actingAs($doctor)
|
||||
->get(route('care.specialty.workspace', ['module' => 'cardiology', 'visit' => $visit]))
|
||||
->get(route('care.specialty.workspace', ['module' => 'cardiology', 'visit' => $visit, 'tab' => 'exam']))
|
||||
->assertOk()
|
||||
->assertSee('view + refer');
|
||||
->assertSee('view + refer')
|
||||
->assertDontSee('Save record')
|
||||
->assertDontSee('Call next')
|
||||
->assertDontSee('Add to invoice')
|
||||
->assertDontSee('Upload document')
|
||||
->assertSee('Refer to specialty queue')
|
||||
->assertDontSee(
|
||||
'action="'.route('care.specialty.consultation.start', ['module' => 'cardiology', 'visit' => $visit], false).'"',
|
||||
false,
|
||||
);
|
||||
|
||||
$this->actingAs($doctor)
|
||||
->post(route('care.specialty.clinical.save', ['module' => 'cardiology', 'visit' => $visit]), [
|
||||
@@ -249,6 +258,82 @@ class CareSpecialtyAccessTest extends TestCase
|
||||
->assertForbidden();
|
||||
}
|
||||
|
||||
public function test_sidebar_and_dashboard_exclude_modules_with_no_access(): void
|
||||
{
|
||||
[$pharmacistUser, $pharmacistMember] = $this->makeStaff('pharmacist', 'rx-nav');
|
||||
|
||||
$enabledForMember = collect($this->modules->enabledModulesForMember($this->organization, $pharmacistMember))
|
||||
->pluck('key')
|
||||
->all();
|
||||
|
||||
$this->assertContains('emergency', $enabledForMember);
|
||||
$this->assertContains('infusion', $enabledForMember);
|
||||
$this->assertNotContains('cardiology', $enabledForMember);
|
||||
$this->assertNotContains('dentistry', $enabledForMember);
|
||||
|
||||
$this->actingAs($pharmacistUser)
|
||||
->get(route('care.dashboard'))
|
||||
->assertOk()
|
||||
->assertSee('Emergency')
|
||||
->assertDontSee('Cardiology');
|
||||
}
|
||||
|
||||
public function test_manage_role_still_sees_mutate_actions_on_restricted_module(): void
|
||||
{
|
||||
[$doctor, $member] = $this->makeStaff('doctor', 'cardio-ui');
|
||||
$dept = Department::query()->where('type', 'cardiology')->firstOrFail();
|
||||
Practitioner::create([
|
||||
'owner_ref' => $this->owner->public_id,
|
||||
'organization_id' => $this->organization->id,
|
||||
'branch_id' => $this->branch->id,
|
||||
'department_id' => $dept->id,
|
||||
'member_id' => $member->id,
|
||||
'user_ref' => $doctor->public_id,
|
||||
'name' => 'Dr Cardio UI',
|
||||
'specialty' => 'Cardiology',
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
$patient = Patient::create([
|
||||
'owner_ref' => $this->owner->public_id,
|
||||
'organization_id' => $this->organization->id,
|
||||
'branch_id' => $this->branch->id,
|
||||
'patient_number' => 'P-CARD-UI',
|
||||
'first_name' => 'Abena',
|
||||
'last_name' => 'Heart',
|
||||
'gender' => 'female',
|
||||
'date_of_birth' => '1985-01-01',
|
||||
]);
|
||||
$visit = \App\Models\Visit::create([
|
||||
'owner_ref' => $this->owner->public_id,
|
||||
'organization_id' => $this->organization->id,
|
||||
'branch_id' => $this->branch->id,
|
||||
'patient_id' => $patient->id,
|
||||
'status' => \App\Models\Visit::STATUS_IN_PROGRESS,
|
||||
'checked_in_at' => now(),
|
||||
]);
|
||||
Appointment::create([
|
||||
'owner_ref' => $this->owner->public_id,
|
||||
'organization_id' => $this->organization->id,
|
||||
'branch_id' => $this->branch->id,
|
||||
'patient_id' => $patient->id,
|
||||
'department_id' => $dept->id,
|
||||
'visit_id' => $visit->id,
|
||||
'type' => Appointment::TYPE_WALK_IN,
|
||||
'status' => Appointment::STATUS_WAITING,
|
||||
'scheduled_at' => now(),
|
||||
'checked_in_at' => now(),
|
||||
]);
|
||||
|
||||
$this->assertTrue($this->modules->memberCanManage($this->organization, $member, 'cardiology'));
|
||||
|
||||
$this->actingAs($doctor)
|
||||
->get(route('care.specialty.workspace', ['module' => 'cardiology', 'visit' => $visit]))
|
||||
->assertOk()
|
||||
->assertDontSee('view + refer')
|
||||
->assertSee('Start');
|
||||
}
|
||||
|
||||
public function test_admin_keeps_full_manage_access(): void
|
||||
{
|
||||
$admin = Member::query()->where('user_ref', $this->owner->public_id)->firstOrFail();
|
||||
|
||||
Reference in New Issue
Block a user