Untitled
unknown
plain_text
10 months ago
17 kB
15
Indexable
import React, { useEffect, useRef, useState } from "react";
import { SelectSeatsInterface, Row, Column, Group } from "../types/Ticket";
import { useTicketMap } from "../hooks/useTicketMap";
import { X } from "lucide-react";
import { SkeletonSeats } from "../components/skeleton/seatsSkeleton";
import { ListDetailPriceSeat } from "../components/listDetailPriceSeat";
import { useTranslation } from "../services/translate/index";
import { Button } from "@/components/ui/button";
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
import { ChevronDown } from "lucide-react";
import idFlag from "../assets/ID.png";
import enFlag from "../assets/en.png";
import "../styles/app.css";
import Logo from "../../exhibitor-dashboard/assets/image/tixcorner_logo.png";
import { formatRupiah } from "../utils/formatRupiah";
import { groups } from "../services/data/seats";
type Language = "id" | "en";
interface LanguageOption {
code: Language;
name: string;
flag: string;
}
const languages: LanguageOption[] = [
{ code: "id", name: "Bahasa Indonesia", flag: idFlag },
{ code: "en", name: "English", flag: enFlag },
];
export const SelectSeats: React.FC = () => {
const [selectedSeats, setSelectedSeats] = useState<SelectSeatsInterface[]>([]);
const [total, setTotal] = useState(0);
const { fetchTicketMap, loadingTicketMap, area, ticket, error } = useTicketMap();
const [selectedLanguage, setSelectedLanguage] = useState<Language>("en");
const { t } = useTranslation(selectedLanguage);
const [scale, setScale] = useState(0.5);
const [position, setPosition] = useState({ x: 0, y: 0 });
const [isDragging, setIsDragging] = useState(false);
const [lastMousePos, setLastMousePos] = useState({ x: 0, y: 0 });
const containerRef = useRef(null);
const handleLanguageChange = (languageCode: Language) => {
setSelectedLanguage(languageCode);
};
const handleWheel = (e) => {
e.preventDefault();
const delta = e.deltaY * -0.001;
const newScale = Math.min(Math.max(0.3, scale + delta), 2);
setScale(newScale);
};
// Handle mouse down untuk mulai drag
const handleMouseDown = (e) => {
setIsDragging(true);
setLastMousePos({ x: e.clientX, y: e.clientY });
};
// Handle mouse move untuk drag
const handleMouseMove = (e) => {
if (!isDragging) return;
const deltaX = e.clientX - lastMousePos.x;
const deltaY = e.clientY - lastMousePos.y;
setPosition((prev) => ({
x: prev.x + deltaX,
y: prev.y + deltaY,
}));
setLastMousePos({ x: e.clientX, y: e.clientY });
};
const handleMouseUp = () => {
setIsDragging(false);
};
const resetView = () => {
setScale(0.6);
setPosition({ x: 0, y: 0 });
};
useEffect(() => {
fetchTicketMap("TWpVMg");
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const toggleSeat = (seat: SelectSeatsInterface) => {
setSelectedSeats((prevSeats) => {
// setIsScaled(false);
const exists = prevSeats.some((s) => s.columnId === seat.columnId && s.columnName === seat.columnName);
if (exists) {
return prevSeats.filter((s) => !(s.columnId === seat.columnId && s.columnName === seat.columnName));
} else {
return [...prevSeats, seat];
}
});
};
useEffect(() => {
let count = 0;
selectedSeats.forEach((item) => {
count += item.price!;
});
setTotal(count);
}, [selectedSeats]);
const sortGroups = (groups: Group[]) => {
return groups.sort((a, b) => {
const aRow = a.groupSequence <= 3 ? 0 : 1;
const bRow = b.groupSequence <= 3 ? 0 : 1;
if (aRow !== bRow) {
return aRow - bRow;
}
return b.groupSequence - a.groupSequence;
});
};
return (
<>
<nav className="sticky py-3 top-0 shadow-xs/05 z-50 w-full text-black bg-white ">
<div className="container w-full md:w-[90vw] lg:w-[81vw] mx-auto px-4">
<div className="flex h-16 items-center justify-between">
{/* Logo/Brand */}
<div className="flex items-center space-x-2">
<div>
<img src={Logo} className="w-[30vw] md:w-[7vw]" alt="" />
</div>
</div>
{/* Language Dropdown */}
<div className="flex items-center ">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button size="sm" variant="ghost" className="border-none shadow-none drop-shadow-none ring-0 ring-offset-0 focus-visible:ring-0 focus-visible:ring-offset-0 hover:bg-transparent active:bg-transparent">
<img src={languages.find((lang) => lang.code === selectedLanguage)?.flag} alt={selectedLanguage} className="w-6 h-6" />
<ChevronDown className="h-4 w-4 text-black" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-48">
{languages.map((language) => (
<DropdownMenuItem key={language.code} onClick={() => handleLanguageChange(language.code)} className={`cursor-pointer ${selectedLanguage === language.code ? "bg-accent text-accent-foreground" : ""}`}>
<img src={language.flag} alt={language.name} className="w-6 h-6" />
{language.name}
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
</div>
</div>
</div>
</nav>
<div className="bg-[#f5f7f9] pt-5">
<div className="mx-auto md:w-[90vw] xl:w-[79vw] px-4 md:px-0">
{loadingTicketMap ? (
<SkeletonSeats />
) : (
<div className="flex justify-between gap-x-4 pb-5 flex-col lg:flex-row">
<div className="lg:w-[70%] bg-white pt-6 py-5 border rounded-md">
<div className="pt-2 text-center">
<div className="h-15 flex justify-center items-center">
<p className="text-lg md:text-xl font-bold text-gray-600">{t("Select Your Seats")}</p>
</div>
</div>
<div className="seat flex flex-col justify-between border-3 border-red-500 h-[50vh] ">
{error ? (
<div className="w-full h-[50vh] flex text-xl text-gray-500 font-semibold justify-center items-center">
<p>{error}</p>
</div>
) : (
<div className="w-full h-screen bg-gray-100 overflow-hidden relative">
{/* Controls */}
<div className="absolute top-4 left-4 z-10 flex gap-2">
<button onClick={() => setScale((prev) => Math.min(prev + 0.2, 2))} className="bg-blue-500 text-white px-3 py-2 rounded hover:bg-blue-600">
Zoom In
</button>
<button onClick={() => setScale((prev) => Math.max(prev - 0.2, 0.3))} className="bg-blue-500 text-white px-3 py-2 rounded hover:bg-blue-600">
Zoom Out
</button>
<button onClick={resetView} className="bg-gray-500 text-white px-3 py-2 rounded hover:bg-gray-600">
Reset
</button>
</div>
{/* Zoom info */}
<div className="absolute top-4 right-4 z-10 bg-white px-3 py-2 rounded shadow">Zoom: {Math.round(scale * 100)}%</div>
{/* Main container yang bisa di-zoom dan drag */}
<div
ref={containerRef}
className="w-full h-full cursor-grab active:cursor-grabbing"
onWheel={handleWheel}
onMouseDown={handleMouseDown}
onMouseMove={handleMouseMove}
onMouseUp={handleMouseUp}
onMouseLeave={handleMouseUp}
style={{
transform: `scale(${scale}) translate(${position.x / scale}px, ${position.y / scale}px)`,
transformOrigin: "center center",
}}
>
{/* Seating chart content - ganti dengan kode Anda */}
<div className="flex md:justify-center pt-10 transition-transform duration-300">
{groups.length > 0 ? (
<div className="w-max px-5">
<div className="flex bg-gray-200 py-2 rounded-md justify-center w-full mb-4">
<p className="font-semibold text-gray-500">STAGE</p>
</div>
<div className="grid grid-cols-3 pt-7 gap-x-4 sm:gap-x-8 w-max md:gap-x-12">
{groups.map((item, indexPos) => (
<div key={indexPos} className="flex flex-col">
{item?.rows?.map((locationRows, indexRows) => (
<div key={indexRows} className="flex items-center py-1 mb-1">
<div className="text-xs sm:text-sm font-semibold w-4 text-gray-600 mr-2 flex-shrink-0">{locationRows.rowName}</div>
<div className="flex gap-x-1 w-full justify-between">
{locationRows.colums?.map((seat) => {
const selectSeat = selectedSeats.some((selectedSeat) => selectedSeat.columnId === seat.columnId);
return (
<div
key={seat.columnId}
className={`
w-6 h-6 sm:w-7 sm:h-7 md:w-8 md:h-8
rounded-md flex items-center justify-center
transition-colors duration-200 shadow-sm text-xs font-medium
${selectSeat ? "bg-green-500 text-white" : "text-white"}
${seat.status !== 0 ? "pointer-events-none opacity-50" : "cursor-pointer hover:opacity-80"}
`}
style={{
backgroundColor: !selectSeat ? seat.areaColor : undefined,
}}
onClick={(e) => {
e.stopPropagation(); // Prevent drag when clicking seat
toggleSeat({
columnId: seat.columnId,
price: seat.ticketPrice,
areaName: seat.areaName,
columnName: seat.columnName,
rowName: locationRows.rowName,
});
}}
>
<span>{seat.columnName}</span>
</div>
);
})}
</div>
</div>
))}
</div>
))}
</div>
</div>
) : (
<div className="w-full h-100 flex items-center justify-center">
<p className="text-xl font-medium text-gray-500">Not found seats</p>
</div>
)}
</div>
</div>
</div>
)}
</div>
{!error && (
<div className="mx-5 space-y-6 mb-7">
<ListDetailPriceSeat t={t} area={area} ticket={ticket} />
</div>
)}
</div>
<div className="lg:w-[29%] pt-5 lg:pt-0 rounded-md">
<div className="border rounded-md bg-white">
{selectedSeats.length > 0 ? (
<div data-slot="card-content" className="p-6 pt-10 ">
<h3 className="text-lg font-bold mb-4 text-gray-600">{t("Selected Seats")}</h3>
<div className="space-y-3">
{selectedSeats.map((seat: SelectSeatsInterface, index) => (
<div key={index} className="flex justify-between items-center">
<span className="text-sm w-[50%] text-foreground">
{seat.rowName}
{seat.columnName}({seat.areaName})
</span>
<div className="flex justify-center gap-x-2 items-center">
<span className="text-sm font-semibold text-foreground">Rp{formatRupiah(seat.price || 0)}</span>
<X
size={15}
onClick={() => {
toggleSeat({
columnId: seat.columnId,
columnName: seat.columnName,
});
}}
className="text-red-600 cursor-pointer"
/>
</div>
</div>
))}
<div className="border-t pt-3 mt-3">
<div className="flex justify-between items-center">
<span className="font-semibold text-foreground">Total</span>
<span className="font-bold text-lg text-foreground">Rp{formatRupiah(total)}</span>
</div>
</div>
</div>
<div className="py-5">
<button
className="inline-flex cursor-pointer items-center justify-center gap-2 whitespace-nowrap text-sm font-medium transition-all w-full text-white py-2 px-4"
style={{
background: "linear-gradient(to top, #1f60c4 0%, #3a80d6 100%)",
borderRadius: "12px",
}}
>
Continue to Booking
</button>
</div>
</div>
) : (
<div data-slot="card-content" className="p-6 min-h-40 flex flex-col justify-center">
<h3 className="text-lg font-bold mb-4 text-gray-500">{t("Selected Seats")}</h3>
<p className="text-muted-foreground text-sm">{t("No seats selected")}</p>
</div>
)}
</div>
</div>
</div>
)}
</div>
</div>
<footer className="bg-gray-300 py-8 px-4">
<div className="max-w-6xl mx-auto">
<div className="text-center mb-6">
<p className="text-gray-600 text-sm mb-2">Organiser Customer Support: Email | Whatsapp</p>
</div>
<div className="text-center">
<p className="text-gray-600 text-sm">
<span className="hover:text-gray-800 cursor-pointer">Kebijakan Privasi</span>
<span className="mx-2">|</span>
<span className="hover:text-gray-800 cursor-pointer">Syarat dan Ketentuan</span>
</p>
</div>
</div>
</footer>
</>
);
};
export default SelectSeats;
Editor is loading...
Leave a Comment