From 2d96978c38ddeb0c47f4b046b00ab10c92f6ea68 Mon Sep 17 00:00:00 2001 From: mehrab esmailnia Date: Wed, 26 Aug 2026 17:40:45 +0330 Subject: [PATCH] feat: Implement Fast Request feature with file upload and request management --- app/Enums/DocumentModalityEnum.php | 14 + .../Client/Doctor/CaseController.php | 20 +- .../Client/Doctor/DashboardController.php | 17 +- .../Client/Doctor/DocumentController.php | 20 +- .../Client/FastRequestController.php | 51 ++++ .../Requests/Client/FastRequestRequest.php | 41 +++ app/Http/Resources/Client/CaseResource.php | 41 +++ app/Http/Resources/Client/DoctorResource.php | 19 ++ .../Resources/Client/FastRequestResource.php | 21 ++ .../Resources/Client/PetArchiveResource.php | 19 ++ app/Services/Document/CaseService.php | 27 ++ app/Services/Document/FastRequestService.php | 79 ++++++ database/seeders/DocumentSeeder.php | 103 +++++++ database/seeders/FastRequestSeeder.php | 127 +++++++++ resources/css/Client/ui/fast-request.css | 112 ++++++++ resources/css/client.css | 1 + .../js/Client/Pages/Dashboard/Cases/Index.vue | 39 ++- .../js/Client/Pages/Dashboard/Dashboard.vue | 108 ++----- .../Pages/Dashboard/Documents/Index.vue | 160 +---------- .../components/dashboard/FastRequestModal.vue | 160 +++++------ .../dashboard/fastRequest/ArchiveCase.vue | 83 +++--- .../dashboard/fastRequest/CaseNumber.vue | 54 ++-- .../dashboard/fastRequest/FileUploadBox.vue | 68 +++++ .../dashboard/fastRequest/RequestCase.vue | 194 ++++--------- .../dashboard/fastRequest/RequestDetails.vue | 267 ++---------------- .../dashboard/fastRequest/RequestDoctor.vue | 46 +-- .../dashboard/fastRequest/RequestReview.vue | 23 +- .../dashboard/fastRequest/RequestType.vue | 26 +- resources/js/Client/data/casesTableColumns.js | 39 +++ .../js/Client/data/fastRequestOptions.js | 44 +++ resources/js/Client/store/FastRequest.js | 106 ++++++- resources/js/Client/utils/fileUtils.js | 13 + routes/web.php | 4 + 33 files changed, 1294 insertions(+), 852 deletions(-) create mode 100644 app/Enums/DocumentModalityEnum.php create mode 100644 app/Http/Controllers/Client/FastRequestController.php create mode 100644 app/Http/Requests/Client/FastRequestRequest.php create mode 100644 app/Http/Resources/Client/CaseResource.php create mode 100644 app/Http/Resources/Client/DoctorResource.php create mode 100644 app/Http/Resources/Client/FastRequestResource.php create mode 100644 app/Http/Resources/Client/PetArchiveResource.php create mode 100644 app/Services/Document/CaseService.php create mode 100644 app/Services/Document/FastRequestService.php create mode 100644 database/seeders/DocumentSeeder.php create mode 100644 database/seeders/FastRequestSeeder.php create mode 100644 resources/css/Client/ui/fast-request.css create mode 100644 resources/js/Client/components/dashboard/fastRequest/FileUploadBox.vue create mode 100644 resources/js/Client/data/casesTableColumns.js create mode 100644 resources/js/Client/data/fastRequestOptions.js create mode 100644 resources/js/Client/utils/fileUtils.js diff --git a/app/Enums/DocumentModalityEnum.php b/app/Enums/DocumentModalityEnum.php new file mode 100644 index 0000000..9a9cb4b --- /dev/null +++ b/app/Enums/DocumentModalityEnum.php @@ -0,0 +1,14 @@ +caseService = $caseService; + } + public function index() { - return Inertia::client('Dashboard/Cases/Index'); + $cases = $this->caseService->pendingForUser(Auth::user()) + ->paginate(10) + ->withQueryString() + ->onEachSide(1); + + return Inertia::client('Dashboard/Cases/Index', [ + 'cases' => CaseResource::collection($cases), + ]); } } diff --git a/app/Http/Controllers/Client/Doctor/DashboardController.php b/app/Http/Controllers/Client/Doctor/DashboardController.php index 9e2b814..b7f6a78 100644 --- a/app/Http/Controllers/Client/Doctor/DashboardController.php +++ b/app/Http/Controllers/Client/Doctor/DashboardController.php @@ -3,13 +3,26 @@ namespace App\Http\Controllers\Client\Doctor; use App\Http\Controllers\Controller; -use Illuminate\Http\Request; +use App\Http\Resources\Client\CaseResource; +use App\Services\Document\CaseService; +use Illuminate\Support\Facades\Auth; use Inertia\Inertia; class DashboardController extends Controller { + protected CaseService $caseService; + + public function __construct(CaseService $caseService) + { + $this->caseService = $caseService; + } + public function index() { - return Inertia::client('Dashboard/Dashboard'); + $cases = $this->caseService->pendingForUser(Auth::user())->take(5)->get(); + + return Inertia::client('Dashboard/Dashboard', [ + 'cases' => CaseResource::collection($cases), + ]); } } diff --git a/app/Http/Controllers/Client/Doctor/DocumentController.php b/app/Http/Controllers/Client/Doctor/DocumentController.php index a332d67..c00831c 100644 --- a/app/Http/Controllers/Client/Doctor/DocumentController.php +++ b/app/Http/Controllers/Client/Doctor/DocumentController.php @@ -3,15 +3,31 @@ namespace App\Http\Controllers\Client\Doctor; use App\Http\Controllers\Controller; +use App\Http\Resources\Client\CaseResource; use App\Models\Document; -use Illuminate\Http\Request; +use App\Services\Document\CaseService; +use Illuminate\Support\Facades\Auth; use Inertia\Inertia; class DocumentController extends Controller { + protected CaseService $caseService; + + public function __construct(CaseService $caseService) + { + $this->caseService = $caseService; + } + public function index() { - return Inertia::client('Dashboard/Documents/Index'); + $documents = $this->caseService->pendingForUser(Auth::user()) + ->paginate(10) + ->withQueryString() + ->onEachSide(1); + + return Inertia::client('Dashboard/Documents/Index', [ + 'documents' => CaseResource::collection($documents), + ]); } public function show(Document $document) diff --git a/app/Http/Controllers/Client/FastRequestController.php b/app/Http/Controllers/Client/FastRequestController.php new file mode 100644 index 0000000..7487670 --- /dev/null +++ b/app/Http/Controllers/Client/FastRequestController.php @@ -0,0 +1,51 @@ +fastRequestService = $fastRequestService; + } + + public function create() + { + $pets = Auth::user()->pets()->latest()->get(); + + $doctors = User::where('level', UserEnum::Doctor->value) + ->latest() + ->get(); + + return response()->json([ + 'pets' => PetArchiveResource::collection($pets), + 'doctors' => DoctorResource::collection($doctors), + ]); + } + + public function store(FastRequestRequest $request) + { + $document = $this->fastRequestService->create( + Auth::user(), + $request->validated(), + $request->file('history_files', []), + $request->file('scan_files', []) + ); + + return response()->json([ + 'document' => new FastRequestResource($document->load('pet')), + ]); + } +} diff --git a/app/Http/Requests/Client/FastRequestRequest.php b/app/Http/Requests/Client/FastRequestRequest.php new file mode 100644 index 0000000..bb1b564 --- /dev/null +++ b/app/Http/Requests/Client/FastRequestRequest.php @@ -0,0 +1,41 @@ + ['required', 'string', Rule::in(DocumentModalityEnum::values())], + 'priority' => ['required', 'string', Rule::in(DocumentPriorityEnum::keys())], + 'note' => ['nullable', 'string', 'max:1000'], + 'pet_id' => ['nullable', 'exists:pets,id'], + 'pet' => ['required_without:pet_id', 'array'], + 'pet.name' => ['required_without:pet_id', 'nullable', 'string', 'max:255'], + 'pet.type' => ['required_without:pet_id', 'nullable', 'string', Rule::in(PetTypeEnum::keys())], + 'pet.gender' => ['nullable', 'string', Rule::in(PetGenderEnum::keys())], + 'pet.breed' => ['nullable', 'string', 'max:255'], + 'pet.age' => ['nullable', 'numeric', 'min:0', 'max:100'], + 'pet.weight' => ['nullable', 'numeric', 'min:0', 'max:1000'], + 'pet.sterilized' => ['nullable', 'boolean'], + 'doctor_id' => ['nullable', 'exists:users,id'], + 'history_files' => ['nullable', 'array'], + 'history_files.*' => ['file', 'image', 'max:10240'], + 'scan_files' => ['nullable', 'array'], + 'scan_files.*' => ['file', 'image', 'max:10240'], + ]; + } +} diff --git a/app/Http/Resources/Client/CaseResource.php b/app/Http/Resources/Client/CaseResource.php new file mode 100644 index 0000000..420832a --- /dev/null +++ b/app/Http/Resources/Client/CaseResource.php @@ -0,0 +1,41 @@ + $this->id, + 'code' => $this->code, + 'status' => $this->status, + 'modality' => $this->modality, + 'requester' => optional($this->requester)->fullname, + 'submitted_at' => $this->submitted_at ? verta($this->submitted_at)->format('%Y/%m/%d - H:i') : null, + 'processing_deadline' => $this->deadlineLabel(), + ]; + } + + protected function deadlineLabel(): ?string + { + if (!$this->processing_deadline) { + return null; + } + + if (now()->greaterThanOrEqualTo($this->processing_deadline)) { + return 'اتمام فرصت رسیدگی'; + } + + $minutes = (int) now()->diffInMinutes($this->processing_deadline, true); + + if ($minutes < 60) { + return $minutes . ' دقیقه'; + } + + return 'کمتر از ' . (int) ceil($minutes / 60) . ' ساعت'; + } +} diff --git a/app/Http/Resources/Client/DoctorResource.php b/app/Http/Resources/Client/DoctorResource.php new file mode 100644 index 0000000..eb979cf --- /dev/null +++ b/app/Http/Resources/Client/DoctorResource.php @@ -0,0 +1,19 @@ + $this->id, + 'fullname' => $this->fullname, + 'specialty' => $this->specialty, + 'profile_url' => $this->profile_url, + ]; + } +} diff --git a/app/Http/Resources/Client/FastRequestResource.php b/app/Http/Resources/Client/FastRequestResource.php new file mode 100644 index 0000000..024fa17 --- /dev/null +++ b/app/Http/Resources/Client/FastRequestResource.php @@ -0,0 +1,21 @@ + $this->id, + 'code' => $this->code, + 'modality' => $this->modality, + 'status' => $this->status, + 'priority' => $this->priority, + 'pet' => new PetArchiveResource($this->whenLoaded('pet')), + ]; + } +} diff --git a/app/Http/Resources/Client/PetArchiveResource.php b/app/Http/Resources/Client/PetArchiveResource.php new file mode 100644 index 0000000..317b244 --- /dev/null +++ b/app/Http/Resources/Client/PetArchiveResource.php @@ -0,0 +1,19 @@ + $this->id, + 'name' => $this->name, + 'type' => $this->type, + 'breed' => $this->breed, + ]; + } +} diff --git a/app/Services/Document/CaseService.php b/app/Services/Document/CaseService.php new file mode 100644 index 0000000..c6bf93d --- /dev/null +++ b/app/Services/Document/CaseService.php @@ -0,0 +1,27 @@ +whereIn('status', [ + DocumentStatusEnum::Registered->value, + DocumentStatusEnum::InProgress->value, + ]) + ->where(function ($query) use ($user) { + $query->where('requester_id', $user->id) + ->orWhereHas('assignments', function ($assignmentQuery) use ($user) { + $assignmentQuery->where('user_id', $user->id); + }); + }) + ->latest('submitted_at'); + } +} diff --git a/app/Services/Document/FastRequestService.php b/app/Services/Document/FastRequestService.php new file mode 100644 index 0000000..fe1786a --- /dev/null +++ b/app/Services/Document/FastRequestService.php @@ -0,0 +1,79 @@ +fileStorageService = $fileStorageService; + } + + public function create(User $requester, array $data, array $historyFiles = [], array $scanFiles = []): Document + { + return DB::transaction(function () use ($requester, $data, $historyFiles, $scanFiles) { + $pet = $this->resolvePet($requester, $data); + + $document = Document::create([ + 'modality' => $data['modality'], + 'priority' => DocumentPriorityEnum::fromName($data['priority'])->value, + 'note' => $data['note'] ?? null, + 'submitted_at' => now(), + 'pet_id' => $pet->id, + 'requester_id' => $requester->id, + ]); + + $this->attachFiles($document, $historyFiles, MediaCollectionEnum::History); + $this->attachFiles($document, $scanFiles, MediaCollectionEnum::Primary); + + if (!empty($data['doctor_id'])) { + $document->assignments()->create([ + 'user_id' => $data['doctor_id'], + 'role' => 'رادیولوژیست', + ]); + } + + return $document; + }); + } + + protected function resolvePet(User $requester, array $data): Pet + { + if (!empty($data['pet_id'])) { + return Pet::findOrFail($data['pet_id']); + } + + $pet = $data['pet']; + + return Pet::create([ + 'name' => $pet['name'], + 'type' => PetTypeEnum::fromName($pet['type'])->value, + 'gender' => isset($pet['gender']) ? PetGenderEnum::fromName($pet['gender'])->value : PetGenderEnum::Unknown->value, + 'breed' => $pet['breed'] ?? null, + 'weight' => $pet['weight'] ?? null, + 'sterilized' => $pet['sterilized'] ?? null, + 'birthday' => isset($pet['age']) ? now()->subYears((int) $pet['age'])->startOfDay() : null, + 'owner_id' => $requester->id, + ]); + } + + protected function attachFiles(Document $document, array $files, MediaCollectionEnum $collection): void + { + foreach ($files as $file) { + $medium = $this->fileStorageService->storeFile($file); + $document->media()->attach($medium->id, ['collection' => $collection->value]); + } + } +} diff --git a/database/seeders/DocumentSeeder.php b/database/seeders/DocumentSeeder.php new file mode 100644 index 0000000..895a447 --- /dev/null +++ b/database/seeders/DocumentSeeder.php @@ -0,0 +1,103 @@ +value)->get(); + $owners = User::whereIn('level', [UserEnum::Admin->value, UserEnum::Supervisor->value, UserEnum::Vet->value])->get(); + + if ($doctors->isEmpty() || $owners->isEmpty()) { + $this->call(FastRequestSeeder::class); + $doctors = User::where('level', UserEnum::Doctor->value)->get(); + $owners = User::whereIn('level', [UserEnum::Admin->value, UserEnum::Supervisor->value, UserEnum::Vet->value])->get(); + } + + $notes = [ + 'لنگش اندام حرکتی جلو از دو روز پیش، درخواست گرافی مفصل شانه و آرنج.', + 'استفراغ مکرر و بی‌اشتهایی، احتمال جسم خارجی در دستگاه گوارش. سونوگرافی شکم انجام شود.', + 'سرفه مزمن و تنگی نفس، درخواست گرافی قفسه سینه در دو نما.', + 'تشنج ناگهانی بدون سابقه قبلی، بررسی جمجمه از نظر ضایعه فضاگیر.', + 'سوفل قلبی درجه سه در سمع، درخواست اکوکاردیوگرافی کامل.', + 'ضعف پیش‌رونده اندام حرکتی عقب، بررسی ستون فقرات کمری از نظر دیسک.', + 'تروما به دنبال تصادف با خودرو، بررسی لگن و اندام حرکتی از نظر شکستگی.', + 'اتساع شکم و درد در لمس، درخواست سونوگرافی اورژانسی جهت رد پیچ‌خوردگی معده.', + 'توده قابل لمس در ناحیه گردن، بررسی ابعاد و منشا توده با سونوگرافی.', + 'کج شدن سر و نیستاگموس، بررسی گوش میانی و داخلی با سی‌تی‌اسکن.', + 'پرنوشی و پرادراری از یک هفته پیش، بررسی کلیه‌ها و مثانه.', + 'خونریزی از بینی و عطسه مکرر، بررسی حفره بینی از نظر جسم خارجی.', + 'عدم تحمل وزن روی اندام عقب چپ، احتمال پارگی رباط صلیبی. گرافی زانو انجام شود.', + 'کاهش وزن مزمن با وجود اشتهای طبیعی، سونوگرافی کامل محوطه بطنی.', + 'دیسترس تنفسی حاد، بررسی پرده جنب از نظر تجمع مایع.', + ]; + + $rejectionReasons = [ + 'کیفیت تصاویر ارسالی برای تفسیر کافی نیست، لطفا مجددا تصویربرداری شود.', + 'اطلاعات بالینی ناقص است، شرح حال کامل بیمار ثبت شود.', + 'نمای درخواستی با شکایت بالینی همخوانی ندارد.', + ]; + + $statuses = [ + DocumentStatusEnum::Registered, + DocumentStatusEnum::InProgress, + DocumentStatusEnum::Registered, + DocumentStatusEnum::InProgress, + DocumentStatusEnum::Registered, + DocumentStatusEnum::Completed, + DocumentStatusEnum::Registered, + DocumentStatusEnum::InProgress, + DocumentStatusEnum::Rejected, + DocumentStatusEnum::Registered, + ]; + + $deadlineMinutes = [-180, 25, 53, 240, 420, null, 700, 1100, 1400, -30, 1900, 2600]; + $modalities = DocumentModalityEnum::cases(); + + foreach ($owners as $owner) { + $pets = $owner->pets; + if ($pets->isEmpty()) { + continue; + } + + $missing = $this->documentsPerOwner - Document::where('requester_id', $owner->id)->count(); + + for ($i = 0; $i < $missing; $i++) { + $status = $statuses[$i % count($statuses)]; + $deadline = $deadlineMinutes[$i % count($deadlineMinutes)]; + $submittedAt = now()->subDays(rand(0, 20))->subMinutes(rand(0, 1440)); + + $document = Document::create([ + 'modality' => $modalities[$i % count($modalities)]->value, + 'priority' => ($i % 4 === 0 ? DocumentPriorityEnum::High : DocumentPriorityEnum::Medium)->value, + 'status' => $status->value, + 'note' => $notes[$i % count($notes)], + 'submitted_at' => $submittedAt, + 'scan_date' => $status === DocumentStatusEnum::Registered ? null : $submittedAt->copy()->addHours(rand(1, 12)), + 'processing_deadline' => $deadline !== null ? now()->addMinutes($deadline) : null, + 'finalized_at' => $status === DocumentStatusEnum::Completed ? $submittedAt->copy()->addDays(rand(1, 3)) : null, + 'finalized_by' => $status === DocumentStatusEnum::Completed ? $doctors->random()->id : null, + 'rejection_reason' => $status === DocumentStatusEnum::Rejected ? $rejectionReasons[$i % count($rejectionReasons)] : null, + 'pet_id' => $pets->random()->id, + 'requester_id' => $owner->id, + ]); + + $document->assignments()->create([ + 'user_id' => $doctors->random()->id, + 'role' => 'رادیولوژیست', + ]); + } + } + } +} diff --git a/database/seeders/FastRequestSeeder.php b/database/seeders/FastRequestSeeder.php new file mode 100644 index 0000000..3a09c33 --- /dev/null +++ b/database/seeders/FastRequestSeeder.php @@ -0,0 +1,127 @@ +seedDoctors(); + $this->seedPets(); + $this->seedDocuments($doctors); + } + + protected function seedDoctors() + { + $doctors = [ + ['email' => 'dr.hosseini@radical.dev', 'fullname' => 'دکتر حسین حسینی', 'specialty' => 'متخصص جراحی پوست و مو'], + ['email' => 'dr.amin@radical.dev', 'fullname' => 'دکتر امین حسینی', 'specialty' => 'متخصص و رادیولوژیست'], + ['email' => 'dr.mahdipour@radical.dev', 'fullname' => 'دکتر مریم مهدی‌پور', 'specialty' => 'متخصص تصویربرداری'], + ['email' => 'dr.golian@radical.dev', 'fullname' => 'دکتر رضا گلیان', 'specialty' => 'متخصص داخلی دام کوچک'], + ['email' => 'dr.karimi@radical.dev', 'fullname' => 'دکتر سارا کریمی', 'specialty' => 'متخصص سونوگرافی و اکو'], + ['email' => 'dr.nazari@radical.dev', 'fullname' => 'دکتر بهرام نظری', 'specialty' => 'متخصص ارتوپدی دام کوچک'], + ]; + + return collect($doctors)->map(function ($doctor) { + return User::firstOrCreate( + ['username' => explode('@', $doctor['email'])[0]], + User::factory()->raw([ + 'email' => $doctor['email'], + 'level' => UserEnum::Doctor->value, + 'fullname' => $doctor['fullname'], + 'specialty' => $doctor['specialty'], + ]) + ); + }); + } + + protected function seedPets(): void + { + $pets = [ + ['name' => 'مایلو', 'type' => PetTypeEnum::Dog, 'gender' => PetGenderEnum::Male, 'breed' => 'ژرمن شپرد', 'weight' => 28, 'sterilized' => true], + ['name' => 'تدی', 'type' => PetTypeEnum::Cat, 'gender' => PetGenderEnum::Male, 'breed' => 'پرشین', 'weight' => 4, 'sterilized' => false], + ['name' => 'پونی', 'type' => PetTypeEnum::Rabbit, 'gender' => PetGenderEnum::Female, 'breed' => 'لوپ هلندی', 'weight' => 2, 'sterilized' => false], + ['name' => 'روبیک', 'type' => PetTypeEnum::Hamster, 'gender' => PetGenderEnum::Male, 'breed' => 'سوری', 'weight' => 1, 'sterilized' => false], + ['name' => 'کولا', 'type' => PetTypeEnum::GuineaPig, 'gender' => PetGenderEnum::Female, 'breed' => 'آمریکایی', 'weight' => 1, 'sterilized' => true], + ['name' => 'هاپو', 'type' => PetTypeEnum::Dog, 'gender' => PetGenderEnum::Male, 'breed' => 'هاسکی سیبری', 'weight' => 24, 'sterilized' => false], + ['name' => 'نازی', 'type' => PetTypeEnum::Cat, 'gender' => PetGenderEnum::Female, 'breed' => 'اسکاتیش فولد', 'weight' => 3, 'sterilized' => true], + ['name' => 'قناری', 'type' => PetTypeEnum::Bird, 'gender' => PetGenderEnum::Unknown, 'breed' => 'قناری رسمی', 'weight' => 1, 'sterilized' => false], + ['name' => 'لاکی', 'type' => PetTypeEnum::Turtle, 'gender' => PetGenderEnum::Female, 'breed' => 'برکه‌ای', 'weight' => 1, 'sterilized' => false], + ['name' => 'اسنوپی', 'type' => PetTypeEnum::Dog, 'gender' => PetGenderEnum::Female, 'breed' => 'شیتزو', 'weight' => 6, 'sterilized' => true], + ]; + + $owners = User::whereIn('level', [UserEnum::Admin->value, UserEnum::Supervisor->value, UserEnum::Vet->value])->get(); + + foreach ($owners as $owner) { + foreach ($pets as $pet) { + Pet::firstOrCreate( + ['name' => $pet['name'], 'owner_id' => $owner->id], + [ + 'type' => $pet['type']->value, + 'gender' => $pet['gender']->value, + 'breed' => $pet['breed'], + 'weight' => $pet['weight'], + 'sterilized' => $pet['sterilized'], + 'birthday' => now()->subYears(rand(1, 8))->startOfDay(), + ] + ); + } + } + } + + protected function seedDocuments($doctors): void + { + $requests = [ + ['pet' => 'مایلو', 'modality' => DocumentModalityEnum::Radiology, 'priority' => DocumentPriorityEnum::High, 'status' => DocumentStatusEnum::Registered, 'deadline_minutes' => -120, 'note' => 'لنگش اندام حرکتی عقب پس از پرش، احتمال آسیب مفصل ران. لطفا گرافی لگن بررسی شود.'], + ['pet' => 'تدی', 'modality' => DocumentModalityEnum::Sonography, 'priority' => DocumentPriorityEnum::Medium, 'status' => DocumentStatusEnum::Registered, 'deadline_minutes' => 53, 'note' => 'بی‌اشتهایی و استفراغ متناوب از سه روز پیش، درخواست سونوگرافی شکمی.'], + ['pet' => 'هاپو', 'modality' => DocumentModalityEnum::CtScan, 'priority' => DocumentPriorityEnum::High, 'status' => DocumentStatusEnum::InProgress, 'deadline_minutes' => 290, 'note' => 'تشنج ناگهانی، درخواست سی‌تی‌اسکن جمجمه جهت رد ضایعه فضاگیر.'], + ['pet' => 'نازی', 'modality' => DocumentModalityEnum::Echo, 'priority' => DocumentPriorityEnum::Medium, 'status' => DocumentStatusEnum::Registered, 'deadline_minutes' => 1400, 'note' => 'سوفل قلبی در معاینه شنیده شد، درخواست اکوکاردیوگرافی.'], + ['pet' => 'اسنوپی', 'modality' => DocumentModalityEnum::Mri, 'priority' => DocumentPriorityEnum::Medium, 'status' => DocumentStatusEnum::InProgress, 'deadline_minutes' => 1900, 'note' => 'ضعف پیش‌رونده اندام حرکتی، بررسی ستون فقرات از نظر دیسک بین‌مهره‌ای.'], + ['pet' => 'پونی', 'modality' => DocumentModalityEnum::Radiology, 'priority' => DocumentPriorityEnum::Medium, 'status' => DocumentStatusEnum::Registered, 'deadline_minutes' => null, 'note' => 'کاهش وزن و دندان قروچه، درخواست گرافی دندان و فک.'], + ['pet' => 'روبیک', 'modality' => DocumentModalityEnum::Sonography, 'priority' => DocumentPriorityEnum::High, 'status' => DocumentStatusEnum::InProgress, 'deadline_minutes' => 45, 'note' => 'تورم ناحیه شکم و بی‌حالی شدید، درخواست سونوگرافی اورژانسی.'], + ['pet' => 'کولا', 'modality' => DocumentModalityEnum::CtScan, 'priority' => DocumentPriorityEnum::Medium, 'status' => DocumentStatusEnum::Registered, 'deadline_minutes' => 600, 'note' => 'کج شدن سر و عدم تعادل، بررسی گوش میانی و داخلی.'], + ]; + + $owners = User::whereIn('level', [UserEnum::Admin->value, UserEnum::Supervisor->value, UserEnum::Vet->value])->get(); + + foreach ($owners as $owner) { + if (Document::where('requester_id', $owner->id)->exists()) { + continue; + } + + foreach ($requests as $index => $request) { + $pet = Pet::where('name', $request['pet'])->where('owner_id', $owner->id)->first(); + if (!$pet) { + continue; + } + + $document = Document::create([ + 'modality' => $request['modality']->value, + 'priority' => $request['priority']->value, + 'status' => $request['status']->value, + 'note' => $request['note'], + 'submitted_at' => now()->subDays(count($requests) - $index), + 'processing_deadline' => $request['deadline_minutes'] !== null ? now()->addMinutes($request['deadline_minutes']) : null, + 'pet_id' => $pet->id, + 'requester_id' => $owner->id, + ]); + + $document->assignments()->create([ + 'user_id' => $doctors[$index % $doctors->count()]->id, + 'role' => 'رادیولوژیست', + ]); + } + } + } +} diff --git a/resources/css/Client/ui/fast-request.css b/resources/css/Client/ui/fast-request.css new file mode 100644 index 0000000..241e092 --- /dev/null +++ b/resources/css/Client/ui/fast-request.css @@ -0,0 +1,112 @@ +.fast-request-dialog .p-select { + border: 1px solid #DEE3EB !important; + border-radius: 8px !important; + height: 37px !important; + display: flex !important; + align-items: center !important; +} + +.fast-request-dialog .p-select-label { + padding: 0 12px; + line-height: 1; + display: flex; + align-items: center; + height: 100%; +} + +.fast-request-dialog .p-select-label.p-placeholder { + color: #6E91C9 !important; + font-size: 11px !important; + font-weight: 400 !important; +} + +.fast-request-dialog .p-inputtext { + font-size: 11px !important; + font-weight: 400 !important; + border: 1px solid #DEE3EB !important; + border-radius: 8px !important; +} + +.fast-request-dialog .p-inputtext, +.fast-request-dialog .p-select, +.fast-request-dialog .p-textarea { + box-shadow: none !important; +} + +.fast-request-dialog .ui-button .p-button { + font-size: 11px !important; + font-weight: 600 !important; + white-space: nowrap !important; + border-radius: 6px !important; + padding: 8px 16px !important; +} + +.fast-request-dialog .ui-button .p-button.p-button-success { + background: #576989 !important; + border-color: #576989 !important; + color: #FFFFFF !important; +} + +.fast-request-dialog .p-inputtext::placeholder { + color: #6E91C9; +} + +.fast-request-dialog .ui-input-wrapper { + @apply !mb-0; +} + +.fast-request-dialog .ui-input-wrapper-border { + border: 1px solid #DEE3EB; + border-radius: 8px; +} + +.fast-request-dialog .ui-input-wrapper-border .p-inputtext { + border: 0 !important; +} + +.fast-request-dialog .p-textarea { + border: 1px solid #DEE3EB !important; + border-radius: 8px !important; + height: 100% !important; + font-size: 11px !important; + font-weight: 400 !important; +} + +.fast-request-dialog .p-textarea::placeholder { + color: #6E91C9 !important; + font-size: 11px !important; + font-weight: 400 !important; +} + +.fast-request-dialog .p-progressbar { + min-width: 60px !important; + height: 4px; +} + +.fast-request-dialog .p-progressbar-value { + background: #576989 !important; +} + +.fast-request-dialog .p-fileupload-choose-button, +.fast-request-dialog .p-fileupload-header { + display: none !important; +} + +.fast-request-dialog .p-fileupload { + width: 100% !important; + height: 100% !important; + display: flex !important; + align-items: center !important; + justify-content: center !important; +} + +.fast-request-dialog .p-fileupload-content { + pointer-events: auto !important; + width: 100%; +} + +.fast-request-dialog .p-fileupload-advanced { + border: 2px solid #C1D0E8 !important; + border-style: dashed !important; + border-radius: 6px !important; +} diff --git a/resources/css/client.css b/resources/css/client.css index 240f50b..c0597ee 100644 --- a/resources/css/client.css +++ b/resources/css/client.css @@ -7,6 +7,7 @@ @import "./Client/ui/button.css"; @import "./Client/ui/card.css"; @import "./Client/ui/select.css"; +@import "./Client/ui/fast-request.css"; @import "./Client/ui/table.css"; @import "tailwindcss"; @import "./Client/sidebar/sidebar.css"; diff --git a/resources/js/Client/Pages/Dashboard/Cases/Index.vue b/resources/js/Client/Pages/Dashboard/Cases/Index.vue index a2b221e..3a42464 100644 --- a/resources/js/Client/Pages/Dashboard/Cases/Index.vue +++ b/resources/js/Client/Pages/Dashboard/Cases/Index.vue @@ -1,11 +1,42 @@ diff --git a/resources/js/Client/Pages/Dashboard/Dashboard.vue b/resources/js/Client/Pages/Dashboard/Dashboard.vue index d76cbe2..5b3ccc9 100644 --- a/resources/js/Client/Pages/Dashboard/Dashboard.vue +++ b/resources/js/Client/Pages/Dashboard/Dashboard.vue @@ -11,6 +11,8 @@ import { ref } from 'vue'; import { Link } from '@inertiajs/inertia-vue3'; import FastRequestModal from '../../components/dashboard/FastRequestModal.vue'; import { useFastRequestStore } from '../../store/FastRequest'; +import { modalities } from '../../data/fastRequestOptions'; +import { caseColumns } from '../../data/casesTableColumns'; const fastRequestStore = useFastRequestStore(); const fastRequestModalVisible = ref(false); @@ -24,90 +26,12 @@ defineProps({ user: { type: Object, required: true + }, + cases: { + type: Object, + required: true } }) - -const data = ref([ - { - id: 1, - file_number: '1234567890', - status: 'مشاهده نشد', - request_type: 'CT', - requester: 'محراب اسماعیل‌نیا', - registration_date: '۱۴۰۴/۰۳/۰۲ ۱۴:۱۸', - processing_deadline: 'اتمام فرصت رسیدگی' - }, - { - id: 2, - file_number: '1234567891', - status: 'مشاهده نشد', - request_type: 'CT', - requester: 'رضا گلیان', - registration_date: '۱۴۰۴/۰۳/۰۲ ۱۴:۱۸', - processing_deadline: '۵۳ دقیقه' - }, - { - id: 3, - file_number: '1234567892', - status: 'مشاهده نشد', - request_type: 'MRI', - requester: 'دکتر مریم مهدی‌پور', - registration_date: '۱۴۰۴/۰۳/۰۲ ۱۴:۱۸', - processing_deadline: 'کمتر از ۳۲ ساعت' - }, - { - id: 4, - file_number: '1234567893', - status: 'در انتظار ثبت گزارش', - request_type: 'MRI', - requester: 'دکتر مریم مهدی‌پور', - registration_date: '۱۴۰۴/۰۳/۰۲ ۱۴:۱۸', - processing_deadline: 'کمتر از ۲۴ ساعت' - }, - { - id: 5, - file_number: '1234567894', - status: 'در انتظار ثبت گزارش', - request_type: 'MRI', - requester: 'نام شخص و یا پزشک', - registration_date: '۱۴۰۴/۰۳/۰۲ ۱۴:۱۸', - processing_deadline: 'کمتر از ۵ ساعت' - } -]) - -const columns = ref([ - { - title: 'شماره پرونده', - key: 'file_number' - }, - { - title: 'وضعیت', - key: 'status' - }, - { - title: 'نوع درخواست', - key: 'request_type' - }, - { - title: 'درخواست‌کننده', - key: 'requester' - }, - { - title: 'تاریخ ثبت', - key: 'registration_date' - }, - { - title: 'فرصت رسیدگی', - key: 'processing_deadline' - }, - { - title: '', - key: 'actions', - width: 80, - style: 'grid grid-cols-1', - slot: true, - } -])