Untitled

 avatar
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