diff --git a/app/Http/Controllers/Client/FastRequestController.php b/app/Http/Controllers/Client/FastRequestController.php
index 3c46dc7..93315bf 100644
--- a/app/Http/Controllers/Client/FastRequestController.php
+++ b/app/Http/Controllers/Client/FastRequestController.php
@@ -2,32 +2,32 @@
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 App\Services\Shift\ShiftScheduleService;
use Illuminate\Support\Facades\Auth;
class FastRequestController extends Controller
{
protected FastRequestService $fastRequestService;
- public function __construct(FastRequestService $fastRequestService)
+ protected ShiftScheduleService $shiftScheduleService;
+
+ public function __construct(FastRequestService $fastRequestService, ShiftScheduleService $shiftScheduleService)
{
$this->fastRequestService = $fastRequestService;
+ $this->shiftScheduleService = $shiftScheduleService;
}
public function create()
{
$pets = Auth::user()->pets()->latest()->get();
- $doctors = User::where('level', UserEnum::Doctor->value)
- ->latest()
- ->get();
+ $doctors = $this->shiftScheduleService->availableDoctorsAt(now());
return response()->json([
'pets' => PetArchiveResource::collection($pets),
diff --git a/app/Http/Controllers/Client/PaymentController.php b/app/Http/Controllers/Client/PaymentController.php
index e6ac324..c5c2195 100644
--- a/app/Http/Controllers/Client/PaymentController.php
+++ b/app/Http/Controllers/Client/PaymentController.php
@@ -17,6 +17,7 @@ use App\Services\Payment\ZibalPaymentService;
use App\Services\Wallet\WalletService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
+use Illuminate\Support\Facades\DB;
class PaymentController extends Controller
{
@@ -68,7 +69,7 @@ class PaymentController extends Controller
}
if ($wasPending && $payment->status === PaymentStatusEnum::Paid) {
- $this->settle($payment);
+ DB::transaction(fn () => $this->settle($payment));
}
session()->flash('payment_result', [
diff --git a/app/Http/Requests/Client/FastRequestRequest.php b/app/Http/Requests/Client/FastRequestRequest.php
index bb1b564..62957d7 100644
--- a/app/Http/Requests/Client/FastRequestRequest.php
+++ b/app/Http/Requests/Client/FastRequestRequest.php
@@ -8,6 +8,7 @@ use App\Enums\DocumentModalityEnum;
use App\Enums\DocumentPriorityEnum;
use App\Enums\PetGenderEnum;
use App\Enums\PetTypeEnum;
+use App\Enums\UserEnum;
class FastRequestRequest extends FormRequest
{
@@ -22,7 +23,7 @@ class FastRequestRequest extends FormRequest
'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_id' => ['nullable', Rule::exists('pets', 'id')->where('owner_id', $this->user()->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())],
@@ -31,7 +32,7 @@ class FastRequestRequest extends FormRequest
'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'],
+ 'doctor_id' => ['nullable', Rule::exists('users', 'id')->where('level', UserEnum::Doctor->value)],
'history_files' => ['nullable', 'array'],
'history_files.*' => ['file', 'image', 'max:10240'],
'scan_files' => ['nullable', 'array'],
diff --git a/app/Services/Document/CaseService.php b/app/Services/Document/CaseService.php
index c9d95b3..411a435 100644
--- a/app/Services/Document/CaseService.php
+++ b/app/Services/Document/CaseService.php
@@ -156,12 +156,13 @@ class CaseService
->where('invoices.paid_at', '>=', $monthStart);
$nextShift = $isDoctor ? $this->shiftScheduleService->nextShiftStart($user, now()) : null;
+ $onShift = $isDoctor && $this->shiftScheduleService->isOnShift($user, now());
return [
'pending_cases' => $this->pendingForUser($user)->count(),
'monthly_reviewed' => $reviewedQuery->count(),
'monthly_amount' => (int) $amountQuery->sum('invoices.amount'),
- 'next_shift_hours' => $nextShift ? (int) ceil(now()->diffInMinutes($nextShift, true) / 60) : null,
+ 'next_shift_hours' => $onShift ? 0 : ($nextShift ? (int) ceil(now()->diffInMinutes($nextShift, true) / 60) : null),
'is_doctor' => $isDoctor,
];
}
diff --git a/app/Services/Document/FastRequestService.php b/app/Services/Document/FastRequestService.php
index ea6eca6..bb9567d 100644
--- a/app/Services/Document/FastRequestService.php
+++ b/app/Services/Document/FastRequestService.php
@@ -71,7 +71,7 @@ class FastRequestService
protected function resolvePet(User $requester, array $data): Pet
{
if (!empty($data['pet_id'])) {
- return Pet::findOrFail($data['pet_id']);
+ return $requester->pets()->findOrFail($data['pet_id']);
}
$pet = $data['pet'];
diff --git a/app/Services/Shift/ShiftScheduleService.php b/app/Services/Shift/ShiftScheduleService.php
index d66f026..1d84a00 100644
--- a/app/Services/Shift/ShiftScheduleService.php
+++ b/app/Services/Shift/ShiftScheduleService.php
@@ -60,12 +60,17 @@ class ShiftScheduleService
->get();
}
+ public function isOnShift(User $doctor, Carbon $moment): bool
+ {
+ return $this->shiftsCoveringMoment($moment)->contains('user_id', $doctor->id);
+ }
+
public function autoSelectDoctor(Carbon $moment): ?User
{
$doctorIds = $this->shiftsCoveringMoment($moment)->pluck('user_id')->filter()->unique();
if ($doctorIds->isEmpty()) {
- return null;
+ return $this->doctorWithNearestShift($moment);
}
return User::where('level', UserEnum::Doctor->value)
@@ -81,6 +86,20 @@ class ShiftScheduleService
->first();
}
+ // fallback when nobody is on shift: the doctor whose next shift starts soonest
+ public function doctorWithNearestShift(Carbon $from): ?User
+ {
+ $doctorIds = Shift::active()->whereNotNull('user_id')->distinct()->pluck('user_id');
+
+ return User::where('level', UserEnum::Doctor->value)
+ ->whereIn('id', $doctorIds)
+ ->get()
+ ->map(fn (User $doctor) => ['doctor' => $doctor, 'start' => $this->nextShiftStart($doctor, $from)])
+ ->filter(fn (array $item) => $item['start'] !== null)
+ ->sortBy('start')
+ ->first()['doctor'] ?? null;
+ }
+
public function nextShiftStart(User $doctor, Carbon $from): ?Carbon
{
$shifts = Shift::active()->where('user_id', $doctor->id)->get();
diff --git a/resources/js/Client/components/dashboard/FastRequestModal.vue b/resources/js/Client/components/dashboard/FastRequestModal.vue
index 3ac591e..a4aa89b 100644
--- a/resources/js/Client/components/dashboard/FastRequestModal.vue
+++ b/resources/js/Client/components/dashboard/FastRequestModal.vue
@@ -11,10 +11,14 @@ import RequestDoctor from './fastRequest/RequestDoctor.vue';
import RequestReview from './fastRequest/RequestReview.vue';
import InvoiceModal from './fastRequest/InvoiceModal.vue';
import { useFastRequestStore } from '../../store/FastRequest';
+import { useAnimatedHeight } from '../../composables/useAnimatedHeight';
const visible = defineModel('visible');
const fastRequestStore = useFastRequestStore();
+const stepContentRef = ref(null);
+const stepContentHeight = useAnimatedHeight(stepContentRef);
+
const invoiceVisible = ref(false);
const invoiceDocument = ref(null);
@@ -83,7 +87,11 @@ const handleNext = async () => {
-
انتخاب در دسترس ترین پزشک جهت بررسی سریع درخواست شما
++ در حال حاضر پزشکی در شیفت نیست؛ درخواست شما به پزشک نزدیکترین شیفت آینده ارجاع داده میشود. +