From 3b920c2461870962291733c495410a78823db44c Mon Sep 17 00:00:00 2001 From: aryabenade Date: Thu, 19 Mar 2026 15:12:44 +0530 Subject: [PATCH] show all attractions with filter and search functionalities --- src/Redux/Store.tsx | 6 +- src/Redux/services/attractions.service.ts | 40 ++ src/components/AttractionsPage.tsx | 561 +++++++++++----------- vite.config.ts | 2 +- 4 files changed, 324 insertions(+), 285 deletions(-) create mode 100644 src/Redux/services/attractions.service.ts diff --git a/src/Redux/Store.tsx b/src/Redux/Store.tsx index fde51a7..f4aab57 100644 --- a/src/Redux/Store.tsx +++ b/src/Redux/Store.tsx @@ -1,9 +1,12 @@ import { configureStore } from "@reduxjs/toolkit"; import { fakeApi } from "./services/fakeApi.service"; +import { attractionsApi } from "./services/attractions.service"; export const store = configureStore({ reducer: { - [fakeApi.reducerPath]:fakeApi.reducer + [fakeApi.reducerPath]:fakeApi.reducer, + [attractionsApi.reducerPath]:attractionsApi.reducer + }, @@ -11,6 +14,7 @@ export const store = configureStore({ getDefaultMiddleware().concat( fakeApi.middleware, +attractionsApi.middleware ), }); diff --git a/src/Redux/services/attractions.service.ts b/src/Redux/services/attractions.service.ts new file mode 100644 index 0000000..ac73733 --- /dev/null +++ b/src/Redux/services/attractions.service.ts @@ -0,0 +1,40 @@ +import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'; + +export const attractionsApi = createApi({ + reducerPath: 'attractionsApi', + baseQuery: fetchBaseQuery({ + baseUrl: 'https://testingapi.citycards.betadelivery.com', + }), + endpoints: (builder) => ({ + getAttractionFilters: builder.query({ + // cityId is passed as the query param + query: (cityId) => `/attractions/customer/filters?cityXid=${cityId}`, + }), + + getCustomerAttractions: builder.query({ + // cityId is required, others optional + query: ({ cityId, categoryId, isBookingRequired, cardType, search }) => { + const params = new URLSearchParams(); + + // required + params.append('cityXid', cityId); + + // optional + if (categoryId) params.append('categoryXid', categoryId); + if (isBookingRequired !== undefined) params.append('isBookingRequired', isBookingRequired); + if (cardType) params.append('cardType', cardType); + if (search) params.append('search', search); + + return `/attractions/customer/customer-attractions?${params.toString()}`; + }, + }), + + getAttractionDetailsById: builder.query({ + query: (id: number) => `/attractions/customer/${id}`, + }), + + + }), +}); + +export const { useGetAttractionFiltersQuery,useGetCustomerAttractionsQuery,useGetAttractionDetailsByIdQuery } = attractionsApi; \ No newline at end of file diff --git a/src/components/AttractionsPage.tsx b/src/components/AttractionsPage.tsx index 66fea7c..c7a97a8 100644 --- a/src/components/AttractionsPage.tsx +++ b/src/components/AttractionsPage.tsx @@ -1,7 +1,7 @@ -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import { motion } from 'motion/react'; import { Search, Star, Clock } from 'lucide-react'; -import { useNavigate } from 'react-router-dom'; +import { useNavigate, useParams } from 'react-router-dom'; import { Button } from './ui/button'; import { Input } from './ui/input'; import { Card, CardContent } from './ui/card'; @@ -9,12 +9,11 @@ import { Badge } from './ui/badge'; import { Checkbox } from './ui/checkbox'; import { ImageWithFallback } from './figma/ImageWithFallback'; import { Layout } from '../Layout'; - +import { useGetAttractionFiltersQuery, useGetCustomerAttractionsQuery } from '../Redux/services/attractions.service'; interface User { email: string; name: string; } - interface Attraction { id: string; name: string; @@ -30,191 +29,187 @@ interface Attraction { passType: string; } -const attractions: Attraction[] = [ - { - id: '1', - name: 'Centipede Tour - Guided Arizona Desert Tour by ATV', - description: 'Experience the thrill of off-road adventure through the stunning Arizona desert landscape', - image: 'https://images.unsplash.com/photo-1682687220742-aba13b6e50ba?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxhdHYlMjBkZXNlcnQlMjB0b3VyfGVufDF8fHx8MTc1ODEwNDg5Nnww&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral', - location: 'Paris, France', - duration: '4 days', - rating: 4.8, - price: 189.25, - category: 'adventure', - hasReservation: true, - reviewCount: 243, - passType: 'unlimited' - }, - { - id: '2', - name: 'Molokini and Turtle Town Snorkeling Adventure Aboard', - description: 'Snorkel in crystal-clear waters and swim alongside sea turtles in this unforgettable marine adventure', - image: 'https://images.unsplash.com/photo-1559827260-dc66d52bef19?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxzbm9ya2VsaW5nJTIwdHVydGxlJTIwYWR2ZW50dXJlfGVufDF8fHx8MTc1ODEwNDkwMHww&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral', - location: 'New York, USA', - duration: '4 days', - rating: 4.8, - price: 225, - category: 'adventure', - hasReservation: false, - reviewCount: 167, - passType: 'selective' - }, - { - id: '3', - name: 'Westminster Walking Tour & Westminster Abbey Entry', - description: 'Explore the heart of London with guided tours of historic Westminster and the famous Abbey', - image: 'https://images.unsplash.com/photo-1533929736458-ca588d08c8be?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHx3ZXN0bWluc3RlciUyMGFiYmV5JTIwbG9uZG9ufGVufDF8fHx8MTc1ODEwNDkwNnww&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral', - location: 'London, UK', - duration: '4 days', - rating: 4.8, - price: 343, - category: 'culture', - hasReservation: true, - reviewCount: 343, - passType: 'unlimited' - }, - { - id: '4', - name: 'All Inclusive Ultimate Circle Island Day Tour with Lunch', - description: 'Comprehensive island tour including all major attractions, lunch, and transportation', - image: 'https://images.unsplash.com/photo-1571019613454-1cb2f99b2d8b?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxpc2xhbmQlMjB0b3VyJTIwYWRvJTIwdHJvcGljYWx8ZW58MXx8fHwxNzU4MTA0OTEwfDA&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral', - location: 'New York, USA', - duration: '4 days', - rating: 4.8, - price: 225, - category: 'adventure', - hasReservation: false, - reviewCount: 243, - passType: 'unlimited' - }, - { - id: '5', - name: 'Space Center Houston Admission Ticket', - description: 'Explore NASA\'s Johnson Space Center and discover the wonders of space exploration', - image: 'https://images.unsplash.com/photo-1446776653964-20c1d3a81b06?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxzcGFjZSUyMGNlbnRlciUyMG5hc2ElMjBob3VzdG9ufGVufDF8fHx8MTc1ODEwNDkxM3ww&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral', - location: 'Paris, France', - duration: '4 days', - rating: 4.8, - price: 225, - category: 'family', - hasReservation: true, - reviewCount: 243, - passType: 'selective' - }, - { - id: '6', - name: 'Melbourne Skydeck Observatory', - description: 'Experience breathtaking 360-degree views from the Southern Hemisphere\'s highest viewing platform', - image: 'https://images.unsplash.com/photo-1677200922658-d0df5b2ac91e?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxtZWxib3VybmUlMjBhdHRyYWN0aW9ucyUyMGZhbW91cyUyMGxhbmRtYXJrc3xlbnwxfHx8fDE3NTc0MDEwODV8MA&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral', - location: 'Melbourne CBD', - duration: '2 hours', - rating: 4.5, - price: 25, - category: 'adventure', - hasReservation: true, - reviewCount: 892, - passType: 'selective' - }, - { - id: '7', - name: 'Royal Botanic Gardens Melbourne', - description: 'Explore 38 hectares of stunning gardens featuring over 8,500 species of plants', - image: 'https://images.unsplash.com/photo-1721272962395-a848331ce92d?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxtZWxib3VybmUlMjByb3lhbCUyMGJvdGFuaWMlMjBnYXJkZW5zfGVufDF8fHx8MTc1NzMzNzc4OXww&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral', - location: 'South Yarra', - duration: '3 hours', - rating: 4.7, - price: 0, - category: 'nature', - hasReservation: false, - reviewCount: 1245, - passType: 'selective' - }, - { - id: '8', - name: 'Federation Square Cultural Precinct', - description: 'Melbourne\'s cultural precinct featuring galleries, museums, and unique architecture', - image: 'https://images.unsplash.com/photo-1580688027085-8220709e3d84?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxmZWRlcmF0aW9uJTIwc3F1YXJlJTIwbWVsYm91cm5lfGVufDF8fHx8MTc1NzQwMTA5Mnww&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral', - location: 'Melbourne CBD', - duration: '3 hours', - rating: 4.3, - price: 0, - category: 'culture', - hasReservation: true, - reviewCount: 672, - passType: 'unlimited' - }, - { - id: '9', - name: 'St Kilda Pier & Little Penguins', - description: 'Watch little penguins return home at sunset while enjoying the scenic pier', - image: 'https://images.unsplash.com/photo-1597889790884-2bb63cfbd4f6?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxzdCUyMGtpbGRhJTIwcGllciUyMG1lbGJvdXJuZXxlbnwxfHx8fDE3NTc0MDEwOTV8MA&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral', - location: 'St Kilda', - duration: '2 hours', - rating: 4.4, - price: 0, - category: 'nature', - hasReservation: false, - reviewCount: 543, - passType: 'unlimited' - }, - { - id: '10', - name: 'Queen Victoria Market Experience', - description: 'Historic market offering fresh produce, gourmet foods, and unique souvenirs', - image: 'https://images.unsplash.com/photo-1676454953709-e0be46f62490?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxxdWVlbiUyMHZpY3RvcmlhJTIwbWFya2V0JTIwbWVsYm91cm5lfGVufDF8fHx8MTc1NzQwMTA5OHww&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral', - location: 'Melbourne CBD', - duration: '2 hours', - rating: 4.6, - price: 0, - category: 'culture', - hasReservation: true, - reviewCount: 987, - passType: 'selective' - }, - { - id: '11', - name: 'Melbourne Zoo Adventure', - description: 'Meet over 320 animal species from around the world in naturalistic habitats', - image: 'https://images.unsplash.com/photo-1681429477985-30dc7b88dd5b?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxtZWxib3VybmUlMjB6b28lMjBhbmltYWxzfGVufDF8fHx8MTc1NzMzNzgxMHww&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral', - location: 'Parkville', - duration: '4 hours', - rating: 4.5, - price: 40, - category: 'family', - hasReservation: false, - reviewCount: 1156, - passType: 'selective' - }, - { - id: '12', - name: 'Great Ocean Road Day Tour', - description: 'Scenic coastal drive featuring the famous Twelve Apostles and stunning ocean views', - image: 'https://images.unsplash.com/photo-1506905925346-21bda4d32df4?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxncmVhdCUyMG9jZWFuJTIwcm9hZCUyMGF1c3RyYWxpYXxlbnwxfHx8fDE3NTgxMDQ5Mzd8MA&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral', - location: 'Great Ocean Road', - duration: '12 hours', - rating: 4.9, - price: 85, - category: 'adventure', - hasReservation: true, - reviewCount: 678, - passType: 'unlimited' - } -]; - -const filterCategories = [ - { value: 'with-reservation', label: 'With Reservation', count: 3 }, - { value: 'without-reservation', label: 'Without Reservation', count: 3 }, - { value: 'beach', label: 'Beach', count: 3 }, - { value: 'adventure', label: 'Adventure', count: 3 }, - { value: 'mountains', label: 'Mountains', count: 3 }, - { value: 'family', label: 'Family Friendly', count: 3 } -]; - -const passTypeCategories = [ - { value: 'selective', label: 'Flexi Pass', count: 6 }, - { value: 'unlimited', label: 'Unlimited Pass', count: 6 } -]; - +// { +// id: '1', +// name: 'Centipede Tour - Guided Arizona Desert Tour by ATV', +// description: 'Experience the thrill of off-road adventure through the stunning Arizona desert landscape', +// image: 'https://images.unsplash.com/photo-1682687220742-aba13b6e50ba?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxhdHYlMjBkZXNlcnQlMjB0b3VyfGVufDF8fHx8MTc1ODEwNDg5Nnww&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral', +// location: 'Paris, France', +// duration: '4 days', +// rating: 4.8, +// price: 189.25, +// category: 'adventure', +// hasReservation: true, +// reviewCount: 243, +// passType: 'unlimited' +// }, +// { +// id: '2', +// name: 'Molokini and Turtle Town Snorkeling Adventure Aboard', +// description: 'Snorkel in crystal-clear waters and swim alongside sea turtles in this unforgettable marine adventure', +// image: 'https://images.unsplash.com/photo-1559827260-dc66d52bef19?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxzbm9ya2VsaW5nJTIwdHVydGxlJTIwYWR2ZW50dXJlfGVufDF8fHx8MTc1ODEwNDkwMHww&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral', +// location: 'New York, USA', +// duration: '4 days', +// rating: 4.8, +// price: 225, +// category: 'adventure', +// hasReservation: false, +// reviewCount: 167, +// passType: 'selective' +// }, +// { +// id: '3', +// name: 'Westminster Walking Tour & Westminster Abbey Entry', +// description: 'Explore the heart of London with guided tours of historic Westminster and the famous Abbey', +// image: 'https://images.unsplash.com/photo-1533929736458-ca588d08c8be?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHx3ZXN0bWluc3RlciUyMGFiYmV5JTIwbG9uZG9ufGVufDF8fHx8MTc1ODEwNDkwNnww&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral', +// location: 'London, UK', +// duration: '4 days', +// rating: 4.8, +// price: 343, +// category: 'culture', +// hasReservation: true, +// reviewCount: 343, +// passType: 'unlimited' +// }, +// { +// id: '4', +// name: 'All Inclusive Ultimate Circle Island Day Tour with Lunch', +// description: 'Comprehensive island tour including all major attractions, lunch, and transportation', +// image: 'https://images.unsplash.com/photo-1571019613454-1cb2f99b2d8b?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxpc2xhbmQlMjB0b3VyJTIwYWRvJTIwdHJvcGljYWx8ZW58MXx8fHwxNzU4MTA0OTEwfDA&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral', +// location: 'New York, USA', +// duration: '4 days', +// rating: 4.8, +// price: 225, +// category: 'adventure', +// hasReservation: false, +// reviewCount: 243, +// passType: 'unlimited' +// }, +// { +// id: '5', +// name: 'Space Center Houston Admission Ticket', +// description: 'Explore NASA\'s Johnson Space Center and discover the wonders of space exploration', +// image: 'https://images.unsplash.com/photo-1446776653964-20c1d3a81b06?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxzcGFjZSUyMGNlbnRlciUyMG5hc2ElMjBob3VzdG9ufGVufDF8fHx8MTc1ODEwNDkxM3ww&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral', +// location: 'Paris, France', +// duration: '4 days', +// rating: 4.8, +// price: 225, +// category: 'family', +// hasReservation: true, +// reviewCount: 243, +// passType: 'selective' +// }, +// { +// id: '6', +// name: 'Melbourne Skydeck Observatory', +// description: 'Experience breathtaking 360-degree views from the Southern Hemisphere\'s highest viewing platform', +// image: 'https://images.unsplash.com/photo-1677200922658-d0df5b2ac91e?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxtZWxib3VybmUlMjBhdHRyYWN0aW9ucyUyMGZhbW91cyUyMGxhbmRtYXJrc3xlbnwxfHx8fDE3NTc0MDEwODV8MA&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral', +// location: 'Melbourne CBD', +// duration: '2 hours', +// rating: 4.5, +// price: 25, +// category: 'adventure', +// hasReservation: true, +// reviewCount: 892, +// passType: 'selective' +// }, +// { +// id: '7', +// name: 'Royal Botanic Gardens Melbourne', +// description: 'Explore 38 hectares of stunning gardens featuring over 8,500 species of plants', +// image: 'https://images.unsplash.com/photo-1721272962395-a848331ce92d?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxtZWxib3VybmUlMjByb3lhbCUyMGJvdGFuaWMlMjBnYXJkZW5zfGVufDF8fHx8MTc1NzMzNzc4OXww&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral', +// location: 'South Yarra', +// duration: '3 hours', +// rating: 4.7, +// price: 0, +// category: 'nature', +// hasReservation: false, +// reviewCount: 1245, +// passType: 'selective' +// }, +// { +// id: '8', +// name: 'Federation Square Cultural Precinct', +// description: 'Melbourne\'s cultural precinct featuring galleries, museums, and unique architecture', +// image: 'https://images.unsplash.com/photo-1580688027085-8220709e3d84?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxmZWRlcmF0aW9uJTIwc3F1YXJlJTIwbWVsYm91cm5lfGVufDF8fHx8MTc1NzQwMTA5Mnww&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral', +// location: 'Melbourne CBD', +// duration: '3 hours', +// rating: 4.3, +// price: 0, +// category: 'culture', +// hasReservation: true, +// reviewCount: 672, +// passType: 'unlimited' +// }, +// { +// id: '9', +// name: 'St Kilda Pier & Little Penguins', +// description: 'Watch little penguins return home at sunset while enjoying the scenic pier', +// image: 'https://images.unsplash.com/photo-1597889790884-2bb63cfbd4f6?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxzdCUyMGtpbGRhJTIwcGllciUyMG1lbGJvdXJuZXxlbnwxfHx8fDE3NTc0MDEwOTV8MA&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral', +// location: 'St Kilda', +// duration: '2 hours', +// rating: 4.4, +// price: 0, +// category: 'nature', +// hasReservation: false, +// reviewCount: 543, +// passType: 'unlimited' +// }, +// { +// id: '10', +// name: 'Queen Victoria Market Experience', +// description: 'Historic market offering fresh produce, gourmet foods, and unique souvenirs', +// image: 'https://images.unsplash.com/photo-1676454953709-e0be46f62490?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxxdWVlbiUyMHZpY3RvcmlhJTIwbWFya2V0JTIwbWVsYm91cm5lfGVufDF8fHx8MTc1NzQwMTA5OHww&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral', +// location: 'Melbourne CBD', +// duration: '2 hours', +// rating: 4.6, +// price: 0, +// category: 'culture', +// hasReservation: true, +// reviewCount: 987, +// passType: 'selective' +// }, +// { +// id: '11', +// name: 'Melbourne Zoo Adventure', +// description: 'Meet over 320 animal species from around the world in naturalistic habitats', +// image: 'https://images.unsplash.com/photo-1681429477985-30dc7b88dd5b?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxtZWxib3VybmUlMjB6b28lMjBhbmltYWxzfGVufDF8fHx8MTc1NzMzNzgxMHww&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral', +// location: 'Parkville', +// duration: '4 hours', +// rating: 4.5, +// price: 40, +// category: 'family', +// hasReservation: false, +// reviewCount: 1156, +// passType: 'selective' +// }, +// { +// id: '12', +// name: 'Great Ocean Road Day Tour', +// description: 'Scenic coastal drive featuring the famous Twelve Apostles and stunning ocean views', +// image: 'https://images.unsplash.com/photo-1506905925346-21bda4d32df4?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxncmVhdCUyMG9jZWFuJTIwcm9hZCUyMGF1c3RyYWxpYXxlbnwxfHx8fDE3NTgxMDQ5Mzd8MA&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral', +// location: 'Great Ocean Road', +// duration: '12 hours', +// rating: 4.9, +// price: 85, +// category: 'adventure', +// hasReservation: true, +// reviewCount: 678, +// passType: 'unlimited' +// } +// ]; +// const filterCategories = [ +// { value: 'with-reservation', label: 'With Reservation', count: 3 }, +// { value: 'without-reservation', label: 'Without Reservation', count: 3 }, +// { value: 'beach', label: 'Beach', count: 3 }, +// { value: 'adventure', label: 'Adventure', count: 3 }, +// { value: 'mountains', label: 'Mountains', count: 3 }, +// { value: 'family', label: 'Family Friendly', count: 3 } +// ]; +// const passTypeCategories = [ +// { value: 'selective', label: 'Flexi Pass', count: 6 }, +// { value: 'unlimited', label: 'Unlimited Pass', count: 6 } +// ]; interface AttractionsPageProps { onSignInClick: () => void; onSignOutClick?: () => void; @@ -226,55 +221,73 @@ export function AttractionsPage({ onSignOutClick, user }: AttractionsPageProps) { + const navigate = useNavigate(); - const [searchQuery, setSearchQuery] = useState(''); - const [selectedCategories, setSelectedCategories] = useState([]); - const [selectedPassTypes, setSelectedPassTypes] = useState([]); - const filteredAttractions = attractions.filter(attraction => { - const matchesSearch = attraction.name.toLowerCase().includes(searchQuery.toLowerCase()) || - attraction.description.toLowerCase().includes(searchQuery.toLowerCase()); + const [search, setSearch] = useState(""); + const [isBookingRequired, setIsBookingRequired] = useState(undefined) + const [selectedCategory, setSelectedCategory] = useState(null); + const [selectedPassType, setSelectedPassType] = useState(null); - const matchesCategory = selectedCategories.length === 0 || - selectedCategories.some(cat => { - if (cat === 'with-reservation') return attraction.hasReservation; - if (cat === 'without-reservation') return !attraction.hasReservation; - return attraction.category === cat; - }); - - const matchesPassType = selectedPassTypes.length === 0 || - selectedPassTypes.includes(attraction.passType); - - return matchesSearch && matchesCategory && matchesPassType; + const cityId = 1 + + const { data: filterData, isLoading } = useGetAttractionFiltersQuery(cityId) + const { data: attractions } = useGetCustomerAttractionsQuery({ + cityId, // required + categoryId: selectedCategory, // optional + isBookingRequired, // optional + cardType: selectedPassType, // optional + search, // optional }); - - const toggleCategory = (category: string) => { - setSelectedCategories(prev => - prev.includes(category) - ? prev.filter(c => c !== category) - : [...prev, category] - ); - }; - - const togglePassType = (passType: string) => { - setSelectedPassTypes(prev => - prev.includes(passType) - ? prev.filter(p => p !== passType) - : [...prev, passType] - ); - }; + + if (isLoading) { + return
Loading...
+ } const handleAttractionClick = (attractionId: string) => { navigate(`/attractions/${attractionId}`); }; - const handleCheckoutClick = () => { navigate('/checkout'); }; const showingFrom = 1; - const showingTo = Math.min(12, filteredAttractions.length); - const totalItems = filteredAttractions.length; + const showingTo = Math.min(12, attractions?.length); + const totalItems = attractions?.length; + + function handlePassTypeSelection(key: string, checked: boolean) { + if (checked) { + setSelectedPassType(key); // only keep the newly selected one + } else { + setSelectedPassType(null); // reset if unchecked + } + } + + function handleCategorySelection(id: number, checked: boolean) { + if (checked) { + if (id === 50) { + setIsBookingRequired(true); + setSelectedCategory(null); // clear normal category + } else if (id === 51) { + setIsBookingRequired(false); + setSelectedCategory(null); // clear normal category + } else { + setSelectedCategory(id); + setIsBookingRequired(undefined); // clear booking filter + } + } else { + // reset if unchecked + if (id === 50 || id === 51) { + setIsBookingRequired(undefined); + } else { + setSelectedCategory(null); + } + } + } + + const handleSearchChange = (e: React.ChangeEvent) => { + setSearch(e.target.value) + } return ( - {/* City Card Promotional Banner */}
@@ -304,20 +316,18 @@ export function AttractionsPage({

Find Your Perfect Adventure

- {/* Search Bar and Button Container */}
{/* Search Bar */}
setSearchQuery(e.target.value)} + value={search} + onChange={handleSearchChange} className="pl-4 pr-12 h-[44px] bg-white/95 backdrop-blur-sm border-0 rounded-lg text-gray-800 placeholder:text-gray-500 font-poppins shadow-lg" />
- {/* Call-to-Action Button */}
- {/* Decorative background elements */}
-
{/* Left Sidebar */}
@@ -344,22 +352,29 @@ export function AttractionsPage({

Search by

- {/* Filter categories */}
- {filterCategories.map(category => ( -
+ {filterData && filterData.categories.map((category: any) => ( +
toggleCategory(category.value)} + id={String(category.id)} + checked={ + category.id === 50 + ? isBookingRequired === true + : category.id === 51 + ? isBookingRequired === false + : selectedCategory === category.id + } + onCheckedChange={(checked: boolean) => + handleCategorySelection(category.id, checked) + } className="border-[#bebebe]" />
))} @@ -367,51 +382,49 @@ export function AttractionsPage({ {/* Divider */}
- {/* Pass Type header */}

Pass Type

- {/* Pass Type filters */}
- {passTypeCategories.map(passType => ( -
+ {filterData && Object.entries(filterData.passType).map(([key, count]) => ( +
togglePassType(passType.value)} + id={key} + checked={selectedPassType === key} + onCheckedChange={(checked: boolean) => + handlePassTypeSelection(key, checked as boolean) + } className="border-[#bebebe]" />
))}
+
- {/* Main Content */}
{/* Header */}

Attractions in Melbourne

- {/* Results count */}

Showing {showingFrom}-{showingTo} of {totalItems} Item(s)

- {/* Attractions Grid */}
- {filteredAttractions.slice(0, 12).map((attraction) => ( + {attractions && attractions.map((attraction: any) => ( FREE - ) : attraction.passType === 'unlimited' ? ( - - Unlimited Pass Exclusive - - ) : ( + ) : attraction.cards[0].cardType.cardTypeDisplayName === "Flexi card" ? ( Flexi Pass + ) : ( + + Unlimited Pass Exclusive + )}
- {attraction.location} + {/* {attraction.location} */}

- {attraction.name} + {attraction.title}

-
-
- {[...Array(5)].map((_, i) => ( - - ))} - - {attraction.rating} ({attraction.reviewCount}) - -
-
- {/* Pricing and Pass Info */}
- {attraction.duration} + {attraction.durations} minutes
Normal visit price
- ${attraction.price} + ${attraction.ticketPriceAdult}
- {/* Included with Pass CTA */}

- ✓ Included with {attraction.passType === 'unlimited' ? 'Unlimited' : 'Selective'} Pass + ✓ Included with {attraction.cards[0].cardType.cardTypeDisplayName === "Flexi card" ? 'Flexi' : 'Unlimited'} Pass

- Save ${attraction.price} + Save ${attraction.cards[0].adultPrice}