api ulandi
This commit is contained in:
@@ -1,458 +1,241 @@
|
||||
"use client";
|
||||
|
||||
import {
|
||||
hotelBadge,
|
||||
hotelFeature,
|
||||
hotelFeatureType,
|
||||
hotelTarif,
|
||||
hotelTransport,
|
||||
hotelType,
|
||||
} from "@/pages/tours/lib/api";
|
||||
import BadgeTable from "@/pages/tours/ui/BadgeTable";
|
||||
import FeaturesTable from "@/pages/tours/ui/FeaturesTable";
|
||||
import FeaturesTableType from "@/pages/tours/ui/FeaturesTableType";
|
||||
import MealTable from "@/pages/tours/ui/MealTable";
|
||||
import TarifTable from "@/pages/tours/ui/TarifTable";
|
||||
import TransportTable from "@/pages/tours/ui/TransportTable";
|
||||
import { Button } from "@/shared/ui/button";
|
||||
import { Card, CardContent } from "@/shared/ui/card";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/shared/ui/dialog";
|
||||
import { Input } from "@/shared/ui/input";
|
||||
import { Label } from "@/shared/ui/label";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/shared/ui/select";
|
||||
import { Tabs, TabsList, TabsTrigger } from "@/shared/ui/tabs";
|
||||
import { Textarea } from "@/shared/ui/textarea";
|
||||
import { Edit2, Plus, Search, Trash2 } from "lucide-react";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/shared/ui/tabs";
|
||||
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { AlertTriangle, Loader2 } from "lucide-react";
|
||||
import React, { useState } from "react";
|
||||
|
||||
interface Badge {
|
||||
id: number;
|
||||
name: string;
|
||||
color: string;
|
||||
}
|
||||
|
||||
interface Tariff {
|
||||
id: number;
|
||||
name: string;
|
||||
price: number;
|
||||
}
|
||||
|
||||
interface Transport {
|
||||
id: number;
|
||||
name: string;
|
||||
price: number;
|
||||
}
|
||||
|
||||
interface MealPlan {
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
interface HotelType {
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
type TabId = "badges" | "tariffs" | "transports" | "mealPlans" | "hotelTypes";
|
||||
|
||||
type DataItem = Badge | Tariff | Transport | MealPlan | HotelType;
|
||||
|
||||
interface FormField {
|
||||
name: string;
|
||||
label: string;
|
||||
type: "text" | "number" | "color" | "textarea" | "select";
|
||||
required: boolean;
|
||||
options?: string[];
|
||||
min?: number;
|
||||
max?: number;
|
||||
}
|
||||
|
||||
interface Tab {
|
||||
id: TabId;
|
||||
label: string;
|
||||
}
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useNavigate, useSearchParams } from "react-router-dom";
|
||||
|
||||
const ToursSetting: React.FC = () => {
|
||||
const [activeTab, setActiveTab] = useState<TabId>("badges");
|
||||
const [searchTerm, setSearchTerm] = useState<string>("");
|
||||
const [isModalOpen, setIsModalOpen] = useState<boolean>(false);
|
||||
const [modalMode, setModalMode] = useState<"add" | "edit">("add");
|
||||
const [currentItem, setCurrentItem] = useState<DataItem | null>(null);
|
||||
const { t } = useTranslation();
|
||||
const [searchParams] = useSearchParams();
|
||||
const [activeTab, setActiveTab] = useState("badge");
|
||||
const [featureId, setFeatureId] = useState<number | null>(null);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [badges, setBadges] = useState<Badge[]>([
|
||||
{ id: 1, name: "Bestseller", color: "#FFD700" },
|
||||
{ id: 2, name: "Yangi", color: "#4CAF50" },
|
||||
]);
|
||||
const page = parseInt(searchParams.get("page") || "1", 10);
|
||||
const pageSize = parseInt(searchParams.get("pageSize") || "10", 10);
|
||||
|
||||
const [tariffs, setTariffs] = useState<Tariff[]>([
|
||||
{ id: 1, name: "Standart", price: 500 },
|
||||
{ id: 2, name: "Premium", price: 1000 },
|
||||
]);
|
||||
const { data, isLoading, isError, refetch } = useQuery({
|
||||
queryKey: ["all_badge", page, pageSize],
|
||||
queryFn: () => hotelBadge({ page, page_size: pageSize }),
|
||||
select: (res) => res.data.data,
|
||||
});
|
||||
|
||||
const [transports, setTransports] = useState<Transport[]>([
|
||||
{ id: 1, name: "Avtobus", price: 200 },
|
||||
{ id: 2, name: "Minivan", price: 500 },
|
||||
]);
|
||||
const pageTarif = parseInt(searchParams.get("pageTarif") || "1", 10);
|
||||
const pageSizeTarif = parseInt(searchParams.get("pageTarifSize") || "10", 10);
|
||||
|
||||
const [mealPlans, setMealPlans] = useState<MealPlan[]>([
|
||||
{ id: 1, name: "BB (Bed & Breakfast)" },
|
||||
{ id: 2, name: "HB (Half Board)" },
|
||||
{ id: 3, name: "FB (Full Board)" },
|
||||
]);
|
||||
const {
|
||||
data: tarifData,
|
||||
isLoading: tarifLoad,
|
||||
isError: tarifError,
|
||||
refetch: tarifRef,
|
||||
} = useQuery({
|
||||
queryKey: ["all_tarif", pageTarif, pageSizeTarif],
|
||||
queryFn: () => hotelTarif({ page: pageTarif, page_size: pageSizeTarif }),
|
||||
select: (res) => res.data.data,
|
||||
});
|
||||
|
||||
const [hotelTypes, setHotelTypes] = useState<HotelType[]>([
|
||||
{ id: 1, name: "3 Yulduz" },
|
||||
{ id: 2, name: "5 Yulduz" },
|
||||
]);
|
||||
|
||||
const [formData, setFormData] = useState<Partial<DataItem>>({});
|
||||
|
||||
const getCurrentData = (): DataItem[] => {
|
||||
switch (activeTab) {
|
||||
case "badges":
|
||||
return badges;
|
||||
case "tariffs":
|
||||
return tariffs;
|
||||
case "transports":
|
||||
return transports;
|
||||
case "mealPlans":
|
||||
return mealPlans;
|
||||
case "hotelTypes":
|
||||
return hotelTypes;
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
const getSetterFunction = (): React.Dispatch<
|
||||
React.SetStateAction<DataItem[]>
|
||||
> => {
|
||||
switch (activeTab) {
|
||||
case "badges":
|
||||
return setBadges as React.Dispatch<React.SetStateAction<DataItem[]>>;
|
||||
case "tariffs":
|
||||
return setTariffs as React.Dispatch<React.SetStateAction<DataItem[]>>;
|
||||
case "transports":
|
||||
return setTransports as React.Dispatch<
|
||||
React.SetStateAction<DataItem[]>
|
||||
>;
|
||||
case "mealPlans":
|
||||
return setMealPlans as React.Dispatch<React.SetStateAction<DataItem[]>>;
|
||||
case "hotelTypes":
|
||||
return setHotelTypes as React.Dispatch<
|
||||
React.SetStateAction<DataItem[]>
|
||||
>;
|
||||
default:
|
||||
return (() => {}) as React.Dispatch<React.SetStateAction<DataItem[]>>;
|
||||
}
|
||||
};
|
||||
|
||||
const filteredData = getCurrentData().filter((item) =>
|
||||
Object.values(item).some((val) =>
|
||||
val?.toString().toLowerCase().includes(searchTerm.toLowerCase()),
|
||||
),
|
||||
const pageTransport = parseInt(searchParams.get("pageTransport") || "1", 10);
|
||||
const pageSizeTransport = parseInt(
|
||||
searchParams.get("pageTransportSize") || "10",
|
||||
10,
|
||||
);
|
||||
|
||||
const getFormFields = (): FormField[] => {
|
||||
switch (activeTab) {
|
||||
case "badges":
|
||||
return [
|
||||
{ name: "name", label: "Nomi", type: "text", required: true },
|
||||
{ name: "color", label: "Rang", type: "color", required: true },
|
||||
];
|
||||
case "tariffs":
|
||||
return [
|
||||
{ name: "name", label: "Tarif nomi", type: "text", required: true },
|
||||
{ name: "price", label: "Narx", type: "number", required: true },
|
||||
];
|
||||
case "transports":
|
||||
return [
|
||||
{
|
||||
name: "name",
|
||||
label: "Transport nomi",
|
||||
type: "text",
|
||||
required: true,
|
||||
},
|
||||
{ name: "capacity", label: "Sig'im", type: "number", required: true },
|
||||
];
|
||||
case "mealPlans":
|
||||
return [{ name: "name", label: "Nomi", type: "text", required: true }];
|
||||
case "hotelTypes":
|
||||
return [
|
||||
{ name: "name", label: "Tur nomi", type: "text", required: true },
|
||||
];
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
};
|
||||
const {
|
||||
data: transportData,
|
||||
isLoading: transportLoad,
|
||||
isError: transportError,
|
||||
refetch: transportRef,
|
||||
} = useQuery({
|
||||
queryKey: ["all_transport", pageTransport, pageSizeTransport],
|
||||
queryFn: () =>
|
||||
hotelTransport({ page: pageTransport, page_size: pageSizeTransport }),
|
||||
select: (res) => res.data.data,
|
||||
});
|
||||
|
||||
const openModal = (
|
||||
mode: "add" | "edit",
|
||||
item: DataItem | null = null,
|
||||
): void => {
|
||||
setModalMode(mode);
|
||||
setCurrentItem(item);
|
||||
if (mode === "edit" && item) {
|
||||
setFormData(item);
|
||||
} else {
|
||||
setFormData({});
|
||||
}
|
||||
setIsModalOpen(true);
|
||||
};
|
||||
const pageType = parseInt(searchParams.get("pageType") || "1", 10);
|
||||
const pageSizeType = parseInt(searchParams.get("pageTypeSize") || "10", 10);
|
||||
|
||||
const closeModal = (): void => {
|
||||
setIsModalOpen(false);
|
||||
setFormData({});
|
||||
setCurrentItem(null);
|
||||
};
|
||||
const {
|
||||
data: typeData,
|
||||
isLoading: typeLoad,
|
||||
isError: typeError,
|
||||
refetch: typeRef,
|
||||
} = useQuery({
|
||||
queryKey: ["all_type", pageType, pageSizeType],
|
||||
queryFn: () => hotelType({ page: pageType, page_size: pageSizeType }),
|
||||
select: (res) => res.data.data,
|
||||
});
|
||||
|
||||
const handleSubmit = (): void => {
|
||||
const setter = getSetterFunction();
|
||||
const pageFeature = parseInt(searchParams.get("pageFeature") || "1", 10);
|
||||
const pageSizeFeature = parseInt(
|
||||
searchParams.get("pageSizeFeature") || "10",
|
||||
10,
|
||||
);
|
||||
|
||||
if (modalMode === "add") {
|
||||
const newId = Math.max(...getCurrentData().map((i) => i.id), 0) + 1;
|
||||
setter([...getCurrentData(), { ...formData, id: newId } as DataItem]);
|
||||
} else {
|
||||
setter(
|
||||
getCurrentData().map((item) =>
|
||||
item.id === currentItem?.id ? { ...item, ...formData } : item,
|
||||
),
|
||||
);
|
||||
}
|
||||
closeModal();
|
||||
};
|
||||
const {
|
||||
data: featureData,
|
||||
isLoading: featureLoad,
|
||||
isError: featureError,
|
||||
refetch: featureRef,
|
||||
} = useQuery({
|
||||
queryKey: ["all_feature", pageFeature, pageSizeFeature],
|
||||
queryFn: () =>
|
||||
hotelFeature({ page: pageFeature, page_size: pageSizeFeature }),
|
||||
select: (res) => res.data.data,
|
||||
});
|
||||
|
||||
const handleDelete = (id: number): void => {
|
||||
if (window.confirm("Rostdan ham o'chirmoqchimisiz?")) {
|
||||
const setter = getSetterFunction();
|
||||
setter(getCurrentData().filter((item) => item.id !== id));
|
||||
}
|
||||
};
|
||||
const {
|
||||
data: featureTypeData,
|
||||
isLoading: featureTypeLoad,
|
||||
isError: featureTypeError,
|
||||
refetch: featureTypeRef,
|
||||
} = useQuery({
|
||||
queryKey: ["all_feature_type", pageFeature, pageSizeFeature, featureId],
|
||||
queryFn: () =>
|
||||
hotelFeatureType({
|
||||
page: pageFeature,
|
||||
page_size: pageSizeFeature,
|
||||
feature_type: featureId!,
|
||||
}),
|
||||
select: (res) => res.data.data,
|
||||
enabled: !!featureId,
|
||||
});
|
||||
|
||||
const tabs: Tab[] = [
|
||||
{ id: "badges", label: "Belgilar" },
|
||||
{ id: "tariffs", label: "Tariflar" },
|
||||
{ id: "transports", label: "Transportlar" },
|
||||
{ id: "mealPlans", label: "Ovqatlanish" },
|
||||
{ id: "hotelTypes", label: "Otel turlari" },
|
||||
];
|
||||
if (
|
||||
isLoading ||
|
||||
tarifLoad ||
|
||||
transportLoad ||
|
||||
typeLoad ||
|
||||
featureLoad ||
|
||||
featureTypeLoad
|
||||
) {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center min-h-screen bg-slate-900 text-white gap-4 w-full">
|
||||
<Loader2 className="w-10 h-10 animate-spin text-cyan-400" />
|
||||
<p className="text-slate-400">{t("Ma'lumotlar yuklanmoqda...")}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const getFieldValue = (fieldName: string): string | number => {
|
||||
return (formData as Record<string, string | number>)[fieldName] || "";
|
||||
if (
|
||||
isError ||
|
||||
tarifError ||
|
||||
transportError ||
|
||||
typeError ||
|
||||
featureError ||
|
||||
featureTypeError
|
||||
) {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center min-h-screen bg-slate-900 w-full text-center text-white gap-4">
|
||||
<AlertTriangle className="w-10 h-10 text-red-500" />
|
||||
<p className="text-lg">
|
||||
{t("Ma'lumotlarni yuklashda xatolik yuz berdi.")}
|
||||
</p>
|
||||
<Button
|
||||
onClick={() => {
|
||||
refetch();
|
||||
tarifRef();
|
||||
transportRef();
|
||||
typeRef();
|
||||
featureRef();
|
||||
featureTypeRef();
|
||||
}}
|
||||
className="bg-gradient-to-r from-blue-600 to-cyan-600 text-white rounded-lg px-5 py-2 hover:opacity-90"
|
||||
>
|
||||
{t("Qayta urinish")}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const handleTabChange = (value: string) => {
|
||||
setActiveTab(value);
|
||||
navigate({
|
||||
pathname: window.location.pathname,
|
||||
search: "",
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-900 p-6 w-full">
|
||||
<div className="max-w-7xl mx-auto space-y-6">
|
||||
<h1 className="text-3xl font-bold">Tur Sozlamalari</h1>
|
||||
|
||||
<div className="min-h-screen bg-gray-900 p-6 w-full text-white">
|
||||
<div className="max-w-[90%] mx-auto space-y-6">
|
||||
<Tabs
|
||||
value={activeTab}
|
||||
onValueChange={(v) => {
|
||||
setActiveTab(v as TabId);
|
||||
setSearchTerm("");
|
||||
}}
|
||||
onValueChange={handleTabChange}
|
||||
className="w-full"
|
||||
>
|
||||
<TabsList className="grid w-full grid-cols-5">
|
||||
{tabs.map((tab) => (
|
||||
<TabsTrigger key={tab.id} value={tab.id}>
|
||||
{tab.label}
|
||||
</TabsTrigger>
|
||||
))}
|
||||
<TabsList className="w-full">
|
||||
<TabsTrigger value="badge">{t("Belgilar (Badge)")}</TabsTrigger>
|
||||
<TabsTrigger value="tarif">{t("Tariflar")}</TabsTrigger>
|
||||
<TabsTrigger value="transport">{t("Transport")}</TabsTrigger>
|
||||
{/* <TabsTrigger value="meal">{t("Ovqatlanish")}</TabsTrigger> */}
|
||||
<TabsTrigger value="hotel_type">{t("Otel turlari")}</TabsTrigger>
|
||||
<TabsTrigger value="hotel_features">
|
||||
{t("Otel sharoitlari")}
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="badge" className="space-y-4">
|
||||
<BadgeTable data={data} page={page} pageSize={pageSize} />
|
||||
</TabsContent>
|
||||
<TabsContent value="tarif" className="space-y-4">
|
||||
<TarifTable
|
||||
data={tarifData}
|
||||
page={pageTarif}
|
||||
pageSize={pageSizeTarif}
|
||||
/>
|
||||
</TabsContent>
|
||||
<TabsContent value="transport" className="space-y-4">
|
||||
<TransportTable
|
||||
data={transportData}
|
||||
page={pageTransport}
|
||||
pageSize={pageSizeTransport}
|
||||
/>
|
||||
</TabsContent>
|
||||
<TabsContent value="hotel_type" className="space-y-4">
|
||||
<MealTable
|
||||
data={typeData}
|
||||
page={pageTransport}
|
||||
pageSize={pageSizeTransport}
|
||||
/>
|
||||
</TabsContent>
|
||||
<TabsContent value="hotel_features" className="space-y-4">
|
||||
<FeaturesTable
|
||||
data={featureData}
|
||||
page={pageFeature}
|
||||
pageSize={pageSizeFeature}
|
||||
setActiveTab={setActiveTab}
|
||||
setFeatureId={setFeatureId}
|
||||
/>
|
||||
</TabsContent>
|
||||
<TabsContent value="feature_type" className="space-y-4">
|
||||
<FeaturesTableType
|
||||
data={featureTypeData}
|
||||
page={pageFeature}
|
||||
featureId={featureId}
|
||||
pageSize={pageSizeFeature}
|
||||
/>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
|
||||
<Card className="bg-gray-900">
|
||||
<CardContent className="pt-6">
|
||||
<div className="flex flex-col sm:flex-row gap-4 justify-between items-center">
|
||||
<div className="relative w-full sm:w-96">
|
||||
<Search
|
||||
className="absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground"
|
||||
size={20}
|
||||
/>
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="Qidirish..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="pl-10"
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
onClick={() => openModal("add")}
|
||||
className="w-full sm:w-auto cursor-pointer"
|
||||
>
|
||||
<Plus size={20} className="mr-2" />
|
||||
Yangi qo'shish
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card className="bg-gray-900">
|
||||
<div className="overflow-x-auto">
|
||||
<div className="min-w-full">
|
||||
<div className="border-b">
|
||||
<div className="flex">
|
||||
<div className="px-6 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider w-20">
|
||||
ID
|
||||
</div>
|
||||
<div className="px-6 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider flex-1">
|
||||
Nomi
|
||||
</div>
|
||||
{activeTab === "badges" && (
|
||||
<div className="px-6 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider w-48">
|
||||
Rang
|
||||
</div>
|
||||
)}
|
||||
{(activeTab === "tariffs" || activeTab === "transports") && (
|
||||
<div className="px-6 py-3 text-left text-xs font-medium text-muted-foreground uppercase tracking-wider w-32">
|
||||
Narx
|
||||
</div>
|
||||
)}
|
||||
<div className="px-6 py-3 text-right text-xs font-medium text-muted-foreground uppercase tracking-wider w-32">
|
||||
Amallar
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="divide-y">
|
||||
{filteredData.length === 0 ? (
|
||||
<div className="px-6 py-8 text-center text-muted-foreground">
|
||||
Ma'lumot topilmadi
|
||||
</div>
|
||||
) : (
|
||||
filteredData.map((item) => (
|
||||
<div
|
||||
key={item.id}
|
||||
className="flex items-center hover:bg-accent/50 transition-colors"
|
||||
>
|
||||
<div className="px-6 py-4 w-20">{item.id}</div>
|
||||
<div className="px-6 py-4 font-medium flex-1">
|
||||
{item.name}
|
||||
</div>
|
||||
{activeTab === "badges" && (
|
||||
<div className="px-6 py-4 w-48">
|
||||
<div className="flex items-center gap-2">
|
||||
<div
|
||||
className="w-6 h-6 rounded border"
|
||||
style={{ backgroundColor: (item as Badge).color }}
|
||||
/>
|
||||
<span>{(item as Badge).color}</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{(activeTab === "tariffs" ||
|
||||
activeTab === "transports") && (
|
||||
<div className="px-6 py-4 w-32">
|
||||
{(item as Tariff).price}
|
||||
</div>
|
||||
)}
|
||||
<div className="px-6 py-4 w-32">
|
||||
<div className="flex justify-end gap-2">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => openModal("edit", item)}
|
||||
>
|
||||
<Edit2 size={18} />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => handleDelete(item.id)}
|
||||
>
|
||||
<Trash2 size={18} className="text-destructive" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Dialog open={isModalOpen} onOpenChange={setIsModalOpen}>
|
||||
<DialogContent className="sm:max-w-[500px] bg-gray-900">
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
{modalMode === "add" ? "Yangi qo'shish" : "Tahrirlash"}
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="space-y-4">
|
||||
{getFormFields().map((field) => (
|
||||
<div key={field.name} className="space-y-2">
|
||||
<Label htmlFor={field.name}>
|
||||
{field.label}
|
||||
{field.required && (
|
||||
<span className="text-destructive">*</span>
|
||||
)}
|
||||
</Label>
|
||||
{field.type === "textarea" ? (
|
||||
<Textarea
|
||||
id={field.name}
|
||||
value={getFieldValue(field.name) as string}
|
||||
onChange={(e) =>
|
||||
setFormData({
|
||||
...formData,
|
||||
[field.name]: e.target.value,
|
||||
})
|
||||
}
|
||||
required={field.required}
|
||||
rows={3}
|
||||
/>
|
||||
) : field.type === "select" ? (
|
||||
<Select
|
||||
value={getFieldValue(field.name) as string}
|
||||
onValueChange={(value) =>
|
||||
setFormData({ ...formData, [field.name]: value })
|
||||
}
|
||||
required={field.required}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Tanlang" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{field.options?.map((opt) => (
|
||||
<SelectItem key={opt} value={opt}>
|
||||
{opt}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
) : (
|
||||
<Input
|
||||
id={field.name}
|
||||
type={field.type}
|
||||
value={getFieldValue(field.name)}
|
||||
onChange={(e) =>
|
||||
setFormData({
|
||||
...formData,
|
||||
[field.name]:
|
||||
field.type === "number"
|
||||
? Number(e.target.value)
|
||||
: e.target.value,
|
||||
})
|
||||
}
|
||||
required={field.required}
|
||||
min={field.min}
|
||||
max={field.max}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
<div className="flex gap-3 pt-4">
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
onClick={closeModal}
|
||||
className="flex-1"
|
||||
>
|
||||
Bekor qilish
|
||||
</Button>
|
||||
<Button type="button" onClick={handleSubmit} className="flex-1">
|
||||
Saqlash
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user