canina/frontend/application/components/PrescriptionUploadModal.tsx
parsa aghaei 8d1b786a24
All checks were successful
Deploy Canina / deploy (push) Successful in 1m50s
chore(brand): rename all Persian occurrences of کانینا to کنینا across the project
2026-08-16 16:40:08 +03:30

214 lines
9.5 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import React, { useState } from "react";
import { motion, AnimatePresence } from "motion/react";
import { Upload, CheckCircle2, X, Send, Stethoscope } from "lucide-react";
import { toast } from "sonner";
interface PrescriptionUploadModalProps {
isOpen: boolean;
onClose: () => void;
}
export default function PrescriptionUploadModal({ isOpen, onClose }: PrescriptionUploadModalProps) {
const [file, setFile] = useState<File | null>(null);
const [petName, setPetName] = useState("");
const [phone, setPhone] = useState("");
const [notes, setNotes] = useState("");
const [isSubmitting, setIsSubmitting] = useState(false);
const [isSuccess, setIsSuccess] = useState(false);
if (!isOpen) return null;
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.files && e.target.files[0]) {
setFile(e.target.files[0]);
}
};
const resetForm = () => {
setFile(null);
setPetName("");
setPhone("");
setNotes("");
setIsSubmitting(false);
setIsSuccess(false);
};
const handleClose = () => {
resetForm();
onClose();
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!file) {
toast.error("لطفاً تصویر یا فایل نسخه پزشکی را انتخاب کنید.");
return;
}
const cleanPhone = phone.trim();
if (!/^09\d{9}$/.test(cleanPhone)) {
toast.error("شماره همراه وارد شده معتبر نیست. (مثال: ۰۹۱۲۳۴۵۶۷۸۹)");
return;
}
setIsSubmitting(true);
try {
const api = (await import("../lib/services/api")).default;
const formData = new FormData();
formData.append("file", file);
const uploadRes = await api.post("/admin/media/upload", formData, {
headers: { "Content-Type": "multipart/form-data" }
});
const fileUrl = uploadRes.data?.data?.url || uploadRes.data?.url || uploadRes.data?.filename || file.name;
// Submit prescription to backend with petName and phone for automatic user & pet profile creation
await api.post("/prescriptions", {
fileUrl,
phone: cleanPhone,
petName: petName.trim() || undefined,
notes: notes.trim() || undefined
});
setIsSubmitting(false);
setIsSuccess(true);
toast.success("نسخه پزشکی شما با موفقیت ثبت و ارسال شد.");
} catch (error) {
console.error("[PrescriptionUpload] Submission error:", error);
setIsSubmitting(false);
toast.error("خطا در ارسال نسخه پزشکی. لطفاً مجدداً تلاش کنید.");
}
};
return (
<AnimatePresence>
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/60 backdrop-blur-sm" dir="rtl font-vazir">
<motion.div
initial={{ opacity: 0, scale: 0.95 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.95 }}
className="bg-white rounded-3xl max-w-lg w-full overflow-hidden shadow-2xl border border-medical-gray-200"
>
{/* Header */}
<div className="bg-canina-blue text-white p-6 relative">
<button
onClick={handleClose}
className="absolute left-5 top-5 text-white/80 hover:text-white bg-white/10 p-1.5 rounded-full transition-colors"
>
<X className="w-5 h-5" />
</button>
<div className="flex items-center gap-3">
<div className="w-12 h-12 rounded-2xl bg-white/20 flex items-center justify-center">
<Stethoscope className="w-6 h-6 text-white" />
</div>
<div>
<h3 className="text-lg font-black font-vazir">خرید سریع با نسخه دامپزشک</h3>
<p className="text-xs text-blue-100 mt-0.5">آپلود نسخه برای تامین فوری و مشاوره تخصصی</p>
</div>
</div>
</div>
{/* Body */}
<div className="p-6">
{isSuccess ? (
<div className="text-center py-8 space-y-4">
<div className="w-16 h-16 bg-green-100 text-green-600 rounded-full flex items-center justify-center mx-auto">
<CheckCircle2 className="w-10 h-10" />
</div>
<h4 className="text-lg font-black text-medical-gray-900">نسخه با موفقیت ثبت شد</h4>
<p className="text-xs text-medical-gray-500 max-w-sm mx-auto leading-relaxed">
کارشناسان و دامپزشکان کنینا نسخه شما را بررسی کرده و ظرف حداکثر ۱۵ دقیقه جهت هماهنگی ارسال دارو با شما تماس خواهند گرفت.
</p>
<button
onClick={handleClose}
className="px-6 py-2.5 bg-canina-blue text-white rounded-xl text-xs font-black hover:bg-canina-dark transition-all"
>
متوجه شدم
</button>
</div>
) : (
<form onSubmit={handleSubmit} className="space-y-4">
{/* Upload Zone */}
<div>
<label className="block text-xs font-black text-medical-gray-700 mb-2">تصویر نسخه پزشکی *</label>
<div className="border-2 border-dashed border-medical-gray-300 hover:border-canina-blue rounded-2xl p-4 text-center cursor-pointer transition-colors relative bg-medical-gray-50/50">
<input
type="file"
accept="image/*,.pdf"
onChange={handleFileChange}
className="absolute inset-0 opacity-0 cursor-pointer"
/>
<Upload className="w-8 h-8 text-medical-gray-400 mx-auto mb-2" />
{file ? (
<span className="text-xs font-bold text-canina-blue block">{file.name}</span>
) : (
<>
<span className="text-xs font-bold text-medical-gray-600 block">کلیک کنید یا فایل نسخه را بکشید</span>
<span className="text-[10px] text-medical-gray-400 block mt-1">فرمتهای JPG، PNG یا PDF (حداکثر ۵ مگابایت)</span>
</>
)}
</div>
</div>
<div className="grid grid-cols-2 gap-3">
<div>
<label className="block text-xs font-black text-medical-gray-700 mb-1.5">نام پت (اختیاری)</label>
<input
type="text"
value={petName}
onChange={(e) => setPetName(e.target.value)}
placeholder="مثال: لوسی"
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-xl px-3 py-2 text-xs font-bold focus:ring-2 focus:ring-canina-blue/20 outline-none"
/>
</div>
<div>
<label className="block text-xs font-black text-medical-gray-700 mb-1.5">شماره همراه جهت هماهنگی *</label>
<input
type="tel"
required
value={phone}
onChange={(e) => setPhone(e.target.value)}
placeholder="۰۹۱۲..."
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-xl px-3 py-2 text-xs font-bold focus:ring-2 focus:ring-canina-blue/20 outline-none text-left"
dir="ltr"
/>
</div>
</div>
<div>
<label className="block text-xs font-black text-medical-gray-700 mb-1.5">توضیحات تکمیلی (اختیاری)</label>
<textarea
rows={2}
value={notes}
onChange={(e) => setNotes(e.target.value)}
placeholder="اگر توضیحات خاصی درباره سابقه درمانی پت دارید بنویسید..."
className="w-full bg-medical-gray-50 border border-medical-gray-200 rounded-xl px-3 py-2 text-xs font-bold focus:ring-2 focus:ring-canina-blue/20 outline-none"
/>
</div>
<div className="pt-2 flex items-center justify-end gap-3 border-t border-medical-gray-100">
<button
type="button"
onClick={handleClose}
className="px-4 py-2.5 rounded-xl text-xs font-bold text-medical-gray-500 hover:bg-medical-gray-100 transition-colors"
>
انصراف
</button>
<button
type="submit"
disabled={isSubmitting}
className="px-6 py-2.5 bg-canina-blue text-white rounded-xl text-xs font-black hover:bg-canina-dark transition-all flex items-center gap-2 shadow-md shadow-canina-blue/20 disabled:opacity-50"
>
<Send className="w-3.5 h-3.5" />
<span>{isSubmitting ? "در حال ارسال..." : "ارسال نسخه و سفارش سریع"}</span>
</button>
</div>
</form>
)}
</div>
</motion.div>
</div>
</AnimatePresence>
);
}