feat(prescriptions & auth): auto-create user and pet profile on prescription submit, validate mobile numbers with regex, allow public upload and auto-reset form state
Some checks are pending
Deploy Canina / deploy (push) Waiting to run
Some checks are pending
Deploy Canina / deploy (push) Waiting to run
This commit is contained in:
parent
b617331683
commit
4e304944ef
@ -30,9 +30,8 @@ export class MediaController {
|
||||
return { success: true, data };
|
||||
}
|
||||
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@Post('upload')
|
||||
@ApiOperation({ summary: 'آپلود فایل جدید' })
|
||||
@ApiOperation({ summary: 'آپلود فایل جدید (عمومی/ادمین)' })
|
||||
@UseInterceptors(FileInterceptor('file'))
|
||||
async uploadFile(@UploadedFile() file: Express.Multer.File) {
|
||||
if (!file) throw new BadRequestException('File is missing');
|
||||
|
||||
@ -31,7 +31,14 @@ export class PrescriptionsController {
|
||||
@ApiOperation({ summary: 'بارگذاری نسخه جدید توسط کاربر' })
|
||||
create(
|
||||
@Req() req: UserReqPayload,
|
||||
@Body() body: { petId?: string; fileUrl: string; notes?: string },
|
||||
@Body()
|
||||
body: {
|
||||
petId?: string;
|
||||
petName?: string;
|
||||
phone?: string;
|
||||
fileUrl: string;
|
||||
notes?: string;
|
||||
},
|
||||
) {
|
||||
const userId = req.user?.id || req.user?.userId || null;
|
||||
return this.prescriptionsService.create(userId, body);
|
||||
|
||||
@ -11,17 +11,73 @@ export class PrescriptionsService {
|
||||
|
||||
async create(
|
||||
userId: string | null,
|
||||
data: { petId?: string; fileUrl: string; notes?: string },
|
||||
data: {
|
||||
petId?: string;
|
||||
petName?: string;
|
||||
phone?: string;
|
||||
fileUrl: string;
|
||||
notes?: string;
|
||||
},
|
||||
) {
|
||||
let finalUserId = userId;
|
||||
let finalPetId = data.petId || null;
|
||||
|
||||
// 1. If phone is provided, find or create user automatically
|
||||
if (data.phone) {
|
||||
const cleanPhone = data.phone.trim();
|
||||
let user = await this.prisma.user.findFirst({
|
||||
where: { mobile: cleanPhone },
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
// Create user with default role Customer
|
||||
user = await this.prisma.user.create({
|
||||
data: {
|
||||
mobile: cleanPhone,
|
||||
role: 'Customer',
|
||||
firstName: data.petName ? `سرپرست ${data.petName}` : 'کاربر',
|
||||
lastName: 'کانینا',
|
||||
},
|
||||
});
|
||||
}
|
||||
finalUserId = user.id;
|
||||
|
||||
// 2. If petName is provided and user exists, find or create pet
|
||||
if (data.petName && !finalPetId) {
|
||||
const cleanPetName = data.petName.trim();
|
||||
let pet = await this.prisma.pet.findFirst({
|
||||
where: {
|
||||
userId: user.id,
|
||||
name: cleanPetName,
|
||||
},
|
||||
});
|
||||
|
||||
if (!pet) {
|
||||
pet = await this.prisma.pet.create({
|
||||
data: {
|
||||
userId: user.id,
|
||||
name: cleanPetName,
|
||||
type: 'سگ', // Default pet type for prescription consultation
|
||||
breed: 'نامشخص',
|
||||
age: 1,
|
||||
weight: 5.0,
|
||||
activityLevel: 'متوسط',
|
||||
},
|
||||
});
|
||||
}
|
||||
finalPetId = pet.id;
|
||||
}
|
||||
}
|
||||
|
||||
return this.prisma.prescription.create({
|
||||
data: {
|
||||
userId: userId || undefined,
|
||||
petId: data.petId,
|
||||
userId: finalUserId || undefined,
|
||||
petId: finalPetId || undefined,
|
||||
fileUrl: data.fileUrl,
|
||||
notes: data.notes,
|
||||
status: 'PENDING',
|
||||
},
|
||||
include: { pet: true },
|
||||
include: { user: true, pet: true },
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -35,10 +35,17 @@ export default function ContactFormClient() {
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (!name.trim() || !phone.trim() || !message.trim()) {
|
||||
const cleanPhone = phone.trim();
|
||||
if (!name.trim() || !cleanPhone || !message.trim()) {
|
||||
setError("لطفاً نام، شماره موبایل و متن پیام را وارد کنید.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!/^09\d{9}$/.test(cleanPhone)) {
|
||||
setError("شماره همراه وارد شده معتبر نیست. (مثال: ۰۹۱۲۳۴۵۶۷۸۹)");
|
||||
return;
|
||||
}
|
||||
|
||||
setError("");
|
||||
setLoading(true);
|
||||
|
||||
|
||||
@ -25,14 +25,30 @@ export default function PrescriptionUploadModal({ isOpen, onClose }: Prescriptio
|
||||
}
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
if (!phone) {
|
||||
toast.error("لطفاً شماره تماس جهت پیگیری را وارد نمایید.");
|
||||
|
||||
const cleanPhone = phone.trim();
|
||||
if (!/^09\d{9}$/.test(cleanPhone)) {
|
||||
toast.error("شماره همراه وارد شده معتبر نیست. (مثال: ۰۹۱۲۳۴۵۶۷۸۹)");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -46,10 +62,12 @@ export default function PrescriptionUploadModal({ isOpen, onClose }: Prescriptio
|
||||
});
|
||||
const fileUrl = uploadRes.data?.data?.url || uploadRes.data?.url || uploadRes.data?.filename || file.name;
|
||||
|
||||
// Submit prescription to backend prescriptions endpoint
|
||||
// Submit prescription to backend with petName and phone for automatic user & pet profile creation
|
||||
await api.post("/prescriptions", {
|
||||
fileUrl,
|
||||
notes: `نام پت: ${petName || "ثبت نشده"} | شماره تماس: ${phone} | توضیحات: ${notes || "ندارد"}`
|
||||
phone: cleanPhone,
|
||||
petName: petName.trim() || undefined,
|
||||
notes: notes.trim() || undefined
|
||||
});
|
||||
|
||||
setIsSubmitting(false);
|
||||
@ -58,8 +76,7 @@ export default function PrescriptionUploadModal({ isOpen, onClose }: Prescriptio
|
||||
} catch (error) {
|
||||
console.error("[PrescriptionUpload] Submission error:", error);
|
||||
setIsSubmitting(false);
|
||||
setIsSuccess(true); // Fallback friendly UX
|
||||
toast.success("نسخه پزشکی دریافت شد و جهت بررسی در صف کارشناسان قرار گرفت.");
|
||||
toast.error("خطا در ارسال نسخه پزشکی. لطفاً مجدداً تلاش کنید.");
|
||||
}
|
||||
};
|
||||
|
||||
@ -75,7 +92,7 @@ export default function PrescriptionUploadModal({ isOpen, onClose }: Prescriptio
|
||||
{/* Header */}
|
||||
<div className="bg-canina-blue text-white p-6 relative">
|
||||
<button
|
||||
onClick={onClose}
|
||||
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" />
|
||||
@ -103,7 +120,7 @@ export default function PrescriptionUploadModal({ isOpen, onClose }: Prescriptio
|
||||
کارشناسان و دامپزشکان کانینا نسخه شما را بررسی کرده و ظرف حداکثر ۱۵ دقیقه جهت هماهنگی ارسال دارو با شما تماس خواهند گرفت.
|
||||
</p>
|
||||
<button
|
||||
onClick={() => { setIsSuccess(false); onClose(); }}
|
||||
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"
|
||||
>
|
||||
متوجه شدم
|
||||
@ -172,7 +189,7 @@ export default function PrescriptionUploadModal({ isOpen, onClose }: Prescriptio
|
||||
<div className="pt-2 flex items-center justify-end gap-3 border-t border-medical-gray-100">
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
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"
|
||||
>
|
||||
انصراف
|
||||
|
||||
Loading…
Reference in New Issue
Block a user