Compare commits
10 Commits
9341a69db6
...
071e94166b
Author | SHA1 | Date | |
---|---|---|---|
071e94166b | |||
bf1e3ff8c7 | |||
8b99410b45 | |||
c9e87ef1bb | |||
b2d8739527 | |||
2816e7e40c | |||
708496ea7e | |||
29927c4922 | |||
b1b9bbf6e6 | |||
9892f4431d |
@ -579,7 +579,7 @@ private function generateActionButtons(Study $study): string
|
|||||||
foreach (WorklistGuard::worklistButtons($study) as $button) {
|
foreach (WorklistGuard::worklistButtons($study) as $button) {
|
||||||
switch ($button) {
|
switch ($button) {
|
||||||
case WorklistButton::StudyMetadata:
|
case WorklistButton::StudyMetadata:
|
||||||
$btns[] = $this->renderImageLink($study->hash, 'info.png', 'showStudy', 'Info');
|
$btns[] = $this->renderImageLink($study->hash, 'info.png', 'show-study', 'Info');
|
||||||
break;
|
break;
|
||||||
case WorklistButton::Assign:
|
case WorklistButton::Assign:
|
||||||
$btns[] = $this->renderImageLink($study->hash, 'assign.png', 'show-assign', 'Assign');
|
$btns[] = $this->renderImageLink($study->hash, 'assign.png', 'show-assign', 'Assign');
|
||||||
@ -587,6 +587,9 @@ private function generateActionButtons(Study $study): string
|
|||||||
case WorklistButton::Notes:
|
case WorklistButton::Notes:
|
||||||
$btns[] = $this->renderImageLink($study->hash, 'chat.png', 'show-notes', 'Chat');
|
$btns[] = $this->renderImageLink($study->hash, 'chat.png', 'show-notes', 'Chat');
|
||||||
break;
|
break;
|
||||||
|
case WorklistButton::Audit:
|
||||||
|
$btns[] = $this->renderImageLink($study->hash, 'audit.png', 'show-audit', 'Audit Trail');
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
37
app/Http/Controllers/Staff/AuditLogController.php
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Staff;
|
||||||
|
|
||||||
|
use App\Http\Controllers\HashedStudyControllerBase;
|
||||||
|
use App\Services\AuditTrail\Activity;
|
||||||
|
use App\Services\AuditTrail\Category;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
class AuditLogController extends HashedStudyControllerBase
|
||||||
|
{
|
||||||
|
public function popup()
|
||||||
|
{
|
||||||
|
$study = $this->getStudy();
|
||||||
|
|
||||||
|
$logs = DB::table('audit_logs')
|
||||||
|
->leftjoin('users', 'users.id', '=', 'audit_logs.user_id')
|
||||||
|
->leftjoin('user_agents', 'user_agents.id', '=', 'audit_logs.user_agent_id')
|
||||||
|
->selectRaw('users.display_name as user_name, audit_logs.created_at as log_time, audit_logs.*, user_agents.*')
|
||||||
|
->orderByDesc('audit_logs.id')
|
||||||
|
->where('audit_logs.study_id', $study->id)
|
||||||
|
->get();
|
||||||
|
|
||||||
|
$logs->each(function ($log) {
|
||||||
|
$log->category_name = Category::from($log->category)->name;
|
||||||
|
$log->activity_name = Str::of(Activity::from($log->activity)->name)->slug('-')->replace('-', ' ')->title();
|
||||||
|
if (filled($log->user_agent)) {
|
||||||
|
$log->browser = "Browser: {$log->browser_name}\nDevice: {$log->device_type}\nPlatform: {$log->platform}";
|
||||||
|
} else {
|
||||||
|
$log->browser = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return view('staff.audit.popup', compact('study', 'logs'));
|
||||||
|
}
|
||||||
|
}
|
@ -10,4 +10,5 @@ enum WorklistButton: string
|
|||||||
case Attachment = 'attachment';
|
case Attachment = 'attachment';
|
||||||
case Assign = 'assign';
|
case Assign = 'assign';
|
||||||
case Report = 'report';
|
case Report = 'report';
|
||||||
|
case Audit = 'audit';
|
||||||
}
|
}
|
||||||
|
@ -51,6 +51,7 @@ public static function worklistButtons(Study $study, User|int|null $usr = null):
|
|||||||
return collect([
|
return collect([
|
||||||
WorklistButton::StudyMetadata,
|
WorklistButton::StudyMetadata,
|
||||||
WorklistButton::Notes,
|
WorklistButton::Notes,
|
||||||
|
WorklistButton::Audit,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,6 +64,7 @@ public static function worklistButtons(Study $study, User|int|null $usr = null):
|
|||||||
if ($study->canAssignRad()) {
|
if ($study->canAssignRad()) {
|
||||||
$buttons->push(WorklistButton::Assign);
|
$buttons->push(WorklistButton::Assign);
|
||||||
}
|
}
|
||||||
|
$buttons->push(WorklistButton::Audit);
|
||||||
|
|
||||||
return $buttons;
|
return $buttons;
|
||||||
}
|
}
|
||||||
|
@ -2,47 +2,48 @@
|
|||||||
|
|
||||||
namespace App\Services\AuditTrail;
|
namespace App\Services\AuditTrail;
|
||||||
|
|
||||||
final class Activity
|
enum Activity: int
|
||||||
{
|
{
|
||||||
// studies
|
// studies
|
||||||
public const int Study_Open = 101;
|
case Study_Open = 101;
|
||||||
|
|
||||||
public const int Study_Metadata_View = 102;
|
case Study_Metadata_View = 102;
|
||||||
|
|
||||||
public const int Study_Metadata_Edit = 103;
|
case Study_Metadata_Edit = 103;
|
||||||
|
|
||||||
public const int Study_History_View = 104;
|
case Study_History_View = 104;
|
||||||
|
|
||||||
public const int Study_History_Update = 105;
|
case Study_History_Update = 105;
|
||||||
|
|
||||||
public const int Study_Create = 106;
|
case Study_Create = 106;
|
||||||
|
|
||||||
public const int Study_Update = 107;
|
case Study_Update = 107;
|
||||||
|
|
||||||
public const int Study_Archive = 108;
|
case Study_Archive = 108;
|
||||||
|
|
||||||
public const int Study_Delete = 109;
|
case Study_Delete = 109;
|
||||||
public const int Study_Lock = 110;
|
case Study_Lock = 110;
|
||||||
public const int Study_Unlock = 111;
|
case Study_Unlock = 111;
|
||||||
|
|
||||||
public const int Attachment_Upload = 112;
|
case Attachment_Upload = 112;
|
||||||
public const int Attachment_Download = 113;
|
case Attachment_Download = 113;
|
||||||
public const int Attachment_Delete = 114;
|
case Attachment_Delete = 114;
|
||||||
|
|
||||||
// report
|
// report
|
||||||
|
|
||||||
public const int Report_Save = 201;
|
case Report_Save = 201;
|
||||||
|
|
||||||
public const int Report_Delete = 202;
|
case Report_Delete = 202;
|
||||||
|
|
||||||
public const int Report_Finalize = 203;
|
case Report_Finalize = 203;
|
||||||
|
|
||||||
public const int User_Login = 301;
|
case User_Login = 301;
|
||||||
|
|
||||||
public const int User_Failed_Login = 302;
|
case User_Failed_Login = 302;
|
||||||
|
|
||||||
public const int User_Logout = 303;
|
case User_Logout = 303;
|
||||||
|
|
||||||
|
case Assign_Physician = 401;
|
||||||
|
case Unassign_Physician = 402;
|
||||||
|
|
||||||
public const int Assign_Physician = 401;
|
|
||||||
public const int Unassign_Physician = 402;
|
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
namespace App\Services\AuditTrail;
|
namespace App\Services\AuditTrail;
|
||||||
|
|
||||||
use App\Models\Study;
|
use App\Models\Study;
|
||||||
|
use App\Services\BrowserService;
|
||||||
use Illuminate\Contracts\Auth\Authenticatable;
|
use Illuminate\Contracts\Auth\Authenticatable;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
@ -12,15 +13,15 @@ class ActivityLogger
|
|||||||
|
|
||||||
private ?int $userId = null;
|
private ?int $userId = null;
|
||||||
|
|
||||||
private int $activity;
|
private Activity $activity;
|
||||||
|
|
||||||
private int $category;
|
private Category $category;
|
||||||
|
|
||||||
private ?string $url = null;
|
private ?string $url = null;
|
||||||
|
|
||||||
private ?string $notes = null;
|
private ?string $notes = null;
|
||||||
|
|
||||||
private ?string $userAgent = null;
|
private ?int $userAgentId = null;
|
||||||
|
|
||||||
private ?string $ipAddr = null;
|
private ?string $ipAddr = null;
|
||||||
|
|
||||||
@ -59,14 +60,14 @@ public function by(Authenticatable|int|null $user = null): static
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function did(int $activity): static
|
public function did(Activity $activity): static
|
||||||
{
|
{
|
||||||
$this->activity = $activity;
|
$this->activity = $activity;
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function category(int $category): static
|
public function category(Category $category): static
|
||||||
{
|
{
|
||||||
$this->category = $category;
|
$this->category = $category;
|
||||||
|
|
||||||
@ -96,7 +97,7 @@ public function notes(string $notes): static
|
|||||||
|
|
||||||
public function ua(?string $agent = null): static
|
public function ua(?string $agent = null): static
|
||||||
{
|
{
|
||||||
$this->userAgent = $agent ?? request()->userAgent();
|
$this->userAgentId = BrowserService::upsertBrowser($agent);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
@ -131,11 +132,11 @@ public function log(bool $initDefaults = true): bool
|
|||||||
->insert([
|
->insert([
|
||||||
'study_id' => $this->studyId,
|
'study_id' => $this->studyId,
|
||||||
'user_id' => $this->userId,
|
'user_id' => $this->userId,
|
||||||
'category' => $this->category,
|
'category' => $this->category->value,
|
||||||
'activity' => $this->activity,
|
'activity' => $this->activity->value,
|
||||||
'orthanc_uuid' => $this->orthancId,
|
'orthanc_uuid' => $this->orthancId,
|
||||||
'ip_addr' => $this->ipAddr,
|
'ip_addr' => $this->ipAddr,
|
||||||
'user_agent' => $this->userAgent,
|
'user_agent_id' => $this->userAgentId,
|
||||||
'url' => $this->url,
|
'url' => $this->url,
|
||||||
'notes' => $this->notes,
|
'notes' => $this->notes,
|
||||||
'created_at' => now(),
|
'created_at' => now(),
|
||||||
|
@ -2,13 +2,13 @@
|
|||||||
|
|
||||||
namespace App\Services\AuditTrail;
|
namespace App\Services\AuditTrail;
|
||||||
|
|
||||||
final class Category
|
enum Category: int
|
||||||
{
|
{
|
||||||
public const int GENERAL = 10;
|
case GENERAL = 10;
|
||||||
|
|
||||||
public const int SYSTEM = 20;
|
case SYSTEM = 20;
|
||||||
|
|
||||||
public const int PACS = 30;
|
case PACS = 30;
|
||||||
|
|
||||||
public const int AUTH = 40;
|
case AUTH = 40;
|
||||||
}
|
}
|
||||||
|
50
app/Services/BrowserService.php
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Services;
|
||||||
|
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
|
final class BrowserService
|
||||||
|
{
|
||||||
|
public static function userAgent()
|
||||||
|
{
|
||||||
|
return app('browser-detect')->userAgent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function findUA(?string $ua = null): ?int
|
||||||
|
{
|
||||||
|
$ua ??= app('browser-detect')->userAgent();
|
||||||
|
$row = DB::table('user_agents')->where('user_agent', $ua)->first(['id']);
|
||||||
|
if ($row) {
|
||||||
|
return $row->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function upsertBrowser(?string $ua = null): int
|
||||||
|
{
|
||||||
|
$browser = blank($ua)
|
||||||
|
? app('browser-detect')->detect()
|
||||||
|
: app('browser-detect')->parse($ua);
|
||||||
|
|
||||||
|
$id = self::findUA($browser->userAgent());
|
||||||
|
if ($id) {
|
||||||
|
return $id;
|
||||||
|
}
|
||||||
|
$params = [
|
||||||
|
'user_agent' => $browser->userAgent(),
|
||||||
|
'device_type' => $browser->deviceType(),
|
||||||
|
'device_family' => $browser->deviceFamily(),
|
||||||
|
'device_model' => $browser->deviceModel(),
|
||||||
|
'browser_name' => $browser->browserName(),
|
||||||
|
'browser_version' => $browser->browserVersion(),
|
||||||
|
'platform' => $browser->platformFamily(),
|
||||||
|
'platform_version' => $browser->platformVersion(),
|
||||||
|
'created_at' => now(),
|
||||||
|
];
|
||||||
|
DB::table('user_agents')->insert($params);
|
||||||
|
|
||||||
|
return self::findUA($browser->userAgent());
|
||||||
|
}
|
||||||
|
}
|
@ -13,6 +13,7 @@
|
|||||||
"culturegr/presenter": "^1.4",
|
"culturegr/presenter": "^1.4",
|
||||||
"filament/filament": "^3.2",
|
"filament/filament": "^3.2",
|
||||||
"hashids/hashids": "^5.0",
|
"hashids/hashids": "^5.0",
|
||||||
|
"hisorange/browser-detect": "^5.0",
|
||||||
"laravel/framework": "^11.31",
|
"laravel/framework": "^11.31",
|
||||||
"laravel/jetstream": "^5.3",
|
"laravel/jetstream": "^5.3",
|
||||||
"laravel/sanctum": "^4.0",
|
"laravel/sanctum": "^4.0",
|
||||||
|
21
config/browser-detect.php
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'cache' => [
|
||||||
|
/**
|
||||||
|
* Interval in seconds, as how long a result should be cached.
|
||||||
|
*/
|
||||||
|
'interval' => 10080,
|
||||||
|
/**
|
||||||
|
* Cache prefix, the user agent string will be hashed and appended at the end.
|
||||||
|
*/
|
||||||
|
'prefix' => 'bd4_',
|
||||||
|
],
|
||||||
|
'security' => [
|
||||||
|
/**
|
||||||
|
* Byte length where the header is cut off, if some attacker sends a long header
|
||||||
|
* then the library will make a cut this byte point.
|
||||||
|
*/
|
||||||
|
'max-header-length' => 2048,
|
||||||
|
],
|
||||||
|
];
|
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('user_agents', static function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->string('user_agent')->unique();
|
||||||
|
$table->string('device_type')->nullable();
|
||||||
|
$table->string('device_family')->nullable();
|
||||||
|
$table->string('device_model')->nullable();
|
||||||
|
$table->string('browser_name')->nullable();
|
||||||
|
$table->string('browser_version')->nullable();
|
||||||
|
$table->string('platform')->nullable();
|
||||||
|
$table->string('platform_version')->nullable();
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('user_agents');
|
||||||
|
}
|
||||||
|
};
|
@ -15,7 +15,7 @@ public function up(): void
|
|||||||
$table->unsignedTinyInteger('category');
|
$table->unsignedTinyInteger('category');
|
||||||
$table->unsignedSmallInteger('activity');
|
$table->unsignedSmallInteger('activity');
|
||||||
$table->ipAddress('ip_addr')->nullable();
|
$table->ipAddress('ip_addr')->nullable();
|
||||||
$table->string('user_agent')->nullable();
|
$table->unsignedBigInteger('user_agent_id')->nullable();
|
||||||
$table->string('orthanc_uuid')->nullable();
|
$table->string('orthanc_uuid')->nullable();
|
||||||
$table->text('url')->nullable();
|
$table->text('url')->nullable();
|
||||||
$table->text('notes')->nullable();
|
$table->text('notes')->nullable();
|
||||||
|
BIN
resources/imgs/assign_64.png
Normal file
After Width: | Height: | Size: 4.1 KiB |
BIN
resources/imgs/audit.png
Normal file
After Width: | Height: | Size: 837 B |
BIN
resources/imgs/audit_64.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
BIN
resources/imgs/browser_64.png
Normal file
After Width: | Height: | Size: 2.8 KiB |
BIN
resources/imgs/check-mark.png
Normal file
After Width: | Height: | Size: 5.7 KiB |
BIN
resources/imgs/details_64.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
resources/imgs/info_64.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
resources/imgs/profile_64.png
Normal file
After Width: | Height: | Size: 3.7 KiB |
BIN
resources/imgs/url_64.png
Normal file
After Width: | Height: | Size: 3.0 KiB |
30
resources/views/staff/audit/popup.blade.php
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<span class="badge rounded-circle p-2">
|
||||||
|
<img src="{{ asset('imgs/audit_64.png') }}" alt="">
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<table class="table table-sm table-striped">
|
||||||
|
@foreach ($logs as $log)
|
||||||
|
<tr>
|
||||||
|
<td>{{ $log->log_time }}</td>
|
||||||
|
<td>{{ $log->user_name }}</td>
|
||||||
|
<td>{{ $log->activity_name }}</td>
|
||||||
|
<td>{{ $log->notes }}</td>
|
||||||
|
<td>
|
||||||
|
@isset($log->ip_addr)
|
||||||
|
<a target="_blank" href="{{ 'https://ipinfo.io/'.$log->ip_addr }}">{{ $log->ip_addr }}</a>
|
||||||
|
@endisset
|
||||||
|
</td>
|
||||||
|
<td>{{ $log->category_name }}</td>
|
||||||
|
<td>
|
||||||
|
@isset($log->browser)
|
||||||
|
@include('_partials._img-tooltip', ['src' => asset('imgs/browser_64.png'), 'tip' => $log->browser, 'class' => 'msg-icon'])
|
||||||
|
@endisset
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
@isset($log->url)
|
||||||
|
@include('_partials._img-tooltip', ['src' => asset('imgs/url_64.png'), 'tip' => $log->url, 'class' => 'msg-icon'])
|
||||||
|
@endisset
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
</table>
|
@ -69,46 +69,12 @@
|
|||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
$(function () {
|
$(function () {
|
||||||
$('body').on('click', '.showStudy', function () {
|
@include('staff.worklist.partials._modal-js', ['selector' => '.show-study', 'url' => route('staff.studies.show'), 'type' => 'study'])
|
||||||
var study_id = $(this).data('id');
|
@include('staff.worklist.partials._modal-js', ['selector' => '.show-attach', 'url' => route('staff.studies.attach'), 'type' => 'attach'])
|
||||||
$.get("{{ route('staff.studies.show') }}", {hashid: study_id}, function (data) {
|
@include('staff.worklist.partials._modal-js', ['selector' => '.show-assign', 'url' => route('staff.assign.show'), 'type' => 'assign'])
|
||||||
$('#study-details').html(data);
|
@include('staff.worklist.partials._modal-js', ['selector' => '.show-reports', 'url' => route('staff.report.popup'), 'type' => 'report'])
|
||||||
$('#study-modal').modal('show');
|
@include('staff.worklist.partials._modal-js', ['selector' => '.show-audit', 'url' => route('staff.audit.popup'), 'type' => 'audit'])
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
$('body').on('click', '.show-attach', function () {
|
|
||||||
var study_id = $(this).data('id');
|
|
||||||
$.get("{{ route('staff.studies.attach') }}", {hashid: study_id}, function (data) {
|
|
||||||
$('#study-details').html(data);
|
|
||||||
$('#study-modal').modal('show');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
$('body').on('click', '.show-assign', function () {
|
|
||||||
var study_id = $(this).data('id');
|
|
||||||
$.get("{{ route('staff.assign.show') }}", {hashid: study_id}, function (data) {
|
|
||||||
$('#assign-details').html(data);
|
|
||||||
$('#assign-modal').modal('show');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
$('body').on('click', '.show-reports', function () {
|
|
||||||
var study_id = $(this).data('id');
|
|
||||||
$.get("{{ route('staff.report.popup') }}", {hashid: study_id}, function (data) {
|
|
||||||
$('#report-details').html(data);
|
|
||||||
$('#report-modal').modal('show');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
$(function () {
|
|
||||||
let _status, _study_from, _study_to, _receive_from, _receive_to, _assign_from, _assign_to, _read_from,
|
let _status, _study_from, _study_to, _receive_from, _receive_to, _assign_from, _assign_to, _read_from,
|
||||||
_read_to, _modality, _read_by = null;
|
_read_to, _modality, _read_by = null;
|
||||||
|
|
||||||
@ -116,19 +82,26 @@ function resetParams() {
|
|||||||
_status, _study_from, _study_to, _receive_from, _receive_to, _assign_from, _assign_to, _read_from, _read_to, _modality, _read_by = null;
|
_status, _study_from, _study_to, _receive_from, _receive_to, _assign_from, _assign_to, _read_from, _read_to, _modality, _read_by = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const strip_dash = str => str.replace(/^_/, '');
|
||||||
|
|
||||||
function generateUrl() {
|
function generateUrl() {
|
||||||
const url = new URL("{{ route('staff.worklist.index') }}");
|
const url = new URL("{{ route('staff.worklist.index') }}");
|
||||||
if (_status) url.searchParams.set('status', _status);
|
const params = {
|
||||||
if (_study_from) url.searchParams.set('study_from', _study_from);
|
_status,
|
||||||
if (_study_to) url.searchParams.set('study_to', _study_to);
|
_study_from,
|
||||||
if (_receive_from) url.searchParams.set('receive_from', _receive_from);
|
_study_to,
|
||||||
if (_receive_to) url.searchParams.set('receive_to', _receive_to);
|
_receive_from,
|
||||||
if (_assign_from) url.searchParams.set('assign_from', _assign_from);
|
_receive_to,
|
||||||
if (_assign_to) url.searchParams.set('assign_to', _assign_to);
|
_assign_from,
|
||||||
if (_read_from) url.searchParams.set('read_from', _read_from);
|
_assign_to,
|
||||||
if (_read_to) url.searchParams.set('read_to', _read_to);
|
_read_from,
|
||||||
if (_modality) url.searchParams.set('modality', _modality);
|
_read_to,
|
||||||
if (_read_by) url.searchParams.set('read_by', _read_by);
|
_modality,
|
||||||
|
_read_by
|
||||||
|
};
|
||||||
|
Object.keys(params).forEach(key => {
|
||||||
|
if (params[key]) url.searchParams.set(strip_dash(key), params[key]);
|
||||||
|
});
|
||||||
return url.toString();
|
return url.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,62 +224,10 @@ function formatDate(date) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="modal fade" id="study-modal" tabindex="-1" aria-labelledby="studyModalLabel" aria-hidden="true">
|
@include('staff.worklist.partials._modal', ['type' => 'study', 'title' => 'Study Information'])
|
||||||
<div class="modal-dialog modal-xl">
|
@include('staff.worklist.partials._modal', ['type' => 'attach', 'title' => 'Attached Docs'])
|
||||||
<div class="modal-content">
|
@include('staff.worklist.partials._modal', ['type' => 'assign', 'title' => 'Assign Radiologist'])
|
||||||
<div class="modal-header">
|
@include('staff.worklist.partials._modal', ['type' => 'report', 'title' => 'Reports'])
|
||||||
<h5 class="modal-title" id="studyModalLabel">Study Information</h5>
|
@include('staff.worklist.partials._modal', ['type' => 'audit', 'title' => 'Audit Log'])
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<div id="study-details"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="modal fade" id="attach-modal" tabindex="-1" aria-labelledby="label-attach" aria-hidden="true">
|
|
||||||
<div class="modal-dialog modal-lg">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h5 class="modal-title" id="label-attach">Attached Docs</h5>
|
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<div id="attach-details"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="modal fade" id="assign-modal" tabindex="-1" aria-labelledby="label-assign" aria-hidden="true">
|
|
||||||
<div class="modal-dialog modal-lg">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h5 class="modal-title" id="label-assign">Assign Radiologist</h5>
|
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<div id="assign-details"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="modal fade" id="report-modal" tabindex="-1" aria-labelledby="label-report" aria-hidden="true">
|
|
||||||
<div class="modal-dialog modal-lg">
|
|
||||||
<div class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h5 class="modal-title" id="label-report">Reports</h5>
|
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<div id="report-details"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
@endsection
|
@endsection
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
$('body').on('click', '{{ $selector }}', function () {
|
||||||
|
var study_id = $(this).data('id');
|
||||||
|
$.get("{{ $url }}", {hashid: study_id}, function (data) {
|
||||||
|
$('#{{ $type }}-body').html(data);
|
||||||
|
$('#{{ $type }}-modal').modal('show');
|
||||||
|
});
|
||||||
|
});
|
13
resources/views/staff/worklist/partials/_modal.blade.php
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<div class="modal fade" id="{{ $type }}-modal" tabindex="-1" aria-labelledby="label-{{ $type }}" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-xl">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="label-{{ $type }}">{{ $title }}</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<div id="{{ $type }}-body"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -5,6 +5,7 @@
|
|||||||
use App\Http\Controllers\SocialLoginController;
|
use App\Http\Controllers\SocialLoginController;
|
||||||
use App\Http\Controllers\Staff\AssignmentController;
|
use App\Http\Controllers\Staff\AssignmentController;
|
||||||
use App\Http\Controllers\Staff\AttachmentController;
|
use App\Http\Controllers\Staff\AttachmentController;
|
||||||
|
use App\Http\Controllers\Staff\AuditLogController;
|
||||||
use App\Http\Controllers\Staff\DicomViewerController;
|
use App\Http\Controllers\Staff\DicomViewerController;
|
||||||
use App\Http\Controllers\Staff\HistoryController;
|
use App\Http\Controllers\Staff\HistoryController;
|
||||||
use App\Http\Controllers\Staff\MetadataController;
|
use App\Http\Controllers\Staff\MetadataController;
|
||||||
@ -78,6 +79,10 @@
|
|||||||
Route::post('save', [ReportController::class, 'save'])->name('save');
|
Route::post('save', [ReportController::class, 'save'])->name('save');
|
||||||
Route::get('download/{uuid}/{format}', ReportDownloadController::class)->name('download');
|
Route::get('download/{uuid}/{format}', ReportDownloadController::class)->name('download');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Route::group(['prefix' => 'audit', 'as' => 'audit.'], function () {
|
||||||
|
Route::get('popup', [AuditLogController::class, 'popup'])->name('popup');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|