first commit
This commit is contained in:
195
src/pages/news/ui/StepTwo.tsx
Normal file
195
src/pages/news/ui/StepTwo.tsx
Normal file
@@ -0,0 +1,195 @@
|
||||
"use client";
|
||||
import { Button } from "@/shared/ui/button";
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormMessage,
|
||||
} from "@/shared/ui/form";
|
||||
import { Input } from "@/shared/ui/input";
|
||||
import { Label } from "@/shared/ui/label";
|
||||
import { Textarea } from "@/shared/ui/textarea";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { PlusCircle, Trash2, XIcon } from "lucide-react";
|
||||
import type { Dispatch, SetStateAction } from "react";
|
||||
import { useFieldArray, useForm } from "react-hook-form";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import z from "zod";
|
||||
|
||||
const newsItemSchema = z.object({
|
||||
desc: z.string().min(2, {
|
||||
message: "Yangilik matni kamida 2 belgidan iborat bo‘lishi kerak",
|
||||
}),
|
||||
banner: z.string().min(1, { message: "Banner rasmi majburiy" }),
|
||||
});
|
||||
|
||||
const newsListSchema = z.object({
|
||||
items: z
|
||||
.array(newsItemSchema)
|
||||
.min(1, { message: "Kamida 1 ta yangilik kerak" }),
|
||||
});
|
||||
|
||||
type NewsFormType = z.infer<typeof newsListSchema>;
|
||||
|
||||
const StepTwo = ({
|
||||
setStep,
|
||||
isEditMode,
|
||||
}: {
|
||||
setStep: Dispatch<SetStateAction<number>>;
|
||||
isEditMode: boolean;
|
||||
}) => {
|
||||
const form = useForm<NewsFormType>({
|
||||
resolver: zodResolver(newsListSchema),
|
||||
defaultValues: {
|
||||
items: [{ desc: "", banner: "" }],
|
||||
},
|
||||
});
|
||||
|
||||
const { fields, append, remove } = useFieldArray({
|
||||
control: form.control,
|
||||
name: "items",
|
||||
});
|
||||
const navigator = useNavigate();
|
||||
|
||||
function onSubmit() {
|
||||
navigator("/news");
|
||||
}
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<form
|
||||
onSubmit={form.handleSubmit(onSubmit)}
|
||||
className="space-y-8 bg-gray-900 p-6 rounded-2xl text-white"
|
||||
>
|
||||
<h2 className="text-2xl font-semibold">Yangiliklar ro‘yxati</h2>
|
||||
|
||||
{fields.map((field, index) => (
|
||||
<div
|
||||
key={field.id}
|
||||
className="relative border border-gray-700 bg-gray-800 rounded-xl p-4 space-y-4"
|
||||
>
|
||||
{/* O'chirish tugmasi */}
|
||||
{fields.length > 1 && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => remove(index)}
|
||||
className="absolute top-3 right-3 text-red-400 hover:text-red-500"
|
||||
>
|
||||
<Trash2 className="size-5" />
|
||||
</button>
|
||||
)}
|
||||
|
||||
{/* DESC FIELD */}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={`items.${index}.desc`}
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<Label className="text-md">Yangilik haqida</Label>
|
||||
<FormControl>
|
||||
<Textarea
|
||||
placeholder="Yangilik haqida"
|
||||
{...field}
|
||||
className="min-h-48 max-h-56 !text-md bg-gray-800 border-gray-700 text-white"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* BANNER FIELD */}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={`items.${index}.banner`}
|
||||
render={() => (
|
||||
<FormItem>
|
||||
<Label className="text-md">Banner rasmi</Label>
|
||||
<FormControl>
|
||||
<div className="flex flex-col gap-3 w-full">
|
||||
<Input
|
||||
type="file"
|
||||
id={`file-${index}`}
|
||||
accept="image/*"
|
||||
onChange={(e) => {
|
||||
const file = e.target.files?.[0];
|
||||
if (file) {
|
||||
const url = URL.createObjectURL(file);
|
||||
form.setValue(`items.${index}.banner`, url);
|
||||
}
|
||||
}}
|
||||
className="hidden"
|
||||
/>
|
||||
|
||||
<label
|
||||
htmlFor={`file-${index}`}
|
||||
className="w-full border-2 border-dashed h-40 border-gray-600 hover:border-gray-500 transition-all flex flex-col items-center gap-2 justify-center py-4 rounded-2xl cursor-pointer"
|
||||
>
|
||||
<p className="font-semibold text-xl text-white">
|
||||
Drag or select files
|
||||
</p>
|
||||
<p className="text-gray-300 text-sm">
|
||||
Drop files here or click to browse
|
||||
</p>
|
||||
</label>
|
||||
|
||||
{form.watch(`items.${index}.banner`) && (
|
||||
<div className="relative size-24 rounded-md overflow-hidden border border-gray-700">
|
||||
<img
|
||||
src={form.watch(`items.${index}.banner`)}
|
||||
alt="Banner preview"
|
||||
className="object-cover w-full h-full"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
form.setValue(`items.${index}.banner`, "")
|
||||
}
|
||||
className="absolute top-1 right-1 bg-white/80 rounded-full p-1 shadow hover:bg-white"
|
||||
>
|
||||
<XIcon className="size-4 text-destructive" />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
<div className="flex justify-end">
|
||||
<Button
|
||||
type="button"
|
||||
onClick={() => append({ desc: "", banner: "" })}
|
||||
className="flex items-center px-6 py-5 text-lg gap-2 bg-gray-600 hover:bg-gray-700 text-white cursor-pointer"
|
||||
>
|
||||
<PlusCircle className="size-5" />
|
||||
Qo‘shish
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Navigatsiya tugmalari */}
|
||||
<div className="w-full flex justify-between pt-4">
|
||||
<Button
|
||||
type="button"
|
||||
onClick={() => setStep(1)}
|
||||
className="bg-gray-600 hover:bg-gray-700 text-white"
|
||||
>
|
||||
Orqaga
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
className="bg-blue-600 hover:bg-blue-700 text-white"
|
||||
>
|
||||
{isEditMode ? "Yangiliklarni saqlash" : "Saqlash"}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
);
|
||||
};
|
||||
|
||||
export default StepTwo;
|
||||
Reference in New Issue
Block a user