post bug fix
This commit is contained in:
2
.env
2
.env
@@ -1 +1 @@
|
||||
VITE_API_URL=https://simple-travel.felixits.uz/api/v1/
|
||||
VITE_API_URL=https://api.simpletravel.uz/api/v1/
|
||||
@@ -6,7 +6,7 @@ interface NewsData {
|
||||
desc: string;
|
||||
title_ru: string;
|
||||
desc_ru: string;
|
||||
category: string;
|
||||
category: number | null;
|
||||
banner: File | undefined | string;
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ export const useNewsStore = create<NewsStore>((set) => ({
|
||||
stepOneData: {
|
||||
title: "",
|
||||
desc: "",
|
||||
category: "",
|
||||
category: null,
|
||||
banner: undefined,
|
||||
desc_ru: "",
|
||||
title_ru: "",
|
||||
@@ -31,7 +31,7 @@ export const useNewsStore = create<NewsStore>((set) => ({
|
||||
stepOneData: {
|
||||
title: "",
|
||||
desc: "",
|
||||
category: "",
|
||||
category: null,
|
||||
banner: undefined,
|
||||
desc_ru: "",
|
||||
title_ru: "",
|
||||
|
||||
@@ -18,12 +18,13 @@ export const newsForm = z.object({
|
||||
desc_ru: z.string().min(2, {
|
||||
message: "Kamida 2 ta belgidan iborat bo‘lishi kerak.",
|
||||
}),
|
||||
category: z.string().min(1, {
|
||||
category: z.number().min(1, {
|
||||
message: "Majburiy maydon",
|
||||
}),
|
||||
banner: fileSchema,
|
||||
});
|
||||
|
||||
// zod schema ni yangilaymiz
|
||||
export const newsPostForm = z.object({
|
||||
desc: z
|
||||
.string()
|
||||
@@ -36,17 +37,22 @@ export const newsPostForm = z.object({
|
||||
sections: z
|
||||
.array(
|
||||
z.object({
|
||||
image: fileSchema,
|
||||
text: z.string().min(1, { message: "Matn bo'sh bo'lmasligi kerak." }),
|
||||
text_ru: z
|
||||
.string()
|
||||
.min(1, { message: "Ruscha matn bo'sh bo'lmasligi kerak." }),
|
||||
image: z.union([z.instanceof(File), z.string()]).optional(),
|
||||
text: z.string().optional(),
|
||||
text_ru: z.string().optional(),
|
||||
}),
|
||||
)
|
||||
.min(1, { message: "Kamida bitta bo‘lim qo‘shing." }),
|
||||
.optional(),
|
||||
|
||||
post_tags: z
|
||||
.array(z.string().min(1, { message: "Teg bo'sh bo'lmasligi kerak." }))
|
||||
.array(
|
||||
z.object({
|
||||
name: z.string().min(1, { message: "Teg bo'sh bo'lmasligi kerak." }),
|
||||
name_ru: z
|
||||
.string()
|
||||
.min(1, { message: "Teg (RU) bo'sh bo'lmasligi kerak." }),
|
||||
}),
|
||||
)
|
||||
.min(1, { message: "Kamida bitta teg kiriting." }),
|
||||
});
|
||||
|
||||
|
||||
@@ -98,12 +98,8 @@ export interface NewsDetail {
|
||||
text_ru: string;
|
||||
text_uz: string;
|
||||
is_public: boolean;
|
||||
category: {
|
||||
name: string;
|
||||
name_ru: string;
|
||||
name_uz: string;
|
||||
};
|
||||
tag: [
|
||||
category: { id: number; name: string; name_ru: string; name_uz: string };
|
||||
post_tags: [
|
||||
{
|
||||
id: number;
|
||||
name: string;
|
||||
|
||||
@@ -4,7 +4,7 @@ import StepOne from "@/pages/news/ui/StepOne";
|
||||
import StepTwo from "@/pages/news/ui/StepTwo";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
|
||||
import { useMemo, useState } from "react";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
@@ -13,7 +13,7 @@ const AddNews = () => {
|
||||
const isEditMode = useMemo(() => !!id, [id]);
|
||||
const [step, setStep] = useState(1);
|
||||
const { t } = useTranslation();
|
||||
const { data } = useQuery({
|
||||
const { data, refetch } = useQuery({
|
||||
queryKey: ["news_detail", id],
|
||||
queryFn: () => getDetailNews(Number(id)),
|
||||
select(data) {
|
||||
@@ -22,6 +22,12 @@ const AddNews = () => {
|
||||
enabled: !!id,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (id) {
|
||||
refetch();
|
||||
}
|
||||
}, [id]);
|
||||
|
||||
return (
|
||||
<div className="p-8 w-full mx-auto bg-gray-900 text-white rounded-2xl shadow-lg">
|
||||
<h1 className="text-3xl font-bold mb-6">
|
||||
@@ -41,9 +47,16 @@ const AddNews = () => {
|
||||
</div>
|
||||
</div>
|
||||
{step === 1 && (
|
||||
<StepOne isEditMode={isEditMode} setStep={setStep} data={data!} />
|
||||
<StepOne
|
||||
isEditMode={isEditMode}
|
||||
setStep={setStep}
|
||||
data={data!}
|
||||
id={Number(id)}
|
||||
/>
|
||||
)}
|
||||
{step === 2 && (
|
||||
<StepTwo key={id} data={data!} isEditMode={isEditMode} id={id!} />
|
||||
)}
|
||||
{step === 2 && <StepTwo data={data!} isEditMode={isEditMode} id={id!} />}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"use client";
|
||||
import { deleteNews, getAllNews } from "@/pages/news/lib/api";
|
||||
import { deleteNews, getAllNews, updateNews } from "@/pages/news/lib/api";
|
||||
import { Badge } from "@/shared/ui/badge";
|
||||
import { Button } from "@/shared/ui/button";
|
||||
import { Card } from "@/shared/ui/card";
|
||||
@@ -10,12 +10,15 @@ import {
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/shared/ui/dialog";
|
||||
import { Switch } from "@/shared/ui/switch";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import clsx from "clsx";
|
||||
import {
|
||||
ChevronLeft,
|
||||
ChevronRight,
|
||||
Edit,
|
||||
Eye,
|
||||
EyeOff,
|
||||
FolderOpen,
|
||||
Loader2,
|
||||
PlusCircle,
|
||||
@@ -41,11 +44,33 @@ const News = () => {
|
||||
queryFn: () => getAllNews({ page: currentPage, page_size: 10 }),
|
||||
});
|
||||
|
||||
const { mutate, isPending } = useMutation({
|
||||
const { mutate: deleteMutate, isPending } = useMutation({
|
||||
mutationFn: (id: number) => deleteNews(id),
|
||||
onSuccess: () => {
|
||||
setDeleteId(null);
|
||||
queryClient.refetchQueries({ queryKey: ["all_news"] });
|
||||
toast.success(t("Yangilik o'chirildi"), {
|
||||
richColors: true,
|
||||
position: "top-center",
|
||||
});
|
||||
},
|
||||
onError: () => {
|
||||
toast.error(t("Xatolik yuz berdi"), {
|
||||
richColors: true,
|
||||
position: "top-center",
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const { mutate: togglePublicMutate } = useMutation({
|
||||
mutationFn: ({ id, body }: { id: number; body: FormData }) =>
|
||||
updateNews({ body, id }),
|
||||
onSuccess: () => {
|
||||
queryClient.refetchQueries({ queryKey: ["all_news"] });
|
||||
toast.success(t("Status o'zgartirildi"), {
|
||||
richColors: true,
|
||||
position: "top-center",
|
||||
});
|
||||
},
|
||||
onError: () => {
|
||||
toast.error(t("Xatolik yuz berdi"), {
|
||||
@@ -57,10 +82,21 @@ const News = () => {
|
||||
|
||||
const confirmDelete = () => {
|
||||
if (deleteId) {
|
||||
mutate(deleteId);
|
||||
deleteMutate(deleteId);
|
||||
}
|
||||
};
|
||||
|
||||
const handleTogglePublic = (id: number, currentStatus: boolean) => {
|
||||
const formData = new FormData();
|
||||
console.log(currentStatus);
|
||||
|
||||
formData.append("is_public", String(currentStatus));
|
||||
togglePublicMutate({
|
||||
id,
|
||||
body: formData,
|
||||
});
|
||||
};
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-900 w-full text-white flex justify-center items-center">
|
||||
@@ -135,10 +171,10 @@ const News = () => {
|
||||
allNews?.data.data.results.map((item) => (
|
||||
<Card
|
||||
key={item.id}
|
||||
className="overflow-hidden bg-neutral-900 hover:bg-neutral-800 transition-all duration-300 border border-neutral-800 hover:border-neutral-700 group"
|
||||
className="overflow-hidden p-0 bg-neutral-900 hover:bg-neutral-800 transition-all duration-300 border border-neutral-800 hover:border-neutral-700 group flex flex-col"
|
||||
>
|
||||
{/* Image */}
|
||||
<div className="relative h-48 overflow-hidden">
|
||||
<div className="relative h-64 overflow-hidden">
|
||||
<img
|
||||
src={item.image}
|
||||
alt={item.title}
|
||||
@@ -158,7 +194,7 @@ const News = () => {
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="p-4 space-y-3">
|
||||
<div className="p-4 space-y-3 flex-1 flex flex-col">
|
||||
{/* Title */}
|
||||
<h2 className="text-xl font-bold line-clamp-2 group-hover:text-blue-400 transition-colors">
|
||||
{item.title}
|
||||
@@ -169,21 +205,47 @@ const News = () => {
|
||||
{item.text}
|
||||
</p>
|
||||
|
||||
{/* Date */}
|
||||
<div className="flex items-center gap-2 text-xs text-gray-500">
|
||||
<span>{item.is_public}</span>
|
||||
</div>
|
||||
|
||||
{/* Slug */}
|
||||
<div className="pt-2 border-t border-neutral-800">
|
||||
{item.tag?.map((e) => (
|
||||
<code className="text-xs text-gray-500 bg-neutral-800 px-2 py-1 rounded">
|
||||
{item.tag && item.tag.length > 0 && (
|
||||
<div className="pt-2 border-t border-neutral-800 flex flex-wrap gap-2">
|
||||
{item.tag.map((e, idx) => (
|
||||
<code
|
||||
key={idx}
|
||||
className="text-xs text-gray-500 bg-neutral-800 px-2 py-1 rounded"
|
||||
>
|
||||
/{e.name}
|
||||
</code>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Actions */}
|
||||
{/* Spacer to push content to bottom */}
|
||||
<div className="flex-1"></div>
|
||||
|
||||
{/* Public/Private Toggle */}
|
||||
<div className="pt-3 border-t border-neutral-800">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
{item.is_public ? (
|
||||
<Eye size={16} className="text-green-500" />
|
||||
) : (
|
||||
<EyeOff size={16} className="text-gray-500" />
|
||||
)}
|
||||
<span className="text-sm text-gray-400">
|
||||
{t("Оmmaviy")}
|
||||
</span>
|
||||
</div>
|
||||
<Switch
|
||||
checked={item.is_public}
|
||||
onCheckedChange={() =>
|
||||
handleTogglePublic(item.id, !item.is_public)
|
||||
}
|
||||
className="data-[state=checked]:bg-green-600"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Actions - at the very bottom */}
|
||||
<div className="flex justify-end gap-2 pt-3">
|
||||
<Button
|
||||
onClick={() => navigate(`/news/edit/${item.id}`)}
|
||||
@@ -240,7 +302,8 @@ const News = () => {
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
<div className="flex justify-end gap-2">
|
||||
{/* Pagination */}
|
||||
<div className="flex justify-end gap-2 w-[90%] mx-auto mt-8">
|
||||
<button
|
||||
disabled={currentPage === 1}
|
||||
onClick={() => setCurrentPage((p) => Math.max(p - 1, 1))}
|
||||
|
||||
@@ -34,11 +34,12 @@ interface Data {
|
||||
text_uz: string;
|
||||
is_public: boolean;
|
||||
category: {
|
||||
id: number;
|
||||
name: string;
|
||||
name_ru: string;
|
||||
name_uz: string;
|
||||
};
|
||||
tag: [
|
||||
post_tags: [
|
||||
{
|
||||
id: number;
|
||||
name: string;
|
||||
@@ -59,20 +60,28 @@ interface Data {
|
||||
const StepOne = ({
|
||||
setStep,
|
||||
data: detail,
|
||||
id,
|
||||
}: {
|
||||
setStep: Dispatch<SetStateAction<number>>;
|
||||
isEditMode: boolean;
|
||||
data: Data;
|
||||
id: number;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const { setStepOneData, stepOneData } = useNewsStore();
|
||||
const hasReset = useRef(false); // 👈 infinite loopni oldini olish uchun
|
||||
const hasReset = useRef(false);
|
||||
|
||||
const { data, fetchNextPage, hasNextPage, isFetchingNextPage } =
|
||||
useInfiniteQuery({
|
||||
queryKey: ["news_category", detail],
|
||||
// News Category fetch - barcha kategoriyalarni bir martada
|
||||
const {
|
||||
data: categoryData,
|
||||
fetchNextPage,
|
||||
hasNextPage,
|
||||
isFetchingNextPage,
|
||||
isLoading: isCategoriesLoading,
|
||||
} = useInfiniteQuery({
|
||||
queryKey: ["news_category"],
|
||||
queryFn: ({ pageParam = 1 }) =>
|
||||
getAllNewsCategory({ page: pageParam, page_size: 10 }),
|
||||
getAllNewsCategory({ page: pageParam, page_size: 100 }),
|
||||
getNextPageParam: (lastPage) => {
|
||||
const currentPage = lastPage.data.data.current_page;
|
||||
const totalPages = lastPage.data.data.total_pages;
|
||||
@@ -81,44 +90,58 @@ const StepOne = ({
|
||||
initialPageParam: 1,
|
||||
});
|
||||
|
||||
const allCategories =
|
||||
data?.pages.flatMap((page) => page.data.data.results) ?? [];
|
||||
// Avtomatik barcha sahifalarni yuklash
|
||||
useEffect(() => {
|
||||
if (hasNextPage && !isFetchingNextPage) {
|
||||
fetchNextPage();
|
||||
}
|
||||
}, [hasNextPage, isFetchingNextPage, fetchNextPage]);
|
||||
|
||||
const allCategories =
|
||||
categoryData?.pages.flatMap((page) => page.data.data.results) ?? [];
|
||||
|
||||
// Form setup
|
||||
const form = useForm<z.infer<typeof newsForm>>({
|
||||
resolver: zodResolver(newsForm),
|
||||
defaultValues: {
|
||||
title: stepOneData.title,
|
||||
category: stepOneData.category,
|
||||
title: stepOneData.title || "",
|
||||
category: stepOneData.category || undefined,
|
||||
banner: stepOneData.banner,
|
||||
desc: stepOneData.desc,
|
||||
desc_ru: stepOneData.desc_ru,
|
||||
title_ru: stepOneData.title_ru,
|
||||
desc: stepOneData.desc || "",
|
||||
desc_ru: stepOneData.desc_ru || "",
|
||||
title_ru: stepOneData.title_ru || "",
|
||||
},
|
||||
});
|
||||
|
||||
// ✅ reset faqat bir marta, ma'lumot tayyor bo'lganda ishlaydi
|
||||
// Reset form when detail & categories are ready (EDIT MODE)
|
||||
useEffect(() => {
|
||||
if (
|
||||
detail &&
|
||||
allCategories.length > 0 &&
|
||||
!hasReset.current // faqat bir marta
|
||||
!detail ||
|
||||
allCategories.length === 0 ||
|
||||
hasReset.current ||
|
||||
isCategoriesLoading
|
||||
) {
|
||||
const foundCategory = allCategories.find(
|
||||
(cat) => cat.name === detail.category.name,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const categoryId = detail.category.id;
|
||||
|
||||
form.reset({
|
||||
banner: detail.image as any,
|
||||
category: foundCategory ? String(foundCategory.id) : "",
|
||||
category: categoryId,
|
||||
title: detail.title_uz,
|
||||
title_ru: detail.title_ru,
|
||||
desc: detail.text_uz,
|
||||
desc_ru: detail.text_ru,
|
||||
});
|
||||
|
||||
hasReset.current = true; // ✅ qayta reset bo‘lmasin
|
||||
}
|
||||
}, [detail, allCategories, form]);
|
||||
// Kategoriyani alohida set qilish
|
||||
setTimeout(() => {
|
||||
form.setValue("category", categoryId, { shouldValidate: false });
|
||||
}, 0);
|
||||
|
||||
hasReset.current = true;
|
||||
}, [detail, allCategories, form, isCategoriesLoading]);
|
||||
|
||||
function onSubmit(values: z.infer<typeof newsForm>) {
|
||||
setStepOneData(values);
|
||||
@@ -135,7 +158,7 @@ const StepOne = ({
|
||||
onSubmit={form.handleSubmit(onSubmit)}
|
||||
className="space-y-6 bg-gray-900"
|
||||
>
|
||||
{/* title */}
|
||||
{/* Title */}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="title"
|
||||
@@ -154,7 +177,7 @@ const StepOne = ({
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* title_ru */}
|
||||
{/* Title RU */}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="title_ru"
|
||||
@@ -173,7 +196,7 @@ const StepOne = ({
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* desc */}
|
||||
{/* Description */}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="desc"
|
||||
@@ -192,7 +215,7 @@ const StepOne = ({
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* desc_ru */}
|
||||
{/* Description RU */}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="desc_ru"
|
||||
@@ -211,17 +234,24 @@ const StepOne = ({
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* category */}
|
||||
{/* Category */}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="category"
|
||||
render={({ field }) => (
|
||||
render={({ field }) => {
|
||||
console.log("Category field value:", field.value);
|
||||
console.log("All categories:", allCategories);
|
||||
|
||||
return (
|
||||
<FormItem>
|
||||
<Label className="text-md">{t("Kategoriya")}</Label>
|
||||
<FormControl>
|
||||
<InfiniteScrollSelect
|
||||
value={field.value}
|
||||
onValueChange={field.onChange}
|
||||
value={field.value ? String(field.value) : ""}
|
||||
onValueChange={(value) => {
|
||||
const numValue = Number(value);
|
||||
field.onChange(numValue);
|
||||
}}
|
||||
placeholder={t("Kategoriya tanlang")}
|
||||
label={t("Kategoriyalar")}
|
||||
data={allCategories}
|
||||
@@ -237,10 +267,11 @@ const StepOne = ({
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
);
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* banner */}
|
||||
{/* Banner */}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="banner"
|
||||
@@ -301,6 +332,7 @@ const StepOne = ({
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* Submit */}
|
||||
<div className="w-full flex justify-end">
|
||||
<Button
|
||||
type="submit"
|
||||
|
||||
@@ -38,22 +38,18 @@ interface Data {
|
||||
name_ru: string;
|
||||
name_uz: string;
|
||||
};
|
||||
tag: [
|
||||
{
|
||||
post_tags: Array<{
|
||||
id: number;
|
||||
name: string;
|
||||
name_ru: string;
|
||||
name_uz: string;
|
||||
},
|
||||
];
|
||||
post_images: [
|
||||
{
|
||||
}>;
|
||||
post_images: Array<{
|
||||
image: string;
|
||||
text: string;
|
||||
text_ru: string;
|
||||
text_uz: string;
|
||||
},
|
||||
];
|
||||
}>;
|
||||
}
|
||||
|
||||
const StepTwo = ({
|
||||
@@ -77,13 +73,12 @@ const StepTwo = ({
|
||||
desc_ru: "",
|
||||
is_public: "yes",
|
||||
sections: [{ image: undefined as any, text: "", text_ru: "" }],
|
||||
post_tags: [""],
|
||||
post_tags: [{ name: "", name_ru: "" }],
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (detail && !hasReset.current) {
|
||||
// 🧠 xavfsiz map qilish
|
||||
const mappedSections =
|
||||
detail.post_images?.map((img) => ({
|
||||
image: img.image,
|
||||
@@ -91,13 +86,18 @@ const StepTwo = ({
|
||||
text_ru: img.text_ru,
|
||||
})) ?? [];
|
||||
|
||||
const mappedTags = detail.tag?.map((t) => t.name_uz) ?? [];
|
||||
const mappedTags =
|
||||
detail.post_tags?.map((t) => ({
|
||||
name: t.name_uz,
|
||||
name_ru: t.name_ru,
|
||||
})) ?? [];
|
||||
|
||||
form.reset({
|
||||
desc: detail.text_uz || "",
|
||||
desc_ru: detail.text_ru || "",
|
||||
is_public: detail.is_public ? "yes" : "no",
|
||||
post_tags: mappedTags.length > 0 ? mappedTags : [""],
|
||||
post_tags:
|
||||
mappedTags.length > 0 ? mappedTags : [{ name: "", name_ru: "" }],
|
||||
sections:
|
||||
mappedSections.length > 0
|
||||
? mappedSections
|
||||
@@ -108,18 +108,31 @@ const StepTwo = ({
|
||||
}
|
||||
}, [detail, form]);
|
||||
|
||||
const { fields, append, remove } = useFieldArray({
|
||||
const {
|
||||
fields: sectionFields,
|
||||
append: appendSection,
|
||||
remove: removeSection,
|
||||
} = useFieldArray({
|
||||
control: form.control,
|
||||
name: "sections",
|
||||
});
|
||||
const { watch, setValue } = form;
|
||||
const postTags = watch("post_tags");
|
||||
const addTag = () => setValue("post_tags", [...postTags, ""]);
|
||||
const removeTag = (i: number) =>
|
||||
setValue(
|
||||
"post_tags",
|
||||
postTags.filter((_, idx) => idx !== i),
|
||||
);
|
||||
|
||||
const {
|
||||
fields: tagFields,
|
||||
append: appendTag,
|
||||
remove: removeTag,
|
||||
} = useFieldArray({
|
||||
control: form.control,
|
||||
name: "post_tags",
|
||||
});
|
||||
|
||||
const handleImageChange = (
|
||||
e: React.ChangeEvent<HTMLInputElement>,
|
||||
index: number,
|
||||
) => {
|
||||
const file = e.target.files?.[0];
|
||||
if (file) form.setValue(`sections.${index}.image`, file);
|
||||
};
|
||||
|
||||
const { mutate: added } = useMutation({
|
||||
mutationFn: (body: FormData) => addNews(body),
|
||||
@@ -139,7 +152,7 @@ const StepTwo = ({
|
||||
|
||||
const { mutate: update } = useMutation({
|
||||
mutationFn: ({ body, id }: { id: number; body: FormData }) =>
|
||||
updateNews({ id: id, body }),
|
||||
updateNews({ id, body }),
|
||||
onSuccess: () => {
|
||||
queryClient.refetchQueries({ queryKey: ["news_detail"] });
|
||||
queryClient.refetchQueries({ queryKey: ["all_news"] });
|
||||
@@ -154,14 +167,6 @@ const StepTwo = ({
|
||||
},
|
||||
});
|
||||
|
||||
const handleImageChange = (
|
||||
e: React.ChangeEvent<HTMLInputElement>,
|
||||
index: number,
|
||||
) => {
|
||||
const file = e.target.files?.[0];
|
||||
if (file) form.setValue(`sections.${index}.image`, file);
|
||||
};
|
||||
|
||||
const onSubmit = (values: NewsPostFormType) => {
|
||||
const formData = new FormData();
|
||||
|
||||
@@ -176,28 +181,27 @@ const StepTwo = ({
|
||||
formData.append("image", stepOneData.banner);
|
||||
}
|
||||
|
||||
// 🔥 sections
|
||||
values.sections.forEach((section, i) => {
|
||||
if (section.image instanceof File) {
|
||||
// Sections
|
||||
values.sections?.forEach((section, i) => {
|
||||
if (section.image instanceof File)
|
||||
formData.append(`post_images[${i}]`, section.image);
|
||||
}
|
||||
formData.append(`post_text[${i}]`, section.text);
|
||||
if (section.text) formData.append(`post_text[${i}]`, section.text);
|
||||
if (section.text_ru)
|
||||
formData.append(`post_text_ru[${i}]`, section.text_ru);
|
||||
});
|
||||
|
||||
// Post Tags
|
||||
values.post_tags.forEach((tag, i) => {
|
||||
formData.append(`post_tags[${i}]`, tag);
|
||||
formData.append(`post_tags[${i}]name`, tag.name);
|
||||
formData.append(`post_tags[${i}]name_ru`, tag.name_ru);
|
||||
});
|
||||
if (id) {
|
||||
update({
|
||||
body: formData,
|
||||
id: Number(id),
|
||||
});
|
||||
} else {
|
||||
added(formData);
|
||||
}
|
||||
|
||||
if (id) update({ body: formData, id: Number(id) });
|
||||
else added(formData);
|
||||
};
|
||||
|
||||
console.log(form.formState.errors);
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<form
|
||||
@@ -239,45 +243,58 @@ const StepTwo = ({
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* Post Tags */}
|
||||
<div className="space-y-4">
|
||||
<Label>{t("Teglar")}</Label>
|
||||
{postTags.map((__, i) => (
|
||||
{tagFields.map((field, i) => (
|
||||
<div key={field.id} className="flex gap-2 items-start">
|
||||
<FormField
|
||||
key={i}
|
||||
control={form.control}
|
||||
name={`post_tags.${i}`}
|
||||
name={`post_tags.${i}.name`}
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<div className="relative">
|
||||
{postTags.length > 1 && (
|
||||
<FormControl>
|
||||
<Input {...field} placeholder={t("Teg (UZ)")} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={`post_tags.${i}.name_ru`}
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder={t("Teg (RU)")} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
{tagFields.length > 1 && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => removeTag(i)}
|
||||
className="absolute top-1 right-1 text-red-400 hover:text-red-500"
|
||||
className="text-red-400 hover:text-red-500 mt-1"
|
||||
>
|
||||
<Trash2 className="size-4" />
|
||||
</button>
|
||||
)}
|
||||
<FormControl>
|
||||
<Input {...field} placeholder={t("Masalan: sport")} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</div>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
))}
|
||||
<Button
|
||||
type="button"
|
||||
onClick={addTag}
|
||||
className="bg-gray-600 hover:bg-gray-700"
|
||||
onClick={() => appendTag({ name: "", name_ru: "" })}
|
||||
className="bg-gray-600 hover:bg-gray-700 mt-2"
|
||||
>
|
||||
<PlusCircle className="size-5 mr-2" />
|
||||
{t("Teg qo'shish")}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{fields.map((field, index) => (
|
||||
{/* Sections */}
|
||||
{sectionFields.map((field, index) => (
|
||||
<div
|
||||
key={field.id}
|
||||
className="border border-gray-700 rounded-lg p-4 space-y-4"
|
||||
@@ -288,7 +305,7 @@ const StepTwo = ({
|
||||
</p>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => remove(index)}
|
||||
onClick={() => removeSection(index)}
|
||||
className="text-red-400 hover:text-red-500"
|
||||
>
|
||||
<Trash2 className="size-4" />
|
||||
@@ -341,7 +358,7 @@ const StepTwo = ({
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Text (UZ) */}
|
||||
{/* Text UZ */}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={`sections.${index}.text`}
|
||||
@@ -355,7 +372,7 @@ const StepTwo = ({
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* Text (RU) */}
|
||||
{/* Text RU */}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={`sections.${index}.text_ru`}
|
||||
@@ -374,7 +391,7 @@ const StepTwo = ({
|
||||
<Button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
append({ image: undefined as any, text: "", text_ru: "" })
|
||||
appendSection({ image: undefined as any, text: "", text_ru: "" })
|
||||
}
|
||||
className="bg-gray-700 hover:bg-gray-600"
|
||||
>
|
||||
|
||||
@@ -530,5 +530,8 @@
|
||||
"Yuborish": "Отправить",
|
||||
"Pulni o'tkazish": "Перевод средств",
|
||||
"To‘lov muvaffaqiyatli amalga oshirildi!": "Оплата успешно выполнена!",
|
||||
"Sizga tizimga kirishga ruxsat berilmagan": "Вам не разрешен доступ к системе."
|
||||
"Sizga tizimga kirishga ruxsat berilmagan": "Вам не разрешен доступ к системе.",
|
||||
"Оmmaviy": "Публичный",
|
||||
"Shaxsiy": "Личный",
|
||||
"Status o'zgartirildi": "Статус изменён"
|
||||
}
|
||||
|
||||
@@ -531,5 +531,8 @@
|
||||
"Yuborish": "Yuborish",
|
||||
"Pulni o'tkazish": "Pulni o'tkazish",
|
||||
"To‘lov muvaffaqiyatli amalga oshirildi!": "To‘lov muvaffaqiyatli amalga oshirildi!",
|
||||
"Sizga tizimga kirishga ruxsat berilmagan": "Sizga tizimga kirishga ruxsat berilmagan"
|
||||
"Sizga tizimga kirishga ruxsat berilmagan": "Sizga tizimga kirishga ruxsat berilmagan",
|
||||
"Оmmaviy": "Ommaviy",
|
||||
"Shaxsiy": "Shaxsiy",
|
||||
"Status o'zgartirildi": "Status o'zgartirildi"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user