229 lines
14 KiB
Vue
229 lines
14 KiB
Vue
<script setup>
|
||
import { computed, onMounted, onUnmounted, ref } from 'vue';
|
||
import { useForm } from '@inertiajs/inertia-vue3';
|
||
import { Inertia } from '@inertiajs/inertia';
|
||
import Dialog from 'primevue/dialog';
|
||
import { toast } from 'vue3-toastify';
|
||
import ClientLayout from '../../../layouts/ClientLayout.vue';
|
||
import UiButton from '../../../components/ui/UiButton.vue';
|
||
import CaseSpecsCard from '../../../components/dashboard/cases/CaseSpecsCard.vue';
|
||
import ReportSectionCard from '../../../components/dashboard/cases/ReportSectionCard.vue';
|
||
import FileUploadBox from '../../../components/dashboard/fastRequest/FileUploadBox.vue';
|
||
import { modalityTitle } from '../../../data/fastRequestOptions';
|
||
import { convertEnglishToPersian } from '../../../utils/numberUtils';
|
||
import { downloadFiles } from '../../../utils/fileUtils';
|
||
|
||
const props = defineProps({
|
||
case: {
|
||
type: Object,
|
||
required: true
|
||
},
|
||
canReport: {
|
||
type: Boolean,
|
||
required: true
|
||
}
|
||
});
|
||
|
||
const petRows = computed(() => [
|
||
{ label: 'نام', value: props.case.pet?.name },
|
||
{ label: 'نوع', value: props.case.pet?.type },
|
||
{ label: 'جنسیت', value: props.case.pet?.gender },
|
||
{ label: 'نژاد', value: props.case.pet?.breed },
|
||
{ label: 'سن', value: props.case.pet?.age !== null && props.case.pet?.age !== undefined ? `${convertEnglishToPersian(props.case.pet.age)} سال` : null },
|
||
{ label: 'وزن', value: props.case.pet?.weight ? `${convertEnglishToPersian(props.case.pet.weight)} کیلو گرم` : null },
|
||
{ label: 'وضعیت عقیمسازی', value: props.case.pet?.sterilized === null ? null : (props.case.pet?.sterilized ? 'عقیم شده' : 'عقیم نشده') },
|
||
]);
|
||
|
||
const requesterRows = computed(() => {
|
||
const requester = props.case.requester || {};
|
||
return [
|
||
{ label: 'نوع', value: requester.level },
|
||
{ label: 'نام و نام خانوادگی', value: requester.fullname },
|
||
{ label: 'شماره موبایل', value: requester.phone ? convertEnglishToPersian(requester.phone) : null },
|
||
{ label: 'تخصص', value: requester.specialty },
|
||
{ label: 'کلینیک', value: requester.clinic_name },
|
||
{ label: 'تلفن کلینیک', value: requester.clinic_phone ? convertEnglishToPersian(requester.clinic_phone) : null },
|
||
{ label: 'محل اقامت', value: [requester.province, requester.city].filter(Boolean).join('، ') || null },
|
||
].filter((row) => row.value);
|
||
});
|
||
|
||
const remainingLabel = ref(null);
|
||
let timerId = null;
|
||
|
||
const updateRemaining = () => {
|
||
if (!props.case.processing_deadline) {
|
||
remainingLabel.value = null;
|
||
return;
|
||
}
|
||
|
||
const diff = Math.floor((new Date(props.case.processing_deadline).getTime() - Date.now()) / 1000);
|
||
|
||
if (diff <= 0) {
|
||
remainingLabel.value = 'اتمام فرصت رسیدگی';
|
||
return;
|
||
}
|
||
|
||
const pad = (value) => String(value).padStart(2, '0');
|
||
remainingLabel.value = convertEnglishToPersian(`${pad(Math.floor(diff / 3600))}:${pad(Math.floor((diff % 3600) / 60))}:${pad(diff % 60)}`) + ' تا پایان زمان ثبت گزارش';
|
||
};
|
||
|
||
onMounted(() => {
|
||
updateRemaining();
|
||
timerId = setInterval(updateRemaining, 1000);
|
||
});
|
||
|
||
onUnmounted(() => clearInterval(timerId));
|
||
|
||
const reportForm = useForm({
|
||
document_id: props.case.id,
|
||
technique: props.case.report?.technique || '',
|
||
finding: props.case.report?.finding || '',
|
||
diagnose: props.case.report?.diagnose || '',
|
||
recommendation: props.case.report?.recommendation || '',
|
||
files: [],
|
||
});
|
||
|
||
const submitReport = () => {
|
||
reportForm.post('/dashboard/reports', {
|
||
forceFormData: true,
|
||
onError: () => toast.error('لطفا موارد مشخصشده را بررسی و اصلاح کنید.'),
|
||
});
|
||
};
|
||
|
||
const rejectModalVisible = ref(false);
|
||
const rejectReason = ref('');
|
||
const rejecting = ref(false);
|
||
|
||
const submitReject = () => {
|
||
if (!rejectReason.value.trim()) {
|
||
toast.error('لطفا دلیل رد درخواست را وارد کنید.');
|
||
return;
|
||
}
|
||
|
||
rejecting.value = true;
|
||
Inertia.post(`/dashboard/cases/${props.case.id}/reject`, { reason: rejectReason.value }, {
|
||
onFinish: () => {
|
||
rejecting.value = false;
|
||
rejectModalVisible.value = false;
|
||
},
|
||
});
|
||
};
|
||
</script>
|
||
|
||
<template>
|
||
<ClientLayout>
|
||
<div class="flex flex-col gap-4">
|
||
<header class="bg-white border border-complement-50 rounded-2xl px-4 py-3 flex items-center justify-between flex-wrap gap-2">
|
||
<h1 class="text-[13px] font-bold text-[#3E4C63]">پرونده شماره {{ convertEnglishToPersian(props.case.code) }}</h1>
|
||
<div class="flex items-center gap-3 text-[11px]">
|
||
<span v-if="canReport && remainingLabel" class="font-semibold text-[#7A1419]">{{ remainingLabel }}</span>
|
||
<span v-else class="font-normal text-[#576989]">وضعیت فعلی</span>
|
||
<span class="font-semibold text-[#3E4C63] border-r border-[#DEE3EB] pr-3">{{ props.case.status }}</span>
|
||
</div>
|
||
</header>
|
||
|
||
<div class="grid grid-cols-[16rem_1fr] max-lg:grid-cols-1 gap-4 items-start">
|
||
<aside class="flex flex-col gap-4">
|
||
<CaseSpecsCard title="مشخصات کیس" :rows="petRows" />
|
||
<CaseSpecsCard v-if="canReport && requesterRows.length" title="مشخصات درخواستدهنده" :rows="requesterRows" />
|
||
</aside>
|
||
|
||
<div class="flex flex-col gap-4">
|
||
<section class="bg-white border border-complement-50 rounded-2xl p-4 flex flex-col gap-3">
|
||
<h2 class="flex items-center gap-2 text-[12px] font-bold text-[#3E4C63]">
|
||
<span class="w-1.5 h-1.5 rounded-full bg-[#7A1419]"></span>
|
||
جزئیات درخواست
|
||
</h2>
|
||
<div class="flex items-center gap-3 flex-wrap text-[11px]">
|
||
<p><span class="font-normal text-[#576989]">گزارش درخواستشده:</span> <span class="font-semibold text-[#7A1419]">{{ modalityTitle(props.case.modality) }}</span></p>
|
||
<span class="text-[#DEE3EB]">|</span>
|
||
<p><span class="font-normal text-[#576989]">نوع درخواست:</span> <span class="font-semibold text-[#7A1419]">{{ props.case.priority }}</span></p>
|
||
<span class="text-[#DEE3EB]">|</span>
|
||
<p><span class="font-normal text-[#576989]">زمان درخواست:</span> <span class="font-semibold text-[#3E4C63]">{{ props.case.submitted_at ? convertEnglishToPersian(props.case.submitted_at) : '-' }}</span></p>
|
||
</div>
|
||
<div class="text-[12px] leading-6">
|
||
<p class="font-normal text-[#576989]">تاریخچه بیماری:</p>
|
||
<p class="font-normal text-[#3E4C63] whitespace-pre-line">{{ props.case.note || 'توضیحی ثبت نشده است.' }}</p>
|
||
</div>
|
||
<div class="flex items-center gap-3 flex-wrap pt-2 border-t border-[#EBEBEB]">
|
||
<UiButton label="دانلود فایل درخواست" severity="secondary" size="small" :disabled="!props.case.primary_files.length" @click="downloadFiles(props.case.primary_files)" />
|
||
<UiButton label="دانلود سوابق پزشکی قبلی" severity="secondary" size="small" :disabled="!props.case.history_files.length" @click="downloadFiles(props.case.history_files)" />
|
||
<UiButton v-if="!canReport && props.case.report" label="دانلود فایل گزارش" severity="secondary" size="small" :disabled="!props.case.report.attachments.length" @click="downloadFiles(props.case.report.attachments)" />
|
||
</div>
|
||
</section>
|
||
|
||
<p v-if="props.case.rejection_reason" class="bg-[#F8E9EA] border border-[#E7C3C5] rounded-2xl p-4 text-[12px] font-normal text-[#7A1419] leading-6">
|
||
دلیل رد درخواست: {{ props.case.rejection_reason }}
|
||
</p>
|
||
|
||
<template v-if="canReport">
|
||
<ReportSectionCard v-model="reportForm.technique" title="تکنیک (Technique)" editable placeholder="شرح تکنیک را وارد کنید" :error="reportForm.errors.technique" />
|
||
<ReportSectionCard v-model="reportForm.finding" title="یافتهها (Finding)" editable placeholder="یافتهها را وارد کنید" :error="reportForm.errors.finding" />
|
||
<ReportSectionCard v-model="reportForm.diagnose" title="تشخیص (Diagnose)" editable placeholder="تشخیص را وارد کنید" :error="reportForm.errors.diagnose" />
|
||
<ReportSectionCard v-model="reportForm.recommendation" title="پیشنهادات (Recommendation)" editable placeholder="پیشنهادات را وارد کنید" :error="reportForm.errors.recommendation" />
|
||
|
||
<section class="bg-white border border-complement-50 rounded-2xl p-4 flex flex-col gap-3">
|
||
<h2 class="flex items-center gap-2 text-[12px] font-bold text-[#3E4C63]">
|
||
<span class="w-1.5 h-1.5 rounded-full bg-[#7A1419]"></span>
|
||
آپلود فایل و مستندات
|
||
</h2>
|
||
<FileUploadBox v-model="reportForm.files" title="آپلود فایل و مستندات گزارش" />
|
||
</section>
|
||
|
||
<footer class="bg-white border border-complement-50 rounded-2xl p-4 flex items-center justify-between flex-wrap gap-3">
|
||
<p class="text-[11px] font-normal text-[#576989]">با تایید گزارش و یا رد درخواست دیگر امکان ویرایش آن را نخواهید داشت.</p>
|
||
<div class="flex items-center gap-3">
|
||
<button type="button" class="text-[11px] font-semibold text-[#7A1419]" @click="rejectModalVisible = true">رد درخواست</button>
|
||
<UiButton label="تایید و ثبت گزارش" severity="success" size="small" :loading="reportForm.processing" :disabled="reportForm.processing" @click="submitReport" />
|
||
</div>
|
||
</footer>
|
||
</template>
|
||
|
||
<template v-else>
|
||
<ReportSectionCard :model-value="props.case.report?.technique || ''" title="تکنیک (Technique)" />
|
||
<ReportSectionCard :model-value="props.case.report?.finding || ''" title="یافتهها (Finding)" />
|
||
<ReportSectionCard :model-value="props.case.report?.diagnose || ''" title="تشخیص (Diagnose)" />
|
||
<ReportSectionCard :model-value="props.case.report?.recommendation || ''" title="پیشنهادات (Recommendation)" />
|
||
|
||
<section class="bg-white border border-complement-50 rounded-2xl p-4 flex flex-col gap-3">
|
||
<h2 class="flex items-center gap-2 text-[12px] font-bold text-[#3E4C63]">
|
||
<span class="w-1.5 h-1.5 rounded-full bg-[#7A1419]"></span>
|
||
آپلود فایل و مستندات
|
||
</h2>
|
||
<div v-if="props.case.report?.attachments?.length" class="flex flex-wrap gap-4">
|
||
<a v-for="file in props.case.report.attachments" :key="file.id" :href="file.url" target="_blank" class="relative w-[5rem] h-[5rem] rounded-lg border border-gray-200 overflow-hidden">
|
||
<img :alt="file.filename" :src="file.url" class="absolute top-0 left-0 w-full h-full object-cover" />
|
||
</a>
|
||
</div>
|
||
<p v-else class="w-full min-h-[5rem] bg-[#F8FAFC] border border-[#DEE3EB] rounded-xl p-4 text-[12px] font-normal text-[#576989] leading-6 flex items-center justify-center text-center">
|
||
عکسی برای این پرونده آپلود نشده است.
|
||
</p>
|
||
</section>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<Dialog v-model:visible="rejectModalVisible" :modal="true" :closable="true" :dismissableMask="false" :style="{ width: '480px' }" class="fast-request-dialog" :pt="{ header: '!bg-client-primaryBlue' }">
|
||
<template #header>
|
||
<div class="w-full flex items-center justify-start">
|
||
<h2 class="text-[14px] font-[700] text-[#576989]">رد درخواست</h2>
|
||
</div>
|
||
</template>
|
||
<div class="w-full flex flex-col gap-3 border-t border-[#EBEBEB] pt-4">
|
||
<p class="text-[12px] font-normal text-[#3E4C63] leading-6">پس از رد درخواست، مبلغ فاکتور به کیف پول درخواستکننده بازگردانده میشود.</p>
|
||
<textarea
|
||
v-model="rejectReason"
|
||
rows="4"
|
||
placeholder="دلیل رد درخواست را وارد کنید"
|
||
class="w-full bg-[#F8FAFC] border border-[#DEE3EB] rounded-xl p-4 text-[12px] font-normal text-[#3E4C63] leading-6 outline-none focus:border-[#C1D0E8] resize-y"
|
||
></textarea>
|
||
</div>
|
||
<div class="w-full pt-3 flex items-center justify-start gap-2 border-t border-[#EBEBEB]">
|
||
<UiButton label="رد درخواست" severity="danger" size="small" :loading="rejecting" :disabled="rejecting" @click="submitReject" />
|
||
<UiButton label="انصراف" severity="secondary" size="small" :disabled="rejecting" @click="rejectModalVisible = false" />
|
||
</div>
|
||
</Dialog>
|
||
</ClientLayout>
|
||
</template>
|