hilingriviw/src/pages/BestLocations/MenuPopup.tsx
2026-06-14 05:53:50 +03:00

134 lines
4.3 KiB
TypeScript

import { useEffect, useState } from "preact/hooks";
import { getMenuItemsService } from "../../services/locations";
export interface MenuItemRow {
id: number;
location_id: number;
name: string;
price: number;
category: string;
description: string;
is_available: boolean;
submitted_by: number;
avg_score: number | null;
}
const CATEGORY_LABELS: Record<string, string> = {
appetizer: 'Appetizer',
main_course: 'Main Course',
dessert: 'Dessert',
beverages: 'Beverages',
snack: 'Snack',
};
function formatPrice(price: number): string {
return 'Rp' + price.toLocaleString('id-ID');
}
function getRatingColor(rating: number): string {
if (rating >= 70) return '#3ba55d';
if (rating >= 40) return '#faa61a';
return '#ed4245';
}
interface Props {
locationId: number;
locationName: string;
onClose: () => void;
}
export default function MenuPopup({ locationId, locationName, onClose }: Props) {
const [items, setItems] = useState<MenuItemRow[]>([]);
const [loading, setLoading] = useState(true);
const [activeCategory, setActiveCategory] = useState('all');
const [search, setSearch] = useState('');
useEffect(() => {
getMenuItemsService(locationId).then(res => {
if (res.data) setItems(res.data);
setLoading(false);
});
}, [locationId]);
const categories = ['all', ...Array.from(new Set(items.map(i => i.category).filter(Boolean)))];
const filtered = items.filter(item => {
const matchCat = activeCategory === 'all' || item.category === activeCategory;
const matchSearch = item.name.toLowerCase().includes(search.toLowerCase());
return matchCat && matchSearch;
});
// split into two columns
const mid = Math.ceil(filtered.length / 2);
const col1 = filtered.slice(0, mid);
const col2 = filtered.slice(mid);
return (
<div className="menu-popup-overlay" onClick={onClose}>
<div className="menu-popup-modal" onClick={e => e.stopPropagation()}>
<div className="menu-popup-header">
<div>
<h2 className="menu-popup-title">{locationName} Menu</h2>
</div>
<div className="menu-popup-header-actions">
<input
className="menu-popup-search"
type="text"
placeholder="Search dishes..."
value={search}
onInput={e => setSearch((e.target as HTMLInputElement).value)}
/>
<button className="menu-popup-close" onClick={onClose}></button>
</div>
</div>
<div className="menu-popup-tabs">
{categories.map(cat => (
<button
key={cat}
className={`menu-popup-tab ${activeCategory === cat ? 'menu-popup-tab--active' : ''}`}
onClick={() => setActiveCategory(cat)}
>
{cat === 'all' ? 'All' : (CATEGORY_LABELS[cat] ?? cat)}
</button>
))}
</div>
<div className="menu-popup-body">
{loading ? (
<p style={{ padding: '20px', opacity: 0.5 }}>Loading...</p>
) : filtered.length === 0 ? (
<p style={{ padding: '20px', opacity: 0.5 }}>No items found.</p>
) : (
<div className="menu-popup-grid">
{[col1, col2].map((col, ci) => (
<div key={ci} className="menu-popup-col">
<div className="menu-popup-col-header">
<span style={{ flex: 1 }}></span>
<span className="menu-popup-col-rating-label">Rating</span>
</div>
{col.map(item => (
<div key={item.id} className="menu-popup-item">
<span className="menu-popup-item-name">{item.name}</span>
<span className="menu-popup-item-price">{formatPrice(item.price)}</span>
{item.avg_score != null ? (
<span className="menu-popup-item-rating" style={{ color: getRatingColor(item.avg_score) }}>
{Math.round(item.avg_score)}
</span>
) : (
<span className="menu-popup-item-rating" style={{ opacity: 0.3 }}></span>
)}
</div>
))}
</div>
))}
</div>
)}
</div>
</div>
</div>
);
}