first commit
This commit is contained in:
210
src/pages/tours/ui/Tours.tsx
Normal file
210
src/pages/tours/ui/Tours.tsx
Normal file
@@ -0,0 +1,210 @@
|
||||
"use client";
|
||||
|
||||
import { Button } from "@/shared/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/shared/ui/dialog";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/shared/ui/table";
|
||||
import { Edit, Plane, PlusCircle, Trash2 } from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
type Tour = {
|
||||
id: number;
|
||||
image?: string;
|
||||
tickets: string;
|
||||
min_price: string;
|
||||
max_price: string;
|
||||
top_duration: string;
|
||||
top_destinations: string;
|
||||
hotel_features_by_type: string;
|
||||
hotel_types: string;
|
||||
hotel_amenities: string;
|
||||
};
|
||||
|
||||
const Tours = () => {
|
||||
const [tours, setTours] = useState<Tour[]>([]);
|
||||
const [page, setPage] = useState(1);
|
||||
const [totalPages, setTotalPages] = useState(3);
|
||||
const [deleteId, setDeleteId] = useState<number | null>(null);
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
const mockData: Tour[] = Array.from({ length: 10 }, (_, i) => ({
|
||||
id: i + 1,
|
||||
image: `/dubai-marina.jpg`,
|
||||
tickets: `Bilet turi ${i + 1}`,
|
||||
min_price: `${200 + i * 50}$`,
|
||||
max_price: `${400 + i * 70}$`,
|
||||
top_duration: `${3 + i} kun`,
|
||||
top_destinations: `Shahar ${i + 1}`,
|
||||
hotel_features_by_type: "Spa, Wi-Fi, Pool",
|
||||
hotel_types: "5 yulduzli mehmonxona",
|
||||
hotel_amenities: "Nonushta, Parking, Bar",
|
||||
}));
|
||||
|
||||
const itemsPerPage = 6;
|
||||
const start = (page - 1) * itemsPerPage;
|
||||
const end = start + itemsPerPage;
|
||||
|
||||
setTotalPages(Math.ceil(mockData.length / itemsPerPage));
|
||||
setTours(mockData.slice(start, end));
|
||||
}, [page]);
|
||||
|
||||
const confirmDelete = () => {
|
||||
if (deleteId !== null) {
|
||||
setTours((prev) => prev.filter((t) => t.id !== deleteId));
|
||||
setDeleteId(null);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-900 text-foreground py-10 px-5 w-full">
|
||||
<div className="flex justify-between items-center mb-8">
|
||||
<h1 className="text-3xl font-semibold">Turlar ro'yxati</h1>
|
||||
<Button onClick={() => navigate("/tours/create")} variant="default">
|
||||
<PlusCircle className="w-5 h-5 mr-2" /> Yangi tur qo'shish
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div className="rounded-xl border overflow-x-auto">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow className="bg-muted/50">
|
||||
<TableHead className="w-[50px] text-center">#</TableHead>
|
||||
<TableHead className="min-w-[150px]">Manzil</TableHead>
|
||||
<TableHead className="min-w-[120px]">Davomiyligi</TableHead>
|
||||
<TableHead className="min-w-[180px]">Mehmonxona</TableHead>
|
||||
<TableHead className="min-w-[200px]">Narx Oralig'i</TableHead>
|
||||
<TableHead className="min-w-[200px]">Imkoniyatlar</TableHead>
|
||||
<TableHead className="min-w-[150px] text-center">
|
||||
Amallar
|
||||
</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{tours.map((tour, idx) => (
|
||||
<TableRow key={tour.id}>
|
||||
<TableCell className="font-medium text-center">
|
||||
{(page - 1) * 6 + idx + 1}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<div className="flex items-center gap-2 font-semibold">
|
||||
<Plane className="w-4 h-4 text-primary" />
|
||||
{tour.top_destinations}
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell className="text-sm text-primary font-medium">
|
||||
{tour.top_duration}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<div className="flex flex-col">
|
||||
<span className="font-medium">{tour.hotel_types}</span>
|
||||
<span className="text-xs text-muted-foreground">
|
||||
{tour.tickets}
|
||||
</span>
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<span className="font-bold text-base text-green-600">
|
||||
{tour.min_price} – {tour.max_price}
|
||||
</span>
|
||||
</TableCell>
|
||||
<TableCell className="text-sm">
|
||||
{tour.hotel_amenities}
|
||||
</TableCell>
|
||||
|
||||
<TableCell className="text-center">
|
||||
<div className="flex gap-2 justify-center">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
onClick={() => navigate(`/tours/${tour.id}/edit`)}
|
||||
>
|
||||
<Edit className="w-4 h-4" />
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="destructive"
|
||||
size="icon"
|
||||
onClick={() => setDeleteId(tour.id)}
|
||||
>
|
||||
<Trash2 className="w-4 h-4" />
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="default"
|
||||
size="sm"
|
||||
onClick={() => navigate(`/tours/${tour.id}`)}
|
||||
>
|
||||
Batafsil
|
||||
</Button>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
|
||||
{/* Delete Confirmation Dialog - Faqat bitta */}
|
||||
<Dialog open={deleteId !== null} onOpenChange={() => setDeleteId(null)}>
|
||||
<DialogContent className="sm:max-w-[425px] bg-gray-900">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="text-xl">
|
||||
Turni o'chirishni tasdiqlang
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="py-4">
|
||||
<p className="text-muted-foreground">
|
||||
Haqiqatan ham bu turni o'chirib tashlamoqchimisiz? Bu amalni ortga
|
||||
qaytarib bo'lmaydi.
|
||||
</p>
|
||||
</div>
|
||||
<DialogFooter className="gap-4 flex">
|
||||
<Button variant="outline" onClick={() => setDeleteId(null)}>
|
||||
Bekor qilish
|
||||
</Button>
|
||||
<Button variant="destructive" onClick={confirmDelete}>
|
||||
<Trash2 className="w-4 h-4 mr-2" />
|
||||
O'chirish
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
<div className="flex justify-center mt-10 gap-3">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setPage((p) => Math.max(1, p - 1))}
|
||||
disabled={page === 1}
|
||||
>
|
||||
Oldingi
|
||||
</Button>
|
||||
<span className="text-sm flex items-center">
|
||||
Sahifa {page} / {totalPages}
|
||||
</span>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setPage((p) => Math.min(totalPages, p + 1))}
|
||||
disabled={page === totalPages}
|
||||
>
|
||||
Keyingi
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Tours;
|
||||
Reference in New Issue
Block a user