// Public pages: Tours listing, Tour detail, Vehicles, Blog, Page, Contact // -------- TOURS LISTING -------- const PublicTours = ({ state, navigate }) => { const [category, setCategory] = useState('all'); const [sort, setSort] = useState('featured'); const [priceRange, setPriceRange] = useState('any'); const [durationFilter, setDurationFilter] = useState('any'); const [destFilter, setDestFilter] = useState('all'); const [page, setPage] = useState(1); const perPage = 8; const destinations = ['all', ...new Set(state.packages.filter(p => p.status === 'published').map(p => p.destination))]; const list = state.packages .filter(p => p.status === 'published') .filter(p => category === 'all' || p.category === category) .filter(p => destFilter === 'all' || p.destination === destFilter) .filter(p => { if (priceRange === 'any') return true; if (priceRange === '<200') return p.priceFrom < 200; if (priceRange === '200-500') return p.priceFrom >= 200 && p.priceFrom <= 500; if (priceRange === '500-1500') return p.priceFrom > 500 && p.priceFrom <= 1500; return p.priceFrom > 1500; }) .filter(p => { if (durationFilter === 'any') return true; if (durationFilter === 'half') return p.durationDays <= 0.5; if (durationFilter === 'day') return p.durationDays === 1; if (durationFilter === 'multi') return p.durationDays >= 2 && p.durationDays <= 3; return p.durationDays > 3; }); const sorted = [...list].sort((a, b) => { if (sort === 'price-asc') return a.priceFrom - b.priceFrom; if (sort === 'price-desc') return b.priceFrom - a.priceFrom; if (sort === 'rating') return b.rating - a.rating; return (b.featured ? 1 : 0) - (a.featured ? 1 : 0); }); const totalPages = Math.ceil(sorted.length / perPage) || 1; const paged = sorted.slice((page - 1) * perPage, page * perPage); return (
Tour packages

All trips. Every season.

{state.packages.filter(p => p.status === 'published').length} packages · inbound Sarawak + outbound from Kuching.

{/* Filter bar */}
{[['all', 'All', state.packages.filter(p => p.status === 'published').length], ['inbound', 'Inbound Sarawak', state.packages.filter(p => p.status === 'published' && p.category === 'inbound').length], ['outbound', 'Outbound', state.packages.filter(p => p.status === 'published' && p.category === 'outbound').length]].map(([k, label, count]) => ( ))}
Showing {(page - 1) * perPage + 1}–{Math.min(page * perPage, sorted.length)} of {sorted.length} packages
{paged.map(p => navigate({ route: 'tour', slug: p.slug })} />)}
{/* Pagination */} {totalPages > 1 && (
{[...Array(totalPages)].map((_, i) => ( ))}
)}
); }; // -------- TOUR DETAIL -------- const PublicTour = ({ state, navigate, slug }) => { const pkg = state.packages.find(p => p.slug === slug); const [galleryIdx, setGalleryIdx] = useState(0); const [openDay, setOpenDay] = useState(0); if (!pkg) return
Package not found. navigate({ route: 'tours' })}>Back to tours
; const related = state.packages.filter(p => p.id !== pkg.id && p.status === 'published' && p.category === pkg.category).slice(0, 3); return (
{/* Gallery */}
{(pkg.gallery.slice(0, 4).length > 1 ? pkg.gallery.slice(0, 4) : [pkg.hero, pkg.hero, pkg.hero, pkg.hero]).map((g, i) => (
setGalleryIdx(i)} style={{ cursor: 'pointer', position: 'relative' }}> {i === 3 &&
+{pkg.gallery.length - 4} more
}
))}
{/* Main content */}
{pkg.category === 'inbound' ? 'Sarawak' : 'Outbound'} {pkg.tags.map(t => {t})}

{pkg.title}

{pkg.destination} {pkg.duration} {pkg.rating} ({pkg.reviews} reviews)

{pkg.short}

{pkg.description}

{/* Itinerary */}
Day by day

Itinerary

{pkg.itinerary.map((d, i) => (
{openDay === i && (
{d.text}
)}
))}
{/* Pricing */} {pkg.pricing.length > 0 && (

Pricing

{pkg.pricing.map((p, i) => (
{p.label}
{fmtMYR(p.price)}/pax
))}
)} {/* In/Ex */}

What's included

    {pkg.inclusions.map((t, i) =>
  • {t}
  • )}

Not included

    {pkg.exclusions.map((t, i) =>
  • {t}
  • )}
{/* Sticky sidebar */}
{/* Related */} {related.length > 0 && (

You might also like

{related.map(r => { navigate({ route: 'tour', slug: r.slug }); window.scrollTo(0, 0); }} />)}
)}
); }; // -------- VEHICLES -------- const PublicVehicles = ({ state, navigate }) => { const [typeFilter, setTypeFilter] = useState('all'); const [capFilter, setCapFilter] = useState('any'); const list = state.vehicles.filter(v => v.status === 'published') .filter(v => typeFilter === 'all' || v.type === typeFilter) .filter(v => capFilter === 'any' || (capFilter === '4' && v.capacity <= 4) || (capFilter === '7' && v.capacity > 4 && v.capacity <= 7) || (capFilter === '15' && v.capacity > 7 && v.capacity <= 15) || (capFilter === 'bus' && v.capacity > 15)); return (
Car rental · Chauffeured transport

Move around. Kuching-wide.

Self-drive economy to executive MPV to 4WD for the interior. All vehicles insured, serviced monthly.

Filter: {['all', 'Sedan', 'SUV', 'MPV', 'Van', 'Bus', '4WD'].map(t => ( ))}
Capacity: setForm({ ...form, name: v })} required hint={errors.name} /> setForm({ ...form, phone: v })} placeholder="+60 12 ..." required hint={errors.phone} />
setForm({ ...form, email: v })} required hint={errors.email} />
setForm({ ...form, date: v })} />
setForm({ ...form, package: v })} placeholder="e.g. Mulu Caves 3D2N" />