From cb033ba1bedcf498d31ed290293cb0dc164ab08d Mon Sep 17 00:00:00 2001 From: Samandar Turgunboyev Date: Fri, 7 Nov 2025 17:20:36 +0500 Subject: [PATCH] update --- src/pages/seo/ui/Seo.tsx | 2 +- src/pages/tours/lib/form.ts | 31 +- src/pages/tours/lib/type.ts | 29 +- src/pages/tours/ui/StepOne.tsx | 678 ++++++++++++---------- src/pages/tours/ui/TicketsImagesModel.tsx | 163 +++--- 5 files changed, 497 insertions(+), 406 deletions(-) diff --git a/src/pages/seo/ui/Seo.tsx b/src/pages/seo/ui/Seo.tsx index f65f449..a0a87aa 100644 --- a/src/pages/seo/ui/Seo.tsx +++ b/src/pages/seo/ui/Seo.tsx @@ -496,7 +496,7 @@ export default function Seo() { - - - ))} + + form.setValue("ticket_itinerary", [ + ...current, + { + ticket_itinerary_image: [{ image: file }], + title, + title_ru: titleRu, + duration, + ticket_itinerary_destinations: [ + { name: dest, name_ru: destRu }, + ], + }, + ]); + + imgInput.value = ""; + titleInput.value = ""; + titleRuInput.value = ""; + durationInput.value = ""; + destInput.value = ""; + destRuInput.value = ""; + } + }} + className="h-12" + > + {t("Qo'shish")} + + - {/* yangi yo‘nalish qo‘shish formasi */} -
- - - - - - - - - - - - - - - -
- - - - - )} + + + ); + }} />
diff --git a/src/pages/tours/ui/TicketsImagesModel.tsx b/src/pages/tours/ui/TicketsImagesModel.tsx index d585387..a233d9b 100644 --- a/src/pages/tours/ui/TicketsImagesModel.tsx +++ b/src/pages/tours/ui/TicketsImagesModel.tsx @@ -12,12 +12,18 @@ import { ImagePlus, XIcon } from "lucide-react"; import { useEffect, useId, useState } from "react"; import { useTranslation } from "react-i18next"; +interface ImageObject { + id?: number; + image: string | File; +} + interface TicketsImagesModelProps { - form: any; // React Hook Form control + form: any; name: string; label?: string; - imageUrl?: string | string[] | undefined; + imageUrl?: string | File | ImageObject | (string | File | ImageObject)[]; multiple?: boolean; + includeId?: boolean; // agar true bo‘lsa — {id, image} jo‘natiladi } export default function TicketsImagesModel({ @@ -26,20 +32,92 @@ export default function TicketsImagesModel({ label = "Rasmlar", multiple = true, imageUrl, + includeId = false, }: TicketsImagesModelProps) { const { t } = useTranslation(); const [previews, setPreviews] = useState([]); const inputId = useId(); useEffect(() => { - if (imageUrl) { - const urls = Array.isArray(imageUrl) ? imageUrl : [imageUrl]; - setPreviews(urls); + if (!imageUrl) return; + const items = Array.isArray(imageUrl) ? imageUrl : [imageUrl]; - // 🔥 form bilan sinxronlash - form.setValue(name, urls); + if (multiple) { + const values: any[] = []; + const urls: string[] = []; + + items.forEach((item) => { + if (typeof item === "string") { + urls.push(item); + values.push(includeId ? { image: item } : item); + } else if (item instanceof File) { + urls.push(URL.createObjectURL(item)); + values.push(includeId ? { image: item } : item); + } else if (typeof item === "object" && "image" in item) { + const img = item.image; + urls.push(typeof img === "string" ? img : URL.createObjectURL(img)); + values.push(includeId ? { id: item.id, image: img } : img); + } + }); + + setPreviews(urls); + form.setValue(name, values); + } else { + const single = items[0]; + let url = ""; + let valueToSet: any = null; + + if (typeof single === "string") { + url = single; + valueToSet = single; + } else if (single instanceof File) { + url = URL.createObjectURL(single); + valueToSet = single; + } else if (typeof single === "object" && "image" in single) { + const img = single.image; + url = typeof img === "string" ? img : URL.createObjectURL(img); + valueToSet = + includeId && single.id ? { id: single.id, image: img } : img; + } + + setPreviews(url ? [url] : []); + form.setValue(name, valueToSet); // 🔥 array emas } - }, [imageUrl, form, name]); + }, [imageUrl, form, name, multiple, includeId]); + + const handleFileChange = (files: FileList | null) => { + if (!files) return; + const newFiles = Array.from(files); + + if (multiple) { + const current = form.getValues(name) || []; + const newValue = includeId + ? [...current, ...newFiles.map((f) => ({ image: f }))] + : [...current, ...newFiles]; + form.setValue(name, newValue); + setPreviews([ + ...previews, + ...newFiles.map((f) => URL.createObjectURL(f)), + ]); + } else { + const singleFile = newFiles[0]; + const valueToSet = includeId ? { image: singleFile } : singleFile; + form.setValue(name, valueToSet); // 🔥 array emas + setPreviews(singleFile ? [URL.createObjectURL(singleFile)] : []); + } + }; + + const handleDelete = (index: number) => { + if (multiple) { + const current = form.getValues(name) || []; + const newValue = current.filter((_: any, i: number) => i !== index); + form.setValue(name, newValue); + setPreviews(previews.filter((_, i) => i !== index)); + } else { + form.setValue(name, null); + setPreviews([]); + } + }; return ( ( -
- {/* File Input */} { - const newFiles = e.target.files - ? Array.from(e.target.files) - : []; - const currentValue = form.getValues(name) || []; - const currentUrls = previews || []; - - if (multiple) { - // ✅ eski URL’larni va yangi fayllarni birlashtirish - const combined = [...currentValue, ...newFiles]; - form.setValue(name, combined); - - const newPreviews = [ - ...currentUrls, - ...newFiles.map((file: File) => - URL.createObjectURL(file), - ), - ]; - setPreviews(newPreviews); - } else { - // ✅ bitta rasm holati - const singleFile = newFiles[0] || null; - form.setValue(name, singleFile); - setPreviews( - singleFile ? [URL.createObjectURL(singleFile)] : [], - ); - } - }} + onChange={(e) => handleFileChange(e.target.files)} /> - {/* Upload Zone */} - {/* Preview Images */} {previews.length > 0 && (
{previews.map((src, i) => ( @@ -121,28 +164,9 @@ export default function TicketsImagesModel({ alt={`preview-${i}`} className="object-cover w-full h-full" /> - - {/* Delete Button */}
- )}