Compare commits

...

15 Commits

Author SHA1 Message Date
a7228b8a61 syntax 2025-01-30 01:40:17 +06:00
fac10cdc25 UI organization 2025-01-30 01:40:08 +06:00
d4c6e58e30 age - dob calculation 2025-01-30 01:35:07 +06:00
9e969df68b improved study parsing 2025-01-30 01:03:06 +06:00
a02adfc685 helper 2025-01-30 00:46:50 +06:00
ce28bb1422 minor 2025-01-30 00:39:19 +06:00
0fce30bd18 FIX - accepted 2025-01-30 00:39:14 +06:00
7a28b892f3 wip 2025-01-30 00:28:48 +06:00
60f32a6468 misc 2025-01-30 00:17:28 +06:00
4e2c1837ee wip 2025-01-30 00:09:14 +06:00
ea341ca8f4 fix relations 2025-01-29 23:53:11 +06:00
9f60cfb32e wip 2025-01-29 23:53:03 +06:00
97481e8473 Merge branch 'merge-statuses' 2025-01-29 23:30:39 +06:00
802f13c702 FIX #34 - merge 2025-01-29 23:30:22 +06:00
3a775c4f21 clean 2025-01-29 23:29:53 +06:00
20 changed files with 360 additions and 75 deletions

View File

@ -50,4 +50,19 @@ public static function worklist_stats(int $days, int $workflow_level)
return $rows; return $rows;
} }
public static function activeRads(): array
{
return cache()
->remember('active_rads',
now()->addMinutes(5),
fn () => DB::table('users')
->join('model_has_roles', 'users.id', '=', 'model_has_roles.model_id')
->join('roles', 'model_has_roles.role_id', '=', 'roles.id')
->where('roles.name', Role::Radiologist->value)
->where('users.is_active', true)
->orderBy('users.display_name')
->pluck('users.display_name', 'users.id')
->toArray());
}
} }

View File

@ -10,7 +10,7 @@ interface IUserStudyLister
{ {
public function setRadiologist(int $radiologist_id): self; public function setRadiologist(int $radiologist_id): self;
public function setWorkflowLevel(WorkflowLevel $status): self; public function setWorkflowLevel(WorkflowLevel $level): self;
public function setPerPage(int $size): self; public function setPerPage(int $size): self;

View File

@ -48,9 +48,9 @@ public function setRadiologist(int $radiologist_id): self
return $this; return $this;
} }
public function setWorkflowLevel(WorkflowLevel $status): self public function setWorkflowLevel(WorkflowLevel $level): self
{ {
$this->workflowLevel = $status; $this->workflowLevel = $level;
return $this; return $this;
} }
@ -71,7 +71,6 @@ public function get(?int $user_id = null): LengthAwarePaginator
$query = $this->applySearch($query); $query = $this->applySearch($query);
$query = $this->applyRadiologist($query); $query = $this->applyRadiologist($query);
$query = $this->applyWorkflowLevel($query); $query = $this->applyWorkflowLevel($query);
$query = $this->applyReportStatus($query);
$query = $this->applyArchived($query); $query = $this->applyArchived($query);
$query = $this->applyLocked($query); $query = $this->applyLocked($query);
$query = $this->applyDateFilters($query); $query = $this->applyDateFilters($query);

View File

@ -13,22 +13,27 @@
use Filament\Tables\Columns\IconColumn; use Filament\Tables\Columns\IconColumn;
use Filament\Tables\Columns\TextColumn; use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table; use Filament\Tables\Table;
use Illuminate\Support\Str;
use Ramsey\Uuid\Uuid; use Ramsey\Uuid\Uuid;
class DepartmentResource extends Resource class DepartmentResource extends Resource
{ {
protected static ?string $model = Department::class; protected static ?string $model = Department::class;
protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack'; protected static ?string $navigationIcon = 'heroicon-o-building-storefront';
public static function form(Form $form): Form public static function form(Form $form): Form
{ {
return $form return $form
->schema([ ->schema([
TextInput::make('guid') TextInput::make('guid')
->label('Unique ID')
->default(sprintf('DEP-%s', Str::of(Uuid::uuid4())->lower()))
->disabled()
->dehydrated()
->required() ->required()
->maxLength(40) ->maxLength(40)
->default(sprintf('FAC-%s', Uuid::uuid4())), ->unique(ignoreRecord: true),
Toggle::make('is_active') Toggle::make('is_active')
->required(), ->required(),
Select::make('organization_id') Select::make('organization_id')
@ -44,15 +49,10 @@ public static function table(Table $table): Table
{ {
return $table return $table
->columns([ ->columns([
TextColumn::make('guid') IconColumn::make('is_active')->boolean(),
->searchable(), TextColumn::make('name')->searchable(),
IconColumn::make('is_active') TextColumn::make('organization.name')->sortable(),
->boolean(), TextColumn::make('guid')->searchable(),
TextColumn::make('organization.name')
->numeric()
->sortable(),
TextColumn::make('name')
->searchable(),
TextColumn::make('created_at') TextColumn::make('created_at')
->dateTime() ->dateTime()
->sortable() ->sortable()

View File

@ -0,0 +1,126 @@
<?php
namespace App\Filament\Resources;
use App\DAL\Radiologists;
use App\Filament\Resources\DicomRoutingRuleResource\Pages;
use App\Models\DicomRoutingRule;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle;
use Filament\Forms\Form;
use Filament\Resources\Resource;
use Filament\Tables;
use Filament\Tables\Columns\IconColumn;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table;
class DicomRoutingRuleResource extends Resource
{
protected static ?string $model = DicomRoutingRule::class;
protected static ?string $modelLabel = 'Routing Rules';
protected static ?string $navigationIcon = 'heroicon-o-academic-cap';
public static function form(Form $form): Form
{
return $form
->schema([
Toggle::make('is_active')
->required(),
Select::make('organization_id')
->label('Organization')
->relationship('organization', 'name')
->required(),
Select::make('department_id')
->label('Department')
->relationship('department', 'name'),
Select::make('user_id')
->label('Radiologist')
->relationship('radiologist', 'display_name')
->options(Radiologists::activeRads()),
Select::make('assignment_panel_id')
->label('Panel')
->relationship('panel', 'name'),
TextInput::make('name')
->maxLength(255),
Textarea::make('condition')
->columnSpanFull()
->required(),
TextInput::make('priority')
->required()
->numeric()
->default(0),
]);
}
public static function table(Table $table): Table
{
return $table
->columns([
IconColumn::make('is_active')
->label('')
->boolean(),
TextColumn::make('name')
->searchable(),
TextColumn::make('condition')
->label('Rule')
->limit(20)
->searchable(),
TextColumn::make('priority')
->numeric()
->sortable(),
TextColumn::make('organization.name')
->label('Org')
->numeric()
->sortable(),
TextColumn::make('department.name')
->label('Dept')
->numeric()
->sortable(),
TextColumn::make('user_id')
->label('Rad')
->numeric()
->sortable(),
TextColumn::make('assignment_panel_id')
->label('Panel')
->numeric()
->sortable(),
TextColumn::make('created_at')
->dateTime()
->sortable()
->toggleable(isToggledHiddenByDefault: true),
TextColumn::make('updated_at')
->dateTime()
->sortable()
->toggleable(isToggledHiddenByDefault: true),
])
->filters([
//
])
->actions([
Tables\Actions\EditAction::make(),
])
->bulkActions([
Tables\Actions\BulkActionGroup::make([
Tables\Actions\DeleteBulkAction::make(),
]),
]);
}
public static function getRelations(): array
{
return [
//
];
}
public static function getPages(): array
{
return [
'index' => Pages\ListDicomRoutingRules::route('/'),
'create' => Pages\CreateDicomRoutingRule::route('/create'),
'edit' => Pages\EditDicomRoutingRule::route('/{record}/edit'),
];
}
}

View File

@ -0,0 +1,11 @@
<?php
namespace App\Filament\Resources\DicomRoutingRuleResource\Pages;
use App\Filament\Resources\DicomRoutingRuleResource;
use Filament\Resources\Pages\CreateRecord;
class CreateDicomRoutingRule extends CreateRecord
{
protected static string $resource = DicomRoutingRuleResource::class;
}

View File

@ -0,0 +1,19 @@
<?php
namespace App\Filament\Resources\DicomRoutingRuleResource\Pages;
use App\Filament\Resources\DicomRoutingRuleResource;
use Filament\Actions;
use Filament\Resources\Pages\EditRecord;
class EditDicomRoutingRule extends EditRecord
{
protected static string $resource = DicomRoutingRuleResource::class;
protected function getHeaderActions(): array
{
return [
Actions\DeleteAction::make(),
];
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace App\Filament\Resources\DicomRoutingRuleResource\Pages;
use App\Filament\Resources\DicomRoutingRuleResource;
use Filament\Actions;
use Filament\Resources\Pages\ListRecords;
class ListDicomRoutingRules extends ListRecords
{
protected static string $resource = DicomRoutingRuleResource::class;
protected function getHeaderActions(): array
{
return [
Actions\CreateAction::make(),
];
}
}

View File

@ -3,12 +3,14 @@
namespace App\Filament\Resources; namespace App\Filament\Resources;
use App\Filament\Resources\DicomServerResource\Pages; use App\Filament\Resources\DicomServerResource\Pages;
use App\Models\Department;
use App\Models\DicomServer; use App\Models\DicomServer;
use App\Services\GeoLocation; use App\Services\GeoLocation;
use Filament\Forms\Components\Select; use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput; use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle; use Filament\Forms\Components\Toggle;
use Filament\Forms\Form; use Filament\Forms\Form;
use Filament\Forms\Get;
use Filament\Resources\Resource; use Filament\Resources\Resource;
use Filament\Tables; use Filament\Tables;
use Filament\Tables\Columns\IconColumn; use Filament\Tables\Columns\IconColumn;
@ -30,6 +32,7 @@ public static function form(Form $form): Form
->required(), ->required(),
TextInput::make('server_name') TextInput::make('server_name')
->required() ->required()
->unique(ignoreRecord: true)
->maxLength(255), ->maxLength(255),
Select::make('geo_code') Select::make('geo_code')
->required() ->required()
@ -48,12 +51,11 @@ public static function form(Form $form): Form
->required() ->required()
->maxLength(255), ->maxLength(255),
TextInput::make('ae_title') TextInput::make('ae_title')
->maxLength(255), ->maxLength(24),
TextInput::make('username') TextInput::make('username')
->maxLength(255), ->maxLength(40),
TextInput::make('password') TextInput::make('password')
->password() ->maxLength(40),
->maxLength(255),
TextInput::make('wado_path') TextInput::make('wado_path')
->maxLength(255), ->maxLength(255),
TextInput::make('stone_viewer_path') TextInput::make('stone_viewer_path')
@ -62,10 +64,21 @@ public static function form(Form $form): Form
->maxLength(255), ->maxLength(255),
TextInput::make('meddream_viewer_path') TextInput::make('meddream_viewer_path')
->maxLength(255), ->maxLength(255),
TextInput::make('organization_id') Select::make('organization_id')
->numeric(), ->label('Organization')
TextInput::make('department_id') ->live()
->numeric(), ->relationship('organization', 'name'),
Select::make('department_id')
->label('Department')
->options(function (Get $get) {
$organization_id = $get('organization_id');
$result = [];
if ($organization_id != null) {
$result = Department::active()->organization($organization_id)->pluck('name', 'id')->toArray();
}
return $result;
}),
]); ]);
} }
@ -88,12 +101,13 @@ public static function table(Table $table): Table
->label('WADO'), ->label('WADO'),
TextColumn::make('dicom_port') TextColumn::make('dicom_port')
->label('DICOM'), ->label('DICOM'),
TextColumn::make('rest_api_endpoint')
->label('REST')
->searchable(),
TextColumn::make('ae_title') TextColumn::make('ae_title')
->label('AET') ->label('AET')
->searchable(), ->searchable(),
TextColumn::make('rest_api_endpoint')
->label('REST')
->url(fn (DicomServer $srv) => $srv->rest_api_endpoint, shouldOpenInNewTab: true)
->searchable(),
TextColumn::make('created_at') TextColumn::make('created_at')
->dateTime() ->dateTime()
->sortable() ->sortable()

View File

@ -4,6 +4,7 @@
use App\Filament\Resources\OrganizationResource\Pages; use App\Filament\Resources\OrganizationResource\Pages;
use App\Models\Organization; use App\Models\Organization;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput; use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Toggle; use Filament\Forms\Components\Toggle;
use Filament\Forms\Form; use Filament\Forms\Form;
@ -14,28 +15,34 @@
use Filament\Tables\Columns\IconColumn; use Filament\Tables\Columns\IconColumn;
use Filament\Tables\Columns\TextColumn; use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Table; use Filament\Tables\Table;
use Illuminate\Support\Str;
use Ramsey\Uuid\Uuid; use Ramsey\Uuid\Uuid;
class OrganizationResource extends Resource class OrganizationResource extends Resource
{ {
protected static ?string $model = Organization::class; protected static ?string $model = Organization::class;
protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack'; protected static ?string $navigationIcon = 'heroicon-o-building-office-2';
public static function form(Form $form): Form public static function form(Form $form): Form
{ {
return $form return $form
->schema([ ->schema([
TextInput::make('guid') TextInput::make('guid')
->label('Unique ID')
->default(sprintf('ORG-%s', Str::of(Uuid::uuid4())->lower()))
->disabled()
->dehydrated()
->required() ->required()
->maxLength(40) ->maxLength(40)
->default(sprintf('INS-%s', Uuid::uuid4())), ->unique(ignoreRecord: true),
TextInput::make('name')
->required()
->maxLength(255),
Toggle::make('is_active') Toggle::make('is_active')
->required(), ->required(),
TextInput::make('address') TextInput::make('name')
->required()
->unique(ignoreRecord: true)
->maxLength(255),
TextArea::make('address')
->maxLength(255), ->maxLength(255),
TextInput::make('logo_path') TextInput::make('logo_path')
->maxLength(255), ->maxLength(255),
@ -46,16 +53,9 @@ public static function table(Table $table): Table
{ {
return $table return $table
->columns([ ->columns([
TextColumn::make('guid') IconColumn::make('is_active')->boolean(),
->searchable(), TextColumn::make('name')->searchable(),
TextColumn::make('name') TextColumn::make('guid')->searchable(),
->searchable(),
IconColumn::make('is_active')
->boolean(),
TextColumn::make('address')
->searchable(),
TextColumn::make('logo_path')
->searchable(),
TextColumn::make('created_at') TextColumn::make('created_at')
->dateTime() ->dateTime()
->sortable() ->sortable()

View File

@ -7,7 +7,6 @@
use App\Models\User; use App\Models\User;
use App\Services\ACL\RoleService; use App\Services\ACL\RoleService;
use App\Services\TimezoneList; use App\Services\TimezoneList;
use Filament\Forms;
use Filament\Forms\Components\DateTimePicker; use Filament\Forms\Components\DateTimePicker;
use Filament\Forms\Components\FileUpload; use Filament\Forms\Components\FileUpload;
use Filament\Forms\Components\Select; use Filament\Forms\Components\Select;
@ -41,7 +40,7 @@ public static function form(Form $form): Form
return $form return $form
->schema([ ->schema([
Forms\Components\TextInput::make('guid') TextInput::make('guid')
->label('Unique ID') ->label('Unique ID')
->default(sprintf('USR-%s', Str::of(Uuid::uuid4())->lower())) ->default(sprintf('USR-%s', Str::of(Uuid::uuid4())->lower()))
->disabled() ->disabled()

View File

@ -21,7 +21,7 @@ public function rules(): array
'referring_physician_name' => ['nullable'], 'referring_physician_name' => ['nullable'],
'institution_name' => ['nullable'], 'institution_name' => ['nullable'],
'priority' => ['required', 'integer'], 'priority' => ['required', 'integer'],
'cancel_read' => ['nullable', 'boolean'], 'cancel_read' => ['accepted'],
/* /*
'referring_physician_id' => ['nullable', 'exists:users,id'], 'referring_physician_id' => ['nullable', 'exists:users,id'],

View File

@ -4,9 +4,8 @@
use App\Models\Traits\Active; use App\Models\Traits\Active;
use App\Models\Traits\HasOrganization; use App\Models\Traits\HasOrganization;
use Illuminate\Database\Eloquent\Model;
class Department extends Model class Department extends BaseModel
{ {
use Active; use Active;
use HasOrganization; use HasOrganization;

View File

@ -4,7 +4,6 @@
use App\Models\Traits\Active; use App\Models\Traits\Active;
use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasOne;
class DicomRoutingRule extends BaseModel class DicomRoutingRule extends BaseModel
{ {
@ -20,14 +19,14 @@ public function department(): BelongsTo
return $this->belongsTo(Department::class); return $this->belongsTo(Department::class);
} }
public function panel(): HasOne public function panel(): BelongsTo
{ {
return $this->hasOne(AssignmentPanel::class); return $this->belongsTo(AssignmentPanel::class, 'assignment_panel_id');
} }
public function radiologist(): HasOne public function radiologist(): BelongsTo
{ {
return $this->hasOne(User::class); return $this->belongsTo(User::class, 'user_id');
} }
protected function casts(): array protected function casts(): array

View File

@ -10,12 +10,12 @@ class DicomServer extends BaseModel
{ {
use Active; use Active;
public function institute(): BelongsTo public function organization(): BelongsTo
{ {
return $this->belongsTo(Organization::class); return $this->belongsTo(Organization::class);
} }
public function facility(): BelongsTo public function department(): BelongsTo
{ {
return $this->belongsTo(Department::class); return $this->belongsTo(Department::class);
} }

View File

@ -17,7 +17,8 @@
final class StudiesSync final class StudiesSync
{ {
public const string SYNC_AGENT = '$$_pacs_agent_$$'; public const SYNC_AGENT = '$$_pacs_agent_$$';
private Collection $study_ids; private Collection $study_ids;
private Collection $insert_queue; private Collection $insert_queue;
@ -147,6 +148,29 @@ public function transformData(mixed $orthanc_src): array
$inst_name = data_get($orthanc_src, 'MainDicomTags.InstitutionName'); $inst_name = data_get($orthanc_src, 'MainDicomTags.InstitutionName');
$patient_name = data_get($orthanc_src, 'PatientMainDicomTags.PatientName'); $patient_name = data_get($orthanc_src, 'PatientMainDicomTags.PatientName');
$name_parts = tokenizeString($patient_name);
$patient_age = data_get($orthanc_src, 'RequestedTags.PatientAge');
if (blank($patient_age) && ! empty($name_parts)) {
// try to get age from last part of patient name
$last = end($name_parts);
if (preg_match('/^\d+[YMD]$/i', $last)) {
$patient_age = $last;
/*
// sanitize patient name
array_pop($name_parts);
$patient_name = implode(' ', $name_parts);
*/
}
}
if ($patient_age !== null) {
$age = strtoupper(ltrim($patient_age, '0'));
if (strlen($age) > 1) {
$patient_age = $age;
}
}
// $patient_name = trim($patient_name, '.^ ');
$descr = $this->getStudyDescription($orthanc_src); $descr = $this->getStudyDescription($orthanc_src);
$study = [ $study = [
@ -160,7 +184,7 @@ public function transformData(mixed $orthanc_src): array
'patient_id' => data_get($orthanc_src, 'PatientMainDicomTags.PatientID'), 'patient_id' => data_get($orthanc_src, 'PatientMainDicomTags.PatientID'),
'patient_name' => $patient_name, 'patient_name' => $patient_name,
'patient_sex' => data_get($orthanc_src, 'PatientMainDicomTags.PatientSex'), 'patient_sex' => data_get($orthanc_src, 'PatientMainDicomTags.PatientSex'),
'patient_age' => data_get($orthanc_src, 'RequestedTags.PatientAge'), 'patient_age' => $patient_age,
'accession_number' => data_get($orthanc_src, 'MainDicomTags.AccessionNumber'), 'accession_number' => data_get($orthanc_src, 'MainDicomTags.AccessionNumber'),
'referring_physician_name' => data_get($orthanc_src, 'MainDicomTags.ReferringPhysicianName'), 'referring_physician_name' => data_get($orthanc_src, 'MainDicomTags.ReferringPhysicianName'),
@ -182,16 +206,40 @@ public function transformData(mixed $orthanc_src): array
$study['workflow_level'] = $stable_study $study['workflow_level'] = $stable_study
? WorkflowLevel::Unassigned->value ? WorkflowLevel::Unassigned->value
: WorkflowLevel::Received->value; : WorkflowLevel::Received->value;
$study['patient_birthdate'] = null;
$patient_birthdate = null;
$dob = data_get($orthanc_src, 'PatientMainDicomTags.PatientBirthDate'); $dob = data_get($orthanc_src, 'PatientMainDicomTags.PatientBirthDate');
if (filled($dob)) { if (filled($dob)) {
try { try {
$study['patient_birthdate'] = Carbon::parse($dob); $patient_birthdate = Carbon::parse($dob);
} catch (Exception $e) {
Log::error('Failed to parse PatientMainDicomTags.PatientBirthDate: {dob}', ['dob' => $dob, 'exception' => $e->getMessage()]);
}
}
if ($patient_birthdate == null && $patient_age !== null) {
try {
// $age = (int) preg_replace('/[^0-9]/', '', $patient_age);
$age_num = (int) filter_var($patient_age, FILTER_SANITIZE_NUMBER_INT);
$now = now();
switch (strtoupper(substr($patient_age, -1))) {
case 'Y':
$patient_birthdate = $now->subYears($age_num);
break;
case 'M':
$patient_birthdate = $now->subMonths($age_num);
break;
case 'D':
$patient_birthdate = $now->subDays($age_num);
break;
}
} catch (Exception) { } catch (Exception) {
Log::error('Failed to parse PatientMainDicomTags.PatientBirthDate: {dob}', ['dob' => $dob]); Log::error('Failed to parse patient_age: {age}', ['age' => $patient_age]);
} }
} }
$study['patient_birthdate'] = $patient_birthdate;
// check for priority in patient name or description // check for priority in patient name or description
if (preg_match('/\b(urgent|stat)\b/i', implode(' ', [$descr, $patient_name]))) { if (preg_match('/\b(urgent|stat)\b/i', implode(' ', [$descr, $patient_name]))) {
$this->setValue($study, 'priority', Priority::Stat->value); $this->setValue($study, 'priority', Priority::Stat->value);
@ -283,7 +331,9 @@ private function getStudyDicomTags(string $study_uuid): array
} }
// randomly sample few instances for tags collection // randomly sample few instances for tags collection
$selectedInstances = count($instances) <= $this->maxInstances ? $instances : array_rand(array_flip($instances), $this->maxInstances); $selectedInstances = count($instances) <= $this->maxInstances
? $instances
: array_intersect_key($instances, array_flip(array_rand($instances, $this->maxInstances)));
$tags = collect(); $tags = collect();
foreach ($selectedInstances as $instance) { foreach ($selectedInstances as $instance) {

View File

@ -123,11 +123,13 @@ function me(User|int|null $user = null): User
{ {
if (is_int($user) && $user > 0) { if (is_int($user) && $user > 0) {
return User::find($user); return User::find($user);
} elseif ($user instanceof User) {
return $user;
} else {
return auth()->user();
} }
if ($user instanceof User) {
return $user;
}
return auth()->user();
} }
} }
@ -141,3 +143,10 @@ function formatTitle(string $str): string
->toString(); ->toString();
} }
} }
if (! function_exists('tokenizeString')) {
function tokenizeString(string $str): array
{
return preg_split('/\W+/', trim($str), -1, PREG_SPLIT_NO_EMPTY);
}
}

View File

@ -10,7 +10,7 @@ public function up(): void
{ {
Schema::create('organizations', static function (Blueprint $table) { Schema::create('organizations', static function (Blueprint $table) {
$table->id(); $table->id();
$table->string('guid', 40)->unique()->index()->default(DB::raw("concat('INS-', gen_random_uuid())")); $table->string('guid', 40)->unique()->index()->default(DB::raw("concat('ORG-', gen_random_uuid())"));
$table->string('name')->unique(); $table->string('name')->unique();
$table->boolean('is_active')->default(false); $table->boolean('is_active')->default(false);
$table->string('address')->nullable(); $table->string('address')->nullable();

View File

@ -11,7 +11,7 @@ public function up(): void
{ {
Schema::create('departments', static function (Blueprint $table) { Schema::create('departments', static function (Blueprint $table) {
$table->id(); $table->id();
$table->string('guid', 40)->unique()->index()->default(DB::raw("concat('FAC-', gen_random_uuid())")); $table->string('guid', 40)->unique()->index()->default(DB::raw("concat('DEP-', gen_random_uuid())"));
$table->boolean('is_active')->default(false)->index(); $table->boolean('is_active')->default(false)->index();
$table->foreignIdFor(Organization::class)->constrained()->cascadeOnDelete(); $table->foreignIdFor(Organization::class)->constrained()->cascadeOnDelete();
$table->string('name'); $table->string('name');

View File

@ -32,13 +32,25 @@
<h5 class="mb-0">Patient Information</h5> <h5 class="mb-0">Patient Information</h5>
</div> </div>
<div class="card-body"> <div class="card-body">
@include('staff.meta.partials._text', ['name' => 'patient_id', 'label' => 'Patient ID', 'value' => $study->patient_id])
@include('staff.meta.partials._text', ['name' => 'patient_name', 'label' => 'Patient Name', 'value' => $study->patient_name]) @include('staff.meta.partials._text', ['name' => 'patient_name', 'label' => 'Patient Name', 'value' => $study->patient_name])
@include('staff.meta.partials._text', ['name' => 'patient_sex', 'label' => 'Sex', 'value' => $study->patient_sex]) <div class="row">
@include('staff.meta.partials._date', ['name' => 'patient_birthdate', 'label' => 'Birth Date', 'value' => $study->patient_birthdate?->toDateString()]) <div class="col-6">
@include('staff.meta.partials._text', ['name' => 'patient_id', 'label' => 'Patient ID', 'value' => $study->patient_id])
</div>
<div class="col-6">
@include('staff.meta.partials._text', ['name' => 'accession_number', 'label' => 'Accession number', 'value' => $study->accession_number]) @include('staff.meta.partials._text', ['name' => 'accession_number', 'label' => 'Accession number', 'value' => $study->accession_number])
</div> </div>
</div> </div>
<div class="row">
<div class="col-6">
@include('staff.meta.partials._text', ['name' => 'patient_sex', 'label' => 'Sex', 'value' => $study->patient_sex])
</div>
<div class="col-6">
@include('staff.meta.partials._date', ['name' => 'patient_birthdate', 'label' => 'Birth Date', 'value' => $study->patient_birthdate?->toDateString()])
</div>
</div>
</div>
</div>
</div> </div>
<div class="col-xl"> <div class="col-xl">
@ -47,10 +59,24 @@
<h5 class="mb-0">Study Information</h5> <h5 class="mb-0">Study Information</h5>
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="row">
<div class="col-6">
@include('staff.meta.partials._text', ['name' => 'study_description', 'label' => 'Study Description', 'value' => $study->study_description]) @include('staff.meta.partials._text', ['name' => 'study_description', 'label' => 'Study Description', 'value' => $study->study_description])
</div>
<div class="col-6">
@include('staff.meta.partials._text', ['name' => 'body_part_examined', 'label' => 'Body Part Examined', 'value' => $study->body_part_examined]) @include('staff.meta.partials._text', ['name' => 'body_part_examined', 'label' => 'Body Part Examined', 'value' => $study->body_part_examined])
</div>
</div>
<div class="row">
<div class="col-6">
@include('staff.meta.partials._text', ['name' => 'institution_name', 'label' => 'Institution', 'value' => $study->institution_name]) @include('staff.meta.partials._text', ['name' => 'institution_name', 'label' => 'Institution', 'value' => $study->institution_name])
</div>
<div class="col-6">
@include('staff.meta.partials._text', ['name' => 'referring_physician_name', 'label' => 'Referring Physician', 'value' => $study->referring_physician_name]) @include('staff.meta.partials._text', ['name' => 'referring_physician_name', 'label' => 'Referring Physician', 'value' => $study->referring_physician_name])
</div>
</div>
<label class="form-check-label">Priority</label> <label class="form-check-label">Priority</label>
<div class="col my-2"> <div class="col my-2">