feat: Implement Fast Request feature with file upload and request management
This commit is contained in:
@@ -0,0 +1,14 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums;
|
||||||
|
|
||||||
|
enum DocumentModalityEnum: string
|
||||||
|
{
|
||||||
|
use EnumHasOptions;
|
||||||
|
|
||||||
|
case CtScan = 'ct-scan';
|
||||||
|
case Mri = 'mri';
|
||||||
|
case Sonography = 'sonography';
|
||||||
|
case Echo = 'echo';
|
||||||
|
case Radiology = 'radiology';
|
||||||
|
}
|
||||||
@@ -3,13 +3,29 @@
|
|||||||
namespace App\Http\Controllers\Client\Doctor;
|
namespace App\Http\Controllers\Client\Doctor;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
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;
|
use Inertia\Inertia;
|
||||||
|
|
||||||
class CaseController extends Controller
|
class CaseController extends Controller
|
||||||
{
|
{
|
||||||
|
protected CaseService $caseService;
|
||||||
|
|
||||||
|
public function __construct(CaseService $caseService)
|
||||||
|
{
|
||||||
|
$this->caseService = $caseService;
|
||||||
|
}
|
||||||
|
|
||||||
public function index()
|
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),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,13 +3,26 @@
|
|||||||
namespace App\Http\Controllers\Client\Doctor;
|
namespace App\Http\Controllers\Client\Doctor;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
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;
|
use Inertia\Inertia;
|
||||||
|
|
||||||
class DashboardController extends Controller
|
class DashboardController extends Controller
|
||||||
{
|
{
|
||||||
|
protected CaseService $caseService;
|
||||||
|
|
||||||
|
public function __construct(CaseService $caseService)
|
||||||
|
{
|
||||||
|
$this->caseService = $caseService;
|
||||||
|
}
|
||||||
|
|
||||||
public function index()
|
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),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,15 +3,31 @@
|
|||||||
namespace App\Http\Controllers\Client\Doctor;
|
namespace App\Http\Controllers\Client\Doctor;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Http\Resources\Client\CaseResource;
|
||||||
use App\Models\Document;
|
use App\Models\Document;
|
||||||
use Illuminate\Http\Request;
|
use App\Services\Document\CaseService;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Inertia\Inertia;
|
use Inertia\Inertia;
|
||||||
|
|
||||||
class DocumentController extends Controller
|
class DocumentController extends Controller
|
||||||
{
|
{
|
||||||
|
protected CaseService $caseService;
|
||||||
|
|
||||||
|
public function __construct(CaseService $caseService)
|
||||||
|
{
|
||||||
|
$this->caseService = $caseService;
|
||||||
|
}
|
||||||
|
|
||||||
public function index()
|
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)
|
public function show(Document $document)
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers\Client;
|
||||||
|
|
||||||
|
use App\Enums\UserEnum;
|
||||||
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Http\Requests\Client\FastRequestRequest;
|
||||||
|
use App\Http\Resources\Client\DoctorResource;
|
||||||
|
use App\Http\Resources\Client\FastRequestResource;
|
||||||
|
use App\Http\Resources\Client\PetArchiveResource;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Services\Document\FastRequestService;
|
||||||
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
|
class FastRequestController extends Controller
|
||||||
|
{
|
||||||
|
protected FastRequestService $fastRequestService;
|
||||||
|
|
||||||
|
public function __construct(FastRequestService $fastRequestService)
|
||||||
|
{
|
||||||
|
$this->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')),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests\Client;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
use App\Enums\DocumentModalityEnum;
|
||||||
|
use App\Enums\DocumentPriorityEnum;
|
||||||
|
use App\Enums\PetGenderEnum;
|
||||||
|
use App\Enums\PetTypeEnum;
|
||||||
|
|
||||||
|
class FastRequestRequest extends FormRequest
|
||||||
|
{
|
||||||
|
public function authorize(): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'modality' => ['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'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Resources\Client;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Http\Resources\Json\JsonResource;
|
||||||
|
|
||||||
|
class CaseResource extends JsonResource
|
||||||
|
{
|
||||||
|
public function toArray(Request $request): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'id' => $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) . ' ساعت';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Resources\Client;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Http\Resources\Json\JsonResource;
|
||||||
|
|
||||||
|
class DoctorResource extends JsonResource
|
||||||
|
{
|
||||||
|
public function toArray(Request $request): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'id' => $this->id,
|
||||||
|
'fullname' => $this->fullname,
|
||||||
|
'specialty' => $this->specialty,
|
||||||
|
'profile_url' => $this->profile_url,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Resources\Client;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Http\Resources\Json\JsonResource;
|
||||||
|
|
||||||
|
class FastRequestResource extends JsonResource
|
||||||
|
{
|
||||||
|
public function toArray(Request $request): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'id' => $this->id,
|
||||||
|
'code' => $this->code,
|
||||||
|
'modality' => $this->modality,
|
||||||
|
'status' => $this->status,
|
||||||
|
'priority' => $this->priority,
|
||||||
|
'pet' => new PetArchiveResource($this->whenLoaded('pet')),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Resources\Client;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Http\Resources\Json\JsonResource;
|
||||||
|
|
||||||
|
class PetArchiveResource extends JsonResource
|
||||||
|
{
|
||||||
|
public function toArray(Request $request): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'id' => $this->id,
|
||||||
|
'name' => $this->name,
|
||||||
|
'type' => $this->type,
|
||||||
|
'breed' => $this->breed,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Services\Document;
|
||||||
|
|
||||||
|
use App\Enums\DocumentStatusEnum;
|
||||||
|
use App\Models\Document;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
|
||||||
|
class CaseService
|
||||||
|
{
|
||||||
|
public function pendingForUser(User $user): Builder
|
||||||
|
{
|
||||||
|
return Document::with(['pet', 'requester'])
|
||||||
|
->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');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Services\Document;
|
||||||
|
|
||||||
|
use App\Enums\DocumentPriorityEnum;
|
||||||
|
use App\Enums\MediaCollectionEnum;
|
||||||
|
use App\Enums\PetGenderEnum;
|
||||||
|
use App\Enums\PetTypeEnum;
|
||||||
|
use App\Models\Document;
|
||||||
|
use App\Models\Pet;
|
||||||
|
use App\Models\User;
|
||||||
|
use App\Services\FileStorageService;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
|
class FastRequestService
|
||||||
|
{
|
||||||
|
protected FileStorageService $fileStorageService;
|
||||||
|
|
||||||
|
public function __construct(FileStorageService $fileStorageService)
|
||||||
|
{
|
||||||
|
$this->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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use App\Enums\DocumentModalityEnum;
|
||||||
|
use App\Enums\DocumentPriorityEnum;
|
||||||
|
use App\Enums\DocumentStatusEnum;
|
||||||
|
use App\Enums\UserEnum;
|
||||||
|
use App\Models\Document;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
|
||||||
|
class DocumentSeeder extends Seeder
|
||||||
|
{
|
||||||
|
protected int $documentsPerOwner = 30;
|
||||||
|
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
$doctors = User::where('level', UserEnum::Doctor->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' => 'رادیولوژیست',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use App\Enums\DocumentModalityEnum;
|
||||||
|
use App\Enums\DocumentPriorityEnum;
|
||||||
|
use App\Enums\DocumentStatusEnum;
|
||||||
|
use App\Enums\PetGenderEnum;
|
||||||
|
use App\Enums\PetTypeEnum;
|
||||||
|
use App\Enums\UserEnum;
|
||||||
|
use App\Models\Document;
|
||||||
|
use App\Models\Pet;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
|
||||||
|
class FastRequestSeeder extends Seeder
|
||||||
|
{
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
$doctors = $this->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' => 'رادیولوژیست',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
@import "./Client/ui/button.css";
|
@import "./Client/ui/button.css";
|
||||||
@import "./Client/ui/card.css";
|
@import "./Client/ui/card.css";
|
||||||
@import "./Client/ui/select.css";
|
@import "./Client/ui/select.css";
|
||||||
|
@import "./Client/ui/fast-request.css";
|
||||||
@import "./Client/ui/table.css";
|
@import "./Client/ui/table.css";
|
||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
@import "./Client/sidebar/sidebar.css";
|
@import "./Client/sidebar/sidebar.css";
|
||||||
|
|||||||
@@ -1,11 +1,42 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import ClientLayout from '../../../layouts/ClientLayout.vue'
|
import ClientLayout from '../../../layouts/ClientLayout.vue';
|
||||||
|
import TableGeneric from '../../../components/ui/TableGeneric.vue';
|
||||||
|
import UiCard from '../../../components/ui/UiCard.vue';
|
||||||
|
import EyeSvg from '../../../components/svg/eye.svg';
|
||||||
|
import { Link } from '@inertiajs/inertia-vue3';
|
||||||
|
import { caseColumns } from '../../../data/casesTableColumns';
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
cases: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ClientLayout>
|
<ClientLayout>
|
||||||
<h1 class="text-2xl font-bold">
|
<UiCard title="لیست کیسها" variant="default">
|
||||||
Cases
|
<TableGeneric
|
||||||
</h1>
|
class="bg-white !rounded-2xl"
|
||||||
|
:data="cases.data"
|
||||||
|
customStyle="!text-[11px] !font-normal"
|
||||||
|
:searchable="false"
|
||||||
|
:columns="caseColumns"
|
||||||
|
:selectable="false"
|
||||||
|
:pagination="cases.meta"
|
||||||
|
:model-value="[]"
|
||||||
|
>
|
||||||
|
<template #column-actions="{ data }">
|
||||||
|
<div class="flex items-center justify-end">
|
||||||
|
<Link :href="`/dashboard/cases/${data.id}`"
|
||||||
|
class="text-primary-500 bg-complement-100 flex items-center justify-end p-1.5 rounded-md"
|
||||||
|
>
|
||||||
|
<EyeSvg class="w-4 h-4 text-primary-500" />
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</TableGeneric>
|
||||||
|
</UiCard>
|
||||||
</ClientLayout>
|
</ClientLayout>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import { ref } from 'vue';
|
|||||||
import { Link } from '@inertiajs/inertia-vue3';
|
import { Link } from '@inertiajs/inertia-vue3';
|
||||||
import FastRequestModal from '../../components/dashboard/FastRequestModal.vue';
|
import FastRequestModal from '../../components/dashboard/FastRequestModal.vue';
|
||||||
import { useFastRequestStore } from '../../store/FastRequest';
|
import { useFastRequestStore } from '../../store/FastRequest';
|
||||||
|
import { modalities } from '../../data/fastRequestOptions';
|
||||||
|
import { caseColumns } from '../../data/casesTableColumns';
|
||||||
const fastRequestStore = useFastRequestStore();
|
const fastRequestStore = useFastRequestStore();
|
||||||
|
|
||||||
const fastRequestModalVisible = ref(false);
|
const fastRequestModalVisible = ref(false);
|
||||||
@@ -24,90 +26,12 @@ defineProps({
|
|||||||
user: {
|
user: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
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,
|
|
||||||
}
|
|
||||||
])
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -116,27 +40,31 @@ const columns = ref([
|
|||||||
<UiCard title="ثبت آنی درخواست جدید" variant="default" class="mt-4">
|
<UiCard title="ثبت آنی درخواست جدید" variant="default" class="mt-4">
|
||||||
<p class="w-full flex items-center justify-start font-[400] text-[11px]">فقط چند قدم ساده برای ثبت درخواست کافیه.</p>
|
<p class="w-full flex items-center justify-start font-[400] text-[11px]">فقط چند قدم ساده برای ثبت درخواست کافیه.</p>
|
||||||
<div class="w-full h-[179px] grid grid-cols-5 gap-3 justify-start pt-[10px]">
|
<div class="w-full h-[179px] grid grid-cols-5 gap-3 justify-start pt-[10px]">
|
||||||
<RequestCard title="CT Scan" icon="ct-scan" severity="primary" @click="openFastRequestModal('ct-scan')"/>
|
<RequestCard
|
||||||
<RequestCard title="MRI" icon="mri" severity="primary" @click="openFastRequestModal('mri')"/>
|
v-for="modality in modalities"
|
||||||
<RequestCard title="سونوگرافی" icon="sonography" severity="primary" @click="openFastRequestModal('sonography')"/>
|
:key="modality.code"
|
||||||
<RequestCard title="اکوکاردیوگرافی" icon="echo" severity="primary" @click="openFastRequestModal('echo')"/>
|
:value="modality.code"
|
||||||
<RequestCard title="رادیولوژی" icon="radiology" severity="primary" @click="openFastRequestModal('radiology')"/>
|
:title="modality.title"
|
||||||
|
:icon="modality.icon"
|
||||||
|
severity="primary"
|
||||||
|
@click="openFastRequestModal(modality.code)"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</UiCard>
|
</UiCard>
|
||||||
<UiCard title="آخرین کیسهای در انتظار بررسی" variant="default" class="mt-4">
|
<UiCard title="آخرین کیسهای در انتظار بررسی" variant="default" class="mt-4">
|
||||||
<TableGeneric
|
<TableGeneric
|
||||||
class="bg-white !rounded-2xl"
|
class="bg-white !rounded-2xl"
|
||||||
:data="data"
|
:data="cases.data"
|
||||||
customStyle="!text-[11px] !font-normal"
|
customStyle="!text-[11px] !font-normal"
|
||||||
:searchable="false"
|
:searchable="false"
|
||||||
:columns="columns"
|
:columns="caseColumns"
|
||||||
:selectable="false"
|
:selectable="false"
|
||||||
:pagination="null"
|
:pagination="null"
|
||||||
:model-value="[]"
|
:model-value="[]"
|
||||||
>
|
>
|
||||||
<template #column-actions="{ data }" class="">
|
<template #column-actions="{ data }" class="">
|
||||||
<div class="flex items-center justify-end">
|
<div class="flex items-center justify-end">
|
||||||
<Link to="/dashboard/doctor/cases/1"
|
<Link :href="`/dashboard/cases/${data.id}`"
|
||||||
class="text-primary-500 bg-complement-100 flex items-center justify-end p-1.5 rounded-md"
|
class="text-primary-500 bg-complement-100 flex items-center justify-end p-1.5 rounded-md"
|
||||||
>
|
>
|
||||||
<EyeSvg class="w-4 h-4 text-primary-500" />
|
<EyeSvg class="w-4 h-4 text-primary-500" />
|
||||||
|
|||||||
@@ -4,157 +4,17 @@ import UiCard from '../../../components/ui/UiCard.vue'
|
|||||||
import TableGeneric from '../../../components/ui/TableGeneric.vue'
|
import TableGeneric from '../../../components/ui/TableGeneric.vue'
|
||||||
import EyeSvg from '../../../components/svg/eye.svg'
|
import EyeSvg from '../../../components/svg/eye.svg'
|
||||||
import { Link } from '@inertiajs/inertia-vue3'
|
import { Link } from '@inertiajs/inertia-vue3'
|
||||||
import { ref } from 'vue'
|
|
||||||
import { useDocumentStore } from '../../../store/Document'
|
import { useDocumentStore } from '../../../store/Document'
|
||||||
|
import { caseColumns } from '../../../data/casesTableColumns'
|
||||||
|
|
||||||
const documentStore = useDocumentStore();
|
const documentStore = useDocumentStore();
|
||||||
|
|
||||||
const selectedItems = ref([])
|
defineProps({
|
||||||
|
documents: {
|
||||||
const data = ref([
|
type: Object,
|
||||||
{
|
required: true
|
||||||
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: 'کمتر از ۵ ساعت'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 6,
|
|
||||||
file_number: '1234567893',
|
|
||||||
status: 'در انتظار ثبت گزارش',
|
|
||||||
request_type: 'MRI',
|
|
||||||
requester: 'دکتر مریم مهدیپور',
|
|
||||||
registration_date: '۱۴۰۴/۰۳/۰۲ ۱۴:۱۸',
|
|
||||||
processing_deadline: 'کمتر از ۲۴ ساعت'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 7,
|
|
||||||
file_number: '1234567894',
|
|
||||||
status: 'در انتظار ثبت گزارش',
|
|
||||||
request_type: 'MRI',
|
|
||||||
requester: 'نام شخص و یا پزشک',
|
|
||||||
registration_date: '۱۴۰۴/۰۳/۰۲ ۱۴:۱۸',
|
|
||||||
processing_deadline: 'کمتر از ۵ ساعت'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 8,
|
|
||||||
file_number: '1234567893',
|
|
||||||
status: 'در انتظار ثبت گزارش',
|
|
||||||
request_type: 'MRI',
|
|
||||||
requester: 'دکتر مریم مهدیپور',
|
|
||||||
registration_date: '۱۴۰۴/۰۳/۰۲ ۱۴:۱۸',
|
|
||||||
processing_deadline: 'کمتر از ۲۴ ساعت'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 9,
|
|
||||||
file_number: '1234567894',
|
|
||||||
status: 'در انتظار ثبت گزارش',
|
|
||||||
request_type: 'MRI',
|
|
||||||
requester: 'نام شخص و یا پزشک',
|
|
||||||
registration_date: '۱۴۰۴/۰۳/۰۲ ۱۴:۱۸',
|
|
||||||
processing_deadline: 'کمتر از ۵ ساعت'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 7,
|
|
||||||
file_number: '1234567894',
|
|
||||||
status: 'در انتظار ثبت گزارش',
|
|
||||||
request_type: 'MRI',
|
|
||||||
requester: 'نام شخص و یا پزشک',
|
|
||||||
registration_date: '۱۴۰۴/۰۳/۰۲ ۱۴:۱۸',
|
|
||||||
processing_deadline: 'کمتر از ۵ ساعت'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 8,
|
|
||||||
file_number: '1234567893',
|
|
||||||
status: 'در انتظار ثبت گزارش',
|
|
||||||
request_type: 'MRI',
|
|
||||||
requester: 'دکتر مریم مهدیپور',
|
|
||||||
registration_date: '۱۴۰۴/۰۳/۰۲ ۱۴:۱۸',
|
|
||||||
processing_deadline: 'کمتر از ۲۴ ساعت'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 9,
|
|
||||||
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,
|
|
||||||
},
|
|
||||||
])
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -162,13 +22,13 @@ const columns = ref([
|
|||||||
<UiCard title="کیسهای در انتظار بررسی" variant="default" class="">
|
<UiCard title="کیسهای در انتظار بررسی" variant="default" class="">
|
||||||
<TableGeneric
|
<TableGeneric
|
||||||
class="bg-white !rounded-2xl"
|
class="bg-white !rounded-2xl"
|
||||||
:data="data"
|
:data="documents.data"
|
||||||
customStyle="!text-[11px] !font-normal"
|
customStyle="!text-[11px] !font-normal"
|
||||||
:searchable="false"
|
:searchable="false"
|
||||||
:columns="columns"
|
:columns="caseColumns"
|
||||||
:selectable="false"
|
:selectable="false"
|
||||||
:pagination="null"
|
:pagination="documents.meta"
|
||||||
:model-value="selectedItems"
|
:model-value="[]"
|
||||||
>
|
>
|
||||||
<template #column-actions="{ data }" class="">
|
<template #column-actions="{ data }" class="">
|
||||||
<div class="flex items-center justify-end">
|
<div class="flex items-center justify-end">
|
||||||
|
|||||||
@@ -1,108 +1,96 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import UiButton from '../../components/ui/UiButton.vue';
|
import UiButton from '../../components/ui/UiButton.vue';
|
||||||
import { ref, computed } from 'vue';
|
import { computed, watch } from 'vue';
|
||||||
import Dialog from 'primevue/dialog';
|
import Dialog from 'primevue/dialog';
|
||||||
import ProgressBar from 'primevue/progressbar';
|
import ProgressBar from 'primevue/progressbar';
|
||||||
|
import { toast } from 'vue3-toastify';
|
||||||
import RequestType from './fastRequest/RequestType.vue';
|
import RequestType from './fastRequest/RequestType.vue';
|
||||||
import RequestCase from './fastRequest/RequestCase.vue';
|
import RequestCase from './fastRequest/RequestCase.vue';
|
||||||
import RequestDetails from './fastRequest/RequestDetails.vue';
|
import RequestDetails from './fastRequest/RequestDetails.vue';
|
||||||
import RequestDoctor from './fastRequest/RequestDoctor.vue';
|
import RequestDoctor from './fastRequest/RequestDoctor.vue';
|
||||||
import RequestReview from './fastRequest/RequestReview.vue';
|
import RequestReview from './fastRequest/RequestReview.vue';
|
||||||
|
import { useFastRequestStore } from '../../store/FastRequest';
|
||||||
|
|
||||||
const props = defineProps({
|
const visible = defineModel('visible');
|
||||||
title: {
|
const fastRequestStore = useFastRequestStore();
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
icon: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const step = ref(1);
|
const steps = [
|
||||||
const stepRef = ref(null);
|
{ label: 'نوع درخواست', component: RequestType },
|
||||||
|
{ label: 'انتخاب کیس', component: RequestCase },
|
||||||
const steps = ref({
|
{ label: 'جزئیات درخواست', component: RequestDetails },
|
||||||
1: RequestType,
|
{ label: 'انتخاب پزشک', component: RequestDoctor },
|
||||||
2: RequestCase,
|
{ label: 'مرور نهایی', component: RequestReview },
|
||||||
3: RequestDetails,
|
];
|
||||||
4: RequestDoctor,
|
|
||||||
5: RequestReview,
|
|
||||||
});
|
|
||||||
|
|
||||||
const currentStepComponent = computed(() => {
|
const currentStepComponent = computed(() => {
|
||||||
return steps.value[step.value] || RequestType
|
return steps[fastRequestStore.step - 1]?.component || RequestType;
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleNext = () => {
|
const isLastStep = computed(() => fastRequestStore.step === steps.length);
|
||||||
step.value++;
|
|
||||||
|
watch(visible, (isVisible) => {
|
||||||
|
if (isVisible) {
|
||||||
|
fastRequestStore.fetchCreateData();
|
||||||
|
} else {
|
||||||
|
fastRequestStore.reset();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleNext = async () => {
|
||||||
|
if (!fastRequestStore.canProceed) {
|
||||||
|
toast.error('لطفا اطلاعات این مرحله را کامل کنید.');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isLastStep.value) {
|
||||||
|
fastRequestStore.nextStep();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const document = await fastRequestStore.submit();
|
||||||
|
toast.success(`درخواست شما با شماره پرونده ${document.code} با موفقیت ثبت شد.`);
|
||||||
|
visible.value = false;
|
||||||
|
} catch (error) {
|
||||||
|
toast.error(error.response?.data?.message || 'خطا در ثبت درخواست. لطفا دوباره تلاش کنید.');
|
||||||
|
}
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Dialog :modal="true" :closable="true" :dismissableMask="true" :maximizable="false" :style="{ width: '688px' , minHeight: '376px' }" :pt="{header : '!bg-client-primaryBlue'}">
|
<Dialog v-model:visible="visible" :modal="true" :closable="true" :dismissableMask="true" :maximizable="false" :style="{ width: '688px' , minHeight: '376px' }" class="fast-request-dialog" :pt="{header : '!bg-client-primaryBlue'}">
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="w-full flex items-center justify-start">
|
<div class="w-full flex items-center justify-start">
|
||||||
<h2 class="text-[14px] font-[700] text-[#576989]">ثبت آنی درخواست</h2>
|
<h2 class="text-[14px] font-[700] text-[#576989]">ثبت آنی درخواست</h2>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div class="w-full h-[56px] flex items-center justify-between border-b border-[#EBEBEB] pb-3">
|
<div class="w-full h-[56px] flex items-center justify-between border-b border-[#EBEBEB] pb-3">
|
||||||
|
<template v-for="(item, index) in steps" :key="item.label">
|
||||||
|
<ProgressBar v-if="index > 0" :value="fastRequestStore.step > index ? 100 : 0" :showValue="false" mode="determinate"></ProgressBar>
|
||||||
<div class="w-auto h-[40px] grid grid-cols-1 items-center justify-center gap-1">
|
<div class="w-auto h-[40px] grid grid-cols-1 items-center justify-center gap-1">
|
||||||
<div class="w-full h-[20px] flex items-center justify-center">
|
<div class="w-full h-[20px] flex items-center justify-center">
|
||||||
<span class="w-[20px] h-[20px] bg-[#576989] rounded-full text-white flex items-center justify-center text-[10px] font-[600]">1</span>
|
<span
|
||||||
|
class="w-[20px] h-[20px] rounded-full text-white flex items-center justify-center text-[10px] font-[600]"
|
||||||
|
:class="fastRequestStore.step >= index + 1 ? 'bg-[#576989]' : 'bg-[#C1D0E8]'"
|
||||||
|
>{{ index + 1 }}</span>
|
||||||
</div>
|
</div>
|
||||||
<span class="text-[10px] font-[600] text-[#7188AD]">نوع درخواست</span>
|
<span class="text-[10px] font-[600]" :class="fastRequestStore.step >= index + 1 ? 'text-[#576989]' : 'text-[#7188AD]'">{{ item.label }}</span>
|
||||||
</div>
|
</div>
|
||||||
<ProgressBar :value="value1" :showValue="false" mode="determinate"></ProgressBar>
|
</template>
|
||||||
<div class="w-auto h-[40px] grid grid-cols-1 items-center justify-center gap-1">
|
|
||||||
<div class="w-full h-[20px] flex items-center justify-center ">
|
|
||||||
<span class="w-[20px] h-[20px] bg-[#576989] rounded-full text-white flex items-center justify-center text-[10px] font-[600]">2</span>
|
|
||||||
</div>
|
</div>
|
||||||
<span class="text-[10px] font-[600] text-[#7188AD]">انتخاب کیس</span>
|
<component :is="currentStepComponent" />
|
||||||
</div>
|
|
||||||
<ProgressBar :value="value2" :showValue="false" mode="determinate"></ProgressBar>
|
|
||||||
<div class="w-auto h-[40px] grid grid-cols-1 items-center justify-center gap-1">
|
|
||||||
<div class="w-full h-[20px] flex items-center justify-center ">
|
|
||||||
<span class="w-[20px] h-[20px] bg-[#576989] rounded-full text-white flex items-center justify-center text-[10px] font-[600]">3</span>
|
|
||||||
</div>
|
|
||||||
<span class="text-[10px] font-[600] text-[#7188AD]">جزئیات درخواست</span>
|
|
||||||
</div>
|
|
||||||
<ProgressBar :value="value3" :showValue="false" mode="determinate"></ProgressBar>
|
|
||||||
<div class="w-auto h-[40px] grid grid-cols-1 items-center justify-center gap-1">
|
|
||||||
<div class="w-full h-[20px] flex items-center justify-center ">
|
|
||||||
<span class="w-[20px] h-[20px] bg-[#576989] rounded-full text-white flex items-center justify-center text-[10px] font-[600]">4</span>
|
|
||||||
</div>
|
|
||||||
<span class="text-[10px] font-[600] text-[#7188AD]">انتخاب پزشک</span>
|
|
||||||
</div>
|
|
||||||
<ProgressBar :value="value4" :showValue="false" mode="determinate"></ProgressBar>
|
|
||||||
<div class="w-auto h-[40px] grid grid-cols-1 items-center justify-center gap-1">
|
|
||||||
<div class="w-full h-[20px] flex items-center justify-center ">
|
|
||||||
<span class="w-[20px] h-[20px] bg-[#576989] rounded-full text-white flex items-center justify-center text-[10px] font-[600]">5</span>
|
|
||||||
</div>
|
|
||||||
<span class="text-[10px] font-[600] text-[#7188AD]">مرور نهایی</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<component :is="currentStepComponent" ref="stepRef" @validated="onValidated"/>
|
|
||||||
|
|
||||||
<div class="w-full pt-3 flex items-center justify-end border-t border-[#EBEBEB]">
|
<div class="w-full pt-3 flex items-center justify-between border-t border-[#EBEBEB]">
|
||||||
<ui-button label="تایید و مرحله بعد" severity="success" size="small" @click="handleNext" />
|
<ui-button v-if="fastRequestStore.step > 1" label="مرحله قبل" severity="secondary" size="small" @click="fastRequestStore.prevStep" />
|
||||||
|
<span v-else></span>
|
||||||
|
<ui-button
|
||||||
|
:label="isLastStep ? 'ثبت نهایی درخواست' : 'تایید و مرحله بعد'"
|
||||||
|
severity="success"
|
||||||
|
size="small"
|
||||||
|
:loading="fastRequestStore.submitting"
|
||||||
|
:disabled="fastRequestStore.submitting"
|
||||||
|
@click="handleNext"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
:deep(.p-progressbar) {
|
|
||||||
min-width: 60px !important;
|
|
||||||
height: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.p-progressbar-value) {
|
|
||||||
background: #576989 !important;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
|
|
||||||
@@ -2,46 +2,36 @@
|
|||||||
import Dialog from 'primevue/dialog';
|
import Dialog from 'primevue/dialog';
|
||||||
import UiButton from '../../ui/UiButton.vue';
|
import UiButton from '../../ui/UiButton.vue';
|
||||||
import TableGeneric from '../../ui/TableGeneric.vue';
|
import TableGeneric from '../../ui/TableGeneric.vue';
|
||||||
import { ref } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import CaseNumber from './CaseNumber.vue';
|
import CaseNumber from './CaseNumber.vue';
|
||||||
|
import { useFastRequestStore } from '../../../store/FastRequest';
|
||||||
|
|
||||||
|
const visible = defineModel('visible');
|
||||||
|
const fastRequestStore = useFastRequestStore();
|
||||||
|
|
||||||
const isChooseCaseNumber = ref(false);
|
const isChooseCaseNumber = ref(false);
|
||||||
|
const selectedPetId = ref(null);
|
||||||
|
|
||||||
const showChooseCaseNumber = () => {
|
const showChooseCaseNumber = () => {
|
||||||
isChooseCaseNumber.value = true;
|
isChooseCaseNumber.value = true;
|
||||||
}
|
}
|
||||||
const data = ref([
|
|
||||||
{
|
const data = computed(() => fastRequestStore.pets.map((pet) => ({
|
||||||
id: 1,
|
id: pet.id,
|
||||||
case_number: '1234567890',
|
case_number: pet.id,
|
||||||
case_name: 'مایلو',
|
case_name: pet.name,
|
||||||
case_type: 'سگ',
|
case_type: pet.breed ? `${pet.type} (${pet.breed})` : pet.type,
|
||||||
},
|
})));
|
||||||
{
|
|
||||||
id: 2,
|
const selectPet = (petId) => {
|
||||||
case_number: '1234567891',
|
selectedPetId.value = petId;
|
||||||
case_name: 'تدی',
|
}
|
||||||
case_type: 'گربه',
|
|
||||||
},
|
const confirmSelection = () => {
|
||||||
{
|
if (!selectedPetId.value) return;
|
||||||
id: 3,
|
fastRequestStore.selectArchivePet(selectedPetId.value);
|
||||||
case_number: '1234567892',
|
visible.value = false;
|
||||||
case_name: 'پونی',
|
|
||||||
case_type: 'خرگوش',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 4,
|
|
||||||
case_number: '1234567893',
|
|
||||||
case_name: 'روبیک',
|
|
||||||
case_type: 'همستر',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 5,
|
|
||||||
case_number: '1234567894',
|
|
||||||
case_name: 'کولا',
|
|
||||||
case_type: 'خوکچه هندی',
|
|
||||||
}
|
}
|
||||||
]);
|
|
||||||
|
|
||||||
const columns = ref([
|
const columns = ref([
|
||||||
{
|
{
|
||||||
@@ -67,7 +57,7 @@ const columns = ref([
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Dialog :modal="true" :closable="true" :dismissableMask="true" :maximizable="false" :style="{ width: '554px' , minHeight: '326px' }">
|
<Dialog v-model:visible="visible" :modal="true" :closable="true" :dismissableMask="true" :maximizable="false" :style="{ width: '554px' , minHeight: '326px' }" class="fast-request-dialog">
|
||||||
<template #header>
|
<template #header>
|
||||||
<h3>انتخاب کیس از آرشیو</h3>
|
<h3>انتخاب کیس از آرشیو</h3>
|
||||||
</template>
|
</template>
|
||||||
@@ -76,7 +66,9 @@ const columns = ref([
|
|||||||
<p>در صورتیکه شماره مرجع یک کیس را در اختیار داشته باشید میتوانید درخواست را برای کیس مربوطه نیز ثبت کنید.</p>
|
<p>در صورتیکه شماره مرجع یک کیس را در اختیار داشته باشید میتوانید درخواست را برای کیس مربوطه نیز ثبت کنید.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="h-[180px] overflow-y-scroll">
|
<div class="h-[180px] overflow-y-scroll">
|
||||||
|
<p v-if="!data.length" class="w-full py-8 text-center text-[11px] font-[400] text-[#7188AD]">کیسی در آرشیو شما ثبت نشده است.</p>
|
||||||
<TableGeneric
|
<TableGeneric
|
||||||
|
v-else
|
||||||
class="bg-white !rounded-2xl"
|
class="bg-white !rounded-2xl"
|
||||||
:data="data"
|
:data="data"
|
||||||
customStyle="!text-[11px] !font-normal"
|
customStyle="!text-[11px] !font-normal"
|
||||||
@@ -88,25 +80,20 @@ const columns = ref([
|
|||||||
>
|
>
|
||||||
<template #column-actions="{ data }">
|
<template #column-actions="{ data }">
|
||||||
<div class="flex items-center justify-end">
|
<div class="flex items-center justify-end">
|
||||||
<UiButton severity="secondary" size="small">
|
<UiButton
|
||||||
<template #icon>
|
:severity="selectedPetId === data.id ? 'success' : 'secondary'"
|
||||||
|
size="small"
|
||||||
</template>
|
:label="selectedPetId === data.id ? 'انتخاب شد' : 'انتخاب'"
|
||||||
</UiButton>
|
@click="selectPet(data.id)"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</TableGeneric>
|
</TableGeneric>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full pt-3 flex items-center justify-end gap-2 border-t border-[#EBEBEB]">
|
<div class="w-full pt-3 flex items-center justify-end gap-2 border-t border-[#EBEBEB]">
|
||||||
<ui-button label="افزودن از طریق شناسه کیس" severity="secondary" @click="showChooseCaseNumber"/>
|
<ui-button label="افزودن از طریق شناسه کیس" severity="secondary" @click="showChooseCaseNumber"/>
|
||||||
<ui-button label="تایید و انتخاب" severity="success" size="small" @click="handleNext" />
|
<ui-button label="تایید و انتخاب" severity="success" size="small" :disabled="!selectedPetId" @click="confirmSelection" />
|
||||||
</div>
|
</div>
|
||||||
<case-number v-model:visible="isChooseCaseNumber"></case-number>
|
<case-number v-model:visible="isChooseCaseNumber" @selected="visible = false"></case-number>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,45 +1,43 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import Dialog from 'primevue/dialog';
|
import Dialog from 'primevue/dialog';
|
||||||
|
import InputText from 'primevue/inputtext';
|
||||||
import UiButton from '../../ui/UiButton.vue';
|
import UiButton from '../../ui/UiButton.vue';
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
|
import { useFastRequestStore } from '../../../store/FastRequest';
|
||||||
|
|
||||||
|
const visible = defineModel('visible');
|
||||||
|
const emit = defineEmits(['selected']);
|
||||||
|
const fastRequestStore = useFastRequestStore();
|
||||||
|
|
||||||
|
const caseNumber = ref(null);
|
||||||
|
|
||||||
|
const confirmCaseNumber = () => {
|
||||||
|
if (!caseNumber.value) return;
|
||||||
|
fastRequestStore.selectArchivePet(Number(caseNumber.value));
|
||||||
|
caseNumber.value = null;
|
||||||
|
visible.value = false;
|
||||||
|
emit('selected');
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Dialog :modal="true" :closable="true" :dismissableMask="true" :maximizable="false" :style="{ width: '408px' , minHeight: '210px' }">
|
<Dialog v-model:visible="visible" :modal="true" :closable="true" :dismissableMask="true" :maximizable="false" :style="{ width: '408px' , minHeight: '210px' }" class="fast-request-dialog">
|
||||||
<template #header>
|
<template #header>
|
||||||
<h3>افزودن کیس از طریق شناسه مرجع</h3>
|
<h3>افزودن کیس از طریق شناسه مرجع</h3>
|
||||||
</template>
|
</template>
|
||||||
<div class="w-full py-2 flex flex-col items-start justify-center border-t border-[#EBEBEB] text-[11px] font-[600]">
|
<div class="w-full py-2 flex flex-col items-start justify-center border-t border-[#EBEBEB] text-[11px] font-[600]">
|
||||||
<p>لطفا شناسه کیس که از پزشک و یا سرپرست کیس دریافت کردهاید را وارد نمایید.</p>
|
<p>لطفا شناسه کیس که از پزشک و یا سرپرست کیس دریافت کردهاید را وارد نمایید.</p>
|
||||||
</div>
|
</div>
|
||||||
<Form ref="formRef" v-slot="$form" class="p-4 grid grid-cols-1 !w-full">
|
<form class="p-4 grid grid-cols-1 w-full" @submit.prevent="confirmCaseNumber">
|
||||||
<FormField v-slot="$field" name="name" initialValue="" class="flex flex-col">
|
|
||||||
<InputText
|
<InputText
|
||||||
type="text"
|
v-model="caseNumber"
|
||||||
|
type="number"
|
||||||
placeholder="شناسه کیس"
|
placeholder="شناسه کیس"
|
||||||
size="large"/>
|
size="large"
|
||||||
</FormField>
|
/>
|
||||||
</Form>
|
</form>
|
||||||
<div class="w-full pt-3 flex items-center justify-end gap-2 border-t border-[#EBEBEB]">
|
<div class="w-full pt-3 flex items-center justify-end gap-2 border-t border-[#EBEBEB]">
|
||||||
<ui-button label="تایید و افزودن" severity="success" size="small"/>
|
<ui-button label="تایید و افزودن" severity="success" size="small" :disabled="!caseNumber" @click="confirmCaseNumber"/>
|
||||||
</div>
|
</div>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
:deep(.p-inputtext::placeholder) {
|
|
||||||
color: #6E91C9;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.p-inputtext) {
|
|
||||||
border: 1px solid #DEE3EB !important;
|
|
||||||
border-radius: 8px !important;
|
|
||||||
font-size: 13px !important;
|
|
||||||
font-weight: 400 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import FileUpload from 'primevue/fileupload';
|
||||||
|
import Badge from 'primevue/badge';
|
||||||
|
import { formatFileSize } from '../../../utils/fileUtils';
|
||||||
|
|
||||||
|
const files = defineModel({ type: Array, default: () => [] });
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const fileUpload = ref(null);
|
||||||
|
|
||||||
|
const openFileDialog = () => {
|
||||||
|
fileUpload.value.choose();
|
||||||
|
}
|
||||||
|
|
||||||
|
const onSelectedFiles = (event) => {
|
||||||
|
files.value = event.files;
|
||||||
|
}
|
||||||
|
|
||||||
|
const onRemoveFile = (removeFileCallback, index) => {
|
||||||
|
removeFileCallback(index);
|
||||||
|
files.value = fileUpload.value.files;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<FileUpload
|
||||||
|
ref="fileUpload"
|
||||||
|
class="w-full !border-dashed !border-[#C1D0E8] !min-h-[100px] flex items-center justify-center"
|
||||||
|
name="files[]"
|
||||||
|
:customUpload="true"
|
||||||
|
:showUploadButton="false"
|
||||||
|
:showCancelButton="false"
|
||||||
|
:showChooseButton="false"
|
||||||
|
:multiple="true"
|
||||||
|
accept="image/*"
|
||||||
|
:maxFileSize="10485760"
|
||||||
|
@select="onSelectedFiles"
|
||||||
|
>
|
||||||
|
<template #content="{ files: selectedFiles, removeFileCallback }">
|
||||||
|
<div v-if="selectedFiles.length > 0" class="flex flex-wrap gap-4 pt-4">
|
||||||
|
<div v-for="(file, index) of selectedFiles" :key="file.name + file.type + file.size" class="relative w-[5rem] h-[5rem] rounded-lg border border-gray-200 flex flex-col items-center overflow-hidden">
|
||||||
|
<img role="presentation" :alt="file.name" :src="file.objectURL" class="absolute top-0 left-0 w-full h-full object-cover"/>
|
||||||
|
<div class="absolute bottom-0 left-0 w-full flex flex-col items-center gap-1 bg-white/80 p-1">
|
||||||
|
<span class="w-full text-[10px] font-[400] text-center text-ellipsis whitespace-nowrap overflow-hidden">{{ file.name }}</span>
|
||||||
|
<span class="text-[9px]">{{ formatFileSize(file.size) }}</span>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="absolute top-1 left-1 w-4 h-4 flex items-center justify-center rounded-full bg-white text-[#7A1419] text-[10px] cursor-pointer" @click.stop="onRemoveFile(removeFileCallback, index)">✕</button>
|
||||||
|
</div>
|
||||||
|
<button type="button" class="w-[5rem] h-[5rem] rounded-lg border border-dashed border-[#C1D0E8] text-[11px] text-[#365891] cursor-pointer" @click="openFileDialog">افزودن</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #empty>
|
||||||
|
<div class="w-full flex items-center justify-center flex-col cursor-pointer" @click="openFileDialog">
|
||||||
|
<p class="text-[11px] font-[600] text-[#365891]">{{ title }}</p>
|
||||||
|
<p class="mt-1 mb-0 text-[11px] font-[400] text-[#365891]">
|
||||||
|
جهت آپلود تصویر کلیک کنید و یا فایل خود را کشیده و در این قسمت رها (Drag and Drop) کنید.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</FileUpload>
|
||||||
|
</template>
|
||||||
@@ -1,172 +1,86 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import Select from 'primevue/select';
|
import Select from 'primevue/select';
|
||||||
|
import InputText from 'primevue/inputtext';
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import ArchiveCase from './ArchiveCase.vue';
|
import ArchiveCase from './ArchiveCase.vue';
|
||||||
import UiInput from '../../ui/UiInput.vue';
|
import { useFastRequestStore } from '../../../store/FastRequest';
|
||||||
|
import { petTypes, petGenders, neuteredOptions } from '../../../data/fastRequestOptions';
|
||||||
|
|
||||||
|
const fastRequestStore = useFastRequestStore();
|
||||||
|
|
||||||
const isChooseArchive = ref(false);
|
const isChooseArchive = ref(false);
|
||||||
|
|
||||||
const showChooseArchive = () => {
|
const showChooseArchive = () => {
|
||||||
isChooseArchive.value = true;
|
isChooseArchive.value = true;
|
||||||
}
|
}
|
||||||
const genders = ref([
|
|
||||||
{ name: 'نر', code: 'male' },
|
|
||||||
{ name: 'ماده', code: 'female' }
|
|
||||||
]);
|
|
||||||
|
|
||||||
const types = ref([
|
|
||||||
{ name: 'سگ', code: 'dog' },
|
|
||||||
{ name: 'گربه', code: 'cat' },
|
|
||||||
{ name: 'پرنده', code: 'bird' },
|
|
||||||
{ name: 'خرگوش', code: 'rabbit' },
|
|
||||||
{ name: 'همستر', code: 'hamster' },
|
|
||||||
{ name: 'خوکچه هندی', code: 'guinea_pig' },
|
|
||||||
{ name: 'لاک پشت', code: 'turtle' },
|
|
||||||
{ name: 'مار', code: 'snake' },
|
|
||||||
{ name: 'مارمولک', code: 'lizard' },
|
|
||||||
]);
|
|
||||||
|
|
||||||
const neuteredStatus = ref([
|
|
||||||
{ name: 'عقیم سازی شده', code: 'neutered' },
|
|
||||||
{ name: 'عقیم سازی نشده', code: 'not_neutered' }
|
|
||||||
]);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="w-full space-y-2 py-6">
|
<div class="w-full space-y-2 py-6">
|
||||||
<div class="w-full h-[12px] flex items-center justify-between">
|
<div class="w-full h-[12px] flex items-center justify-between">
|
||||||
<p class="flex h-full items-center justify-start text-[11px] font-[600] text-[#3E4C63]">مشخصات کیس(حیوان) مورد نظر را وارد نمایید.</p>
|
<p class="flex h-full items-center justify-start text-[11px] font-[600] text-[#3E4C63]">مشخصات کیس(حیوان) مورد نظر را وارد نمایید.</p>
|
||||||
<button @click="showChooseArchive" class="flex h-full items-center justify-end text-[11px] font-[400] text-[#7A1419] cursor-pointer">انتخاب از آرشیو</button>
|
<button type="button" @click="showChooseArchive" class="flex h-full items-center justify-end text-[11px] font-[400] text-[#7A1419] cursor-pointer">انتخاب از آرشیو</button>
|
||||||
</div>
|
</div>
|
||||||
<p class="flex items-center justify-start text-[11px] font-[600] text-[#576989]">
|
<p class="flex items-center justify-start text-[11px] font-[600] text-[#576989]">
|
||||||
در صورتی که قبلا این کیس در پلتفرم رادیکال ثبت شده، برای بررسی سوابق قبلی از دکمه «انتخاب از آرشیو» استفاده نمایید.
|
در صورتی که قبلا این کیس در پلتفرم رادیکال ثبت شده، برای بررسی سوابق قبلی از دکمه «انتخاب از آرشیو» استفاده نمایید.
|
||||||
</p>
|
</p>
|
||||||
<Form ref="formRef" v-slot="$form" class="p-4 grid grid-cols-2 gap-4 !w-full">
|
<div v-if="fastRequestStore.requestCase.petId" class="w-full flex items-center justify-between border border-[#DEE3EB] rounded-[8px] p-3">
|
||||||
<FormField v-slot="$field" name="name" initialValue="" class="flex flex-col">
|
<p class="text-[11px] font-[600] text-[#3E4C63]">
|
||||||
<!-- <InputText
|
کیس انتخابشده از آرشیو:
|
||||||
|
<span v-if="fastRequestStore.selectedPet" class="text-[#7A1419]">{{ fastRequestStore.selectedPet.name }} ({{ fastRequestStore.selectedPet.type }})</span>
|
||||||
|
<span v-else class="text-[#7A1419]">شناسه {{ fastRequestStore.requestCase.petId }}</span>
|
||||||
|
</p>
|
||||||
|
<button type="button" @click="fastRequestStore.clearArchivePet" class="text-[11px] font-[400] text-[#7A1419] cursor-pointer">حذف و ثبت کیس جدید</button>
|
||||||
|
</div>
|
||||||
|
<form v-else class="p-4 grid grid-cols-2 gap-4 w-full">
|
||||||
|
<InputText
|
||||||
|
v-model="fastRequestStore.requestCase.name"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="نام"
|
placeholder="نام"
|
||||||
size="large"/> -->
|
|
||||||
<UiInput
|
|
||||||
type="text"
|
|
||||||
size="large"
|
size="large"
|
||||||
prefixStart="نام"
|
|
||||||
direction="ltr"
|
|
||||||
/>
|
/>
|
||||||
</FormField>
|
|
||||||
<FormField v-slot="$field" name="type" initialValue="" class="flex flex-col">
|
|
||||||
<Select
|
<Select
|
||||||
:options="types"
|
v-model="fastRequestStore.requestCase.type"
|
||||||
|
:options="petTypes"
|
||||||
optionLabel="name"
|
optionLabel="name"
|
||||||
optionValue="code"
|
optionValue="code"
|
||||||
placeholder="نوع"
|
placeholder="نوع"
|
||||||
/>
|
/>
|
||||||
</FormField>
|
|
||||||
<FormField v-slot="$field" name="breed" initialValue="" class="flex flex-col">
|
|
||||||
<InputText
|
<InputText
|
||||||
|
v-model="fastRequestStore.requestCase.breed"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="نژاد"
|
placeholder="نژاد"
|
||||||
size="large"/>
|
|
||||||
</FormField>
|
|
||||||
<FormField v-slot="$field" name="gender" initialValue="" class="flex flex-col">
|
|
||||||
<Select :options="genders" optionLabel="name" optionValue="code" placeholder="جنسیت" size="small"/>
|
|
||||||
</FormField>
|
|
||||||
<FormField v-slot="$field" name="age" initialValue="" class="flex flex-col">
|
|
||||||
<InputText
|
|
||||||
type="text"
|
|
||||||
placeholder="سن"
|
|
||||||
size="large"
|
size="large"
|
||||||
/>
|
/>
|
||||||
</FormField>
|
|
||||||
<FormField v-slot="$field" name="birthday" initialValue="" class="flex flex-col">
|
|
||||||
<InputText
|
|
||||||
type="text"
|
|
||||||
placeholder="وزن"
|
|
||||||
size="large"
|
|
||||||
/>
|
|
||||||
</FormField>
|
|
||||||
<FormField v-slot="$field" name="sterilized" initialValue="" class="flex flex-col">
|
|
||||||
<Select
|
<Select
|
||||||
:options="neuteredStatus"
|
v-model="fastRequestStore.requestCase.gender"
|
||||||
|
:options="petGenders"
|
||||||
|
optionLabel="name"
|
||||||
|
optionValue="code"
|
||||||
|
placeholder="جنسیت"
|
||||||
|
size="small"
|
||||||
|
/>
|
||||||
|
<InputText
|
||||||
|
v-model="fastRequestStore.requestCase.age"
|
||||||
|
type="number"
|
||||||
|
placeholder="سن (سال)"
|
||||||
|
size="large"
|
||||||
|
/>
|
||||||
|
<InputText
|
||||||
|
v-model="fastRequestStore.requestCase.weight"
|
||||||
|
type="number"
|
||||||
|
placeholder="وزن (کیلوگرم)"
|
||||||
|
size="large"
|
||||||
|
/>
|
||||||
|
<Select
|
||||||
|
v-model="fastRequestStore.requestCase.sterilized"
|
||||||
|
:options="neuteredOptions"
|
||||||
optionLabel="name"
|
optionLabel="name"
|
||||||
optionValue="code"
|
optionValue="code"
|
||||||
placeholder="وضعیت عقیم سازی"
|
placeholder="وضعیت عقیم سازی"
|
||||||
size="small"
|
size="small"
|
||||||
/>
|
/>
|
||||||
</FormField>
|
</form>
|
||||||
</Form>
|
|
||||||
<archive-case v-model:visible="isChooseArchive"></archive-case>
|
<archive-case v-model:visible="isChooseArchive"></archive-case>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
:deep(.p-select) {
|
|
||||||
border: 1px solid #DEE3EB !important;
|
|
||||||
border-radius: 8px !important;
|
|
||||||
height: 37px !important;
|
|
||||||
display: flex !important;
|
|
||||||
align-items: center !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.p-select-label) {
|
|
||||||
padding: 0 12px;
|
|
||||||
line-height: 1;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.p-select-label.p-placeholder) {
|
|
||||||
color: #6E91C9 !important;
|
|
||||||
font-size: 11px !important;
|
|
||||||
font-weight: 400 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
:deep(.p-inputtext) {
|
|
||||||
font-size: 11px !important;
|
|
||||||
font-weight: 400 !important;
|
|
||||||
border: 1px solid #DEE3EB !important;
|
|
||||||
border-radius: 8px !important;
|
|
||||||
}
|
|
||||||
:deep(.p-inputtext::placeholder) {
|
|
||||||
color: #6E91C9;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.vpd-main) {
|
|
||||||
border: 1px solid #DEE3EB !important;
|
|
||||||
border-radius: 8px !important;
|
|
||||||
overflow: hidden !important;
|
|
||||||
height: 45px !important;
|
|
||||||
|
|
||||||
}
|
|
||||||
:deep(.vpd-input-group) {
|
|
||||||
border: 0 !important;
|
|
||||||
font-size: 16px !important;
|
|
||||||
border-radius: 8px !important;
|
|
||||||
border: 0 !important;
|
|
||||||
font-weight: 400 !important;
|
|
||||||
height: 45px !important;
|
|
||||||
}
|
|
||||||
:deep(.vpd-input-group .vpd-placeholder) {
|
|
||||||
font-size: 13px !important;
|
|
||||||
font-weight: 400 !important;
|
|
||||||
color: #3E4C63 !important;
|
|
||||||
}
|
|
||||||
:deep(.vpd-input-group input) {
|
|
||||||
border: 0 !important;
|
|
||||||
}
|
|
||||||
:deep(.vpd-icon-btn) {
|
|
||||||
background: #576989 !important;
|
|
||||||
}
|
|
||||||
:deep(.vpd-content) {
|
|
||||||
border-radius: 12px !important;
|
|
||||||
overflow: hidden !important;
|
|
||||||
border: 0 !important;
|
|
||||||
}
|
|
||||||
:deep(.vpd-wrapper) {
|
|
||||||
background-color: rgba(0,0,0,.1) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,256 +1,37 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref } from 'vue';
|
import Select from 'primevue/select';
|
||||||
import { usePrimeVue } from 'primevue/config';
|
import Textarea from 'primevue/textarea';
|
||||||
import { useToast } from "primevue/usetoast";
|
import FileUploadBox from './FileUploadBox.vue';
|
||||||
|
import { useFastRequestStore } from '../../../store/FastRequest';
|
||||||
const $primevue = usePrimeVue();
|
import { priorities, modalityTitle } from '../../../data/fastRequestOptions';
|
||||||
const toast = useToast();
|
|
||||||
|
|
||||||
const totalSize = ref(0);
|
|
||||||
const totalSizePercent = ref(0);
|
|
||||||
const files = ref([]);
|
|
||||||
|
|
||||||
const fileUpload = ref(null)
|
|
||||||
|
|
||||||
const openFileDialog = () => {
|
|
||||||
fileUpload.value.choose()
|
|
||||||
}
|
|
||||||
|
|
||||||
const onRemoveTemplatingFile = (file, removeFileCallback, index) => {
|
|
||||||
removeFileCallback(index);
|
|
||||||
totalSize.value -= parseInt(formatSize(file.size));
|
|
||||||
totalSizePercent.value = totalSize.value / 10;
|
|
||||||
};
|
|
||||||
|
|
||||||
const onClearTemplatingUpload = (clear) => {
|
|
||||||
clear();
|
|
||||||
totalSize.value = 0;
|
|
||||||
totalSizePercent.value = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
const onSelectedFiles = (event) => {
|
|
||||||
files.value = event.files;
|
|
||||||
files.value.forEach((file) => {
|
|
||||||
totalSize.value += parseInt(formatSize(file.size));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const uploadEvent = (callback) => {
|
|
||||||
totalSizePercent.value = totalSize.value / 10;
|
|
||||||
callback();
|
|
||||||
};
|
|
||||||
|
|
||||||
const onTemplatedUpload = () => {
|
|
||||||
toast.add({ severity: "info", summary: "Success", detail: "File Uploaded", life: 3000 });
|
|
||||||
};
|
|
||||||
|
|
||||||
const formatSize = (bytes) => {
|
|
||||||
const k = 1024;
|
|
||||||
const dm = 3;
|
|
||||||
const sizes = $primevue.config.locale.fileSizeTypes;
|
|
||||||
|
|
||||||
if (bytes === 0) {
|
|
||||||
return `0 ${sizes[0]}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
||||||
const formattedSize = parseFloat((bytes / Math.pow(k, i)).toFixed(dm));
|
|
||||||
|
|
||||||
return `${formattedSize} ${sizes[i]}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
const fastRequestStore = useFastRequestStore();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
<p class="text-[11px] font-[400] text-[#3E4C63] py-3">جزئیات درخواست و اطلاعات مورد نیاز را با دقت و حوصله وار نمایید.</p>
|
<p class="text-[11px] font-[400] text-[#3E4C63] py-3">جزئیات درخواست و اطلاعات مورد نیاز را با دقت و حوصله وارد نمایید.</p>
|
||||||
<div class="w-full grid grid-cols-2 gap-2 items-center justify-between">
|
<div class="w-full grid grid-cols-2 gap-2 items-center justify-between">
|
||||||
<Select
|
<Select
|
||||||
:options="types"
|
v-model="fastRequestStore.requestDetails.priority"
|
||||||
|
:options="priorities"
|
||||||
optionLabel="name"
|
optionLabel="name"
|
||||||
optionValue="code"
|
optionValue="code"
|
||||||
placeholder="نوع درخواست"
|
placeholder="اولویت درخواست"
|
||||||
/>
|
/>
|
||||||
<span class="text-[11px] font-[400] text-[#7188AD]">زمان تخمینی ثبت گزارش: ۷۲ ساعت</span>
|
<span class="text-[11px] font-[400] text-[#7188AD]">زمان تخمینی ثبت گزارش: ۷۲ ساعت</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full h-[118px] mt-3">
|
<div class="w-full h-[118px] mt-3">
|
||||||
<Textarea
|
<Textarea
|
||||||
v-model="value1"
|
v-model="fastRequestStore.requestDetails.note"
|
||||||
size="small"
|
size="small"
|
||||||
placeholder="شرح درخواست و توضیحات"
|
placeholder="شرح درخواست و توضیحات"
|
||||||
rows="3"
|
rows="3"
|
||||||
class="w-full h-full" />
|
class="w-full h-full" />
|
||||||
|
|
||||||
</div>
|
|
||||||
<div class="w-full grid grid-cols-2 gap-2 items-center justify-between my-4">
|
|
||||||
<FileUpload class="w-full !border-dashed !border-[#C1D0E8] !min-h-[100px] flex items-center justify-center" ref="fileUpload" name="files[]" :customUpload="true" :showUploadButton="false" :showCancelButton="false" :showChooseButton="false" url="/api/upload" @upload="onTemplatedUpload($event)" :multiple="true" accept="image/*" :maxFileSize="1000000" @select="onSelectedFiles">
|
|
||||||
<template #content="{ files, uploadedFiles, removeUploadedFileCallback, removeFileCallback }">
|
|
||||||
<div class="flex flex-col gap-8 pt-4">
|
|
||||||
<div v-if="files.length > 0">
|
|
||||||
<div class="flex flex-wrap gap-4">
|
|
||||||
<div v-for="(file, index) of files" :key="file.name + file.type + file.size" class="relative w-[5rem] h-[5rem] rounded-lg border border-gray-200 bg-amber-100 flex flex-col items-center gap-4 overflow-hidden">
|
|
||||||
<div class="w-full h-full absolute top-0 left-0">
|
|
||||||
<img role="presentation" :alt="file.name" :src="file.objectURL" class="w-full h-full object-cover"/>
|
|
||||||
</div>
|
|
||||||
<div class="w-full h-full flex flex-col items-center justify-center gap-2 absolute bottom-0 left-0">
|
|
||||||
<span class="w-full px-2 text-xs font-[400] text-ellipsis whitespace-nowrap overflow-hidden">{{ file.name }}</span>
|
|
||||||
<div>{{ formatSize(file.size) }}</div>
|
|
||||||
<Badge value="Pending" severity="warn" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="uploadedFiles.length > 0">
|
|
||||||
<h5>Completed</h5>
|
|
||||||
<div class="flex flex-wrap gap-4">
|
|
||||||
<div v-for="(file, index) of uploadedFiles" :key="file.name + file.type + file.size" class="p-8 rounded-border flex flex-col border border-surface items-center gap-4">
|
|
||||||
<div>
|
|
||||||
<img role="presentation" :alt="file.name" :src="file.objectURL" width="100" height="50" />
|
|
||||||
</div>
|
|
||||||
<span class="font-semibold text-ellipsis max-w-60 whitespace-nowrap overflow-hidden">{{ file.name }}</span>
|
|
||||||
<div>{{ formatSize(file.size) }}</div>
|
|
||||||
<Badge value="Completed" class="mt-4" severity="success" />
|
|
||||||
<Button icon="pi pi-times" @click="removeUploadedFileCallback(index)" variant="outlined" rounded severity="danger" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="w-full grid grid-cols-2 gap-2 items-start justify-between my-4">
|
||||||
|
<FileUploadBox v-model="fastRequestStore.historyFiles" title="سوابق پزشکی قبلی (اختیاری)" />
|
||||||
|
<FileUploadBox v-model="fastRequestStore.scanFiles" :title="`فایل «${modalityTitle(fastRequestStore.requestType)}»`" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #empty>
|
|
||||||
<div class="w-full flex items-center justify-center flex-col cursor-pointer" @click="openFileDialog">
|
|
||||||
<Button
|
|
||||||
icon="pi pi-images"
|
|
||||||
rounded
|
|
||||||
variant="outlined"
|
|
||||||
severity="secondary"
|
|
||||||
class="!hidden"
|
|
||||||
@click.stop="openFileDialog"/>
|
|
||||||
<p class="text-[11px] font-[600] text-[#365891]">سوابق پزشکی قبلی (اختیاری)</p>
|
|
||||||
<p class="mt-1 mb-0 text-[11px] font-[400] text-[#365891]">
|
|
||||||
جهت آپلود تصویر کلیک کنید و یا فایل خود کشیده و در این قسمت رها (Drag and Drup) کنید.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</FileUpload>
|
|
||||||
<FileUpload class="w-full !border-dashed !border-[#C1D0E8] !min-h-[100px] flex items-center justify-center" ref="fileUpload" name="files[]" :customUpload="true" :showUploadButton="false" :showCancelButton="false" :showChooseButton="false" url="/api/upload" @upload="onTemplatedUpload($event)" :multiple="true" accept="image/*" :maxFileSize="1000000" @select="onSelectedFiles">
|
|
||||||
<template #content="{ files, uploadedFiles, removeUploadedFileCallback, removeFileCallback }">
|
|
||||||
<div class="flex flex-col gap-8 pt-4">
|
|
||||||
<div v-if="files.length > 0">
|
|
||||||
<div class="flex flex-wrap gap-4">
|
|
||||||
<div v-for="(file, index) of files" :key="file.name + file.type + file.size" class="relative w-[5rem] h-[5rem] rounded-lg border border-gray-200 bg-amber-100 flex flex-col items-center gap-4 overflow-hidden">
|
|
||||||
<div class="w-full h-full absolute top-0 left-0">
|
|
||||||
<img role="presentation" :alt="file.name" :src="file.objectURL" class="w-full h-full object-cover"/>
|
|
||||||
</div>
|
|
||||||
<div class="w-full h-full flex flex-col items-center justify-center gap-2 absolute bottom-0 left-0">
|
|
||||||
<span class="w-full px-2 text-xs font-[400] text-ellipsis whitespace-nowrap overflow-hidden">{{ file.name }}</span>
|
|
||||||
<div>{{ formatSize(file.size) }}</div>
|
|
||||||
<Badge value="Pending" severity="warn" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="uploadedFiles.length > 0">
|
|
||||||
<h5>Completed</h5>
|
|
||||||
<div class="flex flex-wrap gap-4">
|
|
||||||
<div v-for="(file, index) of uploadedFiles" :key="file.name + file.type + file.size" class="p-8 rounded-border flex flex-col border border-surface items-center gap-4">
|
|
||||||
<div>
|
|
||||||
<img role="presentation" :alt="file.name" :src="file.objectURL" width="100" height="50" />
|
|
||||||
</div>
|
|
||||||
<span class="font-semibold text-ellipsis max-w-60 whitespace-nowrap overflow-hidden">{{ file.name }}</span>
|
|
||||||
<div>{{ formatSize(file.size) }}</div>
|
|
||||||
<Badge value="Completed" class="mt-4" severity="success" />
|
|
||||||
<Button icon="pi pi-times" @click="removeUploadedFileCallback(index)" variant="outlined" rounded severity="danger" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template #empty>
|
|
||||||
<div class="w-full flex items-center justify-center flex-col cursor-pointer" @click="openFileDialog">
|
|
||||||
<Button
|
|
||||||
icon="pi pi-images"
|
|
||||||
rounded
|
|
||||||
variant="outlined"
|
|
||||||
severity="secondary"
|
|
||||||
class="!hidden"
|
|
||||||
@click.stop="openFileDialog"/>
|
|
||||||
<p class="text-[11px] font-[600] text-[#365891]">فایل «CT Scan»</p>
|
|
||||||
<p class="mt-1 mb-0 text-[11px] font-[400] text-[#365891]">
|
|
||||||
جهت آپلود تصویر کلیک کنید و یا فایل خود کشیده و در این قسمت رها (Drag and Drup) کنید.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</FileUpload>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
:deep(.p-select) {
|
|
||||||
border: 1px solid #DEE3EB !important;
|
|
||||||
border-radius: 8px !important;
|
|
||||||
height: 37px !important;
|
|
||||||
display: flex !important;
|
|
||||||
align-items: center !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.p-select-label) {
|
|
||||||
padding: 0 12px;
|
|
||||||
line-height: 1;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.p-select-label.p-placeholder) {
|
|
||||||
color: #6E91C9 !important;
|
|
||||||
font-size: 11px !important;
|
|
||||||
font-weight: 400 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.p-textarea) {
|
|
||||||
border: 1px solid #DEE3EB !important;
|
|
||||||
border-radius: 8px !important;
|
|
||||||
height: 100% !important;
|
|
||||||
display: flex !important;
|
|
||||||
align-items: center !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.p-textarea::placeholder) {
|
|
||||||
color: #6E91C9 !important;
|
|
||||||
font-size: 13px !important;
|
|
||||||
font-weight: 400 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.p-fileupload-content {
|
|
||||||
pointer-events: auto !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.p-fileupload-choose-button) {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.p-fileupload-header) {
|
|
||||||
display: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.p-fileupload) {
|
|
||||||
width: 100% !important;
|
|
||||||
height: 100% !important;
|
|
||||||
display: flex !important;
|
|
||||||
align-items: center !important;
|
|
||||||
justify-content: center !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
:deep(.p-fileupload-advanced) {
|
|
||||||
border: none !important;
|
|
||||||
border: 2px solid #C1D0E8 !important;
|
|
||||||
border-style: dashed !important;
|
|
||||||
border-radius: 6px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,29 +1,35 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
|
import { useFastRequestStore } from '../../../store/FastRequest';
|
||||||
|
|
||||||
|
const fastRequestStore = useFastRequestStore();
|
||||||
|
|
||||||
|
const selectDoctor = (doctorId) => {
|
||||||
|
fastRequestStore.doctorId = doctorId;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="w-full h-full grid grid-cols-3 gap-2 items-center justify-between py-4">
|
<div class="w-full h-full grid grid-cols-3 gap-2 items-center justify-between py-4">
|
||||||
<div class="w-full bg-white h-[134px] flex flex-col items-center justify-center border-[2px] border-[#576989] rounded-[12px]">
|
<button
|
||||||
|
type="button"
|
||||||
|
@click="selectDoctor(null)"
|
||||||
|
class="w-full bg-white h-[134px] flex flex-col items-center justify-center rounded-[12px] cursor-pointer"
|
||||||
|
:class="fastRequestStore.doctorId === null ? 'border-[2px] border-[#576989]' : 'border-[1px] border-[#DEE3EB]'"
|
||||||
|
>
|
||||||
<h3 class="text-[13px] font-[600] text-[#576989]">انتخاب خودکار</h3>
|
<h3 class="text-[13px] font-[600] text-[#576989]">انتخاب خودکار</h3>
|
||||||
<p class="w-[190px] text-center text-[11px] font-[600] text-[#3E4C63]">انتخاب در دسترس ترین پزشک جهت بررسی سریع درخواست شما</p>
|
<p class="w-[190px] text-center text-[11px] font-[600] text-[#3E4C63]">انتخاب در دسترس ترین پزشک جهت بررسی سریع درخواست شما</p>
|
||||||
</div>
|
</button>
|
||||||
<div class="w-full bg-white h-[134px] flex flex-col items-center justify-center border-[1px] border-[#DEE3EB] rounded-[12px]">
|
<button
|
||||||
<img src="/public/images/profile_default.png" alt="doctor" class="w-[65px] h-[65px] rounded-[50px] object-contain ">
|
v-for="doctor in fastRequestStore.doctors"
|
||||||
<h3 class="text-[13px] font-[600] text-[#576989]">دکتر حسین حسینی</h3>
|
:key="doctor.id"
|
||||||
<p class="text-center text-[11px] font-[600] text-[#7188AD]">متخصص جراحی پوست و مو</p>
|
type="button"
|
||||||
</div>
|
@click="selectDoctor(doctor.id)"
|
||||||
<div class="w-full bg-white h-[134px] flex flex-col items-center justify-center border-[1px] border-[#DEE3EB] rounded-[12px]">
|
class="w-full bg-white h-[134px] flex flex-col items-center justify-center rounded-[12px] cursor-pointer"
|
||||||
<img src="/public/images/profile_default.png" alt="doctor" class="w-[65px] h-[65px] rounded-[50px] object-contain ">
|
:class="fastRequestStore.doctorId === doctor.id ? 'border-[2px] border-[#576989]' : 'border-[1px] border-[#DEE3EB]'"
|
||||||
<h3 class="text-[13px] font-[600] text-[#576989]">دکتر امین حسینی</h3>
|
>
|
||||||
<p class="text-center text-[11px] font-[600] text-[#7188AD]">متخصص و رادیولوژیست</p>
|
<img :src="doctor.profile_url || '/images/profile_default.png'" :alt="doctor.fullname" class="w-[65px] h-[65px] rounded-full object-cover">
|
||||||
</div>
|
<h3 class="text-[13px] font-[600] text-[#576989]">{{ doctor.fullname }}</h3>
|
||||||
|
<p class="text-center text-[11px] font-[600] text-[#7188AD]">{{ doctor.specialty }}</p>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,15 +1,22 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import { useFastRequestStore } from '../../../store/FastRequest';
|
||||||
|
import { modalityTitle, priorityTitle } from '../../../data/fastRequestOptions';
|
||||||
|
|
||||||
|
const fastRequestStore = useFastRequestStore();
|
||||||
|
|
||||||
|
const caseName = computed(() => fastRequestStore.selectedPet?.name || fastRequestStore.requestCase.name || 'کیس جدید');
|
||||||
|
const caseNumber = computed(() => fastRequestStore.requestCase.petId || 'جدید');
|
||||||
|
const doctorName = computed(() => fastRequestStore.selectedDoctor?.fullname || 'اولین پزشک در دسترس');
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="w-full h-full flex flex-col items-start justify-between gap-8 py-8">
|
<div class="w-full h-full flex flex-col items-start justify-between gap-8 py-8">
|
||||||
<p class="text-[13px] font-[400] text-[#3E4C63]">
|
<p class="text-[13px] font-[400] text-[#3E4C63]">
|
||||||
شما در حال تکمیل فرایند ثبت درخواست
|
شما در حال تکمیل فرایند ثبت درخواست
|
||||||
<span class="font-[600] text-[#7A1419]">((CT Scan))</span>
|
<span class="font-[600] text-[#7A1419]">(({{ modalityTitle(fastRequestStore.requestType) }}))</span>
|
||||||
با اولویت
|
با اولویت
|
||||||
<span class="font-[600] text-[#7A1419]">((اورژانسی))</span> برای کیس <span class="font-[600] text-[#7A1419]">((مایلو))</span> با شماره کیس <span class="font-[600] text-[#7A1419]">((۱۲۳۴۵۶))</span> میباشید. درخواست شما در ابتدا توسط کارشناسان ما بررسی و در صورت تایید به <span class="font-[600] text-[#7A1419]">((اولین پزشک در دسترس))</span> جهت بررسی و گزارش نویسی ارجاع خواهد شد.
|
<span class="font-[600] text-[#7A1419]">(({{ priorityTitle(fastRequestStore.requestDetails.priority) }}))</span> برای کیس <span class="font-[600] text-[#7A1419]">(({{ caseName }}))</span> با شماره کیس <span class="font-[600] text-[#7A1419]">(({{ caseNumber }}))</span> میباشید. درخواست شما در ابتدا توسط کارشناسان ما بررسی و در صورت تایید به <span class="font-[600] text-[#7A1419]">(({{ doctorName }}))</span> جهت بررسی و گزارش نویسی ارجاع خواهد شد.
|
||||||
</p>
|
</p>
|
||||||
<p class="text-[13px] font-[400] text-[#3E4C63]">
|
<p class="text-[13px] font-[400] text-[#3E4C63]">
|
||||||
وضعیت و مراحل پرونده از طریق <span class="font-[600] text-[#7A1419]">((پیامک))</span> به شما اطلاعرسانی خواهد شد.
|
وضعیت و مراحل پرونده از طریق <span class="font-[600] text-[#7A1419]">((پیامک))</span> به شما اطلاعرسانی خواهد شد.
|
||||||
@@ -17,9 +24,3 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,23 +1,21 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import RequestCard from '../../../components/dashboard/RequestCard.vue';
|
import RequestCard from '../../../components/dashboard/RequestCard.vue';
|
||||||
|
import { modalities } from '../../../data/fastRequestOptions';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="w-full space-y-3 py-6">
|
<div class="w-full space-y-3 py-6">
|
||||||
<p class="h-[17px] flex items-center justify-start text-[11px] font-[600] text-[#3E4C63]">نوع درخواست خود را مشخص کنید</p>
|
<p class="h-[17px] flex items-center justify-start text-[11px] font-[600] text-[#3E4C63]">نوع درخواست خود را مشخص کنید</p>
|
||||||
<div class="w-full h-[133px] grid grid-cols-5 gap-3 justify-start">
|
<div class="w-full h-[133px] grid grid-cols-5 gap-3 justify-start">
|
||||||
<RequestCard value="ct-scan" title="CT Scan" icon="ct-scan" severity="primary" :selectable="true"/>
|
<RequestCard
|
||||||
<RequestCard value="mri" title="MRI" icon="mri" severity="primary" :selectable="true"/>
|
v-for="modality in modalities"
|
||||||
<RequestCard value="sonography" title="سونوگرافی" icon="sonography" severity="primary" :selectable="true"/>
|
:key="modality.code"
|
||||||
<RequestCard value="echo" title="اکوکاردیوگرافی" icon="echo" severity="primary" :selectable="true"/>
|
:value="modality.code"
|
||||||
<RequestCard value="radiology" title="رادیولوژی" icon="radiology" severity="primary" :selectable="true"/>
|
:title="modality.title"
|
||||||
|
:icon="modality.icon"
|
||||||
|
severity="primary"
|
||||||
|
:selectable="true"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import { modalityTitle } from './fastRequestOptions'
|
||||||
|
import { convertEnglishToPersian } from '../utils/numberUtils'
|
||||||
|
|
||||||
|
export const caseColumns = [
|
||||||
|
{
|
||||||
|
title: 'شماره پرونده',
|
||||||
|
key: 'code'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'وضعیت',
|
||||||
|
key: 'status'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'نوع درخواست',
|
||||||
|
key: 'modality',
|
||||||
|
render: (row) => modalityTitle(row.modality)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'درخواستکننده',
|
||||||
|
key: 'requester'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'تاریخ ثبت',
|
||||||
|
key: 'submitted_at',
|
||||||
|
render: (row) => row.submitted_at ? convertEnglishToPersian(row.submitted_at) : '-'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'فرصت رسیدگی',
|
||||||
|
key: 'processing_deadline',
|
||||||
|
render: (row) => row.processing_deadline ? convertEnglishToPersian(row.processing_deadline) : '-'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '',
|
||||||
|
key: 'actions',
|
||||||
|
width: 80,
|
||||||
|
style: 'grid grid-cols-1',
|
||||||
|
slot: true,
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
export const modalities = [
|
||||||
|
{ code: 'ct-scan', title: 'CT Scan', icon: 'ct-scan' },
|
||||||
|
{ code: 'mri', title: 'MRI', icon: 'mri' },
|
||||||
|
{ code: 'sonography', title: 'سونوگرافی', icon: 'sonography' },
|
||||||
|
{ code: 'echo', title: 'اکوکاردیوگرافی', icon: 'echo' },
|
||||||
|
{ code: 'radiology', title: 'رادیولوژی', icon: 'radiology' },
|
||||||
|
]
|
||||||
|
|
||||||
|
export const petTypes = [
|
||||||
|
{ name: 'سگ', code: 'Dog' },
|
||||||
|
{ name: 'گربه', code: 'Cat' },
|
||||||
|
{ name: 'پرنده', code: 'Bird' },
|
||||||
|
{ name: 'ماهی', code: 'Fish' },
|
||||||
|
{ name: 'خرگوش', code: 'Rabbit' },
|
||||||
|
{ name: 'همستر', code: 'Hamster' },
|
||||||
|
{ name: 'خوکچه هندی', code: 'GuineaPig' },
|
||||||
|
{ name: 'لاک پشت', code: 'Turtle' },
|
||||||
|
{ name: 'مار', code: 'Snake' },
|
||||||
|
{ name: 'مارمولک', code: 'Lizard' },
|
||||||
|
{ name: 'سایر', code: 'Other' },
|
||||||
|
]
|
||||||
|
|
||||||
|
export const petGenders = [
|
||||||
|
{ name: 'نر', code: 'Male' },
|
||||||
|
{ name: 'ماده', code: 'Female' },
|
||||||
|
]
|
||||||
|
|
||||||
|
export const neuteredOptions = [
|
||||||
|
{ name: 'عقیم سازی شده', code: true },
|
||||||
|
{ name: 'عقیم سازی نشده', code: false },
|
||||||
|
]
|
||||||
|
|
||||||
|
export const priorities = [
|
||||||
|
{ name: 'عادی', code: 'Medium' },
|
||||||
|
{ name: 'اورژانسی', code: 'High' },
|
||||||
|
]
|
||||||
|
|
||||||
|
export const modalityTitle = (code) => {
|
||||||
|
return modalities.find((modality) => modality.code === code)?.title || code
|
||||||
|
}
|
||||||
|
|
||||||
|
export const priorityTitle = (code) => {
|
||||||
|
return priorities.find((priority) => priority.code === code)?.name || code
|
||||||
|
}
|
||||||
@@ -1,22 +1,104 @@
|
|||||||
import { step } from '@primeuix/themes/aura/stepper'
|
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
export const useFastRequestStore = defineStore('FastRequestStore', {
|
|
||||||
state: () => ({
|
const emptyCase = () => ({
|
||||||
step: 1,
|
petId: null,
|
||||||
requestType: null,
|
|
||||||
requestCase: {
|
|
||||||
name: null,
|
name: null,
|
||||||
species: null,
|
type: null,
|
||||||
gender: null,
|
gender: null,
|
||||||
|
breed: null,
|
||||||
age: null,
|
age: null,
|
||||||
weight: null,
|
weight: null,
|
||||||
neutered: null,
|
sterilized: null,
|
||||||
},
|
})
|
||||||
requestDetails: null,
|
|
||||||
requestDoctor: null,
|
|
||||||
requestReview: null,
|
|
||||||
}),
|
|
||||||
actions: {
|
|
||||||
|
|
||||||
|
export const useFastRequestStore = defineStore('FastRequestStore', {
|
||||||
|
state: () => ({
|
||||||
|
endpoints: {
|
||||||
|
create: '/dashboard/fast-requests/create',
|
||||||
|
store: '/dashboard/fast-requests',
|
||||||
|
},
|
||||||
|
step: 1,
|
||||||
|
requestType: null,
|
||||||
|
requestCase: emptyCase(),
|
||||||
|
requestDetails: {
|
||||||
|
priority: null,
|
||||||
|
note: null,
|
||||||
|
},
|
||||||
|
historyFiles: [],
|
||||||
|
scanFiles: [],
|
||||||
|
doctorId: null,
|
||||||
|
pets: [],
|
||||||
|
doctors: [],
|
||||||
|
submitting: false,
|
||||||
|
submittedDocument: null,
|
||||||
|
}),
|
||||||
|
getters: {
|
||||||
|
selectedPet: (state) => state.pets.find((pet) => pet.id === state.requestCase.petId) || null,
|
||||||
|
selectedDoctor: (state) => state.doctors.find((doctor) => doctor.id === state.doctorId) || null,
|
||||||
|
canProceed: (state) => {
|
||||||
|
switch (state.step) {
|
||||||
|
case 1:
|
||||||
|
return !!state.requestType
|
||||||
|
case 2:
|
||||||
|
return !!state.requestCase.petId || (!!state.requestCase.name && !!state.requestCase.type)
|
||||||
|
case 3:
|
||||||
|
return !!state.requestDetails.priority
|
||||||
|
default:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
async fetchCreateData() {
|
||||||
|
const { data } = await window.axios.get(this.endpoints.create)
|
||||||
|
this.pets = data.pets
|
||||||
|
this.doctors = data.doctors
|
||||||
|
},
|
||||||
|
selectArchivePet(petId) {
|
||||||
|
this.requestCase = emptyCase()
|
||||||
|
this.requestCase.petId = petId
|
||||||
|
},
|
||||||
|
clearArchivePet() {
|
||||||
|
this.requestCase.petId = null
|
||||||
|
},
|
||||||
|
nextStep() {
|
||||||
|
if (this.step < 5) this.step++
|
||||||
|
},
|
||||||
|
prevStep() {
|
||||||
|
if (this.step > 1) this.step--
|
||||||
|
},
|
||||||
|
async submit() {
|
||||||
|
this.submitting = true
|
||||||
|
try {
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('modality', this.requestType)
|
||||||
|
formData.append('priority', this.requestDetails.priority)
|
||||||
|
if (this.requestDetails.note) formData.append('note', this.requestDetails.note)
|
||||||
|
|
||||||
|
if (this.requestCase.petId) {
|
||||||
|
formData.append('pet_id', this.requestCase.petId)
|
||||||
|
} else {
|
||||||
|
Object.entries(this.requestCase).forEach(([key, value]) => {
|
||||||
|
if (key !== 'petId' && value !== null && value !== '') {
|
||||||
|
formData.append(`pet[${key}]`, key === 'sterilized' ? (value ? 1 : 0) : value)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.doctorId) formData.append('doctor_id', this.doctorId)
|
||||||
|
|
||||||
|
this.historyFiles.forEach((file) => formData.append('history_files[]', file))
|
||||||
|
this.scanFiles.forEach((file) => formData.append('scan_files[]', file))
|
||||||
|
|
||||||
|
const { data } = await window.axios.post(this.endpoints.store, formData)
|
||||||
|
this.submittedDocument = data.document
|
||||||
|
return data.document
|
||||||
|
} finally {
|
||||||
|
this.submitting = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
reset() {
|
||||||
|
this.$reset()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
const sizes = ['B', 'KB', 'MB', 'GB']
|
||||||
|
|
||||||
|
export const formatFileSize = (bytes) => {
|
||||||
|
if (!bytes) {
|
||||||
|
return `0 ${sizes[0]}`
|
||||||
|
}
|
||||||
|
|
||||||
|
const k = 1024
|
||||||
|
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
||||||
|
const formattedSize = parseFloat((bytes / Math.pow(k, i)).toFixed(1))
|
||||||
|
|
||||||
|
return `${formattedSize} ${sizes[i]}`
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
use Inertia\Inertia;
|
use Inertia\Inertia;
|
||||||
use App\Http\Controllers\Client\AuthController;
|
use App\Http\Controllers\Client\AuthController;
|
||||||
|
use App\Http\Controllers\Client\FastRequestController;
|
||||||
use App\Http\Controllers\Client\Doctor\DashboardController;
|
use App\Http\Controllers\Client\Doctor\DashboardController;
|
||||||
use App\Http\Controllers\Client\Doctor\CaseController;
|
use App\Http\Controllers\Client\Doctor\CaseController;
|
||||||
use App\Http\Controllers\Client\Doctor\DocumentController;
|
use App\Http\Controllers\Client\Doctor\DocumentController;
|
||||||
@@ -38,6 +39,9 @@ Route::group(['prefix' => 'auth','middleware' => 'guest'] , function (){
|
|||||||
Route::group(['prefix' => 'dashboard' , 'middleware' => 'auth' , 'as' => 'dashboard.'] , function (){
|
Route::group(['prefix' => 'dashboard' , 'middleware' => 'auth' , 'as' => 'dashboard.'] , function (){
|
||||||
Route::get('/', [DashboardController::class, 'index'])->name('index');
|
Route::get('/', [DashboardController::class, 'index'])->name('index');
|
||||||
|
|
||||||
|
Route::get('fast-requests/create', [FastRequestController::class, 'create'])->name('fastRequests.create');
|
||||||
|
Route::post('fast-requests', [FastRequestController::class, 'store'])->name('fastRequests.store');
|
||||||
|
|
||||||
Route::resource('/documents', DocumentController::class);
|
Route::resource('/documents', DocumentController::class);
|
||||||
Route::resource('cases', CaseController::class);
|
Route::resource('cases', CaseController::class);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user