// @ts-nocheck
'use client';
import { useState, useEffect, useRef, Suspense } from 'react';
import ReserveForm from '@/components/ui/Reserve';
import { Event, Deal, Tag } from '@/types/types';
import { motion } from 'framer-motion';
import { createClient } from '@/utils/supabase/client';
import { MdLocalOffer, MdAddCircleOutline } from 'react-icons/md';
// Animation variants
const fadeIn = {
hidden: { opacity: 0, y: 20 },
visible: { opacity: 1, y: 0, transition: { duration: 0.5, ease: "easeOut" } }
};
const staggerContainer = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.1
}
}
};
export default function ReservationsPage() {
const supabase = createClient();
const [selectedEvent, setSelectedEvent] = useState<Event | undefined>(undefined);
const [eventType, setEventType] = useState("Резервация");
const [deals, setDeals] = useState<Deal[]>([]);
const [filteredDeals, setFilteredDeals] = useState<Deal[]>([]);
const [tags, setTags] = useState<Tag[]>([]);
const [dealTags, setDealTags] = useState<Record<number, Tag[]>>({});
const [persons, setPersons] = useState<string>('');
const [isLoading, setIsLoading] = useState(true);
const [additionalDescription, setAdditionalDescription] = useState<string>('');
const reserveFormRef = useRef<any>(null);
// Check for event data in sessionStorage (from events page)
useEffect(() => {
const storedEvent = sessionStorage.getItem('selectedEvent');
if (storedEvent) {
try {
const parsedEvent = JSON.parse(storedEvent);
setSelectedEvent(parsedEvent);
setEventType(parsedEvent.title || "Резервация");
// Clear the stored event after retrieving it
sessionStorage.removeItem('selectedEvent');
} catch (e) {
console.error("Error parsing stored event:", e);
}
}
// Fetch deals when component mounts
fetchDeals();
}, []);
// Filter deals when persons count changes
useEffect(() => {
if (deals.length > 0) {
filterDealsByPersons();
}
}, [persons, deals]);
const fetchDeals = async () => {
try {
setIsLoading(true);
// Fetch deals
const { data: dealsData, error: dealsError } = await supabase
.from('deals')
.select('*');
if (dealsError) throw dealsError;
setDeals(dealsData || []);
// Fetch tags
const { data: tagsData, error: tagsError } = await supabase
.from('tags')
.select('*');
if (tagsError) throw tagsError;
setTags(tagsData || []);
// Fetch deal-tag relationships
const { data: dealTagsData, error: dealTagsError } = await supabase
.from('deal_tags')
.select('*');
if (dealTagsError) throw dealTagsError;
// Build map of deal_id -> tags[]
const tagMap: Record<number, Tag[]> = {};
if (dealTagsData && tagsData) {
for (const dt of dealTagsData) {
const tag = tagsData?.find(t => t.id === dt.tag_id);
if (tag) {
if (!tagMap[dt.deal_id]) tagMap[dt.deal_id] = [];
tagMap[dt.deal_id].push(tag);
}
}
}
setDealTags(tagMap);
// Initial filtering (will update when persons changes)
setFilteredDeals(dealsData || []);
} catch (err) {
console.error('Error fetching deals:', err);
} finally {
setIsLoading(false);
}
};
const filterDealsByPersons = () => {
if (!persons || persons === '') {
// Sort all deals by price in ascending order
const sortedDeals = [...deals].sort((a, b) => a.price - b.price);
setFilteredDeals(sortedDeals);
return;
}
const personsCount = parseInt(persons, 10);
// Filter deals based on min_group_size and max_group_size
const filtered = deals.filter(deal => {
// If no min/max specified, always include
if (!deal.min_group_size && !deal.max_group_size) return true;
// Check if persons count is within range
const withinMinRange = !deal.min_group_size || personsCount >= deal.min_group_size;
const withinMaxRange = !deal.max_group_size || personsCount <= deal.max_group_size;
return withinMinRange && withinMaxRange;
});
// Sort filtered deals by price in ascending order
const sortedFilteredDeals = filtered.sort((a, b) => a.price - b.price);
setFilteredDeals(sortedFilteredDeals);
};
// Handler for when the persons field changes in the reservation form
const handlePersonsChange = (value: string) => {
setPersons(value);
};
// Function to add a deal to the description
const handleAddDeal = (deal: Deal) => {
const dealInfo = `Добавена оферта: ${deal.title} (${deal.price} лв.)`;
// Update the additional description
setAdditionalDescription(prev => {
const newDesc = prev ? `${prev}\n${dealInfo}` : dealInfo;
// If we have access to the reserveFormRef component, update its description
if (reserveFormRef.current && typeof reserveFormRef.current.updateDescription === 'function') {
reserveFormRef.current.updateDescription(newDesc);
}
return newDesc;
});
};
// Deal card component
const DealCard = ({ deal }: { deal: Deal }) => {
const dealTagsList = dealTags[deal.id] || [];
const [expandedBenefits, setExpandedBenefits] = useState(false);
return (
<motion.div
variants={fadeIn}
className="bg-white rounded-xl shadow-md overflow-hidden transform transition-all duration-300 hover:shadow-lg border border-gray-100"
>
<div className="p-5">
<div className="flex justify-between items-center mb-3">
<h3 className="text-lg font-bold tracking-tight">{deal.title}</h3>
<div className="flex items-center bg-orange-100 rounded-full overflow-hidden shadow-sm">
<div className="px-2 py-1">
<MdLocalOffer className="text-orange-500" />
</div>
<div className="px-3 py-1 bg-orange-500 text-white text-sm font-medium">
{deal.price} лв
</div>
</div>
</div>
<p className="text-gray-600 mb-4 text-sm">{deal.description}</p>
{/* Tags */}
{dealTagsList.length > 0 && (
<div className="flex flex-wrap gap-1 mb-4">
{dealTagsList.map(tag => (
<span key={tag.id} className="border border-gray-200 text-gray-600 text-xs px-2 py-0.5 rounded-full">
{tag.name}
</span>
))}
</div>
)}
{deal.benefits && deal.benefits.length > 0 && (
<>
<h4 className="font-semibold text-gray-800 mb-2 text-sm">Включва:</h4>
<ul className="space-y-1 mb-4 text-sm">
{(expandedBenefits ? deal.benefits : deal.benefits.slice(0, 2)).map((item, index) => (
<li key={index} className="flex items-start">
<span className="text-orange-500 mr-2">✓</span>
<span className="text-gray-700">{item}</span>
</li>
))}
{!expandedBenefits && deal.benefits.length > 2 && (
<li
className="text-orange-500 text-xs italic cursor-pointer hover:underline flex items-center"
onClick={() => setExpandedBenefits(true)}
>
+ още {deal.benefits.length - 2}
</li>
)}
{expandedBenefits && (
<li
className="text-orange-500 text-xs italic cursor-pointer hover:underline flex items-center"
onClick={() => setExpandedBenefits(false)}
>
Скрий
</li>
)}
</ul>
</>
)}
<button
className="w-full btn btn-sm btn-outline border-orange-500 text-orange-500 hover:bg-orange-500 hover:text-white"
onClick={() => handleAddDeal(deal)}
>
<MdAddCircleOutline className="mr-1" /> добави
</button>
</div>
</motion.div>
);
};
return (
<div className="max-w-6xl mx-auto px-6 py-12">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
>
<h1 className="text-3xl font-bold text-center">
{selectedEvent ? selectedEvent.title : "Резервация"}
</h1>
<div className="h-2" />
<p className="text-center text-gray-500">{selectedEvent?.description || "Направете онлайн резервация"}</p>
<div className="h-8" />
<Suspense fallback={<div className="h-[300px] flex items-center justify-center">Зареждане...</div>}>
<ReserveForm
eventType={eventType}
event={selectedEvent}
onPersonsChange={handlePersonsChange}
ref={reserveFormRef}
/>
</Suspense>
{persons && (
<div className="mt-12">
<div className="text-xl font-bold text-center mb-8">
Специални предложения за <span style={{ fontFamily: 'sans-serif !important' }}>{persons}</span> {parseInt(persons, 10) === 1 ? 'човек' : 'човека'}
</div>
{isLoading ? (
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{[1, 2, 3].map(i => (
<div key={i} className="bg-gray-100 h-64 rounded-xl animate-pulse"></div>
))}
</div>
) : filteredDeals.length > 0 ? (
<motion.div
variants={staggerContainer}
initial="hidden"
animate="visible"
className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"
>
{filteredDeals.map(deal => (
<DealCard key={deal.id} deal={deal} />
))}
</motion.div>
) : (
<p className="text-center text-gray-500">
Няма специални предложения за избрания брой гости.
</p>
)}
</div>
)}
<div className="mt-8 text-center">
<motion.p
className="text-gray-500 text-sm"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.3, duration: 0.5 }}
>
отнема само 20 секунди!
</motion.p>
</div>
</motion.div>
</div>
);
}