Merge pull request 'main' (#31) from main into uat-beta
All checks were successful
CityCards-Website / Build-CityCards-Website (push) Successful in 23s

Reviewed-on: #31
This commit is contained in:
2026-04-24 14:43:25 +00:00
137 changed files with 13679 additions and 8701 deletions

View File

@@ -1,15 +1,16 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>CityCards Travel 22-8-2025</title> <link rel="icon" type="image/png" href="/src/assets/citycards customer app.png" />
<title>CityCards Customer-web</title>
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>
<script type="module" src="/src/main.tsx"></script> <script type="module" src="/src/main.tsx"></script>
</body> </body>
</html>
</html>

43
package-lock.json generated
View File

@@ -35,12 +35,15 @@
"@radix-ui/react-toggle-group": "^1.1.2", "@radix-ui/react-toggle-group": "^1.1.2",
"@radix-ui/react-tooltip": "^1.1.8", "@radix-ui/react-tooltip": "^1.1.8",
"@reduxjs/toolkit": "^2.11.2", "@reduxjs/toolkit": "^2.11.2",
"@stripe/react-stripe-js": "^6.2.0",
"@stripe/stripe-js": "^9.2.0",
"@tailwindcss/postcss": "^4.1.13", "@tailwindcss/postcss": "^4.1.13",
"@tailwindcss/vite": "^4.1.14", "@tailwindcss/vite": "^4.1.14",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "*", "clsx": "*",
"cmdk": "^1.1.1", "cmdk": "^1.1.1",
"embla-carousel-react": "^8.6.0", "embla-carousel-react": "^8.6.0",
"i18n-iso-countries": "^7.14.0",
"input-otp": "^1.4.2", "input-otp": "^1.4.2",
"lucide-react": "^0.487.0", "lucide-react": "^0.487.0",
"motion": "*", "motion": "*",
@@ -54,7 +57,7 @@
"react-resizable-panels": "^2.1.7", "react-resizable-panels": "^2.1.7",
"react-router-dom": "^7.9.4", "react-router-dom": "^7.9.4",
"recharts": "^2.15.2", "recharts": "^2.15.2",
"sonner": "^2.0.3", "sonner": "^2.0.7",
"tailwind-merge": "*", "tailwind-merge": "*",
"tailwindcss": "^4.1.14", "tailwindcss": "^4.1.14",
"vaul": "^1.1.2" "vaul": "^1.1.2"
@@ -2237,6 +2240,28 @@
"integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/@stripe/react-stripe-js": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/@stripe/react-stripe-js/-/react-stripe-js-6.2.0.tgz",
"integrity": "sha512-GSCErjljZEQv9LaxP30xGOwstcMyyUzb5JyihXwvjOU95yrfhbiPG4K2KkwxYxn+WY0/AyHsRhPPoGRw7urBzg==",
"dependencies": {
"prop-types": "^15.7.2"
},
"peerDependencies": {
"@stripe/stripe-js": ">=9.2.0 <10.0.0",
"react": ">=16.8.0 <20.0.0",
"react-dom": ">=16.8.0 <20.0.0"
}
},
"node_modules/@stripe/stripe-js": {
"version": "9.2.0",
"resolved": "https://registry.npmjs.org/@stripe/stripe-js/-/stripe-js-9.2.0.tgz",
"integrity": "sha512-YSzLC0t6VS9MDdPTynSMqU8IxrItFUjkDORALFT6sSMR/XZ5Vgm3RDp/Gk7z727MC4A9s4MFVel0gF0c7+kdrg==",
"peer": true,
"engines": {
"node": ">=12.16"
}
},
"node_modules/@swc/core": { "node_modules/@swc/core": {
"version": "1.13.5", "version": "1.13.5",
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.13.5.tgz", "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.13.5.tgz",
@@ -3346,6 +3371,11 @@
"integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/diacritics": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/diacritics/-/diacritics-1.3.0.tgz",
"integrity": "sha512-wlwEkqcsaxvPJML+rDh/2iS824jbREk6DUMUKkEaSlxdYHeS43cClJtsWglvw2RfeXGm6ohKDqsXteJ5sP5enA=="
},
"node_modules/dom-helpers": { "node_modules/dom-helpers": {
"version": "5.2.1", "version": "5.2.1",
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
@@ -3527,6 +3557,17 @@
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
"license": "ISC" "license": "ISC"
}, },
"node_modules/i18n-iso-countries": {
"version": "7.14.0",
"resolved": "https://registry.npmjs.org/i18n-iso-countries/-/i18n-iso-countries-7.14.0.tgz",
"integrity": "sha512-nXHJZYtNrfsi1UQbyRqm3Gou431elgLjKl//CYlnBGt5aTWdRPH1PiS2T/p/n8Q8LnqYqzQJik3Q7mkwvLokeg==",
"dependencies": {
"diacritics": "1.3.0"
},
"engines": {
"node": ">= 12"
}
},
"node_modules/immer": { "node_modules/immer": {
"version": "11.1.4", "version": "11.1.4",
"resolved": "https://registry.npmjs.org/immer/-/immer-11.1.4.tgz", "resolved": "https://registry.npmjs.org/immer/-/immer-11.1.4.tgz",

View File

@@ -30,12 +30,15 @@
"@radix-ui/react-toggle-group": "^1.1.2", "@radix-ui/react-toggle-group": "^1.1.2",
"@radix-ui/react-tooltip": "^1.1.8", "@radix-ui/react-tooltip": "^1.1.8",
"@reduxjs/toolkit": "^2.11.2", "@reduxjs/toolkit": "^2.11.2",
"@stripe/react-stripe-js": "^6.2.0",
"@stripe/stripe-js": "^9.2.0",
"@tailwindcss/postcss": "^4.1.13", "@tailwindcss/postcss": "^4.1.13",
"@tailwindcss/vite": "^4.1.14", "@tailwindcss/vite": "^4.1.14",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "*", "clsx": "*",
"cmdk": "^1.1.1", "cmdk": "^1.1.1",
"embla-carousel-react": "^8.6.0", "embla-carousel-react": "^8.6.0",
"i18n-iso-countries": "^7.14.0",
"input-otp": "^1.4.2", "input-otp": "^1.4.2",
"lucide-react": "^0.487.0", "lucide-react": "^0.487.0",
"motion": "*", "motion": "*",
@@ -49,7 +52,7 @@
"react-resizable-panels": "^2.1.7", "react-resizable-panels": "^2.1.7",
"react-router-dom": "^7.9.4", "react-router-dom": "^7.9.4",
"recharts": "^2.15.2", "recharts": "^2.15.2",
"sonner": "^2.0.3", "sonner": "^2.0.7",
"tailwind-merge": "*", "tailwind-merge": "*",
"tailwindcss": "^4.1.14", "tailwindcss": "^4.1.14",
"vaul": "^1.1.2" "vaul": "^1.1.2"

View File

@@ -185,7 +185,7 @@ function App() {
</div> </div>
{/* Card Title in Orange */} {/* Card Title in Orange */}
<p className="absolute font-poppins font-medium leading-[1.3] left-[50%] text-[#ffb23f] text-[24px] text-center top-[65px] tracking-[-0.96px] translate-x-[-50%] w-[202px]" style={{ fontVariationSettings: "'wdth' 100" }}> <p className="absolute font-appoppins font-medium leading-[1.3] left-[50%] text-[#ffb23f] text-[24px] text-center top-[65px] tracking-[-0.96px] translate-x-[-50%] w-[202px]" style={{ fontVariationSettings: "'wdth' 100" }}>
{stickyCardType === 'unlimited' ? ( {stickyCardType === 'unlimited' ? (
<>Melbourne Unlimited Card</> <>Melbourne Unlimited Card</>
) : ( ) : (

View File

@@ -2,38 +2,42 @@ import { Routes, Route, useParams, useLocation, useNavigate } from 'react-router
import { motion, AnimatePresence } from 'motion/react'; import { motion, AnimatePresence } from 'motion/react';
// Import all your pages // Import all your pages
import { LoginModal } from './components/LoginModal'; import { ProtectedRoute } from './components/ProtectedRoute';
import { MelbournePage } from './components/MelbournePage'; import { MelbournePage } from './pages/MelbournePage';
import { PassesPage } from './components/PassesPage'; import { PassesPage } from './pages/PassesPage';
import { AttractionsPage } from './components/AttractionsPage'; import { AttractionsPage } from './pages/AttractionsPage';
import { AttractionDetailsPage } from './components/AttractionDetailsPage'; import { AttractionDetailsPage } from './pages/AttractionDetailsPage';
import { CheckoutPage } from './components/CheckoutPage'; import { BlogsPage } from './pages/BlogsPage';
import { SecureCheckoutPage } from './components/SecureCheckoutPage'; import { BlogDetailsPage } from './pages/BlogDetailsPage';
import { BlogsPage } from './components/BlogsPage';
import { BlogDetailsPage } from './components/BlogDetailsPage';
import { HowItWorksPage } from './components/HowItWorksPage';
import { FAQPage } from './components/FAQPage'; import { FAQPage } from './components/FAQPage';
import { PrivacyPolicyPage } from './components/PrivacyPolicyPage'; import { PrivacyPolicyPage } from './pages/PrivacyPolicyPage';
import { AboutUsPage } from './components/AboutUsPage'; import { AboutUsPage } from './pages/AboutUsPage';
import { ProfilePage } from './components/ProfilePage'; import { ProfilePage } from './pages/ProfilePage';
import { CreateMagicItineraryPage } from './components/CreateMagicItineraryPage'; import { OffersPage } from './pages/OffersPage';
import { ItineraryViewPage } from './components/ItineraryViewPage'; import { CityCardsPage } from './pages/CityCardsPage';
import { OffersPage } from './components/OffersPage'; import { MagicItineraryPage } from './pages/MagicItineraryPage';
import { CityCardsPage } from './components/CityCardsPage'; import { PostCardsPage } from './pages/PostCardsPage';
import { MagicItineraryPage } from './components/MagicItineraryPage'; import { DownloadAppPage } from './pages/DownloadAppPage';
import { PostCardsPage } from './components/PostCardsPage'; import { HotelDiscountsPage } from './pages/HotelDiscountsPage';
import { DownloadAppPage } from './components/DownloadAppPage'; import { ContactUsPage } from './pages/ContactUsPage';
import { EsimsPage } from './components/EsimsPage';
import { HotelDiscountsPage } from './components/HotelDiscountsPage';
import { ContactUsPage } from './components/ContactUsPage';
import { pageTransition } from './utils/animations'; import { pageTransition } from './utils/animations';
import { LandingPage } from './pages/landingPage'; import { LandingPage } from './pages/landingPage';
import ComingSoonPage from './pages/ComingSoonPage'; import ComingSoonPage from './pages/ComingSoonPage';
import { SuperSavingsPage } from './components/SuperSavingsPage'; import { SuperSavingsPage } from './pages/SuperSavingsPage';
import { WhatsIncluded } from './components/WhatsIncluded'; import { WhatsIncluded } from './pages/WhatsIncluded';
import { LandingMagicItineraryPage } from './components/LandingMagicItineraryPage'; import { LandingMagicItineraryPage } from './pages/LandingMagicItineraryPage';
import { DiscoverPage } from './components/DiscoverPage'; import { DiscoverPage } from './pages/DiscoverPage';
import { CartPage } from './pages/CartPage';
import { PaymentDetailsPage } from './pages/PaymentDetailsPage';
import { SuperSavingsDetailsPage } from './pages/SuperSavingsDetailsPage';
import { ViewCardDetailsPage } from './pages/ViewCardDetailsPage';
import ItinerarySummaryPage from './pages/ItinerarySummaryPage';
import { PaymentSuccessPage } from './pages/PaymentSuccessPage';
import { PaymentCancelPage } from './pages/PaymentCancelPage';
import { ItineraryViewPage } from './pages/ItineraryViewPage';
import { CheckoutPage } from './pages/CheckoutPage';
import { CreateMagicItineraryPage } from './pages/CreateMagicIternaryPage';
import RegisterPage from './components/RegisterPage';
// User type definition // User type definition
interface User { interface User {
@@ -90,7 +94,7 @@ export function AppRouter({
} /> } />
{/* Home Route */} {/* Home Route */}
<Route path="/melbourne" element={ <Route path="/:cityName" element={
<motion.div key="home" {...pageTransition}> <motion.div key="home" {...pageTransition}>
<MelbournePage {...commonNavHandlers} /> <MelbournePage {...commonNavHandlers} />
</motion.div> </motion.div>
@@ -125,19 +129,6 @@ export function AppRouter({
</motion.div> </motion.div>
} /> } />
{/* Checkout Routes */}
<Route path="/checkout" element={
<motion.div key="checkout" {...pageTransition}>
<CheckoutPage {...commonNavHandlers} />
</motion.div>
} />
<Route path="/secure-checkout" element={
<motion.div key="secure-checkout" {...pageTransition}>
<SecureCheckoutPage {...commonNavHandlers} />
</motion.div>
} />
{/* Blog Routes */} {/* Blog Routes */}
<Route path="/blogs" element={ <Route path="/blogs" element={
<motion.div key="blogs" {...pageTransition}> <motion.div key="blogs" {...pageTransition}>
@@ -188,20 +179,43 @@ export function AppRouter({
{/* User Routes */} {/* User Routes */}
<Route path="/profile" element={ <Route path="/profile" element={
<motion.div key="profile" {...pageTransition}> <motion.div key="profile" {...pageTransition}>
<ProtectedRoute>
<ProfilePage {...commonNavHandlers} /> <ProfilePage {...commonNavHandlers} />
</ProtectedRoute>
</motion.div> </motion.div>
} /> } />
<Route path="/view-card-details/:cardId" element={
<motion.div key="profile" {...pageTransition}>
<ProtectedRoute>
<ViewCardDetailsPage {...commonNavHandlers} />
</ProtectedRoute>
</motion.div>
} />
{/* Itinerary Routes */} {/* Itinerary Routes */}
<Route path="/create-itinerary" element={ <Route path="/create-itinerary" element={
<motion.div key="create-itinerary" {...pageTransition}> <motion.div key="create-itinerary" {...pageTransition}>
<ProtectedRoute>
<CreateMagicItineraryPage {...commonNavHandlers} /> <CreateMagicItineraryPage {...commonNavHandlers} />
</ProtectedRoute>
</motion.div> </motion.div>
} /> } />
<Route path="/itinerary-view" element={ <Route path="/view-itinerary/:itineraryId" element={
<motion.div key="itinerary-view" {...pageTransition}> <motion.div key="itinerary-view" {...pageTransition}>
<ProtectedRoute>
<ItineraryViewPage {...commonNavHandlers} /> <ItineraryViewPage {...commonNavHandlers} />
</ProtectedRoute>
</motion.div>
} />
<Route path="/itinerary-summary/:itineraryId" element={
<motion.div key="itinerary-summary" {...pageTransition}>
<ProtectedRoute>
<ItinerarySummaryPage {...commonNavHandlers} />
</ProtectedRoute>
</motion.div> </motion.div>
} /> } />
@@ -272,6 +286,73 @@ export function AppRouter({
<SuperSavingsPage {...commonNavHandlers} /> <SuperSavingsPage {...commonNavHandlers} />
</motion.div> </motion.div>
} /> } />
<Route path="/cart" element={
<motion.div key="super-savings" {...pageTransition}>
<ProtectedRoute>
<CartPage {...commonNavHandlers} />
</ProtectedRoute>
</motion.div>
} />
<Route path="/checkout" element={
<motion.div key="super-savings" {...pageTransition}>
<ProtectedRoute>
<CheckoutPage {...commonNavHandlers} />
</ProtectedRoute>
</motion.div>
} />
<Route path="/register" element={
<motion.div key="register" {...pageTransition}>
<RegisterPage {...commonNavHandlers} />
</motion.div>
} />
<Route path="/payment/:bookingId" element={
<motion.div key="super-savings" {...pageTransition}>
<ProtectedRoute>
<PaymentDetailsPage {...commonNavHandlers} />
</ProtectedRoute>
</motion.div>
} />
<Route path="/super-savings/:id" element={
<motion.div key="super-savings" {...pageTransition}>
<SuperSavingsDetailsPage {...commonNavHandlers}
onBackClick={() => navigate(-1)} />
</motion.div>
} />
<Route path="/success" element={
<motion.div key="super-savings" {...pageTransition}>
<ProtectedRoute>
<PaymentSuccessPage
// onHomeClick={onHomeClick}
// onPassesClick={onPassesClick}
onSignInClick={onSignInClick}
onSignOutClick={onSignOutClick}
currentPage="success"
user={user}
/>
</ProtectedRoute>
</motion.div>
} />
<Route path="/cancel" element={
<motion.div key="super-savings" {...pageTransition}>
<ProtectedRoute>
<PaymentCancelPage
// onHomeClick={onHomeClick}
// onPassesClick={onPassesClick}
onSignInClick={onSignInClick}
onSignOutClick={onSignOutClick}
currentPage="cancel"
user={user}
/>
</ProtectedRoute>
</motion.div>
} />
</Routes> </Routes>
</AnimatePresence> </AnimatePresence>
</> </>

View File

@@ -1,23 +1,33 @@
import { configureStore } from "@reduxjs/toolkit"; import { configureStore } from "@reduxjs/toolkit";
import { fakeApi } from "./services/fakeApi.service";
import { attractionsApi } from "./services/attractions.service"; import { attractionsApi } from "./services/attractions.service";
import { citiesApi } from "./services/cities.service"; import { citiesApi } from "./services/cities.service";
import { authApi } from "./services/auth.service";
import { profileApi } from "./services/profile.service";
import { cardsApi } from "./services/cards.service";
import { itineraryApi } from "./services/itinerary.service";
import { blogsApi } from "./services/blogs.service";
export const store = configureStore({ export const store = configureStore({
reducer: { reducer: {
[fakeApi.reducerPath]:fakeApi.reducer,
[attractionsApi.reducerPath]: attractionsApi.reducer, [attractionsApi.reducerPath]: attractionsApi.reducer,
[citiesApi.reducerPath]:citiesApi.reducer [citiesApi.reducerPath]: citiesApi.reducer,
[authApi.reducerPath]: authApi.reducer,
[profileApi.reducerPath]: profileApi.reducer,
[cardsApi.reducerPath]:cardsApi.reducer,
[itineraryApi.reducerPath]:itineraryApi.reducer,
[blogsApi.reducerPath]:blogsApi.reducer
}, },
middleware: (getDefaultMiddleware) => middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat( getDefaultMiddleware().concat(
fakeApi.middleware,
attractionsApi.middleware, attractionsApi.middleware,
citiesApi.middleware citiesApi.middleware,
authApi.middleware,
profileApi.middleware,
cardsApi.middleware,
itineraryApi.middleware,
blogsApi.middleware
), ),
}); });
export type RootState = ReturnType<typeof store.getState>; export type RootState = ReturnType<typeof store.getState>;

View File

@@ -8,10 +8,9 @@ export const baseQuery = fetchBaseQuery({
const token = localStorage.getItem("accessToken"); const token = localStorage.getItem("accessToken");
if (token) { if (token) {
headers.set("Authorization", `Bearer ${token}`); headers.set("Authorization", `Bearer ${token}`);
// headers.set("access-token", token); headers.set("access-token", token);
} }
// headers.set("Content-Type", "application/json"); // headers.set("Content-Type", "application/json");
return headers; return headers;
}, },
}); });

View File

@@ -30,6 +30,20 @@ export const attractionsApi = createApi({
return `/attractions/customer/customer-attractions?${params.toString()}`; return `/attractions/customer/customer-attractions?${params.toString()}`;
}, },
}), }),
getAttractionsForHomePage: builder.query({
// cityId is required, others optional
query: ({ cityId, categoryId}) => {
const params = new URLSearchParams();
// required
params.append('cityXid', cityId);
// optional
if (categoryId) params.append('categoryXid', categoryId);
return `/attractions/list/city-attractions?${params.toString()}`;
},
}),
getAttractionDetailsById: builder.query({ getAttractionDetailsById: builder.query({
query: (id: number) => `/attractions/customer/${id}`, query: (id: number) => `/attractions/customer/${id}`,
@@ -38,4 +52,4 @@ export const attractionsApi = createApi({
}), }),
}); });
export const { useGetAttractionFiltersQuery,useGetCustomerAttractionsQuery,useGetAttractionDetailsByIdQuery } = attractionsApi; export const { useGetAttractionFiltersQuery,useGetCustomerAttractionsQuery,useGetAttractionDetailsByIdQuery,useGetAttractionsForHomePageQuery } = attractionsApi;

View File

@@ -0,0 +1,53 @@
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
import { baseQuery } from "../baseQuery";
export const authApi = createApi({
reducerPath: "authApi",
baseQuery: baseQuery,
tagTypes: ["profile", "Transaction"],
endpoints: (builder) => ({
// Login
login: builder.mutation({
query: (credentials) => ({
url: "/website/send-otp",
method: "POST",
body: credentials,
}),
}),
verifyOtp: builder.mutation({
query: (credentials) => ({
url: "/website/user/verify-otp",
method: "POST",
body: credentials,
}),
}),
register: builder.mutation({
query: (credentials) => ({
url: "/website/user/register",
method: "POST",
body: credentials,
}),
}),
logoutUser: builder.mutation({
query: () => ({
url: "/website/user/logout",
method: "POST"
})
})
}),
});
export const {
useLoginMutation,
useVerifyOtpMutation,
useRegisterMutation,
useLogoutUserMutation
} = authApi;

View File

@@ -0,0 +1,28 @@
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
import { baseQuery } from "../baseQuery";
export const blogsApi = createApi({
reducerPath: 'blogsApi',
baseQuery,
endpoints: (builder) => ({
getBlogsForCity: builder.query({
// cityId is required, others optional
query: ({ cityId, categoryId }) => {
const params = new URLSearchParams();
// required
params.append('cityXid', cityId);
// optional
if (categoryId) params.append('categoryXid', categoryId);
return `/website/list/blogs?${params.toString()}`;
},
}),
}),
});
export const { useGetBlogsForCityQuery } = blogsApi;

View File

@@ -0,0 +1,77 @@
import { createApi } from "@reduxjs/toolkit/query/react";
import { baseQuery } from "../baseQuery";
export const cardsApi = createApi({
reducerPath: "cardsApi",
baseQuery,
tagTypes: ["cardsInCart"],
endpoints: (builder) => ({
getCardsinCart: builder.query({
query: (cityId) => {
const params = new URLSearchParams()
params.append('cityXid', cityId);
return `/website/passes/cart/passes?${params.toString()}`
},
providesTags: ["cardsInCart"]
}),
getCheckoutPageData: builder.query({
query: (cityId) => `/website/pass/${cityId}`,
}),
getCardBookingDetails: builder.query({
query: (bookingId) => `/website/passes/${bookingId}/details`,
}),
storeRecipientDetails: builder.mutation({
query: ({ recipientDetails, bookingId }) => ({ // keep the name of the variables being passed here same as when calling the mutation hook
url: `/website/passes/${bookingId}/store-gift-details`,
method: "PUT",
body: recipientDetails
}),
}),
addCardToCart: builder.mutation({
query: (cardBookingDetails) => ({ // keep the name of the variables being passed here same as when calling the mutation hook
url: `/website/passes/add-to-cart`,
method: "POST",
body: cardBookingDetails
}),
}),
payForCard: builder.mutation({
query: (id) => ({
url: `/website/passes/${id}/pay`,
method: "POST",
body: {},
}),
}),
confirmCardPayment: builder.mutation({
query: (payload: { id: string; checkoutSessionId: string }) => ({
url: `/website/passes/${payload.id}/${payload.checkoutSessionId}/confirm-payment/`,
method: "POST",
}),
}),
}),
});
export const {
useGetCardsinCartQuery,
useGetCheckoutPageDataQuery,
useGetCardBookingDetailsQuery,
useStoreRecipientDetailsMutation,
useAddCardToCartMutation,
usePayForCardMutation,
useConfirmCardPaymentMutation
} = cardsApi;

View File

@@ -1,11 +1,8 @@
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'; import { createApi } from '@reduxjs/toolkit/query/react';
import { baseQuery } from "../baseQuery"; import { baseQuery } from "../baseQuery";
export const citiesApi = createApi({ export const citiesApi = createApi({
reducerPath: 'citiesApi', reducerPath: 'citiesApi',
// baseQuery: fetchBaseQuery({
// baseUrl: 'https://testingapi.citycards.betadelivery.com',
// }),
baseQuery, baseQuery,
endpoints: (builder) => ({ endpoints: (builder) => ({
@@ -20,11 +17,34 @@ export const citiesApi = createApi({
}), }),
getUpcomingCities: builder.query({ getUpcomingCities: builder.query({
query: (listType) => `/cities/list/all?listType=${listType}`, query: (listType) => `/cities/list/all?listType=${listType}`,
}) }),
getSelectedCityDetails: builder.query({
query: (cityId) => `/website/${cityId}`,
}),
getSelectedCityOffers: builder.query({
query: ({ cityId, categoryId, page, limit }) => {
const params = new URLSearchParams()
params.append('cityXid', cityId);
if (categoryId) params.append('categoryXid', categoryId);
if (page) params.append('page', page);
if (limit) params.append('limit', limit);
return `/website/super-savings/list/offers?${params.toString()}`;
}
}),
getOfferDetailsById: builder.query({
query: (id: number) => `/website/super-savings/list/offers/${id}`,
}),
}), }),
}); });
export const { useGetCityListWithBannerQuery,useGetUpcomingCitiesQuery } = citiesApi; export const { useGetCityListWithBannerQuery, useGetUpcomingCitiesQuery, useGetSelectedCityDetailsQuery, useGetSelectedCityOffersQuery, useGetOfferDetailsByIdQuery } = citiesApi;

View File

@@ -1,19 +0,0 @@
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
export const fakeApi = createApi({
reducerPath: 'fakeApi',
baseQuery: fetchBaseQuery({
baseUrl: " https://fakestoreapi.com",
}),
endpoints: (builder) => ({
getProducts: builder.query<any, void>({
query: () => ({
url: 'products',
method: 'GET',
}),
}),
}),
})
export const { useGetProductsQuery} = fakeApi

View File

@@ -0,0 +1,50 @@
import { createApi } from "@reduxjs/toolkit/query/react";
import { baseQuery } from "../baseQuery";
export const itineraryApi = createApi({
reducerPath: "itApi",
baseQuery,
endpoints: (builder) => ({
createMagicItinerary: builder.mutation({
query: (itineraryDetails) => ({ // keep the name of the variables being passed here same as when calling the mutation hook
url: `/website/itinerary`,
method: "POST",
body: itineraryDetails
}),
}),
getItineraryDetailsById: builder.query({
query: (itineraryId: number) => `/website/itinerary/${itineraryId}`,
}),
getUserItineraries: builder.query({
query: (cityId) => {
const params = new URLSearchParams()
params.append('cityId', cityId);
return `/website/itinerary/all-initineraries?${params.toString()}`
}
}),
downloadItinerary: builder.query<Blob, string>({
query: (id) => ({
url: `/mobile/itinerary/${id}/download`,
method: 'GET',
responseHandler: (response) => response.blob(),
}),
}),
})
});
export const {
useCreateMagicItineraryMutation,
useGetItineraryDetailsByIdQuery,
useGetUserItinerariesQuery,
useDownloadItineraryQuery,
} = itineraryApi;

View File

@@ -0,0 +1,51 @@
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
import { baseQuery } from "../baseQuery";
export const profileApi = createApi({
reducerPath: "profileApi",
baseQuery,
tagTypes: ["userDetails"],
endpoints: (builder) => ({
getUserProfileDetails: builder.query({
query: (id) => `/website/user/${id}`,
providesTags: ["userDetails"]
}),
updateUserProfileDetails: builder.mutation({
query: ({ userDetails, userId }) => ({ // keep the name of the variables being passed here same as when calling the mutation hook
url: `/website/user/${userId}`,
method: "PUT",
body: userDetails
}),
invalidatesTags: ["userDetails"]
}),
getUserCards: builder.query({
query: ({sort,cityId}) => {
const params = new URLSearchParams()
params.append('cityXid', cityId);
if (sort) params.append('sort', sort);
return `/website/passes/all?${params.toString()}`
}
}),
getUserCardDetails: builder.query({
query: (cardId) => `/website/passes/${cardId}/details`,
}),
})
});
export const {
useGetUserProfileDetailsQuery,
useUpdateUserProfileDetailsMutation,
useGetUserCardsQuery,
useGetUserCardDetailsQuery
} = profileApi;

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
src/assets/front.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

View File

@@ -0,0 +1,42 @@
function Text() {
return (
<div className="h-[14px] relative shrink-0 w-[13.703px]" data-name="Text">
<div className="bg-clip-padding border-0 border-[transparent] border-solid box-border h-[14px] relative w-[13.703px]">
<div className="absolute font-['Poppins:Bold',_sans-serif] leading-[0] left-0 not-italic text-[10.5px] text-nowrap text-white top-0">
<p className="leading-[14px] whitespace-pre">JD</p>
</div>
</div>
</div>
);
}
function Container() {
return (
<div className="box-border content-stretch flex items-center justify-center pl-0 pr-[0.016px] py-0 relative rounded-[3.35544e+07px] shrink-0 size-[28px]" data-name="Container" style={{ backgroundImage: "linear-gradient(90deg, rgba(0, 0, 0, 0.4) 0%, rgba(0, 0, 0, 0.4) 100%), linear-gradient(90deg, rgb(249, 95, 98) 0%, rgb(249, 95, 98) 100%)" }}>
<Text />
</div>
);
}
function Text1() {
return (
<div className="content-stretch flex h-[17.5px] items-start relative shrink-0 w-[86.047px]" data-name="Text">
<div className="font-['Poppins:SemiBold',_sans-serif] leading-[0] not-italic relative shrink-0 text-[12.25px] text-nowrap text-white">
<p className="leading-[17.5px] whitespace-pre">MY CITY CARD</p>
</div>
</div>
);
}
export default function AfterLogin() {
return (
<div className="bg-[#f95f62] relative rounded-[3.35544e+07px] shadow-[0px_10px_15px_-3px_rgba(0,0,0,0.1),0px_4px_6px_-4px_rgba(0,0,0,0.1)] size-full" data-name="After Login">
<div className="flex flex-row items-center justify-center relative size-full">
<div className="box-border content-stretch flex gap-[4px] items-center justify-center overflow-clip px-[15px] py-[12px] relative size-full">
<Container />
<Text1 />
</div>
</div>
</div>
);
}

View File

@@ -0,0 +1,21 @@
function Text() {
return (
<div className="content-stretch flex h-[17.5px] items-start relative shrink-0" data-name="Text">
<div className="font-['Poppins:SemiBold',_sans-serif] leading-[0] not-italic relative shrink-0 text-[12.25px] text-nowrap text-white">
<p className="leading-[17.5px] whitespace-pre">GET A CITY CARD</p>
</div>
</div>
);
}
export default function BeforeLogin() {
return (
<div className="bg-[#f95f62] relative rounded-[3.35544e+07px] shadow-[0px_10px_15px_-3px_rgba(0,0,0,0.1),0px_4px_6px_-4px_rgba(0,0,0,0.1)] size-full" data-name="Before Login">
<div className="flex flex-col items-center relative size-full">
<div className="box-border content-stretch flex flex-col gap-[8px] items-center overflow-clip px-[47px] py-[17px] relative size-full">
<Text />
</div>
</div>
</div>
);
}

View File

@@ -1,6 +1,6 @@
import { motion } from 'motion/react'; import { motion } from 'motion/react';
import BeforeLogin from '../imports/BeforeLogin'; import BeforeLogin from './BeforeLogin';
import AfterLogin from '../imports/AfterLogin'; import AfterLogin from './AfterLogin';
interface User { interface User {
email: string; email: string;

View File

@@ -1,735 +0,0 @@
import { useState, useEffect } from 'react';
import { motion, AnimatePresence } from 'motion/react';
import { ArrowLeft, CreditCard, Users, Calendar, MapPin, Shield, Truck, Clock, ChevronRight, Check, ChevronDown, X, Mail, Smartphone } from 'lucide-react';
import { Button } from './ui/button';
import { Input } from './ui/input';
import { Label } from './ui/label';
import { Card, CardContent, CardHeader, CardTitle } from './ui/card';
import { Separator } from './ui/separator';
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from './ui/dialog';
import { RadioGroup, RadioGroupItem } from './ui/radio-group';
import { Checkbox } from './ui/checkbox';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './ui/select';
import { Badge } from './ui/badge';
import { Textarea } from './ui/textarea';
import Navbar from './Navbar';
import { Footer } from './Footer';
import { ImageWithFallback } from './figma/ImageWithFallback';
import { Layout } from '../Layout';
interface CheckoutPageProps {
onBackClick?: () => void;
onHomeClick?: () => void;
onMelbourneClick?: () => void;
onPassesClick?: () => void;
onCheckoutClick?: () => void;
onSignInClick?: () => void;
onSignOutClick?: () => void;
onAttractionsClick?: () => void;
onBlogsClick?: () => void;
onHowItWorksClick?: () => void;
onFAQClick?: () => void;
onPrivacyPolicyClick?: () => void;
onAboutUsClick?: () => void;
onProfileClick?: () => void;
onCityCardsClick?: () => void;
onMagicItineraryClick?: () => void;
onPostCardsClick?: () => void;
onOffersClick?: () => void;
onSecureCheckoutClick?: () => void;
onContactUsClick?: () => void;
onEsimsClick?: () => void;
onHotelDiscountsClick?: () => void;
currentPage?: string;
user?: { email: string; name: string } | null;
}
// Mock cart data
const mockCartItems = [
{
id: '1',
name: 'Paris Unlimited Pass',
type: '7-Day Pass',
price: 79,
originalPrice: 149,
discount: 47,
attractions: 45,
validity: '7 days',
image: 'https://images.unsplash.com/photo-1502602898536-47ad22581b52?w=400',
features: ['Skip-the-line access', 'Mobile voucher', 'Free cancellation']
}
];
export function CheckoutPage({
onBackClick,
onHomeClick,
onMelbourneClick,
onPassesClick,
onCheckoutClick,
onSignInClick,
onSignOutClick,
onAttractionsClick,
onBlogsClick,
onHowItWorksClick,
onFAQClick,
onPrivacyPolicyClick,
onAboutUsClick,
onProfileClick,
onCityCardsClick,
onMagicItineraryClick,
onPostCardsClick,
onOffersClick,
onSecureCheckoutClick,
onContactUsClick,
onEsimsClick,
onHotelDiscountsClick,
currentPage,
user,
}: CheckoutPageProps) {
const [purchaseType, setPurchaseType] = useState<'self' | 'gift'>('self');
const [selectedPayment, setSelectedPayment] = useState('credit-card');
const [showEmailVerification, setShowEmailVerification] = useState(false);
const [verificationCode, setVerificationCode] = useState('');
const [isEmailVerified, setIsEmailVerified] = useState(false);
const [formData, setFormData] = useState({
email: '',
firstName: '',
lastName: '',
phone: '',
country: '',
address: '',
city: '',
postalCode: '',
cardNumber: '',
expiry: '',
cvv: '',
cardName: '',
agreeTerms: false,
subscribeNewsletter: false
});
const [giftData, setGiftData] = useState({
recipientName: '',
recipientPhone: '',
recipientEmail: '',
personalizedMessage: ''
});
const subtotal = mockCartItems.reduce((sum, item) => sum + item.price, 0);
const tax = Math.round(subtotal * 0.1);
const total = subtotal + tax;
const totalSavings = mockCartItems.reduce((sum, item) => sum + (item.originalPrice - item.price), 0);
const handleInputChange = (field: string, value: string | boolean) => {
setFormData(prev => ({ ...prev, [field]: value }));
// Trigger email verification when email is complete
if (field === 'email' && typeof value === 'string' && value.includes('@') && value.includes('.') && !isEmailVerified) {
setTimeout(() => {
setShowEmailVerification(true);
}, 1000);
}
};
const handleGiftInputChange = (field: string, value: string) => {
setGiftData(prev => ({ ...prev, [field]: value }));
};
const handleEmailVerification = () => {
if (verificationCode === '123456') {
setIsEmailVerified(true);
setShowEmailVerification(false);
}
};
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (!isEmailVerified) {
setShowEmailVerification(true);
return;
}
const checkoutData = {
purchaseType,
formData,
...(purchaseType === 'gift' && { giftData }),
selectedPayment,
cartItems: mockCartItems
};
console.log('Processing checkout...', checkoutData);
};
const paymentMethods = [
{
id: 'credit-card',
name: 'Credit Card',
icon: <CreditCard className="w-5 h-5" />,
description: 'Visa, Mastercard, American Express'
},
{
id: 'paypal',
name: 'PayPal',
icon: <div className="w-5 h-5 bg-blue-600 rounded-sm flex items-center justify-center text-xs font-bold text-white">P</div>,
description: 'Pay with your PayPal account'
},
{
id: 'google-pay',
name: 'Google Pay',
icon: <div className="w-5 h-5 bg-green-600 rounded-sm flex items-center justify-center text-xs font-bold text-white">G</div>,
description: 'Pay with Google Pay'
}
];
return (
<div className="min-h-screen bg-background">
<Layout
activeCity="Melbourne"
onSignInClick={onSignInClick}
onSignOutClick={onSignOutClick}
user={user}
>
{/* Header Section */}
<section className="pt-32 pb-8 bg-gradient-to-br from-muted/30 to-background">
<div className="container mx-auto px-4 pt-8">
{/* Back Button */}
<motion.button
onClick={onBackClick}
className="flex items-center gap-2 text-gray-600 hover:text-gray-900 mb-6 transition-colors duration-200"
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.5 }}
>
<ArrowLeft className="w-5 h-5" />
<span className="font-medium">Back to Cart</span>
</motion.button>
{/* Page Title */}
<motion.div
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.1 }}
>
<h1 className="font-merchant text-3xl md:text-4xl lg:text-5xl mb-4">
<span className="font-light">Secure</span>{' '}
<span className="font-bold italic bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent">Checkout</span>
</h1>
<p className="font-poppins text-lg text-gray-600">
Complete your purchase and start exploring Paris
</p>
</motion.div>
</div>
</section>
{/* Main Checkout Content */}
<section className="py-12">
<div className="container mx-auto px-4">
<form onSubmit={handleSubmit} className="grid grid-cols-1 lg:grid-cols-5 gap-8">
{/* Left Column - Form Inputs (3/5 width) */}
<div className="lg:col-span-3 space-y-8">
{/* Purchase Type Selection */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: 0.1 }}
>
<Card>
<CardHeader>
<CardTitle className="font-merchant">Purchase Type</CardTitle>
</CardHeader>
<CardContent>
<RadioGroup
value={purchaseType}
onValueChange={(value) => setPurchaseType(value as 'self' | 'gift')}
className="grid grid-cols-1 md:grid-cols-2 gap-4"
>
<div className="relative">
<RadioGroupItem value="self" id="self" className="peer sr-only" />
<Label
htmlFor="self"
className="flex flex-col items-center justify-between rounded-lg border-2 border-muted bg-white p-4 hover:bg-accent hover:text-accent-foreground peer-data-[state=checked]:border-primary [&:has([data-state=checked])]:border-primary cursor-pointer transition-all min-h-[44px]"
>
<div className="flex items-center gap-3 w-full">
<Users className="w-5 h-5" />
<div className="flex-1">
<p className="font-poppins font-medium">Buy for Myself</p>
<p className="text-sm text-muted-foreground font-poppins">Purchase a pass for your own use</p>
</div>
</div>
</Label>
</div>
<div className="relative">
<RadioGroupItem value="gift" id="gift" className="peer sr-only" />
<Label
htmlFor="gift"
className="flex flex-col items-center justify-between rounded-lg border-2 border-muted bg-white p-4 hover:bg-accent hover:text-accent-foreground peer-data-[state=checked]:border-primary [&:has([data-state=checked])]:border-primary cursor-pointer transition-all min-h-[44px]"
>
<div className="flex items-center gap-3 w-full">
<Mail className="w-5 h-5" />
<div className="flex-1">
<p className="font-poppins font-medium">Gift a Pass</p>
<p className="text-sm text-muted-foreground font-poppins">Send as a gift to someone special</p>
</div>
</div>
</Label>
</div>
</RadioGroup>
</CardContent>
</Card>
</motion.div>
{/* Gift Recipient Information - Only shown when gift is selected */}
{purchaseType === 'gift' && (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: 0.2 }}
>
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2 font-merchant">
<Mail className="w-5 h-5" />
Gift Recipient Details
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div>
<Label htmlFor="recipientName" className="font-poppins">Recipient Name *</Label>
<Input
id="recipientName"
value={giftData.recipientName}
onChange={(e) => handleGiftInputChange('recipientName', e.target.value)}
placeholder="Jane Smith"
required={purchaseType === 'gift'}
className="mt-1 font-poppins"
/>
</div>
<div>
<Label htmlFor="recipientEmail" className="font-poppins">Recipient Email Address *</Label>
<Input
id="recipientEmail"
type="email"
value={giftData.recipientEmail}
onChange={(e) => handleGiftInputChange('recipientEmail', e.target.value)}
placeholder="recipient@email.com"
required={purchaseType === 'gift'}
className="mt-1 font-poppins"
/>
</div>
<div>
<Label htmlFor="recipientPhone" className="font-poppins">Recipient Phone Number *</Label>
<Input
id="recipientPhone"
type="tel"
value={giftData.recipientPhone}
onChange={(e) => handleGiftInputChange('recipientPhone', e.target.value)}
placeholder="+1 (555) 123-4567"
required={purchaseType === 'gift'}
className="mt-1 font-poppins"
/>
</div>
<div>
<Label htmlFor="personalizedMessage" className="font-poppins">Personalized Message</Label>
<Textarea
id="personalizedMessage"
value={giftData.personalizedMessage}
onChange={(e) => handleGiftInputChange('personalizedMessage', e.target.value)}
placeholder="Write a special message for your gift recipient..."
rows={4}
className="mt-1 font-poppins"
/>
<p className="text-xs text-muted-foreground mt-1 font-poppins">Optional: Add a personal touch to your gift</p>
</div>
</CardContent>
</Card>
</motion.div>
)}
{/* Contact Information */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: purchaseType === 'gift' ? 0.3 : 0.2 }}
>
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2 font-merchant">
<Users className="w-5 h-5" />
{purchaseType === 'gift' ? 'Your Information (Purchaser)' : 'Contact Information'}
{isEmailVerified && <Check className="w-5 h-5 text-green-600" />}
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div>
<Label htmlFor="email" className="font-poppins">Email Address *</Label>
<div className="relative">
<Input
id="email"
type="email"
value={formData.email}
onChange={(e) => handleInputChange('email', e.target.value)}
placeholder="your@email.com"
required
className={`mt-1 font-poppins ${isEmailVerified ? 'border-green-600 bg-green-50' : ''}`}
/>
{isEmailVerified && (
<Check className="absolute right-3 top-1/2 transform -translate-y-1/2 w-5 h-5 text-green-600" />
)}
</div>
{isEmailVerified && (
<p className="text-sm text-green-600 mt-1 font-poppins">Email verified </p>
)}
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<Label htmlFor="firstName" className="font-poppins">First Name *</Label>
<Input
id="firstName"
value={formData.firstName}
onChange={(e) => handleInputChange('firstName', e.target.value)}
placeholder="John"
required
className="mt-1 font-poppins"
/>
</div>
<div>
<Label htmlFor="lastName" className="font-poppins">Last Name *</Label>
<Input
id="lastName"
value={formData.lastName}
onChange={(e) => handleInputChange('lastName', e.target.value)}
placeholder="Doe"
required
className="mt-1 font-poppins"
/>
</div>
</div>
<div>
<Label htmlFor="phone" className="font-poppins">Phone Number</Label>
<Input
id="phone"
type="tel"
value={formData.phone}
onChange={(e) => handleInputChange('phone', e.target.value)}
placeholder="+1 (555) 123-4567"
className="mt-1 font-poppins"
/>
</div>
</CardContent>
</Card>
</motion.div>
{/* Payment Method */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: 0.3 }}
>
</motion.div>
{/* Billing Address */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: purchaseType === 'gift' ? 0.4 : 0.3 }}
>
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2 font-merchant">
<MapPin className="w-5 h-5" />
Billing Address
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div>
<Label htmlFor="country" className="font-poppins">Country *</Label>
<Select value={formData.country} onValueChange={(value) => handleInputChange('country', value)}>
<SelectTrigger className="mt-1">
<SelectValue placeholder="Select country" />
</SelectTrigger>
<SelectContent>
<SelectItem value="us">United States</SelectItem>
<SelectItem value="au">Australia</SelectItem>
<SelectItem value="uk">United Kingdom</SelectItem>
<SelectItem value="ca">Canada</SelectItem>
<SelectItem value="de">Germany</SelectItem>
<SelectItem value="fr">France</SelectItem>
<SelectItem value="in">India</SelectItem>
<SelectItem value="jp">Japan</SelectItem>
</SelectContent>
</Select>
</div>
<div>
<Label htmlFor="address" className="font-poppins">Street Address *</Label>
<Input
id="address"
value={formData.address}
onChange={(e) => handleInputChange('address', e.target.value)}
placeholder="123 Main Street"
required
className="mt-1 font-poppins"
/>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<Label htmlFor="city" className="font-poppins">City *</Label>
<Input
id="city"
value={formData.city}
onChange={(e) => handleInputChange('city', e.target.value)}
placeholder="New York"
required
className="mt-1 font-poppins"
/>
</div>
<div>
<Label htmlFor="postalCode" className="font-poppins">Postal Code *</Label>
<Input
id="postalCode"
value={formData.postalCode}
onChange={(e) => handleInputChange('postalCode', e.target.value)}
placeholder="10001"
required
className="mt-1 font-poppins"
/>
</div>
</div>
</CardContent>
</Card>
</motion.div>
{/* Terms and Newsletter */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: purchaseType === 'gift' ? 0.5 : 0.4 }}
className="space-y-4"
>
<div className="flex items-start space-x-2">
<Checkbox
id="terms"
checked={formData.agreeTerms}
onCheckedChange={(checked) => handleInputChange('agreeTerms', checked as boolean)}
/>
<div className="grid gap-1.5 leading-none">
<label
htmlFor="terms"
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 font-poppins"
>
I agree to the Terms of Service and Privacy Policy *
</label>
</div>
</div>
<div className="flex items-start space-x-2">
<Checkbox
id="newsletter"
checked={formData.subscribeNewsletter}
onCheckedChange={(checked) => handleInputChange('subscribeNewsletter', checked as boolean)}
/>
<div className="grid gap-1.5 leading-none">
<label
htmlFor="newsletter"
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 font-poppins"
>
Subscribe to our newsletter for travel tips and special offers
</label>
</div>
</div>
</motion.div>
</div>
{/* Right Column - Order Summary (2/5 width) */}
<div className="lg:col-span-2">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: purchaseType === 'gift' ? 0.4 : 0.3 }}
className="sticky top-24"
>
<Card>
<CardHeader>
<CardTitle className="font-merchant flex items-center gap-2">
Order Summary
{purchaseType === 'gift' && (
<Badge variant="secondary" className="font-poppins">
<Mail className="w-3 h-3 mr-1" />
Gift Purchase
</Badge>
)}
</CardTitle>
</CardHeader>
<CardContent className="space-y-6">
{/* Gift Indicator */}
{purchaseType === 'gift' && giftData.recipientName && (
<div className="bg-gradient-to-r from-primary/10 to-secondary/10 border border-primary/20 rounded-lg p-4">
<div className="flex items-start gap-3">
<Mail className="w-5 h-5 text-primary mt-0.5" />
<div className="flex-1">
<p className="font-medium text-sm font-poppins mb-1">Gift Recipient</p>
<p className="text-sm text-gray-700 font-poppins">{giftData.recipientName}</p>
<p className="text-xs text-gray-500 font-poppins mt-1">{giftData.recipientEmail}</p>
</div>
</div>
</div>
)}
{/* Cart Items */}
<div className="space-y-4">
{mockCartItems.map((item) => (
<div key={item.id} className="flex gap-4">
<ImageWithFallback
src={item.image}
alt={item.name}
className="w-20 h-20 rounded-lg object-cover"
/>
<div className="flex-1 min-w-0">
<h4 className="font-medium text-base mb-1 font-poppins">{item.name}</h4>
<p className="text-sm text-gray-500 mb-2 font-poppins">{item.type}</p>
<div className="flex items-center gap-2 mb-2">
<span className="font-bold text-lg font-poppins">{item.price}</span>
<span className="text-sm text-gray-500 line-through font-poppins">{item.originalPrice}</span>
<Badge variant="outline" className="text-xs">
-{item.discount}%
</Badge>
</div>
<div className="text-sm text-gray-600 font-poppins">
{item.attractions} attractions Valid for {item.validity}
</div>
</div>
</div>
))}
</div>
<Separator />
{/* Features Summary */}
<div className="space-y-3">
<h4 className="font-medium font-merchant">What's Included:</h4>
<div className="space-y-2">
<div className="flex items-center gap-2 text-sm font-poppins">
<Check className="w-4 h-4 text-green-600" />
<span>Skip-the-line access to {mockCartItems[0].attractions} attractions</span>
</div>
<div className="flex items-center gap-2 text-sm font-poppins">
<Check className="w-4 h-4 text-green-600" />
<span>Valid for {mockCartItems[0].validity}</span>
</div>
<div className="flex items-center gap-2 text-sm font-poppins">
<Check className="w-4 h-4 text-green-600" />
<span>Instant mobile delivery</span>
</div>
<div className="flex items-center gap-2 text-sm font-poppins">
<Check className="w-4 h-4 text-green-600" />
<span>Free cancellation</span>
</div>
</div>
</div>
<Separator />
{/* Pricing Breakdown */}
<div className="space-y-3">
<div className="flex justify-between text-sm font-poppins">
<span>Subtotal</span>
<span>€{subtotal}</span>
</div>
<div className="flex justify-between text-sm font-poppins">
<span>Taxes & Fees</span>
<span>€{tax}</span>
</div>
<div className="flex justify-between text-sm text-green-600 font-poppins">
<span>Total Savings</span>
<span>-€{totalSavings}</span>
</div>
<Separator />
<div className="flex justify-between font-bold text-xl font-poppins">
<span>Total</span>
<span>€{total}</span>
</div>
</div>
{/* Checkout Button */}
<Button
type="button"
onClick={() => onSecureCheckoutClick?.()}
className="w-full bg-gradient-to-r from-primary to-secondary hover:from-primary/90 hover:to-secondary/90 text-white font-medium py-4 text-lg font-poppins"
disabled={
!formData.agreeTerms ||
!formData.email ||
!formData.firstName ||
!formData.lastName ||
!isEmailVerified ||
(purchaseType === 'gift' && (!giftData.recipientName || !giftData.recipientEmail || !giftData.recipientPhone))
}
>
{purchaseType === 'gift' ? 'Send Gift & Proceed to Checkout' : 'Proceed to Secure Checkout'}
<ChevronRight className="w-5 h-5 ml-2" />
</Button>
{/* Security Notice */}
<div className="flex items-center gap-2 text-xs text-gray-500 justify-center font-poppins">
<Shield className="w-4 h-4" />
<span>Secure 256-bit SSL encryption</span>
</div>
</CardContent>
</Card>
</motion.div>
</div>
</form>
</div>
</section>
{/* Email Verification Popup */}
<Dialog open={showEmailVerification} onOpenChange={setShowEmailVerification}>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<DialogTitle className="flex items-center gap-2 font-merchant">
<Mail className="w-5 h-5" />
Verify Your Email
</DialogTitle>
<DialogDescription className="font-poppins">
Enter the verification code sent to your email to continue with your booking.
</DialogDescription>
</DialogHeader>
<div className="space-y-4">
<p className="text-sm text-gray-600 font-poppins">
We've sent a verification code to <strong>{formData.email}</strong>
</p>
<div>
<Label htmlFor="verification" className="font-poppins">Verification Code</Label>
<Input
id="verification"
value={verificationCode}
onChange={(e) => setVerificationCode(e.target.value)}
placeholder="Enter 6-digit code"
className="mt-1 font-poppins text-center text-lg letter-spacing-2"
maxLength={6}
/>
</div>
<div className="flex gap-3">
<Button
onClick={handleEmailVerification}
className="flex-1 bg-gradient-to-r from-primary to-secondary hover:from-primary/90 hover:to-secondary/90 text-white font-poppins"
disabled={verificationCode.length !== 6}
>
Verify Email
</Button>
<Button
variant="outline"
onClick={() => setShowEmailVerification(false)}
className="font-poppins"
>
Skip for now
</Button>
</div>
<p className="text-xs text-gray-500 text-center font-poppins">
Demo code: <strong>123456</strong>
</p>
</div>
</DialogContent>
</Dialog>
</Layout>
</div>
);
}

View File

@@ -7,6 +7,7 @@ import { Input } from './ui/input';
import { motion, AnimatePresence } from 'motion/react'; import { motion, AnimatePresence } from 'motion/react';
import { ImageWithFallback } from './figma/ImageWithFallback'; import { ImageWithFallback } from './figma/ImageWithFallback';
import { useGetCityListWithBannerQuery } from '../Redux/services/cities.service'; import { useGetCityListWithBannerQuery } from '../Redux/services/cities.service';
import LoadingSpinner from './LoadingSpinner';
interface City { interface City {
id: number; id: number;
@@ -20,6 +21,9 @@ interface CitySelectionDialogProps {
onCitySelect?: (cityId: string) => void; // ✅ Updated to pass cityId onCitySelect?: (cityId: string) => void; // ✅ Updated to pass cityId
} }
export const slugify = (name: string | null) =>
name?.toLowerCase().replace(/\s+/g, '-');
export function CitySelectionDialog({ export function CitySelectionDialog({
isOpen, isOpen,
onClose, onClose,
@@ -31,21 +35,17 @@ export function CitySelectionDialog({
const { data: cities, isLoading } = useGetCityListWithBannerQuery({ search }) const { data: cities, isLoading } = useGetCityListWithBannerQuery({ search })
if (isLoading) { if (isLoading) {
return <div>Loading...</div> return (
<LoadingSpinner/>
);
} }
const handleCityClick = (city: City) => { const handleCityClick = (city: City) => {
console.log('Selected city:', city.cityName); console.log('Selected city:', city.cityName);
navigate(`/${slugify(city.cityName)}`);
// ✅ Call the onCitySelect callback if provided (passing cityId) localStorage.setItem("cityId", String(city.id))
if (onCitySelect) { localStorage.setItem("cityName", String(city.cityName))
onCitySelect(String(city.id)); sessionStorage.setItem("citySelected", String(city.cityName))
} else {
// ✅ Default behavior: navigate to passes page
navigate(`/passes?city=${encodeURIComponent(city.cityName)}`);
}
onClose(); onClose();
}; };

File diff suppressed because it is too large Load Diff

View File

@@ -4,7 +4,7 @@ import { Button } from './ui/button';
import { ImageWithFallback } from './figma/ImageWithFallback'; import { ImageWithFallback } from './figma/ImageWithFallback';
import { motion, useMotionValue, useSpring, useTransform, useInView } from 'motion/react'; import { motion, useMotionValue, useSpring, useTransform, useInView } from 'motion/react';
import { HandwrittenText, useHandwrittenText } from './HandwrittenText'; import { HandwrittenText, useHandwrittenText } from './HandwrittenText';
import postcardImage from 'figma:asset/d3a880cf8b7f1bec6da9b3f2ce4a76e822e483cf.png'; import front from '../assets/front.jpg'
interface EditableCardProps { interface EditableCardProps {
isEditing: boolean; isEditing: boolean;
@@ -190,485 +190,485 @@ export function CustomPostcards() {
}; };
// Ultra-realistic vintage postcard with responsive scaling and animations // Ultra-realistic vintage postcard with responsive scaling and animations
const PostcardFrame = () => { // const PostcardFrame = () => {
return ( // return (
<motion.div // <motion.div
className="relative shadow-2xl transform rotate-[0.5deg] w-full h-full" // className="relative shadow-2xl transform rotate-[0.5deg] w-full h-full"
initial={{ opacity: 0, scale: 0.9, rotate: 0 }} // initial={{ opacity: 0, scale: 0.9, rotate: 0 }}
animate={{ opacity: 1, scale: 1, rotate: 0.5 }} // animate={{ opacity: 1, scale: 1, rotate: 0.5 }}
transition={{ duration: 0.8, ease: "easeOut" }} // transition={{ duration: 0.8, ease: "easeOut" }}
style={{ // style={{
background: ` // background: `
radial-gradient(circle at 20% 80%, rgba(210, 180, 140, 0.15) 0%, transparent 50%), // radial-gradient(circle at 20% 80%, rgba(210, 180, 140, 0.15) 0%, transparent 50%),
radial-gradient(circle at 80% 20%, rgba(160, 130, 100, 0.1) 0%, transparent 50%), // radial-gradient(circle at 80% 20%, rgba(160, 130, 100, 0.1) 0%, transparent 50%),
radial-gradient(circle at 40% 40%, rgba(190, 160, 120, 0.08) 0%, transparent 50%), // radial-gradient(circle at 40% 40%, rgba(190, 160, 120, 0.08) 0%, transparent 50%),
linear-gradient(135deg, #f8f4e6 0%, #f0e7d3 25%, #ede2d0 50%, #e8dcc8 75%, #e3d5c2 100%) // linear-gradient(135deg, #f8f4e6 0%, #f0e7d3 25%, #ede2d0 50%, #e8dcc8 75%, #e3d5c2 100%)
`, // `,
borderRadius: '10px', // borderRadius: '10px',
boxShadow: ` // boxShadow: `
inset 0px 1px 30px rgba(139, 115, 85, 0.2), // inset 0px 1px 30px rgba(139, 115, 85, 0.2),
inset 0px -1px 20px rgba(160, 130, 100, 0.15), // inset 0px -1px 20px rgba(160, 130, 100, 0.15),
0px 12px 40px rgba(0, 0, 0, 0.15), // 0px 12px 40px rgba(0, 0, 0, 0.15),
0px 4px 12px rgba(0, 0, 0, 0.1) // 0px 4px 12px rgba(0, 0, 0, 0.1)
`, // `,
border: '1px solid rgba(139, 115, 85, 0.2)' // border: '1px solid rgba(139, 115, 85, 0.2)'
}} // }}
> // >
{/* Realistic paper texture with variations */} // {/* Realistic paper texture with variations */}
<div // <div
className="absolute inset-0 opacity-40 rounded-[10px] pointer-events-none" // className="absolute inset-0 opacity-40 rounded-[10px] pointer-events-none"
style={{ // style={{
backgroundImage: ` // backgroundImage: `
url("data:image/svg+xml,%3Csvg width='100' height='100' viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%23d4af9a' fill-opacity='0.08'%3E%3Cpath d='M11 18c3.866 0 7-3.134 7-7s-3.134-7-7-7-7 3.134-7 7 3.134 7 7 7zm48 25c3.866 0 7-3.134 7-7s-3.134-7-7-7-7 3.134-7 7 3.134 7 7 7zm-43-7c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zm63 31c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zM34 90c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zm56-76c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zM12 86c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm28-65c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm23-11c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zm-6 60c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm29 22c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zM32 63c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zm57-13c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zm-9-21c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2zM60 91c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2zM35 41c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E"), // url("data:image/svg+xml,%3Csvg width='100' height='100' viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%23d4af9a' fill-opacity='0.08'%3E%3Cpath d='M11 18c3.866 0 7-3.134 7-7s-3.134-7-7-7-7 3.134-7 7 3.134 7 7 7zm48 25c3.866 0 7-3.134 7-7s-3.134-7-7-7-7 3.134-7 7 3.134 7 7 7zm-43-7c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zm63 31c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zM34 90c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zm56-76c1.657 0 3-1.343 3-3s-1.343-3-3-3-3 1.343-3 3 1.343 3 3 3zM12 86c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm28-65c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm23-11c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zm-6 60c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm29 22c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zM32 63c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zm57-13c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zm-9-21c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2zM60 91c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2zM35 41c1.105 0 2-.895 2-2s-.895-2-2-2-2 .895-2 2 .895 2 2 2z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E"),
radial-gradient(circle at 25% 75%, rgba(180, 150, 120, 0.1) 0%, transparent 40%), // radial-gradient(circle at 25% 75%, rgba(180, 150, 120, 0.1) 0%, transparent 40%),
radial-gradient(circle at 75% 25%, rgba(160, 130, 100, 0.08) 0%, transparent 35%) // radial-gradient(circle at 75% 25%, rgba(160, 130, 100, 0.08) 0%, transparent 35%)
` // `
}} // }}
/> // />
{/* Age spots and stains */} // {/* Age spots and stains */}
<div className="absolute inset-0 rounded-[10px] pointer-events-none opacity-30"> // <div className="absolute inset-0 rounded-[10px] pointer-events-none opacity-30">
<div // <div
className="absolute w-8 h-6 rounded-full" // className="absolute w-8 h-6 rounded-full"
style={{ // style={{
background: 'radial-gradient(ellipse, rgba(160, 120, 80, 0.15) 0%, transparent 70%)', // background: 'radial-gradient(ellipse, rgba(160, 120, 80, 0.15) 0%, transparent 70%)',
top: '15%', // top: '15%',
right: '20%', // right: '20%',
transform: 'rotate(-15deg)' // transform: 'rotate(-15deg)'
}} // }}
/> // />
<div // <div
className="absolute w-6 h-8 rounded-full" // className="absolute w-6 h-8 rounded-full"
style={{ // style={{
background: 'radial-gradient(ellipse, rgba(140, 110, 70, 0.12) 0%, transparent 60%)', // background: 'radial-gradient(ellipse, rgba(140, 110, 70, 0.12) 0%, transparent 60%)',
bottom: '25%', // bottom: '25%',
left: '15%', // left: '15%',
transform: 'rotate(25deg)' // transform: 'rotate(25deg)'
}} // }}
/> // />
<div // <div
className="absolute w-4 h-4 rounded-full" // className="absolute w-4 h-4 rounded-full"
style={{ // style={{
background: 'radial-gradient(circle, rgba(180, 140, 100, 0.2) 0%, transparent 50%)', // background: 'radial-gradient(circle, rgba(180, 140, 100, 0.2) 0%, transparent 50%)',
top: '60%', // top: '60%',
right: '10%' // right: '10%'
}} // }}
/> // />
</div> // </div>
{/* Corner wear and creases */} // {/* Corner wear and creases */}
<div // <div
className="absolute top-0 right-0 w-12 h-12 opacity-25" // className="absolute top-0 right-0 w-12 h-12 opacity-25"
style={{ // style={{
background: 'radial-gradient(circle at top right, rgba(139, 115, 85, 0.3) 20%, rgba(160, 130, 100, 0.2) 40%, transparent 70%)', // background: 'radial-gradient(circle at top right, rgba(139, 115, 85, 0.3) 20%, rgba(160, 130, 100, 0.2) 40%, transparent 70%)',
borderTopRightRadius: '10px' // borderTopRightRadius: '10px'
}} // }}
/> // />
<div // <div
className="absolute bottom-0 left-0 w-16 h-16 opacity-20" // className="absolute bottom-0 left-0 w-16 h-16 opacity-20"
style={{ // style={{
background: 'radial-gradient(circle at bottom left, rgba(120, 95, 70, 0.25) 25%, rgba(140, 115, 85, 0.15) 50%, transparent 75%)', // background: 'radial-gradient(circle at bottom left, rgba(120, 95, 70, 0.25) 25%, rgba(140, 115, 85, 0.15) 50%, transparent 75%)',
borderBottomLeftRadius: '10px' // borderBottomLeftRadius: '10px'
}} // }}
/> // />
{/* Subtle crease lines */} // {/* Subtle crease lines */}
<div // <div
className="absolute w-full h-px bg-gradient-to-r from-transparent via-amber-800/10 to-transparent opacity-40" // className="absolute w-full h-px bg-gradient-to-r from-transparent via-amber-800/10 to-transparent opacity-40"
style={{ // style={{
top: '35%', // top: '35%',
transform: 'rotate(-0.5deg)' // transform: 'rotate(-0.5deg)'
}} // }}
/> // />
{/* Animated Vintage Vector Logo - Top Right - Mobile Optimized */} // {/* Animated Vintage Vector Logo - Top Right - Mobile Optimized */}
<motion.div // <motion.div
className="absolute opacity-60 pointer-events-none" // className="absolute opacity-60 pointer-events-none"
style={{ // style={{
top: '4.3%', // top: '4.3%',
right: '3.5%', // right: '3.5%',
width: '7.5%', // width: '7.5%',
height: '11.5%', // height: '11.5%',
filter: 'sepia(30%) saturate(120%) hue-rotate(15deg) brightness(85%) contrast(110%)' // filter: 'sepia(30%) saturate(120%) hue-rotate(15deg) brightness(85%) contrast(110%)'
}} // }}
animate={{ // animate={{
rotate: [5, 7, 3, 5], // rotate: [5, 7, 3, 5],
scale: [1, 1.02, 0.98, 1] // scale: [1, 1.02, 0.98, 1]
}} // }}
transition={{ // transition={{
duration: 8, // duration: 8,
repeat: Infinity, // repeat: Infinity,
ease: "easeInOut" // ease: "easeInOut"
}} // }}
> // >
<ImageWithFallback // <ImageWithFallback
src="https://images.unsplash.com/photo-1702825328124-dab63d85490e?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHx2aW50YWdlJTIwbG9nbyUyMHZlY3RvciUyMHBvc3RhbCUyMHN0YW1wfGVufDF8fHx8MTc1ODk5MjExN3ww&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral" // src="https://images.unsplash.com/photo-1702825328124-dab63d85490e?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHx2aW50YWdlJTIwbG9nbyUyMHZlY3RvciUyMHBvc3RhbCUyMHN0YW1wfGVufDF8fHx8MTc1ODk5MjExN3ww&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral"
alt="Vintage logo" // alt="Vintage logo"
className="w-full h-full object-contain" // className="w-full h-full object-contain"
style={{ // style={{
mixBlendMode: 'multiply', // mixBlendMode: 'multiply',
opacity: 0.8 // opacity: 0.8
}} // }}
/> // />
{/* Subtle aging overlay for the vector */} // {/* Subtle aging overlay for the vector */}
<div // <div
className="absolute inset-0 rounded-full" // className="absolute inset-0 rounded-full"
style={{ // style={{
background: ` // background: `
radial-gradient(circle at 30% 30%, rgba(160, 130, 90, 0.2) 0%, transparent 60%), // radial-gradient(circle at 30% 30%, rgba(160, 130, 90, 0.2) 0%, transparent 60%),
radial-gradient(circle at 70% 70%, rgba(140, 110, 70, 0.15) 0%, transparent 50%) // radial-gradient(circle at 70% 70%, rgba(140, 110, 70, 0.15) 0%, transparent 50%)
`, // `,
mixBlendMode: 'multiply' // mixBlendMode: 'multiply'
}} // }}
/> // />
</motion.div> // </motion.div>
{/* For Correspondence Text */} // {/* For Correspondence Text */}
<div // <div
className="absolute pointer-events-none" // className="absolute pointer-events-none"
style={{ // style={{
position: 'absolute', // position: 'absolute',
width: '33.3%', // width: '33.3%',
height: '72.3%', // height: '72.3%',
left: '4.9%', // left: '4.9%',
top: '7.4%', // top: '7.4%',
display: 'flex', // display: 'flex',
alignItems: 'flex-start', // alignItems: 'flex-start',
justifyContent: 'flex-start', // justifyContent: 'flex-start',
paddingTop: '8%' // paddingTop: '8%'
}} // }}
> // >
<p // <p
className="font-poppins font-light" // className="font-poppins font-light"
style={{ // style={{
fontSize: 'clamp(10px, 2.2vw, 16px)', // fontSize: 'clamp(10px, 2.2vw, 16px)',
color: 'rgba(101, 84, 63, 0.4)', // color: 'rgba(101, 84, 63, 0.4)',
letterSpacing: '0.5px', // letterSpacing: '0.5px',
lineHeight: '1.4', // lineHeight: '1.4',
textShadow: '0px 0.5px 1px rgba(0, 0, 0, 0.05)' // textShadow: '0px 0.5px 1px rgba(0, 0, 0, 0.05)'
}} // }}
> // >
For correspondence // For correspondence
</p> // </p>
</div> // </div>
{/* Realistic vertical divider with ink bleeding - Responsive */} // {/* Realistic vertical divider with ink bleeding - Responsive */}
<div // <div
className="absolute" // className="absolute"
style={{ // style={{
top: '10.6%', // top: '10.6%',
bottom: '10.6%', // bottom: '10.6%',
left: '43.1%', // left: '43.1%',
width: '2px', // width: '2px',
background: ` // background: `
linear-gradient(to bottom, // linear-gradient(to bottom,
transparent 0%, // transparent 0%,
rgba(101, 84, 63, 0.4) 10%, // rgba(101, 84, 63, 0.4) 10%,
rgba(101, 84, 63, 0.6) 30%, // rgba(101, 84, 63, 0.6) 30%,
rgba(101, 84, 63, 0.8) 50%, // rgba(101, 84, 63, 0.8) 50%,
rgba(101, 84, 63, 0.6) 70%, // rgba(101, 84, 63, 0.6) 70%,
rgba(101, 84, 63, 0.4) 90%, // rgba(101, 84, 63, 0.4) 90%,
transparent 100% // transparent 100%
) // )
`, // `,
filter: 'blur(0.3px)' // filter: 'blur(0.3px)'
}} // }}
/> // />
{/* Ink bleed effect around divider - Responsive */} // {/* Ink bleed effect around divider - Responsive */}
<div // <div
className="absolute opacity-20" // className="absolute opacity-20"
style={{ // style={{
top: '10.6%', // top: '10.6%',
bottom: '10.6%', // bottom: '10.6%',
left: '42.8%', // left: '42.8%',
width: '6px', // width: '6px',
background: ` // background: `
linear-gradient(to bottom, // linear-gradient(to bottom,
transparent 0%, // transparent 0%,
rgba(101, 84, 63, 0.1) 20%, // rgba(101, 84, 63, 0.1) 20%,
rgba(101, 84, 63, 0.15) 50%, // rgba(101, 84, 63, 0.15) 50%,
rgba(101, 84, 63, 0.1) 80%, // rgba(101, 84, 63, 0.1) 80%,
transparent 100% // transparent 100%
) // )
`, // `,
filter: 'blur(1px)' // filter: 'blur(1px)'
}} // }}
/> // />
{/* Right side content area - Responsive */} // {/* Right side content area - Responsive */}
<div // <div
className="absolute pointer-events-none" // className="absolute pointer-events-none"
style={{ // style={{
top: '10.6%', // top: '10.6%',
left: '47.2%', // left: '47.2%',
width: '47.2%', // width: '47.2%',
height: '78.7%' // height: '78.7%'
}} // }}
> // >
{/* Editable Address Label Card - Responsive */} // {/* Editable Address Label Card - Responsive */}
<EditableCard // <EditableCard
isEditing={editingCard === 'label'} // isEditing={editingCard === 'label'}
onEdit={() => setEditingCard(editingCard === 'label' ? null : 'label')} // onEdit={() => setEditingCard(editingCard === 'label' ? null : 'label')}
style={{ // style={{
position: 'absolute', // position: 'absolute',
top: '4.1%', // top: '4.1%',
left: '0%', // left: '0%',
pointerEvents: 'auto', // pointerEvents: 'auto',
transform: 'rotate(-0.3deg)' // transform: 'rotate(-0.3deg)'
}} // }}
editIcon={<Type className="w-3 h-3 text-gray-600" />} // editIcon={<Type className="w-3 h-3 text-gray-600" />}
> // >
<div // <div
className="px-2 py-1 rounded text-xs md:text-sm" // className="px-2 py-1 rounded text-xs md:text-sm"
style={{ // style={{
fontWeight: '600', // fontWeight: '600',
letterSpacing: '2px', // letterSpacing: '2px',
textTransform: 'uppercase', // textTransform: 'uppercase',
color: 'rgba(101, 84, 63, 0.8)', // color: 'rgba(101, 84, 63, 0.8)',
textShadow: '0px 1px 2px rgba(0, 0, 0, 0.1)' // textShadow: '0px 1px 2px rgba(0, 0, 0, 0.1)'
}} // }}
> // >
{postcardData.addressLabel} // {postcardData.addressLabel}
</div> // </div>
</EditableCard> // </EditableCard>
{/* Realistic horizontal ruled lines with ink bleeding - Responsive */} // {/* Realistic horizontal ruled lines with ink bleeding - Responsive */}
<div // <div
className="absolute pointer-events-none" // className="absolute pointer-events-none"
style={{ // style={{
top: '14.9%', // top: '14.9%',
left: '0%', // left: '0%',
width: '94.1%', // width: '94.1%',
height: '59.5%' // height: '59.5%'
}} // }}
> // >
{[...Array(9)].map((_, i) => ( // {[...Array(9)].map((_, i) => (
<div key={i} className="relative" style={{ marginBottom: '6.5%' }}> // <div key={i} className="relative" style={{ marginBottom: '6.5%' }}>
{/* Main line */} // {/* Main line */}
<div // <div
style={{ // style={{
height: '1px', // height: '1px',
width: i % 2 === 0 ? '100%' : '92%', // width: i % 2 === 0 ? '100%' : '92%',
background: `linear-gradient(to right, // background: `linear-gradient(to right,
rgba(101, 84, 63, 0.3) 0%, // rgba(101, 84, 63, 0.3) 0%,
rgba(101, 84, 63, 0.5) 20%, // rgba(101, 84, 63, 0.5) 20%,
rgba(101, 84, 63, 0.6) 50%, // rgba(101, 84, 63, 0.6) 50%,
rgba(101, 84, 63, 0.4) 80%, // rgba(101, 84, 63, 0.4) 80%,
rgba(101, 84, 63, 0.2) 95%, // rgba(101, 84, 63, 0.2) 95%,
transparent 100% // transparent 100%
)`, // )`,
transform: `rotate(${(Math.random() - 0.5) * 0.5}deg)` // transform: `rotate(${(Math.random() - 0.5) * 0.5}deg)`
}} // }}
/> // />
{/* Ink bleed effect */} // {/* Ink bleed effect */}
<div // <div
className="absolute top-0 left-0 opacity-30" // className="absolute top-0 left-0 opacity-30"
style={{ // style={{
height: '3px', // height: '3px',
width: i % 2 === 0 ? '98%' : '90%', // width: i % 2 === 0 ? '98%' : '90%',
background: `linear-gradient(to right, // background: `linear-gradient(to right,
rgba(101, 84, 63, 0.1) 0%, // rgba(101, 84, 63, 0.1) 0%,
rgba(101, 84, 63, 0.2) 30%, // rgba(101, 84, 63, 0.2) 30%,
rgba(101, 84, 63, 0.15) 70%, // rgba(101, 84, 63, 0.15) 70%,
transparent 100% // transparent 100%
)`, // )`,
filter: 'blur(1px)', // filter: 'blur(1px)',
transform: 'translateY(-1px)' // transform: 'translateY(-1px)'
}} // }}
/> // />
</div> // </div>
))} // ))}
</div> // </div>
{/* Editable Message Card with Handwritten Animation - Responsive */} // {/* Editable Message Card with Handwritten Animation - Responsive */}
<EditableCard // <EditableCard
isEditing={editingCard === 'message'} // isEditing={editingCard === 'message'}
onEdit={() => setEditingCard(editingCard === 'message' ? null : 'message')} // onEdit={() => setEditingCard(editingCard === 'message' ? null : 'message')}
style={{ // style={{
position: 'absolute', // position: 'absolute',
top: '17.6%', // top: '17.6%',
left: '4.4%', // left: '4.4%',
width: '88.2%', // width: '88.2%',
pointerEvents: 'auto', // pointerEvents: 'auto',
transform: 'rotate(-0.7deg)' // transform: 'rotate(-0.7deg)'
}} // }}
editIcon={<Edit3 className="w-3 h-3 text-gray-600" />} // editIcon={<Edit3 className="w-3 h-3 text-gray-600" />}
> // >
<div className="px-2 py-2 rounded leading-relaxed"> // <div className="px-2 py-2 rounded leading-relaxed">
{editingCard === 'message' ? ( // {editingCard === 'message' ? (
// Show static text when editing // // Show static text when editing
<div // <div
style={{ // style={{
fontFamily: "'Dancing Script', 'Brush Script MT', cursive", // fontFamily: "'Dancing Script', 'Brush Script MT', cursive",
fontSize: 'clamp(14px, 3.6vw, 26px)', // fontSize: 'clamp(14px, 3.6vw, 26px)',
lineHeight: '1.7', // lineHeight: '1.7',
color: 'rgba(85, 70, 50, 0.9)', // color: 'rgba(85, 70, 50, 0.9)',
textShadow: ` // textShadow: `
1px 1px 2px rgba(0, 0, 0, 0.08), // 1px 1px 2px rgba(0, 0, 0, 0.08),
0px 0px 3px rgba(101, 84, 63, 0.1) // 0px 0px 3px rgba(101, 84, 63, 0.1)
`, // `,
whiteSpace: 'pre-line', // whiteSpace: 'pre-line',
filter: 'contrast(110%) brightness(98%)' // filter: 'contrast(110%) brightness(98%)'
}} // }}
> // >
{postcardData.message} // {postcardData.message}
</div> // </div>
) : ( // ) : (
// Show animated handwritten text when not editing // // Show animated handwritten text when not editing
<HandwrittenText // <HandwrittenText
text={postcardData.message} // text={postcardData.message}
speed={6} // speed={6}
startDelay={0} // startDelay={0}
autoStart={false} // autoStart={false}
onComplete={handwrittenControl.onComplete} // onComplete={handwrittenControl.onComplete}
style={{ // style={{
fontFamily: "'Dancing Script', 'Brush Script MT', cursive", // fontFamily: "'Dancing Script', 'Brush Script MT', cursive",
fontSize: 'clamp(14px, 3.6vw, 26px)', // fontSize: 'clamp(14px, 3.6vw, 26px)',
lineHeight: '1.7', // lineHeight: '1.7',
color: 'rgba(85, 70, 50, 0.9)', // color: 'rgba(85, 70, 50, 0.9)',
textShadow: ` // textShadow: `
1px 1px 2px rgba(0, 0, 0, 0.08), // 1px 1px 2px rgba(0, 0, 0, 0.08),
0px 0px 3px rgba(101, 84, 63, 0.1) // 0px 0px 3px rgba(101, 84, 63, 0.1)
`, // `,
filter: 'contrast(110%) brightness(98%)' // filter: 'contrast(110%) brightness(98%)'
}} // }}
/> // />
)} // )}
</div> // </div>
</EditableCard> // </EditableCard>
{/* Editable Date Card - Responsive */} // {/* Editable Date Card - Responsive */}
<EditableCard // <EditableCard
isEditing={editingCard === 'date'} // isEditing={editingCard === 'date'}
onEdit={() => setEditingCard(editingCard === 'date' ? null : 'date')} // onEdit={() => setEditingCard(editingCard === 'date' ? null : 'date')}
style={{ // style={{
position: 'absolute', // position: 'absolute',
bottom: '16.2%', // bottom: '16.2%',
right: '7.4%', // right: '7.4%',
pointerEvents: 'auto', // pointerEvents: 'auto',
transform: 'rotate(-1.5deg)' // transform: 'rotate(-1.5deg)'
}} // }}
editIcon={<Calendar className="w-3 h-3 text-gray-600" />} // editIcon={<Calendar className="w-3 h-3 text-gray-600" />}
> // >
<div // <div
className="px-2 py-1 rounded text-xs" // className="px-2 py-1 rounded text-xs"
style={{ // style={{
fontWeight: '500', // fontWeight: '500',
letterSpacing: '1px', // letterSpacing: '1px',
color: 'rgba(101, 84, 63, 0.7)', // color: 'rgba(101, 84, 63, 0.7)',
textShadow: '0px 1px 1px rgba(0, 0, 0, 0.05)', // textShadow: '0px 1px 1px rgba(0, 0, 0, 0.05)',
fontFamily: "'Poppins', sans-serif" // fontFamily: "'Poppins', sans-serif"
}} // }}
> // >
{postcardData.date} // {postcardData.date}
</div> // </div>
</EditableCard> // </EditableCard>
</div> // </div>
{/* Ultra-realistic stamp - Responsive */} // {/* Ultra-realistic stamp - Responsive */}
<EditableCard // <EditableCard
isEditing={editingCard === 'stamp'} // isEditing={editingCard === 'stamp'}
onEdit={() => setEditingCard(editingCard === 'stamp' ? null : 'stamp')} // onEdit={() => setEditingCard(editingCard === 'stamp' ? null : 'stamp')}
style={{ // style={{
position: 'absolute', // position: 'absolute',
width: '12.5%', // width: '12.5%',
height: '19.1%', // height: '19.1%',
right: '3.5%', // right: '3.5%',
bottom: '5.3%', // bottom: '5.3%',
transform: 'rotate(-12deg)' // transform: 'rotate(-12deg)'
}} // }}
editIcon={<Edit3 className="w-3 h-3 text-gray-600" />} // editIcon={<Edit3 className="w-3 h-3 text-gray-600" />}
> // >
<div // <div
className="w-full h-full border-2 border-dashed rounded-full flex items-center justify-center relative" // className="w-full h-full border-2 border-dashed rounded-full flex items-center justify-center relative"
style={{ // style={{
borderColor: 'rgba(139, 115, 85, 0.8)', // borderColor: 'rgba(139, 115, 85, 0.8)',
background: ` // background: `
radial-gradient(circle at 30% 30%, rgba(220, 190, 150, 0.95) 0%, rgba(200, 170, 130, 0.9) 40%, rgba(180, 150, 110, 0.85) 100%), // radial-gradient(circle at 30% 30%, rgba(220, 190, 150, 0.95) 0%, rgba(200, 170, 130, 0.9) 40%, rgba(180, 150, 110, 0.85) 100%),
linear-gradient(135deg, rgba(240, 220, 180, 0.3) 0%, transparent 50%) // linear-gradient(135deg, rgba(240, 220, 180, 0.3) 0%, transparent 50%)
`, // `,
boxShadow: ` // boxShadow: `
inset 0px 2px 8px rgba(0, 0, 0, 0.25), // inset 0px 2px 8px rgba(0, 0, 0, 0.25),
inset 0px -1px 4px rgba(255, 255, 255, 0.2), // inset 0px -1px 4px rgba(255, 255, 255, 0.2),
0px 4px 12px rgba(0, 0, 0, 0.15) // 0px 4px 12px rgba(0, 0, 0, 0.15)
` // `
}} // }}
> // >
{/* Stamp aging and wear */} // {/* Stamp aging and wear */}
<div // <div
className="absolute inset-0 rounded-full pointer-events-none" // className="absolute inset-0 rounded-full pointer-events-none"
style={{ // style={{
background: ` // background: `
radial-gradient(circle at 70% 20%, rgba(160, 130, 90, 0.3) 0%, transparent 40%), // radial-gradient(circle at 70% 20%, rgba(160, 130, 90, 0.3) 0%, transparent 40%),
radial-gradient(circle at 20% 80%, rgba(140, 110, 70, 0.2) 0%, transparent 35%) // radial-gradient(circle at 20% 80%, rgba(140, 110, 70, 0.2) 0%, transparent 35%)
`, // `,
mixBlendMode: 'multiply' // mixBlendMode: 'multiply'
}} // }}
/> // />
{/* Stamp inner details */} // {/* Stamp inner details */}
<div className="absolute inset-3 border border-amber-800/50 rounded-full" /> // <div className="absolute inset-3 border border-amber-800/50 rounded-full" />
<div className="absolute inset-5 border border-amber-800/30 rounded-full" /> // <div className="absolute inset-5 border border-amber-800/30 rounded-full" />
{/* Stamp text */} // {/* Stamp text */}
<div className="text-center leading-tight z-10" style={{ color: 'rgba(101, 84, 63, 0.9)' }}> // <div className="text-center leading-tight z-10" style={{ color: 'rgba(101, 84, 63, 0.9)' }}>
<div style={{ fontSize: 'clamp(6px, 1.25vw, 9px)', fontWeight: '700', letterSpacing: '1px' }}> // <div style={{ fontSize: 'clamp(6px, 1.25vw, 9px)', fontWeight: '700', letterSpacing: '1px' }}>
TRAVEL // TRAVEL
</div> // </div>
<div style={{ fontSize: 'clamp(5px, 0.97vw, 7px)', fontWeight: '600', marginTop: '2px', letterSpacing: '0.5px' }}> // <div style={{ fontSize: 'clamp(5px, 0.97vw, 7px)', fontWeight: '600', marginTop: '2px', letterSpacing: '0.5px' }}>
MEMORIES // MEMORIES
</div> // </div>
<div style={{ fontSize: 'clamp(5px, 0.97vw, 7px)', fontWeight: '500', marginTop: '1px' }}> // <div style={{ fontSize: 'clamp(5px, 0.97vw, 7px)', fontWeight: '500', marginTop: '1px' }}>
2024 // 2024
</div> // </div>
</div> // </div>
{/* Stamp perforations with realistic variations */} // {/* Stamp perforations with realistic variations */}
{[...Array(20)].map((_, i) => ( // {[...Array(20)].map((_, i) => (
<div // <div
key={i} // key={i}
className="absolute rounded-full" // className="absolute rounded-full"
style={{ // style={{
width: '2px', // width: '2px',
height: '2px', // height: '2px',
backgroundColor: 'rgba(101, 84, 63, 0.4)', // backgroundColor: 'rgba(101, 84, 63, 0.4)',
top: '50%', // top: '50%',
left: '50%', // left: '50%',
transform: `translate(-50%, -50%) rotate(${i * 18}deg) translateY(-42px)`, // transform: `translate(-50%, -50%) rotate(${i * 18}deg) translateY(-42px)`,
opacity: Math.random() > 0.1 ? 1 : 0.3 // Random missing perforations // opacity: Math.random() > 0.1 ? 1 : 0.3 // Random missing perforations
}} // }}
/> // />
))} // ))}
{/* Stamp smudge mark */} // {/* Stamp smudge mark */}
<div // <div
className="absolute w-3 h-2 opacity-25" // className="absolute w-3 h-2 opacity-25"
style={{ // style={{
background: 'radial-gradient(ellipse, rgba(101, 84, 63, 0.4) 0%, transparent 70%)', // background: 'radial-gradient(ellipse, rgba(101, 84, 63, 0.4) 0%, transparent 70%)',
bottom: '15%', // bottom: '15%',
right: '20%', // right: '20%',
transform: 'rotate(20deg)', // transform: 'rotate(20deg)',
filter: 'blur(0.5px)' // filter: 'blur(0.5px)'
}} // }}
/> // />
</div> // </div>
</EditableCard> // </EditableCard>
{/* Additional realistic aging effects */} // {/* Additional realistic aging effects */}
<div // <div
className="absolute w-3 h-1 opacity-15" // className="absolute w-3 h-1 opacity-15"
style={{ // style={{
background: 'linear-gradient(45deg, rgba(120, 100, 70, 0.3), transparent)', // background: 'linear-gradient(45deg, rgba(120, 100, 70, 0.3), transparent)',
top: '20%', // top: '20%',
left: '60%', // left: '60%',
transform: 'rotate(45deg)', // transform: 'rotate(45deg)',
filter: 'blur(0.5px)' // filter: 'blur(0.5px)'
}} // }}
/> // />
</motion.div> // </motion.div>
); // );
}; // };
return ( return (
<section ref={sectionRef} className="py-12 md:py-16 lg:py-20 bg-white relative overflow-hidden"> <section ref={sectionRef} className="py-12 md:py-16 lg:py-20 bg-white relative overflow-hidden">
@@ -700,7 +700,7 @@ export function CustomPostcards() {
<h2 className="text-3xl md:text-4xl lg:text-5xl xl:text-6xl text-gray-900 mb-6"> <h2 className="text-3xl md:text-4xl lg:text-5xl xl:text-6xl text-gray-900 mb-6">
<span className="font-light">The Only Card That Sends Your</span> <span className="font-light">The Only Card That Sends Your</span>
<span className="block"> <span className="block">
<span className="font-bold text-warm-coral italic"> <span className="font-bold text-warm-coral italic bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent pr-2">
Holiday Holiday
</span>{' '} </span>{' '}
<span className="font-normal">Home.</span> <span className="font-normal">Home.</span>
@@ -783,7 +783,8 @@ export function CustomPostcards() {
WebkitBackfaceVisibility: "hidden" WebkitBackfaceVisibility: "hidden"
}} }}
> >
<PostcardFrame /> {/* <PostcardFrame /> */}
<img src={front} alt="Postcard Image" />
{/* Subtle glow effect */} {/* Subtle glow effect */}
<motion.div <motion.div

View File

@@ -20,11 +20,11 @@ interface Testimonial {
company: string; company: string;
signature: string; signature: string;
} }
const cityName = localStorage.getItem('cityName') || 'the city';
const testimonials: Testimonial[] = [ const testimonials: Testimonial[] = [
{ {
id: 1, id: 1,
quote: "CityCards transformed our Melbourne trip into an unforgettable adventure. The seamless access to attractions and insider recommendations made every moment magical.", quote: `CityCards transformed our ${cityName} trip into an unforgettable adventure. The seamless access to attractions and insider recommendations made every moment magical.`,
name: "Sarah Chen", name: "Sarah Chen",
company: "Travel Blogger", company: "Travel Blogger",
signature: "Sarah" signature: "Sarah"
@@ -91,7 +91,7 @@ export function EnhancedTestimonials() {
> >
<h2 className="text-4xl lg:text-5xl xl:text-6xl mb-6"> <h2 className="text-4xl lg:text-5xl xl:text-6xl mb-6">
<span className="font-light">What our</span>{' '} <span className="font-light">What our</span>{' '}
<span className="bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent font-bold italic"> <span className="bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent font-bold italic pr-1">
travelers travelers
</span>{' '} </span>{' '}
<span className="font-normal">say</span> <span className="font-normal">say</span>
@@ -126,7 +126,7 @@ export function EnhancedTestimonials() {
style={{ style={{
transform: `rotate(${cardRotation}deg) translateY(${cardOffset}px)`, transform: `rotate(${cardRotation}deg) translateY(${cardOffset}px)`,
transformOrigin: 'center center', transformOrigin: 'center center',
minHeight: '480px', minHeight: '360px',
background: ` background: `
radial-gradient(circle at 20% 80%, rgba(255, 248, 235, 0.8) 0%, transparent 50%), radial-gradient(circle at 20% 80%, rgba(255, 248, 235, 0.8) 0%, transparent 50%),
radial-gradient(circle at 80% 20%, rgba(250, 245, 230, 0.6) 0%, transparent 50%), radial-gradient(circle at 80% 20%, rgba(250, 245, 230, 0.6) 0%, transparent 50%),

View File

@@ -244,7 +244,7 @@ export function FAQPage({
<h1 className="font-merchant font-light text-4xl md:text-5xl lg:text-6xl text-gray-900 mb-6"> <h1 className="font-merchant font-light text-4xl md:text-5xl lg:text-6xl text-gray-900 mb-6">
Frequently Asked{' '} Frequently Asked{' '}
<span className="font-bold italic bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent"> <span className="font-bold italic bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent pr-2">
Questions Questions
</span> </span>
</h1> </h1>

View File

@@ -45,10 +45,10 @@ export function Footer({
/> />
{/* Enhanced White Gradient Overlay at Top */} {/* Enhanced White Gradient Overlay at Top */}
<div className="absolute top-0 left-0 right-0 h-48 bg-gradient-to-b from-white via-white/95 via-white/80 via-white/60 via-white/40 to-transparent z-10" /> <div className="absolute top-0 left-0 right-0 h-24 bg-gradient-to-b from-white via-white/95 via-white/80 via-white/60 via-white/40 to-transparent z-10" />
{/* Additional Smooth Transition Layer */} {/* Additional Smooth Transition Layer */}
<div className="absolute top-0 left-0 right-0 h-24 bg-gradient-to-b from-white via-white/90 to-white/70 z-10" /> <div className="absolute top-0 left-0 right-0 h-4 bg-gradient-to-b from-white via-white/90 to-white/70 z-10" />
{/* Dark overlay for text readability */} {/* Dark overlay for text readability */}
<div className="absolute inset-0 bg-gradient-to-b from-transparent via-black/30 to-black/70 z-20" /> <div className="absolute inset-0 bg-gradient-to-b from-transparent via-black/30 to-black/70 z-20" />
@@ -61,7 +61,7 @@ export function Footer({
</div> </div>
{/* Content Overlay */} {/* Content Overlay */}
<div className="relative z-30 py-24"> <div className="relative z-30 py-4">
<div className="container mx-auto px-4"> <div className="container mx-auto px-4">
{/* Footer Content Grid */} {/* Footer Content Grid */}
<div className="w-full mt-48 bg-primary/10 backdrop-blur-lg rounded-[10px] border border-white/10 p-12"> <div className="w-full mt-48 bg-primary/10 backdrop-blur-lg rounded-[10px] border border-white/10 p-12">

View File

@@ -17,13 +17,13 @@ export function FooterBottom({ onPrivacyPolicyClick }: FooterBottomProps) {
<div className="flex flex-col lg:flex-row justify-between items-center space-y-6 lg:space-y-0"> <div className="flex flex-col lg:flex-row justify-between items-center space-y-6 lg:space-y-0">
{/* Copyright */} {/* Copyright */}
<p className="text-white/60 text-sm"> <p className="text-white/60 text-sm">
© 2024 CityCards. All rights reserved. © 2026 CityCards. All rights reserved.
</p> </p>
{/* Right Section - Legal Links and Social Icons */} {/* Right Section - Legal Links and Social Icons */}
<div className="flex flex-col md:flex-row items-center space-y-4 md:space-y-0 md:space-x-8"> <div className="flex flex-col md:flex-row items-center space-y-4 md:space-y-0 md:space-x-8">
{/* Legal Links */} {/* Legal Links */}
<div className="flex space-x-6 text-sm"> {/* <div className="flex space-x-6 text-sm">
<motion.button <motion.button
onClick={onPrivacyPolicyClick} onClick={onPrivacyPolicyClick}
className="text-white/70 hover:text-white transition-colors duration-200" className="text-white/70 hover:text-white transition-colors duration-200"
@@ -48,7 +48,7 @@ export function FooterBottom({ onPrivacyPolicyClick }: FooterBottomProps) {
> >
Cookie Policy Cookie Policy
</motion.a> </motion.a>
</div> </div> */}
{/* Social Icons - Horizontal Layout */} {/* Social Icons - Horizontal Layout */}
<div className="flex space-x-3"> <div className="flex space-x-3">

View File

@@ -1,37 +1,21 @@
import { motion } from 'motion/react'; import { motion } from 'motion/react';
import { footerSections } from '../utils/footerConstants'; import { footerSections } from '../utils/footerConstants';
import { Link } from 'react-router-dom';
interface FooterNavigationProps { const linkRoutes: Record<string, string> = {
onHomeClick?: () => void; 'Home': '/',
onMelbourneClick?: () => void; // 'Cancellation policy': '/cancellation-policy',
onPassesClick?: () => void; 'How It Works': '/how-it-works',
onSignInClick?: () => void; 'FAQ': '/faq',
onAttractionsClick?: () => void; 'Blog': '/blogs',
onBlogsClick?: () => void; 'Contact Us': '/contact-us',
onHowItWorksClick?: () => void; 'Privacy Policy': '/privacy-policy',
onFAQClick?: () => void; // 'Terms of Service': '/terms',
onPrivacyPolicyClick?: () => void; };
onAboutUsClick?: () => void;
onContactUsClick?: () => void;
currentPage?: string;
}
export function FooterNavigation({ export function FooterNavigation() {
onHomeClick,
onMelbourneClick,
onPassesClick,
onSignInClick,
onAttractionsClick,
onBlogsClick,
onHowItWorksClick,
onFAQClick,
onPrivacyPolicyClick,
onAboutUsClick,
onContactUsClick,
currentPage
}: FooterNavigationProps) {
return ( return (
<div className="lg:col-span-8 grid grid-cols-2 md:grid-cols-4 gap-8"> <div className="lg:col-span-8 grid grid-cols-2 md:grid-cols-3 gap-8">
{Object.entries(footerSections).map(([key, section]) => ( {Object.entries(footerSections).map(([key, section]) => (
<motion.div <motion.div
key={key} key={key}
@@ -45,50 +29,20 @@ export function FooterNavigation({
}} }}
> >
<h4 className="font-semibold text-white">{section.title}</h4> <h4 className="font-semibold text-white">{section.title}</h4>
<ul className="space-y-3"> <ul className="space-y-3">
{section.links.map((link, index) => { {section.links.map((link) => (
const getClickHandler = () => {
switch (link) {
case 'Home': return onHomeClick;
case 'Melbourne': return onMelbourneClick;
case 'Passes': return onPassesClick;
case 'Sign In': return onSignInClick;
case 'Attractions': return onAttractionsClick;
case 'Blog': return onBlogsClick;
case 'How It Works': return onHowItWorksClick;
case 'FAQ': return onFAQClick;
case 'Privacy Policy': return onPrivacyPolicyClick;
case 'Contact Us': return onContactUsClick;
default: return undefined;
}
};
const clickHandler = getClickHandler();
return (
<li key={link}> <li key={link}>
{clickHandler ? ( <motion.div whileHover={{ x: 4 }} transition={{ duration: 0.2 }}>
<motion.button <Link
onClick={(e) => { to={linkRoutes[link] || ""}
e.preventDefault(); className="text-white/80 hover:text-white transition-colors duration-200 text-sm"
clickHandler();
}}
className="text-white/80 hover:text-white transition-colors duration-200 text-sm text-left"
whileHover={{ x: 4 }}
transition={{ duration: 0.2 }}
> >
{link} {link}
</motion.button> </Link>
) : ( </motion.div>
<motion.span
className="text-white/80 cursor-default text-sm"
>
{link}
</motion.span>
)}
</li> </li>
); ))}
})}
</ul> </ul>
</motion.div> </motion.div>
))} ))}

View File

@@ -19,13 +19,15 @@ export function HeroBannerCarousel({
const [currentSlide, setCurrentSlide] = useState(0); const [currentSlide, setCurrentSlide] = useState(0);
const [isPaused, setIsPaused] = useState(false); const [isPaused, setIsPaused] = useState(false);
const cityName = localStorage.getItem("cityName")
const slides = [ const slides = [
{ {
id: 1, id: 1,
title: "Discover", title: "Discover",
highlight: "Melbourne", highlight: cityName,
subtitle: "Ultimate Guide to Iconic City", subtitle: "Ultimate Guide to Iconic City",
description: "From Flinders Street to St Kilda Beach: explore the best of Melbourne's landmarks, culture, food and more!", description: cityName === "Melbourne" ? "From Flinders Street to St Kilda Beach: explore the best of Melbourne's landmarks, culture, food and more!" : "From the Sydney Opera House to Bondi Beach: explore the best of Sydneys landmarks, culture, food and more!",
image: "https://images.unsplash.com/photo-1757470238279-0e9f331d02c9?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxNZWxib3VybmUlMjBza3lsaW5lJTIwc3Vuc2V0fGVufDF8fHx8MTc2MDUwOTIyMHww&ixlib=rb-4.1.0&q=80&w=1080", image: "https://images.unsplash.com/photo-1757470238279-0e9f331d02c9?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxNZWxib3VybmUlMjBza3lsaW5lJTIwc3Vuc2V0fGVufDF8fHx8MTc2MDUwOTIyMHww&ixlib=rb-4.1.0&q=80&w=1080",
cta: "Get Started", cta: "Get Started",
onClick: onCheckoutClick onClick: onCheckoutClick

View File

@@ -7,6 +7,7 @@ interface HotelEsimOffersProps {
} }
export function HotelEsimOffers({ onEsimsClick, onHotelDiscountsClick }: HotelEsimOffersProps) { export function HotelEsimOffers({ onEsimsClick, onHotelDiscountsClick }: HotelEsimOffersProps) {
const cityName = localStorage.getItem("cityName")
return ( return (
<div> <div>
<div className="space-y-0"> <div className="space-y-0">
@@ -64,7 +65,7 @@ export function HotelEsimOffers({ onEsimsClick, onHotelDiscountsClick }: HotelEs
> >
<Wifi className="w-4 h-4 text-primary" /> <Wifi className="w-4 h-4 text-primary" />
<span className="font-poppins text-sm font-medium text-primary"> <span className="font-poppins text-sm font-medium text-primary">
Stay Connected in Melbourne Stay Connected in {cityName}
</span> </span>
</motion.div> </motion.div>
<h2 className="font-poppins text-3xl md:text-5xl lg:text-6xl leading-tight text-foreground mb-6"> <h2 className="font-poppins text-3xl md:text-5xl lg:text-6xl leading-tight text-foreground mb-6">
@@ -72,7 +73,7 @@ export function HotelEsimOffers({ onEsimsClick, onHotelDiscountsClick }: HotelEs
<span className="font-bold text-primary italic">Connected</span> <span className="font-bold text-primary italic">Connected</span>
</h2> </h2>
<p className="font-poppins text-xl leading-relaxed font-normal text-gray-700 max-w-2xl mx-auto"> <p className="font-poppins text-xl leading-relaxed font-normal text-gray-700 max-w-2xl mx-auto">
Get instant e-SIM connectivity across Australia. Stay online from the moment you land in Melbourne. Get instant e-SIM connectivity across Australia. Stay online from the moment you land in {cityName}.
</p> </p>
</motion.div> </motion.div>
@@ -105,10 +106,10 @@ export function HotelEsimOffers({ onEsimsClick, onHotelDiscountsClick }: HotelEs
<span className="font-poppins text-xs font-semibold text-white">🇦🇺 AUSTRALIA-WIDE COVERAGE</span> <span className="font-poppins text-xs font-semibold text-white">🇦🇺 AUSTRALIA-WIDE COVERAGE</span>
</div> </div>
<h3 className="font-poppins text-3xl md:text-4xl font-semibold text-white mb-4 leading-tight"> <h3 className="font-poppins text-3xl md:text-4xl font-semibold text-white mb-4 leading-tight">
Exclusive e-SIM Offers for Melbourne Visitors Exclusive e-SIM Offers for {cityName} Visitors
</h3> </h3>
<p className="font-poppins text-base text-white/90 mb-6"> <p className="font-poppins text-base text-white/90 mb-6">
No more hunting for local SIM cards at the airport. Activate your e-SIM instantly and explore Melbourne with seamless connectivity. No more hunting for local SIM cards at the airport. Activate your e-SIM instantly and explore {cityName} with seamless connectivity.
</p> </p>
<motion.div <motion.div
@@ -207,7 +208,7 @@ export function HotelEsimOffers({ onEsimsClick, onHotelDiscountsClick }: HotelEs
> >
<Hotel className="w-4 h-4 text-primary" /> <Hotel className="w-4 h-4 text-primary" />
<span className="font-poppins text-sm font-medium text-primary"> <span className="font-poppins text-sm font-medium text-primary">
Premium Melbourne Hotels Premium {cityName} Hotels
</span> </span>
</motion.div> </motion.div>
<h2 className="font-poppins text-3xl md:text-5xl lg:text-6xl leading-tight text-foreground mb-6"> <h2 className="font-poppins text-3xl md:text-5xl lg:text-6xl leading-tight text-foreground mb-6">
@@ -215,7 +216,7 @@ export function HotelEsimOffers({ onEsimsClick, onHotelDiscountsClick }: HotelEs
<span className="font-bold text-primary italic">Luxury</span> <span className="font-bold text-primary italic">Luxury</span>
</h2> </h2>
<p className="font-poppins text-xl leading-relaxed font-normal text-gray-700 max-w-2xl mx-auto"> <p className="font-poppins text-xl leading-relaxed font-normal text-gray-700 max-w-2xl mx-auto">
Unlock exclusive rates at Melbourne's finest hotels. Your CityCard membership opens doors to premium CBD and waterfront stays. Unlock exclusive rates at {cityName}'s finest hotels. Your CityCard membership opens doors to premium CBD and waterfront stays.
</p> </p>
</motion.div> </motion.div>
@@ -236,10 +237,10 @@ export function HotelEsimOffers({ onEsimsClick, onHotelDiscountsClick }: HotelEs
<span className="font-poppins text-xs font-semibold text-white">🔥 MARRIOTT BONVOY PARTNER</span> <span className="font-poppins text-xs font-semibold text-white">🔥 MARRIOTT BONVOY PARTNER</span>
</div> </div>
<h3 className="font-poppins text-3xl md:text-4xl font-semibold text-foreground mb-4 leading-tight"> <h3 className="font-poppins text-3xl md:text-4xl font-semibold text-foreground mb-4 leading-tight">
Melbourne Premium Stays at <span className="text-primary italic">Unbeatable Prices</span> {cityName} Premium Stays at <span className="text-primary italic">Unbeatable Prices</span>
</h3> </h3>
<p className="font-poppins text-base text-gray-700 mb-6"> <p className="font-poppins text-base text-gray-700 mb-6">
Access exclusive member rates at Melbourne's top hotels including Crown Towers, W Melbourne, and premium CBD properties. Enjoy complimentary upgrades and special amenities. Access exclusive member rates at {cityName}'s top hotels including Crown Towers, W {cityName}, and premium CBD properties. Enjoy complimentary upgrades and special amenities.
</p> </p>
</div> </div>
@@ -253,7 +254,7 @@ export function HotelEsimOffers({ onEsimsClick, onHotelDiscountsClick }: HotelEs
<div className="bg-gradient-to-br from-primary to-orange-500 rounded-3xl p-8 text-center shadow-xl"> <div className="bg-gradient-to-br from-primary to-orange-500 rounded-3xl p-8 text-center shadow-xl">
<div className="font-poppins text-6xl font-bold text-white mb-2">25%</div> <div className="font-poppins text-6xl font-bold text-white mb-2">25%</div>
<div className="font-poppins text-lg text-white/90 mb-1">Average Savings</div> <div className="font-poppins text-lg text-white/90 mb-1">Average Savings</div>
<div className="font-poppins text-sm text-white/70">on Melbourne hotels</div> <div className="font-poppins text-sm text-white/70">on {cityName} hotels</div>
</div> </div>
<motion.div <motion.div
className="absolute -top-2 -right-2 bg-yellow-400 rounded-full px-3 py-1" className="absolute -top-2 -right-2 bg-yellow-400 rounded-full px-3 py-1"

View File

@@ -2,6 +2,7 @@ import { CreditCard, MapPin, Calendar, Zap, ChevronLeft, ChevronRight, Smartphon
import { useState } from 'react'; import { useState } from 'react';
import { motion, AnimatePresence } from 'motion/react'; import { motion, AnimatePresence } from 'motion/react';
import { ImageWithFallback } from './figma/ImageWithFallback'; import { ImageWithFallback } from './figma/ImageWithFallback';
import { useLocation, useNavigate } from 'react-router-dom';
const steps = [ const steps = [
{ {
@@ -50,6 +51,9 @@ const steps = [
export function HowItWorks() { export function HowItWorks() {
const [activeStep, setActiveStep] = useState(0); // Start with first step active const [activeStep, setActiveStep] = useState(0); // Start with first step active
const navigate=useNavigate()
const cityName = localStorage.getItem("cityName") || "Melbourne";
const location = useLocation()
const nextStep = () => { const nextStep = () => {
setActiveStep((prev) => (prev + 1) % steps.length); setActiveStep((prev) => (prev + 1) % steps.length);
@@ -78,8 +82,10 @@ export function HowItWorks() {
</div> </div>
<h2 className="text-4xl md:text-5xl lg:text-6xl text-gray-900 mb-4"> <h2 className="text-4xl md:text-5xl lg:text-6xl text-gray-900 mb-4">
<span className="font-light">How Your</span>{' '} <span className="font-light">How Your</span>{' '}
<span className="font-normal">Melbourne</span>{' '} {!location.pathname.includes("landing") &&
<span className="font-bold bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent italic">City Card</span>{' '} <span className="font-normal">{cityName}</span>
}
<span className="font-bold bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent italic pr-2">City Card</span>{' '}
<span className="font-light">Works.</span> <span className="font-light">Works.</span>
</h2> </h2>
<p className="text-xl text-gray-600 max-w-3xl mx-auto"> <p className="text-xl text-gray-600 max-w-3xl mx-auto">
@@ -557,8 +563,7 @@ export function HowItWorks() {
<button <button
key={index} key={index}
onClick={() => goToStep(index)} onClick={() => goToStep(index)}
className={`h-1.5 rounded-full transition-all duration-200 ${ className={`h-1.5 rounded-full transition-all duration-200 ${index === activeStep ? 'bg-primary w-6' : 'bg-gray-200 w-1.5 hover:bg-gray-300'
index === activeStep ? 'bg-primary w-6' : 'bg-gray-200 w-1.5 hover:bg-gray-300'
}`} }`}
/> />
))} ))}
@@ -1619,8 +1624,7 @@ export function HowItWorks() {
].map((action, idx) => ( ].map((action, idx) => (
<motion.button <motion.button
key={idx} key={idx}
className={`p-2 rounded-lg text-xs font-medium flex flex-col items-center gap-1 ${ className={`p-2 rounded-lg text-xs font-medium flex flex-col items-center gap-1 ${action.active ? 'bg-primary text-white' : 'bg-gray-100 text-gray-600'
action.active ? 'bg-primary text-white' : 'bg-gray-100 text-gray-600'
}`} }`}
animate={{ animate={{
backgroundColor: action.active ? backgroundColor: action.active ?
@@ -1679,8 +1683,7 @@ export function HowItWorks() {
].map((tab, idx) => ( ].map((tab, idx) => (
<motion.div <motion.div
key={idx} key={idx}
className={`flex flex-col items-center justify-center flex-1 ${ className={`flex flex-col items-center justify-center flex-1 ${tab.active ? 'text-primary' : 'text-gray-400'
tab.active ? 'text-primary' : 'text-gray-400'
}`} }`}
animate={{ animate={{
color: tab.active ? color: tab.active ?
@@ -2115,8 +2118,7 @@ export function HowItWorks() {
<button <button
key={index} key={index}
onClick={() => goToStep(index)} onClick={() => goToStep(index)}
className={`h-1.5 rounded-full transition-all duration-200 ${ className={`h-1.5 rounded-full transition-all duration-200 ${index === activeStep ? 'bg-primary w-6' : 'bg-gray-200 w-1.5 hover:bg-gray-300'
index === activeStep ? 'bg-primary w-6' : 'bg-gray-200 w-1.5 hover:bg-gray-300'
}`} }`}
/> />
))} ))}
@@ -2210,9 +2212,10 @@ export function HowItWorks() {
Join thousands of travelers who have discovered the easiest way to explore cities Join thousands of travelers who have discovered the easiest way to explore cities
</p> </p>
<motion.button <motion.button
className="bg-gradient-to-r from-primary to-secondary text-white py-4 px-12 rounded-lg text-lg hover:shadow-lg hover:shadow-primary/25 transition-all duration-200" className="cursor-pointer bg-gradient-to-r from-primary to-secondary text-white py-4 px-12 rounded-lg text-lg hover:shadow-lg hover:shadow-primary/25 transition-all duration-200"
whileHover={{ scale: 1.02 }} whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }} whileTap={{ scale: 0.98 }}
onClick={() => navigate("/passes")}
> >
Get Your City Card Get Your City Card
</motion.button> </motion.button>

View File

@@ -1,732 +0,0 @@
import React, { useState } from 'react';
import { motion } from 'motion/react';
import { ArrowLeft, Calendar, Clock, MapPin, Users, Star, Heart, Share2, Download, CheckCircle, Navigation, Cloud, Sun } from 'lucide-react';
import { Button } from './ui/button';
import { Card, CardContent } from './ui/card';
import { Badge } from './ui/badge';
import Navbar from './Navbar';
import { Footer } from './Footer';
import { ImageWithFallback } from './figma/ImageWithFallback';
interface ItineraryViewPageProps {
onBackClick: () => void;
onHomeClick: () => void;
onMelbourneClick: () => void;
onPassesClick: () => void;
onCheckoutClick: () => void;
onSignInClick: () => void;
onSignOutClick: () => void;
onAttractionsClick: () => void;
onBlogsClick: () => void;
onHowItWorksClick: () => void;
onFAQClick: () => void;
onPrivacyPolicyClick: () => void;
onAboutUsClick: () => void;
onProfileClick: () => void;
onCityCardsClick: () => void;
onMagicItineraryClick: () => void;
onPostCardsClick: () => void;
onOffersClick: () => void;
onCreateItineraryClick: () => void;
onContactUsClick?: () => void;
onEsimsClick?: () => void;
onHotelDiscountsClick?: () => void;
currentPage: string;
user?: { email: string; name: string; } | null;
}
// Enhanced activity type with more details
interface Activity {
time: string;
activity: string;
location: string;
address: string;
image: string;
categories: string[];
description: string[];
isFavorite?: boolean;
}
export function ItineraryViewPage({
onBackClick,
onHomeClick,
onMelbourneClick,
onPassesClick,
onCheckoutClick,
onSignInClick,
onSignOutClick,
onAttractionsClick,
onBlogsClick,
onHowItWorksClick,
onFAQClick,
onPrivacyPolicyClick,
onAboutUsClick,
onProfileClick,
onCityCardsClick,
onMagicItineraryClick,
onPostCardsClick,
onOffersClick,
onCreateItineraryClick,
onContactUsClick,
onEsimsClick,
onHotelDiscountsClick,
currentPage,
user
}: ItineraryViewPageProps) {
const [viewMode, setViewMode] = useState<'daily' | 'summary'>('daily');
const [favorites, setFavorites] = useState<Set<string>>(new Set());
const toggleFavorite = (activityKey: string) => {
setFavorites(prev => {
const newSet = new Set(prev);
if (newSet.has(activityKey)) {
newSet.delete(activityKey);
} else {
newSet.add(activityKey);
}
return newSet;
});
};
// Enhanced itinerary data with images, addresses, and detailed info
const generatedItinerary = {
destination: {
name: 'Melbourne',
country: 'Australia',
weather: '18°C, Sunny',
image: 'https://images.unsplash.com/photo-1514395462725-fb4566210144?w=400&h=300&fit=crop'
},
totalDays: 3,
estimatedCost: '$450 AUD',
includedActivities: 18,
dailyPlans: [
{
day: 1,
title: "City Center & Culture",
activities: [
{
time: '8:00 am',
activity: 'The Langham Melbourne',
location: 'The Langham Melbourne',
address: '1 Southgate Avenue, Southbank VIC 3006',
image: 'https://images.unsplash.com/photo-1566073771259-6a8506099945?w=800&h=600&fit=crop',
categories: ['Accommodation', 'Luxury'],
description: [
'Check-in at luxury riverside hotel',
'Enjoy complimentary breakfast',
'Relax at the spa facilities',
'Explore the surrounding Southbank area'
]
},
{
time: '10:00 am',
activity: 'Federation Square',
location: 'Federation Square',
address: 'Corner Swanston & Flinders Streets, Melbourne VIC 3000',
image: 'https://images.unsplash.com/photo-1514395462725-fb4566210144?w=800&h=600&fit=crop',
categories: ['Culture', 'Landmark'],
description: [
'Explore Melbourne\'s cultural precinct',
'Visit the ACMI museum',
'Enjoy street performances',
'Take photos at iconic locations'
]
},
{
time: '12:00 pm',
activity: 'Degrave Street Café',
location: 'Degrave Street Espresso Bar',
address: '23-25 Degraves Street, Melbourne VIC 3000',
image: 'https://images.unsplash.com/photo-1554118811-1e0d58224f24?w=800&h=600&fit=crop',
categories: ['Food', 'Drinks', 'Culture'],
description: [
'Coffee at Pellegrini\'s Espresso Bar (iconic old-school cafe)',
'Try the famous jam doughnuts',
'Shop for fresh produce in the Deli Hall',
'Pick up unique souvenirs in the General Merchandise section'
]
},
{
time: '2:00 pm',
activity: 'Royal Botanic Gardens',
location: 'Royal Botanic Gardens Victoria',
address: 'Birdwood Avenue, South Yarra VIC 3141',
image: 'https://images.unsplash.com/photo-1585320806297-9794b3e4eeae?w=800&h=600&fit=crop',
categories: ['Nature', 'Culture'],
description: [
'Stroll through stunning landscaped gardens',
'Visit the Australian Forest Walk',
'Relax by the Ornamental Lake',
'Join a free guided walking tour'
]
},
{
time: '4:00 pm',
activity: 'National Gallery of Victoria',
location: 'NGV International',
address: '180 St Kilda Road, Melbourne VIC 3006',
image: 'https://images.unsplash.com/photo-1564399577149-749794d74eee?w=800&h=600&fit=crop',
categories: ['Culture', 'Art'],
description: [
'Explore Australia\'s oldest art museum',
'View international and Australian art collections',
'Visit the stunning water wall entrance',
'Browse the NGV design store'
]
},
{
time: '7:00 pm',
activity: 'Dinner at Chin Chin',
location: 'Chin Chin Restaurant',
address: '125 Flinders Lane, Melbourne VIC 3000',
image: 'https://images.unsplash.com/photo-1552566626-52f8b828add9?w=800&h=600&fit=crop',
categories: ['Food', 'Drinks'],
description: [
'Experience modern Thai cuisine',
'Try signature dishes like the Betel Leaf',
'Enjoy the vibrant atmosphere',
'Book ahead or walk-in for bar seating'
]
}
]
},
{
day: 2,
title: "Markets & Neighborhoods",
activities: [
{
time: '8:00 am',
activity: 'Queen Victoria Market',
location: 'Queen Victoria Market',
address: 'Queen Street, Melbourne VIC 3000',
image: 'https://images.unsplash.com/photo-1555939594-58d7cb561ad1?w=800&h=600&fit=crop',
categories: ['Food', 'Shopping', 'Culture'],
description: [
'Explore Melbourne\'s historic market (since 1878)',
'Sample fresh local produce',
'Shop for artisan goods and souvenirs',
'Grab breakfast at the Deli Hall'
]
},
{
time: '10:30 am',
activity: 'Fitzroy Street Art Tour',
location: 'Fitzroy Arts Precinct',
address: 'Gertrude Street, Fitzroy VIC 3065',
image: 'https://images.unsplash.com/photo-1499781350541-7783f6c6a0c8?w=800&h=600&fit=crop',
categories: ['Culture', 'Art'],
description: [
'Walk through famous street art laneways',
'Discover works by renowned artists',
'Visit independent galleries',
'Explore vintage and record stores'
]
},
{
time: '12:30 pm',
activity: 'Brunswick Street Lunch',
location: 'Brunswick Street Precinct',
address: 'Brunswick Street, Fitzroy VIC 3065',
image: 'https://images.unsplash.com/photo-1517248135467-4c7edcad34c4?w=800&h=600&fit=crop',
categories: ['Food', 'Drinks'],
description: [
'Choose from diverse dining options',
'Try local cafes and restaurants',
'Explore bookshops and boutiques',
'Enjoy the vibrant neighborhood atmosphere'
]
},
{
time: '2:30 pm',
activity: 'Carlton Gardens',
location: 'Carlton Gardens',
address: 'Carlton Gardens, Carlton VIC 3053',
image: 'https://images.unsplash.com/photo-1519331379826-f10be5486c6f?w=800&h=600&fit=crop',
categories: ['Nature', 'Culture', 'Landmark'],
description: [
'Visit the UNESCO World Heritage site',
'See the Royal Exhibition Building',
'Stroll through Victorian-era gardens',
'Relax by the ornamental fountains'
]
},
{
time: '4:00 pm',
activity: 'Melbourne Museum',
location: 'Melbourne Museum',
address: '11 Nicholson Street, Carlton VIC 3053',
image: 'https://images.unsplash.com/photo-1566127992631-137a642a90f4?w=800&h=600&fit=crop',
categories: ['Culture', 'Museum'],
description: [
'Explore natural and cultural history',
'Visit the Bunjilaka Aboriginal Centre',
'See the Forest Gallery living ecosystem',
'Discover Melbourne\'s story exhibition'
]
},
{
time: '7:00 pm',
activity: 'Rooftop Bar Experience',
location: 'Naked for Satan',
address: '285 Brunswick Street, Fitzroy VIC 3065',
image: 'https://images.unsplash.com/photo-1514933651103-005eec06c04b?w=800&h=600&fit=crop',
categories: ['Drinks', 'Food'],
description: [
'Enjoy sunset views from the rooftop',
'Try Spanish-style pintxos',
'Sample craft cocktails and local beers',
'Experience Melbourne\'s bar culture'
]
}
]
},
{
day: 3,
title: "Coastal Adventure",
activities: [
{
time: '8:00 am',
activity: 'St. Kilda Beach',
location: 'St. Kilda Beach',
address: 'Jacka Boulevard, St Kilda VIC 3182',
image: 'https://images.unsplash.com/photo-1505142468610-359e7d316be0?w=800&h=600&fit=crop',
categories: ['Nature', 'Beach'],
description: [
'Morning walk along the iconic beach',
'Visit the historic St Kilda Pier',
'See the little penguins at sunset',
'Explore the Sunday Esplanade Market (weekends)'
]
},
{
time: '10:00 am',
activity: 'Acland Street Cafes',
location: 'Acland Street',
address: 'Acland Street, St Kilda VIC 3182',
image: 'https://images.unsplash.com/photo-1495474472287-4d71bcdd2085?w=800&h=600&fit=crop',
categories: ['Food', 'Drinks'],
description: [
'Brunch at famous cake shops',
'Try traditional European pastries',
'Visit Lentil as Anything (pay-as-you-feel)',
'Browse vintage shops and bookstores'
]
},
{
time: '12:00 pm',
activity: 'Luna Park Melbourne',
location: 'Luna Park Melbourne',
address: '18 Lower Esplanade, St Kilda VIC 3182',
image: 'https://images.unsplash.com/photo-1513026705753-bc3fffca8bf4?w=800&h=600&fit=crop',
categories: ['Entertainment', 'Landmark'],
description: [
'Visit Melbourne\'s iconic amusement park',
'Ride the historic Scenic Railway (1912)',
'Take photos at Mr Moon entrance',
'Enjoy carnival games and rides'
]
},
{
time: '2:00 pm',
activity: 'Brighton Beach Boxes',
location: 'Brighton Beach',
address: 'Esplanade, Brighton VIC 3186',
image: 'https://images.unsplash.com/photo-1520208422220-d12a3c588e6c?w=800&h=600&fit=crop',
categories: ['Culture', 'Landmark'],
description: [
'Photograph the famous colorful bathing boxes',
'Walk along the pristine beach',
'Learn about the heritage structures',
'Relax in the beachside atmosphere'
]
},
{
time: '4:00 pm',
activity: 'Southbank Promenade',
location: 'Southbank',
address: 'Southbank Promenade, Southbank VIC 3006',
image: 'https://images.unsplash.com/photo-1559827260-dc66d52bef19?w=800&h=600&fit=crop',
categories: ['Culture', 'Shopping'],
description: [
'Stroll along the Yarra River',
'Visit arts and craft markets',
'Explore restaurants and cafes',
'Enjoy river views and street performers'
]
},
{
time: '7:00 pm',
activity: 'Farewell Dinner at Vue de Monde',
location: 'Vue de Monde',
address: 'Level 55, Rialto, 525 Collins Street, Melbourne VIC 3000',
image: 'https://images.unsplash.com/photo-1414235077428-338989a2e8c0?w=800&h=600&fit=crop',
categories: ['Food', 'Drinks', 'Luxury'],
description: [
'Experience fine dining at 55th floor',
'Enjoy panoramic Melbourne views',
'Taste modern Australian cuisine',
'Celebrate the end of your journey'
]
}
]
}
]
};
return (
<div className="min-h-screen bg-background">
{/* Navbar */}
<Navbar
activeCity=""
onCityChange={() => {}}
onSignInClick={onSignInClick}
onPassesClick={onPassesClick}
onCheckoutClick={onCheckoutClick}
onHomeClick={onHomeClick}
onMelbourneClick={onMelbourneClick}
onAttractionsClick={onAttractionsClick}
onBlogsClick={onBlogsClick}
onHowItWorksClick={onHowItWorksClick}
onFAQClick={onFAQClick}
onPrivacyPolicyClick={onPrivacyPolicyClick}
onAboutUsClick={onAboutUsClick}
onProfileClick={onProfileClick}
onCityCardsClick={onCityCardsClick}
onMagicItineraryClick={onMagicItineraryClick}
onPostCardsClick={onPostCardsClick}
onOffersClick={onOffersClick}
currentPage="itinerary-view"
isUserSignedIn={!!user}
user={user}
/>
{/* Header Section */}
<section className="pt-32 pb-8 bg-gradient-to-br from-primary/5 to-secondary/5">
<div className="container mx-auto px-4 pt-32">
<motion.div
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6 }}
className="text-center"
>
<Button
variant="ghost"
onClick={onBackClick}
className="mb-6 hover:bg-primary/5 font-poppins font-medium"
>
<ArrowLeft className="w-4 h-4 mr-2" />
Back to Magic Itinerary
</Button>
<div className="flex items-center justify-center gap-2 text-primary mb-4">
<Star className="w-6 h-6 fill-current" />
<h1 className="font-merchant text-4xl md:text-5xl lg:text-6xl leading-tight">
<span className="font-light">Your</span>{' '}
<span className="font-bold italic bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent">Magic Itinerary</span>
</h1>
<Star className="w-6 h-6 fill-current" />
</div>
<p className="font-poppins text-xl leading-relaxed font-normal text-gray-600 max-w-2xl mx-auto">
Here's your personalized {generatedItinerary.totalDays}-day adventure in {generatedItinerary.destination.name}!
</p>
</motion.div>
</div>
</section>
{/* Main Content */}
<section className="py-8">
<div className="container mx-auto px-4">
<div className="max-w-6xl mx-auto space-y-8">
{/* View Toggle */}
<div className="flex justify-center">
<div className="bg-muted p-1 rounded-lg">
<Button
variant={viewMode === 'daily' ? 'default' : 'ghost'}
size="sm"
onClick={() => setViewMode('daily')}
className="rounded-md font-poppins font-medium"
>
Daily View
</Button>
<Button
variant={viewMode === 'summary' ? 'default' : 'ghost'}
size="sm"
onClick={() => setViewMode('summary')}
className="rounded-md font-poppins font-medium"
>
Summary
</Button>
</div>
</div>
{/* Itinerary Overview */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.2 }}
>
<Card className="p-6">
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<div className="text-center">
<div className="font-merchant text-3xl text-primary mb-2">{generatedItinerary.totalDays}</div>
<div className="font-poppins text-sm text-muted-foreground font-normal">Days</div>
</div>
<div className="text-center">
<div className="font-merchant text-3xl text-primary mb-2">{generatedItinerary.includedActivities}</div>
<div className="font-poppins text-sm text-muted-foreground font-normal">Activities</div>
</div>
<div className="text-center">
<div className="font-merchant text-3xl text-primary mb-2">{generatedItinerary.estimatedCost}</div>
<div className="font-poppins text-sm text-muted-foreground font-normal">Estimated Cost</div>
</div>
</div>
</Card>
</motion.div>
{/* Daily Plans - Enhanced View */}
{viewMode === 'daily' && (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.3 }}
className="space-y-12"
>
{generatedItinerary.dailyPlans.map((day, dayIndex) => (
<div key={dayIndex} className="space-y-6">
{/* Location Header with Weather - Only show for first day or when location changes */}
{(dayIndex === 0 ||
(dayIndex > 0 &&
generatedItinerary.dailyPlans[dayIndex - 1] &&
(generatedItinerary.dailyPlans[dayIndex - 1] as any).destination?.name !== generatedItinerary.destination.name)) && (
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.6, delay: 0.4 + dayIndex * 0.1 }}
className="bg-gray-50 rounded-2xl p-6 shadow-sm border border-gray-200"
>
<div className="flex items-center justify-between">
<div>
<h2 className="font-merchant text-3xl md:text-4xl leading-tight mb-2">
{generatedItinerary.destination.name}, {generatedItinerary.destination.country}
</h2>
<p className="font-poppins text-base text-primary font-medium">{generatedItinerary.destination.weather}</p>
</div>
<div className="flex items-center gap-2">
<Sun className="w-10 h-10 text-amber-500" />
</div>
</div>
</motion.div>
)}
{/* Day Header */}
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.6, delay: 0.5 + dayIndex * 0.1 }}
className="flex items-center gap-4 pl-2"
>
<div className="bg-gradient-to-br from-primary to-secondary text-white w-16 h-16 rounded-full flex items-center justify-center shadow-lg">
<span className="font-merchant text-2xl font-semibold">{day.day}</span>
</div>
<div>
<h3 className="font-merchant text-2xl md:text-3xl leading-snug font-semibold">Day {day.day}</h3>
<p className="font-poppins text-base text-muted-foreground font-normal">{day.title}</p>
</div>
</motion.div>
{/* GMT Label */}
<div className="pl-2">
<p className="font-poppins text-sm text-gray-500 font-normal">GMT</p>
</div>
{/* Activity Cards - Desktop Grid Layout */}
<div className="space-y-8">
{day.activities.map((activity, actIndex) => {
const activityKey = `day${day.day}-act${actIndex}`;
const isFavorite = favorites.has(activityKey);
return (
<motion.div
key={actIndex}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: 0.6 + dayIndex * 0.1 + actIndex * 0.05 }}
className="flex gap-6"
>
{/* Time Column */}
<div className="flex-shrink-0 w-24 pt-2">
<div className="font-poppins text-base font-medium text-gray-700">{activity.time}</div>
</div>
{/* Activity Card */}
<div className="flex-1">
<Card className="overflow-hidden hover:shadow-xl transition-shadow duration-300 border-2 border-gray-100">
<CardContent className="p-0">
{/* Hero Image with Overlay Buttons */}
<div className="relative h-64 md:h-72 bg-gray-200">
<ImageWithFallback
src={activity.image}
alt={activity.activity}
className="w-full h-full object-cover"
/>
{/* Favorite Heart Button - Top Right */}
<div className="absolute top-4 right-4">
<Button
size="icon"
variant="secondary"
className="bg-white/95 hover:bg-white shadow-lg backdrop-blur-sm rounded-full w-12 h-12"
onClick={() => toggleFavorite(activityKey)}
>
<Heart className={`w-5 h-5 ${isFavorite ? 'fill-primary text-primary' : 'text-gray-700'}`} />
</Button>
</div>
{/* Get Directions Button - Bottom Left */}
<div className="absolute bottom-4 left-4">
<Button
className="bg-primary hover:bg-primary/90 text-white font-poppins font-semibold shadow-lg px-6 py-3 rounded-xl"
>
<Navigation className="w-4 h-4 mr-2" />
Get Directions
</Button>
</div>
</div>
{/* Content Section */}
<div className="p-6 space-y-4">
{/* Location Name & Address */}
<div className="space-y-2">
<h4 className="font-merchant text-xl md:text-2xl leading-snug font-semibold text-gray-900">
{activity.activity}
</h4>
<div className="flex items-start gap-2 text-gray-600">
<MapPin className="w-4 h-4 mt-1 flex-shrink-0 text-primary" />
<span className="font-poppins text-sm font-normal leading-relaxed">{activity.address}</span>
</div>
</div>
{/* Category Badges */}
<div className="flex flex-wrap gap-2">
{activity.categories.map((category, catIndex) => (
<Badge
key={catIndex}
variant="secondary"
className="font-poppins font-normal text-sm bg-primary/10 text-primary hover:bg-primary/20 px-3 py-1"
>
{category}
</Badge>
))}
</div>
{/* Activity Details - Bullet Points */}
<div className="space-y-2 pt-2">
{activity.description.map((detail, detailIndex) => (
<div key={detailIndex} className="flex items-start gap-3">
<span className="text-primary font-semibold mt-1.5 flex-shrink-0">•</span>
<span className="font-poppins text-sm font-normal text-gray-600 leading-relaxed">{detail}</span>
</div>
))}
</div>
</div>
</CardContent>
</Card>
</div>
</motion.div>
);
})}
</div>
</div>
))}
</motion.div>
)}
{/* Summary View */}
{viewMode === 'summary' && (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.3 }}
className="space-y-4"
>
<h3 className="font-merchant text-2xl md:text-3xl text-center mb-8 leading-tight font-semibold">Trip Summary</h3>
<Card className="p-6">
<div className="space-y-6">
{generatedItinerary.dailyPlans.map((day, index) => (
<div key={index} className="border-l-4 border-primary pl-6">
<div className="flex items-center gap-2 mb-3">
<Calendar className="w-5 h-5 text-primary" />
<h4 className="font-merchant text-lg md:text-xl leading-snug font-semibold">Day {day.day}: {day.title}</h4>
</div>
<div className="space-y-2">
{day.activities.map((activity, actIndex) => (
<div key={actIndex} className="flex items-start gap-3 text-sm">
<CheckCircle className="w-4 h-4 text-green-500 flex-shrink-0 mt-0.5" />
<div className="flex-1">
<span className="font-poppins text-gray-700 font-medium">{activity.activity}</span>
<div className="flex items-center gap-2 mt-1">
<span className="font-poppins text-gray-500 text-xs font-normal">{activity.time}</span>
<span className="text-gray-400"></span>
<span className="font-poppins text-gray-500 text-xs font-normal">{activity.location}</span>
</div>
</div>
</div>
))}
</div>
</div>
))}
</div>
</Card>
</motion.div>
)}
{/* Action Buttons */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.5 }}
className="flex flex-col sm:flex-row gap-4 justify-center pt-8"
>
<Button
variant="outline"
onClick={onCreateItineraryClick}
className="font-poppins font-medium px-8 py-3 text-lg"
>
<Heart className="w-5 h-5 mr-2" />
Create Another
</Button>
<Button
className="bg-primary hover:bg-primary/90 font-poppins font-semibold px-8 py-3 text-lg"
>
<Download className="w-5 h-5 mr-2" />
Save Itinerary
</Button>
<Button
variant="outline"
className="font-poppins font-medium px-8 py-3 text-lg"
>
<Share2 className="w-5 h-5 mr-2" />
Share Trip
</Button>
</motion.div>
</div>
</div>
</section>
{/* Footer */}
<Footer
onHomeClick={onHomeClick}
onMelbourneClick={onMelbourneClick}
onPassesClick={onPassesClick}
onSignInClick={onSignInClick}
onAttractionsClick={onAttractionsClick}
onBlogsClick={onBlogsClick}
onHowItWorksClick={onHowItWorksClick}
onFAQClick={onFAQClick}
onPrivacyPolicyClick={onPrivacyPolicyClick}
onContactUsClick={onContactUsClick}
currentPage={currentPage}
/>
</div>
);
}

View File

@@ -31,7 +31,7 @@ export function LandingMagicItinerary() {
}; };
return ( return (
<section className="relative py-20 lg:py-32 overflow-hidden -mt-20 pt-32 z-[100]"> <section className="relative py-20 lg:py-32 overflow-hidden -mt-20 pt-32 z-[49]">
{/* Dynamic Background */} {/* Dynamic Background */}
<div className="absolute inset-0 overflow-hidden pointer-events-none z-[5]"> <div className="absolute inset-0 overflow-hidden pointer-events-none z-[5]">
{/* Background Image as fallback */} {/* Background Image as fallback */}
@@ -112,7 +112,7 @@ export function LandingMagicItinerary() {
> >
<Wand2 className="w-6 h-6 text-warm-coral drop-shadow-lg" /> <Wand2 className="w-6 h-6 text-warm-coral drop-shadow-lg" />
</motion.div> </motion.div>
<span className="font-semibold text-gray-800">AI-Powered Magic Itinerary</span> <span className="font-semibold text-gray-800">Magic Itinerary</span>
<motion.div <motion.div
className="w-2 h-2 bg-warm-coral rounded-full" className="w-2 h-2 bg-warm-coral rounded-full"
animate={{ animate={{
@@ -131,7 +131,7 @@ export function LandingMagicItinerary() {
viewport={{ once: true }} viewport={{ once: true }}
> >
<span className="font-light">Plan Your</span>{' '} <span className="font-light">Plan Your</span>{' '}
<span className="font-bold italic bg-gradient-to-r from-warm-coral via-orange-500 to-rose-500 bg-clip-text text-transparent drop-shadow-lg"> <span className="font-bold italic bg-gradient-to-r via-orange-500 to-rose-500 bg-clip-text pr-2 text-transparent drop-shadow-lg">
Dream Journey Dream Journey
</span> </span>
<br /> <br />
@@ -250,7 +250,7 @@ export function LandingMagicItinerary() {
> >
<Button <Button
withShine={true} withShine={true}
className="py-6 px-14 rounded-full text-lg font-bold bg-gradient-to-r from-warm-coral via-orange-500 to-rose-500 hover:from-warm-coral/90 hover:via-orange-500/90 hover:to-rose-500/90 shadow-2xl hover:shadow-warm-coral/50 transition-all hover:scale-105 hover:-translate-y-1" className="py-6 px-14 rounded-full text-lg font-bold bg-gradient-to-r via-orange-500 to-rose-500 hover:from-warm-coral/90 hover:via-orange-500/90 hover:to-rose-500/90 shadow-2xl hover:shadow-warm-coral/50 transition-all hover:scale-105 hover:-translate-y-1"
> >
<span className="flex items-center gap-3"> <span className="flex items-center gap-3">
<Wand2 className="w-5 h-5" /> <Wand2 className="w-5 h-5" />
@@ -258,11 +258,11 @@ export function LandingMagicItinerary() {
</span> </span>
</Button> </Button>
<p className="text-gray-600 text-sm flex items-center gap-2"> {/* <p className="text-gray-600 text-sm flex items-center gap-2">
<Sparkles className="w-4 h-4 text-warm-coral" /> <Sparkles className="w-4 h-4 text-warm-coral" />
<span>Free to use • No credit card required</span> <span>Free to use • No credit card required</span>
<Sparkles className="w-4 h-4 text-warm-coral" /> <Sparkles className="w-4 h-4 text-warm-coral" />
</p> </p> */}
</motion.div> </motion.div>
</div> </div>
</div> </div>

View File

@@ -4,6 +4,7 @@ import { Button } from './ui/button';
import { useRef, useState, useEffect } from 'react'; import { useRef, useState, useEffect } from 'react';
import Image592Traced from '../imports/Image592Traced-5025-559'; import Image592Traced from '../imports/Image592Traced-5025-559';
import { useGetUpcomingCitiesQuery } from '../Redux/services/cities.service'; import { useGetUpcomingCitiesQuery } from '../Redux/services/cities.service';
import LoadingSpinner from './LoadingSpinner';
// const upcomingCities = [ // const upcomingCities = [
// { // {
@@ -114,7 +115,9 @@ export function LandingUpcomingCities() {
const { data, isLoading } = useGetUpcomingCitiesQuery(listType) const { data, isLoading } = useGetUpcomingCitiesQuery(listType)
if (isLoading) { if (isLoading) {
return <div>Loading...</div> return (
<LoadingSpinner/>
);
} }
const handleMouseDown = (e: React.MouseEvent) => { const handleMouseDown = (e: React.MouseEvent) => {

View File

@@ -150,10 +150,10 @@ export function LandingVarietyOfAdventures() {
const extendedCategories = [...melbourneCategories, ...melbourneCategories, ...melbourneCategories]; const extendedCategories = [...melbourneCategories, ...melbourneCategories, ...melbourneCategories];
return ( return (
<section className="py-20 lg:py-28 bg-white overflow-hidden"> <section className="lg: bg-white overflow-hidden">
<div className="container mx-auto px-4"> <div className="container mx-auto px-4">
{/* Header */} {/* Header */}
<div className="text-center mb-16 max-w-4xl mx-auto"> <div className="text-center mb-2 max-w-4xl mx-auto">
<motion.h2 <motion.h2
className="font-poppins text-2xl md:text-3xl lg:text-4xl leading-tight text-foreground mb-6" className="font-poppins text-2xl md:text-3xl lg:text-4xl leading-tight text-foreground mb-6"
initial={{ opacity: 0, y: 30 }} initial={{ opacity: 0, y: 30 }}
@@ -332,8 +332,8 @@ export function LandingVarietyOfAdventures() {
</motion.div> </motion.div>
{/* Gradient Fade Edges */} {/* Gradient Fade Edges */}
<div className="absolute left-0 top-0 bottom-0 w-32 bg-white/80 pointer-events-none z-10" /> <div className="absolute left-0 top-0 bottom-0 w-32 bg-white pointer-events-none z-10" style={{ boxShadow: '47px 2px 40px 0px #fff' }} />
<div className="absolute right-0 top-0 bottom-0 w-32 bg-white/80 pointer-events-none z-10" /> <div className="absolute right-0 top-0 bottom-0 w-32 bg-white pointer-events-none z-10" style={{ boxShadow: '-47px 2px 40px 0px #fff' }} />
</div> </div>
</div> </div>

View File

@@ -0,0 +1,12 @@
const LoadingSpinner = () => {
return (
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
<div className="text-center">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-[#F95F62] mx-auto"></div>
<p className="mt-4 text-gray-600">Loading...</p>
</div>
</div>
)
}
export default LoadingSpinner

View File

@@ -1,40 +1,51 @@
// LoginModal.tsx
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { motion, AnimatePresence } from 'motion/react'; import { motion, AnimatePresence } from 'motion/react';
import { X } from 'lucide-react'; import { X } from 'lucide-react';
import { Button } from './ui/button'; import { Button } from './ui/button';
import { Input } from './ui/input'; import { Input } from './ui/input';
import { Label } from './ui/label'; import { Label } from './ui/label';
import { useNavigate } from 'react-router-dom';
import { useAuth } from '../context/AuthContext'; import { useAuth } from '../context/AuthContext';
import { useLoginMutation, useVerifyOtpMutation } from '../Redux/services/auth.service';
import { toast } from 'sonner';
import { useNavigate } from 'react-router-dom';
interface LoginModalProps { interface LoginModalProps {
isOpen: boolean; isOpen: boolean;
onClose: () => void; onClose: () => void;
// onLoginSuccess: (userData: { email: string; name: string }) => void;
} }
export function LoginModal({ isOpen, onClose, }: LoginModalProps) { export function LoginModal({ isOpen, onClose }: LoginModalProps) {
const [step, setStep] = useState<'email' | 'otp'>('email'); const [step, setStep] = useState<'email' | 'otp'>('email');
const [email, setEmail] = useState(''); const [email, setEmail] = useState('');
const [otp, setOtp] = useState(['', '', '', '', '', '']); const [otp, setOtp] = useState(['', '', '', '', '', '']);
const [countdown, setCountdown] = useState(0); const [countdown, setCountdown] = useState(0);
const [isLoading, setIsLoading] = useState(false);
const [helperText, setHelperText] = useState(''); const [helperText, setHelperText] = useState('');
const [error, setError] = useState('');
const { login } = useAuth(); // from AuthContext const { login } = useAuth();
const navigate = useNavigate()
// Reset modal state when closed const [sendOtp, { isLoading: isSendingOtp }] = useLoginMutation();
const [verifyOtp, { isLoading: isVerifying }] = useVerifyOtpMutation();
// Reset modal when closed
useEffect(() => { useEffect(() => {
if (!isOpen) { if (!isOpen) {
resetModal();
}
}, [isOpen]);
const resetModal = () => {
setStep('email'); setStep('email');
setEmail(''); setEmail('');
setOtp(['', '', '', '', '', '']); setOtp(['', '', '', '', '', '']);
setCountdown(0); setCountdown(0);
setHelperText(''); setHelperText('');
} setError('');
}, [isOpen]); };
// Countdown timer for OTP resend // Countdown timer
useEffect(() => { useEffect(() => {
if (countdown > 0) { if (countdown > 0) {
const timer = setTimeout(() => setCountdown(countdown - 1), 1000); const timer = setTimeout(() => setCountdown(countdown - 1), 1000);
@@ -42,67 +53,122 @@ export function LoginModal({ isOpen, onClose, }: LoginModalProps) {
} }
}, [countdown]); }, [countdown]);
const handleSendOTP = async () => { // ==================== PASTE OTP FEATURE ====================
if (!email || !email.includes('@')) { const handlePaste = (e: React.ClipboardEvent) => {
setHelperText('Please enter a valid email address'); e.preventDefault();
return; const pastedData = e.clipboardData.getData('text').trim();
// Extract only digits
const digits = pastedData.replace(/\D/g, '').slice(0, 6);
if (digits.length === 0) return;
const newOtp = [...otp];
// Fill the OTP array with pasted digits
for (let i = 0; i < digits.length; i++) {
newOtp[i] = digits[i];
} }
setIsLoading(true); setOtp(newOtp);
setHelperText('');
// Simulate API call // Auto-focus the next empty field or the last one
setTimeout(() => { const nextIndex = digits.length < 6 ? digits.length : 5;
setStep('otp'); const nextInput = document.querySelector(
setCountdown(120); // 2 minutes countdown `input[data-otp-index="${nextIndex}"]`
setIsLoading(false); ) as HTMLInputElement;
setHelperText('OTP sent successfully');
}, 1500); nextInput?.focus();
}; };
const handleOTPChange = (index: number, value: string) => { const handleOTPChange = (index: number, value: string) => {
if (value.length > 1) return; // Only allow single digit if (value.length > 1) return;
const newOtp = [...otp]; const newOtp = [...otp];
newOtp[index] = value; newOtp[index] = value;
setOtp(newOtp); setOtp(newOtp);
// Auto-focus next input
if (value && index < 5) { if (value && index < 5) {
const nextInput = document.querySelector(`input[data-otp-index="${index + 1}"]`) as HTMLInputElement; const nextInput = document.querySelector(
`input[data-otp-index="${index + 1}"]`
) as HTMLInputElement;
nextInput?.focus(); nextInput?.focus();
} }
}; };
const handleOTPKeyDown = (index: number, e: React.KeyboardEvent) => { const handleOTPKeyDown = (index: number, e: React.KeyboardEvent) => {
if (e.key === 'Backspace' && !otp[index] && index > 0) { if (e.key === "Backspace" && !otp[index] && index > 0) {
const prevInput = document.querySelector(`input[data-otp-index="${index - 1}"]`) as HTMLInputElement; const prevInput = document.querySelector(
`input[data-otp-index="${index - 1}"]`
) as HTMLInputElement;
prevInput?.focus(); prevInput?.focus();
} }
// ✅ Trigger verify on Enter if all 6 digits are filled
if (e.key === "Enter") {
const otpString = otp.join("");
if (otpString.length === 6) {
handleVerifyLogin();
}
}
};
// Rest of your functions remain the same
const handleSendOTP = async () => {
if (!email || !email.includes('@')) {
setError('Please enter a valid email address');
return;
}
setError('');
setHelperText('');
try {
await sendOtp({ emailAddress: email }).unwrap();
setStep('otp');
setCountdown(120);
setHelperText('OTP sent successfully to your email');
} catch (err: any) {
setError(err?.data?.message || 'Failed to send OTP. Please try again.');
}
}; };
const handleVerifyLogin = async () => { const handleVerifyLogin = async () => {
const otpString = otp.join(''); const otpString = otp.join('');
if (otpString.length !== 6) { if (otpString.length !== 6) {
setHelperText('Please enter complete OTP'); setError('Please enter complete 6-digit OTP');
return; return;
} }
setIsLoading(true); setError('');
setHelperText('');
// Simulate API call try {
setTimeout(() => { const response = await verifyOtp({
// Generate name from email for demo emailAddress: email,
const emailParts = email.split('@')[0]; otp: otpString
const name = emailParts.charAt(0).toUpperCase() + emailParts.slice(1); }).unwrap();
login({ email, name })
setIsLoading(false); if (!response?.userExists) {
// navigate("/melbourne") localStorage.setItem("userEmail",email)
navigate("/register")
} else {
const userData = {
userId: response?.user?.id,
email: response?.email || email,
name: response?.name || email.split('@')[0].charAt(0).toUpperCase() + email.split('@')[0].slice(1),
accessToken: response?.accessToken,
};
login(userData);
toast.success("User Logged in successfully")
navigate("/passes")
}
onClose(); onClose();
}, 1500); } catch (err: any) {
setError(err?.data?.message || 'Invalid OTP. Please try again.');
toast.error(err?.data?.message)
}
}; };
const formatCountdown = (seconds: number) => { const formatCountdown = (seconds: number) => {
@@ -112,33 +178,29 @@ export function LoginModal({ isOpen, onClose, }: LoginModalProps) {
}; };
return ( return (
<>
<AnimatePresence> <AnimatePresence>
{isOpen && ( {isOpen && (
<> <>
{/* Backdrop */}
<motion.div <motion.div
initial={{ opacity: 0 }} initial={{ opacity: 0 }}
animate={{ opacity: 1 }} animate={{ opacity: 1 }}
exit={{ opacity: 0 }} exit={{ opacity: 0 }}
transition={{ duration: 0.2 }}
className="fixed inset-0 bg-black/50 backdrop-blur-sm z-50" className="fixed inset-0 bg-black/50 backdrop-blur-sm z-50"
onClick={onClose} onClick={onClose}
/> />
{/* Modal */}
<motion.div <motion.div
initial={{ opacity: 0, scale: 0.95, y: 20 }} initial={{ opacity: 0, scale: 0.95, y: 20 }}
animate={{ opacity: 1, scale: 1, y: 0 }} animate={{ opacity: 1, scale: 1, y: 0 }}
exit={{ opacity: 0, scale: 0.95, y: 20 }} exit={{ opacity: 0, scale: 0.95, y: 20 }}
transition={{ duration: 0.3, ease: "easeOut" }}
className="fixed inset-0 flex items-center justify-center z-50 p-4" className="fixed inset-0 flex items-center justify-center z-50 p-4"
> >
<div className="bg-white rounded-3xl shadow-2xl w-full max-w-md mx-auto overflow-hidden"> <div className="bg-white rounded-3xl shadow-2xl w-full max-w-md mx-auto overflow-hidden">
{/* Header */}
<div className="relative px-8 pt-8 pb-4"> <div className="relative px-8 pt-8 pb-4">
<button <button
onClick={onClose} onClick={onClose}
className="absolute top-6 right-6 w-8 h-8 flex items-center justify-center rounded-full bg-gray-100 hover:bg-gray-200 transition-colors" className="absolute top-6 right-6 w-8 h-8 flex items-center justify-center rounded-full bg-gray-100 hover:bg-gray-200 transition-colors cursor-pointer"
> >
<X className="w-4 h-4 text-gray-600" /> <X className="w-4 h-4 text-gray-600" />
</button> </button>
@@ -147,73 +209,50 @@ export function LoginModal({ isOpen, onClose, }: LoginModalProps) {
Login Login
</h2> </h2>
<p className="font-poppins text-sm text-gray-600"> <p className="font-poppins text-sm text-gray-600">
Enter your email Id and verify with OTP sent on it. Enter your email and verify with OTP
</p> </p>
</div> </div>
{/* Content */}
<div className="px-8 pb-8"> <div className="px-8 pb-8">
{step === 'email' ? ( {step === 'email' ? (
// ... Email step (unchanged)
<div className="space-y-6"> <div className="space-y-6">
{/* Email Input */}
<div className="space-y-2"> <div className="space-y-2">
<Label className="font-poppins text-sm font-medium text-gray-700"> <Label className="font-poppins text-sm font-medium text-gray-700">
Email Email Address
</Label> </Label>
<Input <Input
type="email" type="email"
placeholder="Name@example.com" placeholder="name@example.com"
value={email} value={email}
onChange={(e) => setEmail(e.target.value)} onChange={(e) => setEmail(e.target.value)}
className="font-poppins text-base h-12 bg-gray-50 border-0 rounded-xl placeholder:text-gray-400"
onKeyDown={(e) => e.key === 'Enter' && handleSendOTP()} onKeyDown={(e) => e.key === 'Enter' && handleSendOTP()}
className="font-poppins text-base h-12 bg-gray-50 border-0 rounded-xl placeholder:text-gray-400"
/> />
{helperText && ( {error && <p className="text-red-500 text-xs">{error}</p>}
<p className={`font-poppins text-xs ${helperText.includes('success') ? 'text-green-600' : 'text-red-500'}`}> {helperText && <p className="text-green-600 text-xs">{helperText}</p>}
{helperText}
</p>
)}
</div> </div>
{/* Send OTP Button */}
<Button <Button
onClick={handleSendOTP} onClick={handleSendOTP}
disabled={isLoading} disabled={isSendingOtp}
className="w-full h-12 bg-gray-800 hover:bg-gray-900 cursor-pointer text-white font-poppins font-semibold rounded-xl transition-colors" className="w-full h-12 bg-gray-800 hover:bg-gray-900 text-white font-poppins font-semibold rounded-xl cursor-pointer"
> >
{isLoading ? ( {isSendingOtp ? 'Sending OTP...' : 'Send OTP'}
<div className="flex items-center gap-2">
<div className="w-4 h-4 border-2 border-white/20 border-t-white rounded-full animate-spin" />
Sending OTP...
</div>
) : (
<>
Send OTP
<svg className="w-4 h-4 ml-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
</svg>
</>
)}
</Button> </Button>
</div> </div>
) : ( ) : (
<div className="space-y-6"> <div className="space-y-6">
{/* Email Display */} {/* Email Display */}
<div className="space-y-2"> <div className="space-y-2">
<Label className="font-poppins text-sm font-medium text-gray-700"> <Label className="font-poppins text-sm font-medium text-gray-700">Email</Label>
Email <div className="h-12 bg-gray-50 rounded-xl flex items-center px-4 font-poppins text-base text-gray-600">
</Label> {email}
<div className="h-12 bg-gray-50 rounded-xl flex items-center px-4">
<span className="font-poppins text-base text-gray-600">{email}</span>
</div> </div>
{helperText && (
<p className={`font-poppins text-xs ${helperText.includes('success') ? 'text-green-600' : 'text-red-500'}`}>
{helperText}
</p>
)}
</div> </div>
{/* OTP Input */} {/* OTP Inputs with Paste Support */}
<div className="space-y-3"> <div className="space-y-3">
<Label className="font-poppins text-sm font-medium text-gray-700"> <Label className="font-poppins text-sm font-medium text-gray-700">
Enter OTP Enter OTP
@@ -224,56 +263,47 @@ export function LoginModal({ isOpen, onClose, }: LoginModalProps) {
key={index} key={index}
type="text" type="text"
inputMode="numeric" inputMode="numeric"
pattern="[0-9]*"
maxLength={1} maxLength={1}
value={digit} value={digit}
onChange={(e) => handleOTPChange(index, e.target.value.replace(/\D/g, ''))} onChange={(e) => handleOTPChange(index, e.target.value.replace(/\D/g, ''))}
onKeyDown={(e) => handleOTPKeyDown(index, e)} onKeyDown={(e) => handleOTPKeyDown(index, e)}
onPaste={handlePaste} // ← Paste support added here
data-otp-index={index} data-otp-index={index}
className="w-12 h-12 text-center font-poppins font-semibold text-lg bg-gray-300 border-0 rounded-xl focus:bg-white focus:ring-2 focus:ring-primary focus:outline-none transition-colors" className="w-12 h-12 text-center font-poppins font-semibold text-lg bg-gray-300 border-0 rounded-xl focus:bg-white focus:ring-2 focus:ring-gray-800 focus:outline-none transition-all"
/> />
))} ))}
</div> </div>
{/* Countdown */}
{countdown > 0 && ( {countdown > 0 && (
<p className="font-poppins text-xs text-gray-500 text-center"> <p className="text-center text-xs text-gray-500">
{formatCountdown(countdown)} Resend OTP in {formatCountdown(countdown)}
</p> </p>
)} )}
</div> </div>
{/* Verify Button */} {error && <p className="text-red-500 text-xs text-center">{error}</p>}
<Button <Button
onClick={handleVerifyLogin} onClick={handleVerifyLogin}
disabled={isLoading || otp.join('').length !== 6} disabled={isVerifying || otp.join('').length !== 6}
className="w-full h-12 bg-gray-800 hover:bg-gray-900 cursor-pointer text-white font-poppins font-semibold rounded-xl transition-colors disabled:opacity-50" className="w-full h-12 bg-gray-800 hover:bg-gray-900 text-white font-poppins font-semibold rounded-xl disabled:opacity-50 cursor-pointer"
> >
{isLoading ? ( {isVerifying ? 'Verifying...' : 'Verify & Login'}
<div className="flex items-center gap-2">
<div className="w-4 h-4 border-2 border-white/20 border-t-white rounded-full animate-spin" />
Verifying...
</div>
) : (
<>
Verify and Login
<svg className="w-4 h-4 ml-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
</svg>
</>
)}
</Button> </Button>
{/* Resend OTP */}
{countdown === 0 && ( {countdown === 0 && (
<button <button
onClick={() => { onClick={() => {
setStep('email'); setStep('email');
setOtp(['', '', '', '', '', '']); setOtp(['', '', '', '', '', '']);
setHelperText("")
setError('');
}} }}
className="w-full font-poppins text-sm text-gray-600 hover:text-gray-800 transition-colors" className="w-full text-sm text-gray-600 hover:text-gray-800 font-poppins cursor-pointer"
> >
Didn't receive OTP? Send again Didn't receive OTP?
<span className="text-primary font-semibold"> Send again</span>
{/* Send again */}
</button> </button>
)} )}
</div> </div>
@@ -282,7 +312,9 @@ export function LoginModal({ isOpen, onClose, }: LoginModalProps) {
</div> </div>
</motion.div> </motion.div>
</> </>
)} )
}
</AnimatePresence > </AnimatePresence >
</>
); );
} }

View File

@@ -1,368 +0,0 @@
import React from 'react';
import { motion } from 'motion/react';
import { ArrowLeft, Sparkles, MapPin, Clock, Users, Calendar, Star, Zap, Heart, Camera } from 'lucide-react';
import { Button } from './ui/button';
import { Card, CardContent } from './ui/card';
import { Badge } from './ui/badge';
import Navbar from './Navbar';
// import SubNavbar from './SubNavbar';
import { Footer } from './Footer';
import { MobileAppSection } from './MobileAppSection';
import { EnhancedTestimonials } from './EnhancedTestimonials';
import { HowItWorks } from './HowItWorks';
import { ImageWithFallback } from './figma/ImageWithFallback';
import { Layout } from '../Layout';
interface User {
email: string;
name: string;
}
interface MagicItineraryPageProps {
onBackClick: () => void;
onHomeClick: () => void;
onMelbourneClick: () => void;
onPassesClick: () => void;
onCheckoutClick: () => void;
onSignInClick: () => void;
onSignOutClick?: () => void;
onAttractionsClick: () => void;
onBlogsClick: () => void;
onHowItWorksClick: () => void;
onFAQClick: () => void;
onPrivacyPolicyClick: () => void;
onAboutUsClick: () => void;
onProfileClick: () => void;
onCityCardsClick: () => void;
onMagicItineraryClick: () => void;
onPostCardsClick: () => void;
onOffersClick: () => void;
onContactUsClick?: () => void;
onEsimsClick?: () => void;
onHotelDiscountsClick?: () => void;
onCreateItineraryClick: () => void;
onViewItineraryClick: () => void;
currentPage: string;
user: User | null;
}
export function MagicItineraryPage({
onBackClick,
onHomeClick,
onMelbourneClick,
onPassesClick,
onCheckoutClick,
onSignInClick,
onSignOutClick,
onAttractionsClick,
onBlogsClick,
onHowItWorksClick,
onFAQClick,
onPrivacyPolicyClick,
onAboutUsClick,
onProfileClick,
onCityCardsClick,
onMagicItineraryClick,
onPostCardsClick,
onOffersClick,
onContactUsClick,
onEsimsClick,
onHotelDiscountsClick,
onCreateItineraryClick,
onViewItineraryClick,
currentPage,
user
}: MagicItineraryPageProps) {
return (
<div className="min-h-screen bg-background">
{/* Navbar */}
<Layout
activeCity="Melbourne"
onSignInClick={onSignInClick}
onSignOutClick={onSignOutClick}
user={user}
>
{/* Sub Navbar */}
{/* <SubNavbar
activeTab="magic-itinerary"
onCityCardsClick={onCityCardsClick}
onMagicItineraryClick={onMagicItineraryClick}
onPostCardsClick={onPostCardsClick}
onOffersClick={onOffersClick}
onEsimsClick={onEsimsClick}
onHotelDiscountsClick={onHotelDiscountsClick}
/> */}
{/* Hero Section */}
<section className="relative pt-52 pb-20 overflow-hidden">
{/* Background gradient */}
<div className="absolute inset-0 bg-gradient-to-br from-primary/5 via-secondary/5 to-background"></div>
<div className="container mx-auto px-4 relative z-10">
<motion.div
className="max-w-4xl mx-auto text-center"
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6 }}
>
<h1 className="font-merchant text-4xl md:text-5xl lg:text-6xl leading-tight mb-6">
<span className="font-light">Plan Your Perfect</span>{' '}
<span className="font-bold italic bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent">
Melbourne Adventure
</span>
</h1>
<p className="font-poppins text-xl leading-relaxed font-normal text-gray-600 mb-8 max-w-2xl mx-auto">
Let our AI create a personalized itinerary just for you. Answer a few questions about your preferences,
and we'll craft the perfect Melbourne experience tailored to your interests.
</p>
<Button
onClick={onCreateItineraryClick}
className="bg-primary hover:bg-primary/90 text-white px-8 py-6 font-poppins font-semibold text-lg"
>
Create My Magic Itinerary
</Button>
</motion.div>
</div>
{/* Decorative elements */}
<div className="absolute top-20 left-10 w-20 h-20 bg-primary/10 rounded-full blur-xl"></div>
<div className="absolute bottom-20 right-10 w-32 h-32 bg-secondary/10 rounded-full blur-xl"></div>
</section>
{/* How It Works Section */}
<HowItWorks />
{/* Features Section */}
<section className="py-16 bg-gradient-to-br from-gray-50 to-gray-100">
<div className="container mx-auto px-4">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-12 items-center">
{/* Left side - Features */}
<motion.div
initial={{ opacity: 0, x: -30 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.6, delay: 0.2 }}
>
<h2 className="font-merchant text-3xl mb-6">
<span className="text-gray-900">Smart Features for</span><br />
<span className="bg-gradient-to-r from-purple-600 to-pink-600 bg-clip-text text-transparent">Smart Travelers</span>
</h2>
<div className="space-y-6">
{[
{
icon: <Sparkles className="w-6 h-6 text-purple-600" />,
title: 'AI-Powered Recommendations',
description: 'Our advanced AI analyzes your preferences to suggest the perfect experiences'
},
{
icon: <Clock className="w-6 h-6 text-pink-600" />,
title: 'Optimized Scheduling',
description: 'Smart timing that considers opening hours, travel time, and crowd patterns'
},
{
icon: <MapPin className="w-6 h-6 text-purple-600" />,
title: 'Location-Based Planning',
description: 'Efficiently planned routes that minimize travel time and maximize experiences'
},
{
icon: <Camera className="w-6 h-6 text-pink-600" />,
title: 'Instagram-Worthy Spots',
description: 'Discover hidden gems and perfect photo opportunities along your journey'
}
].map((feature, index) => (
<motion.div
key={index}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.3 + index * 0.1 }}
className="flex items-start space-x-4"
>
<div className="flex-shrink-0 w-12 h-12 bg-white rounded-lg flex items-center justify-center shadow-md">
{feature.icon}
</div>
<div>
<h3 className="font-merchant text-lg mb-2">{feature.title}</h3>
<p className="text-gray-600 font-poppins">{feature.description}</p>
</div>
</motion.div>
))}
</div>
</motion.div>
{/* Right side - Visual */}
<motion.div
initial={{ opacity: 0, x: 30 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.6, delay: 0.4 }}
className="relative"
>
<div className="bg-gradient-to-br from-purple-500 to-pink-500 rounded-2xl p-8 text-white">
<h3 className="font-merchant text-2xl mb-6">Sample Itinerary</h3>
<div className="space-y-4">
{[
{ time: '9:00 AM', activity: 'Coffee at Degraves Street', duration: '30 min' },
{ time: '10:00 AM', activity: 'Royal Botanic Gardens', duration: '2 hours' },
{ time: '1:00 PM', activity: 'Lunch at Queen Victoria Market', duration: '1 hour' },
{ time: '3:00 PM', activity: 'Street Art Tour in Hosier Lane', duration: '1.5 hours' },
{ time: '6:00 PM', activity: 'Sunset at Eureka Skydeck', duration: '1 hour' }
].map((item, index) => (
<div key={index} className="flex items-center space-x-4 bg-white/10 rounded-lg p-3">
<div className="text-sm font-bold bg-white/20 px-2 py-1 rounded">
{item.time}
</div>
<div className="flex-1">
<div className="font-medium">{item.activity}</div>
<div className="text-xs text-white/80">{item.duration}</div>
</div>
</div>
))}
</div>
</div>
</motion.div>
</div>
</div>
</section>
{/* Benefits Section */}
<section className="py-16">
<div className="container mx-auto px-4">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.2 }}
className="text-center mb-12"
>
<h2 className="font-merchant text-3xl mb-4">Why Use Magic Itinerary?</h2>
<p className="text-gray-600 font-poppins max-w-2xl mx-auto">
Save time, discover more, and create unforgettable memories with personalized planning
</p>
</motion.div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
{[
{
icon: <Clock className="w-12 h-12 text-purple-600" />,
title: 'Save Planning Time',
description: 'Skip hours of research. Get a complete itinerary in under 5 minutes.',
stat: '90% faster than manual planning'
},
{
icon: <Star className="w-12 h-12 text-pink-600" />,
title: 'Discover Hidden Gems',
description: 'Find unique experiences and local favorites you might have missed.',
stat: '50+ curated hidden spots'
},
{
icon: <Heart className="w-12 h-12 text-purple-600" />,
title: 'Personalized Experience',
description: 'Every itinerary is unique, tailored specifically to your preferences.',
stat: '1000+ possible combinations'
}
].map((benefit, index) => (
<motion.div
key={index}
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.3 + index * 0.1 }}
>
<Card className="h-full text-center hover:shadow-lg transition-shadow duration-300">
<CardContent className="p-8">
<div className="mb-4">
{benefit.icon}
</div>
<h3 className="font-merchant text-xl mb-3">{benefit.title}</h3>
<p className="text-gray-600 font-poppins mb-4">{benefit.description}</p>
<Badge className="bg-gradient-to-r from-purple-100 to-pink-100 text-purple-700 border-none">
{benefit.stat}
</Badge>
</CardContent>
</Card>
</motion.div>
))}
</div>
</div>
</section>
{/* What's Included Section */}
<section className="py-16 bg-gradient-to-br from-purple-50 to-pink-50">
<div className="container mx-auto px-4">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.2 }}
className="text-center mb-12"
>
<h2 className="font-merchant text-3xl mb-4">What's Included</h2>
<p className="text-gray-600 font-poppins max-w-2xl mx-auto">
Your Magic Itinerary comes with everything you need for an amazing Melbourne experience
</p>
</motion.div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
{[
{
title: 'Detailed Timeline',
description: 'Hour-by-hour schedule with optimal timing'
},
{
title: 'Transportation Tips',
description: 'Best routes and transport options between locations'
},
{
title: 'Local Recommendations',
description: 'Insider tips on food, shopping, and experiences'
},
{
title: 'Budget Planning',
description: 'Estimated costs and money-saving suggestions'
},
{
title: 'Weather Backup Plans',
description: 'Alternative indoor activities for rainy days'
},
{
title: 'Photo Opportunities',
description: 'Best spots and times for Instagram-worthy shots'
},
{
title: 'Cultural Insights',
description: 'Local history and interesting facts about each location'
},
{
title: 'Real-time Updates',
description: 'Live information on closures, events, and crowds'
}
].map((item, index) => (
<motion.div
key={index}
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.3 + index * 0.05 }}
>
<Card className="h-full hover:shadow-md transition-shadow duration-300">
<CardContent className="p-6 text-center">
<h3 className="font-merchant text-lg mb-2">{item.title}</h3>
<p className="text-gray-600 font-poppins text-sm">{item.description}</p>
</CardContent>
</Card>
</motion.div>
))}
</div>
</div>
</section>
{/* CTA Section */}
{/* Mobile App Section */}
<MobileAppSection />
{/* Customer Reviews */}
<EnhancedTestimonials />
</Layout>
</div>
);
}

View File

@@ -2,156 +2,37 @@ import { useState } from 'react';
import { ChevronLeft, ChevronRight, Clock, Users, Star, Zap, CheckCircle, MapPin, Volume2, Camera, Coffee, Palette, Eye } from 'lucide-react'; import { ChevronLeft, ChevronRight, Clock, Users, Star, Zap, CheckCircle, MapPin, Volume2, Camera, Coffee, Palette, Eye } from 'lucide-react';
import { ImageWithFallback } from './figma/ImageWithFallback'; import { ImageWithFallback } from './figma/ImageWithFallback';
import { motion } from 'motion/react'; import { motion } from 'motion/react';
import { useNavigate } from 'react-router-dom';
const melbourneAttractions = [ import { useGetAttractionsForHomePageQuery } from '../Redux/services/attractions.service';
{
id: 1,
name: "Royal Botanic Gardens",
city: "Melbourne",
country: "Australia",
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",
rating: 4.8,
reviews: "15,600+",
category: "Gardens",
originalPrice: "Free",
includedValue: "$25",
perks: [
{ icon: Volume2, label: "Audio garden tour", color: "text-green-600" },
{ icon: MapPin, label: "Garden maps", color: "text-blue-600" },
{ icon: Camera, label: "Photo spots guide", color: "text-purple-600" }
]
},
{
id: 2,
name: "Federation Square",
city: "Melbourne",
country: "Australia",
image: "https://images.unsplash.com/photo-1639655001512-e4b58d4874b8?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxtZWxib3VybmUlMjBmZWRlcmF0aW9uJTIwc3F1YXJlfGVufDF8fHx8MTc1NzMzNzc5Mnww&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral",
rating: 4.6,
reviews: "22,400+",
category: "Landmarks",
originalPrice: "Free",
includedValue: "$35",
perks: [
{ icon: Volume2, label: "Cultural tours", color: "text-orange-600" },
{ icon: Eye, label: "Gallery access", color: "text-blue-600" },
{ icon: Users, label: "Event access", color: "text-purple-600" }
]
},
{
id: 3,
name: "Queen Victoria Market",
city: "Melbourne",
country: "Australia",
image: "https://images.unsplash.com/photo-1676454953709-e0be46f62490?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxtZWxib3VybmUlMjBxdWVlbiUyMHZpY3RvcmlhJTIwbWFya2V0fGVufDF8fHx8MTc1NzMzNzc5Nnww&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral",
rating: 4.7,
reviews: "18,200+",
category: "Markets",
originalPrice: "$45",
includedValue: "$45",
perks: [
{ icon: Users, label: "Food tours", color: "text-orange-600" },
{ icon: Coffee, label: "Tastings", color: "text-brown-600" },
{ icon: Volume2, label: "History guide", color: "text-blue-600" }
]
},
{
id: 4,
name: "Eureka Skydeck",
city: "Melbourne",
country: "Australia",
image: "https://images.unsplash.com/photo-1629677713183-29248e1268d7?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxtZWxib3VybmUlMjBldXJla2ElMjB0b3dlcnxlbnwxfHx8fDE3NTczMzc4MDB8MA&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral",
rating: 4.9,
reviews: "11,800+",
category: "Views",
originalPrice: "$32",
includedValue: "$32",
perks: [
{ icon: Zap, label: "Skip-the-line", color: "text-green-600" },
{ icon: Eye, label: "360° views", color: "text-purple-600" },
{ icon: Camera, label: "Photo experiences", color: "text-blue-600" }
]
},
{
id: 5,
name: "St Kilda Beach & Pier",
city: "Melbourne",
country: "Australia",
image: "https://images.unsplash.com/photo-1674732954456-159835c0a46b?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxtZWxib3VybmUlMjBzdCUyMGtpbGRhJTIwYmVhY2h8ZW58MXx8fHwxNzU3MzM3ODAzfDA&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral",
rating: 4.5,
reviews: "14,300+",
category: "Beach",
originalPrice: "Free",
includedValue: "$20",
perks: [
{ icon: Users, label: "Penguin tours", color: "text-blue-600" },
{ icon: MapPin, label: "Beach activities", color: "text-green-600" },
{ icon: Camera, label: "Sunset spots", color: "text-purple-600" }
]
},
{
id: 6,
name: "Melbourne Laneways",
city: "Melbourne",
country: "Australia",
image: "https://images.unsplash.com/photo-1705120624704-0970afc29fea?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxtZWxib3VybmUlMjBsYW5ld2F5cyUyMHN0cmVldCUyMGFydHxlbnwxfHx8fDE3NTczMzc4MDd8MA&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral",
rating: 4.8,
reviews: "19,500+",
category: "Street Art",
originalPrice: "$55",
includedValue: "$55",
perks: [
{ icon: Palette, label: "Art tours", color: "text-pink-600" },
{ icon: Coffee, label: "Café stops", color: "text-brown-600" },
{ icon: Camera, label: "Photo walks", color: "text-purple-600" }
]
},
{
id: 7,
name: "Melbourne Zoo",
city: "Melbourne",
country: "Australia",
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",
rating: 4.7,
reviews: "13,900+",
category: "Wildlife",
originalPrice: "$42",
includedValue: "$42",
perks: [
{ icon: Zap, label: "Skip-the-line", color: "text-green-600" },
{ icon: Users, label: "Animal encounters", color: "text-orange-600" },
{ icon: Volume2, label: "Keeper talks", color: "text-blue-600" }
]
},
{
id: 8,
name: "Royal Exhibition Building",
city: "Melbourne",
country: "Australia",
image: "https://images.unsplash.com/photo-1720523794299-c3b445d71a51?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxtZWxib3VybmUlMjByb3lhbCUyMGV4aGliaXRpb24lMjBidWlsZGluZ3xlbnwxfHx8fDE3NTczMzc4MTR8MA&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral",
rating: 4.6,
reviews: "8,700+",
category: "Heritage",
originalPrice: "$25",
includedValue: "$25",
perks: [
{ icon: Volume2, label: "Audio tours", color: "text-blue-600" },
{ icon: Eye, label: "Exhibitions", color: "text-purple-600" },
{ icon: MapPin, label: "Heritage walks", color: "text-green-600" }
]
}
];
const categories = ["All", "Landmarks", "Gardens", "Markets", "Views", "Beach", "Street Art", "Wildlife", "Heritage"];
export function MelbourneAttractions() { export function MelbourneAttractions() {
const [activeCategory, setActiveCategory] = useState("All"); const [selectedCategoryId, setSelectedCategoryId] = useState<number | null>(null);
const navigate = useNavigate();
const cityName = localStorage.getItem("cityName");
const cityId = localStorage.getItem("cityId");
const filteredAttractions = activeCategory === "All" const { data: homePageAttractionsData } = useGetAttractionsForHomePageQuery({ cityId });
? melbourneAttractions
: melbourneAttractions.filter(attraction => attraction.category === activeCategory);
const AttractionCard = ({ attraction, index }: { attraction: typeof melbourneAttractions[0], index: number }) => ( const apiAttractions = homePageAttractionsData?.attractions || [];
const apiCategories = homePageAttractionsData?.categories || [];
// Filter attractions by selected category
const filteredAttractions = selectedCategoryId === null
? apiAttractions
: apiAttractions.filter((attraction: any) =>
attraction.categories?.some((cat: any) => cat.id === selectedCategoryId)
);
const AttractionCard = ({ attraction, index }: { attraction: any; index: number }) => {
// Get cover image or first image from galleries
const coverImage = attraction.galleries?.find((g: any) => g.isCoverImage)?.filePathUrl
|| attraction.galleries?.[0]?.filePathUrl
|| '';
// Filter only inclusions (isInclusion: true)
const inclusions = attraction.inclusions?.filter((inc: any) => inc.isInclusion) || [];
return (
<motion.div <motion.div
initial={{ opacity: 0, y: 30 }} initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
@@ -159,86 +40,102 @@ export function MelbourneAttractions() {
viewport={{ once: true }} viewport={{ once: true }}
className="group cursor-pointer flex-shrink-0 w-[280px] md:w-auto md:flex-shrink h-96 flip-card-container" className="group cursor-pointer flex-shrink-0 w-[280px] md:w-auto md:flex-shrink h-96 flip-card-container"
> >
{/* 3D Flip Container */}
<div className="flip-card-inner group-hover:[transform:rotateY(180deg)] relative w-full h-full"> <div className="flip-card-inner group-hover:[transform:rotateY(180deg)] relative w-full h-full">
{/* FRONT FACE */} {/* FRONT FACE */}
<div className="flip-card-face absolute inset-0 w-full h-full rounded-2xl overflow-hidden shadow-lg"> <div className="flip-card-face absolute inset-0 w-full h-full rounded-2xl overflow-hidden shadow-lg">
{/* Background Image */}
<ImageWithFallback <ImageWithFallback
src={attraction.image} src={coverImage}
alt={attraction.name} alt={attraction.title}
className="w-full h-full object-cover" className="w-full h-full object-cover"
/> />
{/* Rating Badge */}
{/* <div className="absolute top-4 right-4 bg-white/95 backdrop-blur-sm rounded-full px-3 py-1.5 flex items-center gap-1 shadow-lg z-10">
<div className="w-4 h-4 bg-gradient-to-r from-yellow-400 to-yellow-500 rounded-full flex items-center justify-center">
<span className="text-white text-xs">★</span>
</div>
<span className="text-sm font-medium text-gray-900">{attraction.rating}</span>
</div> */}
{/* Front Content - Clean Title & Location */}
<div className="absolute bottom-0 left-0 right-0"> <div className="absolute bottom-0 left-0 right-0">
<div className="bg-gradient-to-t from-black/80 via-black/50 to-transparent p-6"> <div className="bg-gradient-to-t from-black/80 via-black/50 to-transparent p-6">
<h3 className="font-bold text-xl text-white mb-1">{attraction.name}</h3> <h3 className="font-bold text-xl text-white mb-1">{attraction.title}</h3>
<p className="text-white/90 text-sm"> <p className="text-white/90 text-sm">{attraction.city?.cityName}, Australia</p>
{attraction.city}, {attraction.country}
</p>
</div> </div>
</div> </div>
</div> </div>
{/* BACK FACE */} {/* BACK FACE */}
<div className="flip-card-face flip-card-back absolute inset-0 w-full h-full rounded-2xl overflow-hidden shadow-lg bg-gradient-to-br from-gray-900 to-black"> <div className="flip-card-face flip-card-back absolute inset-0 w-full h-full rounded-2xl overflow-hidden shadow-lg bg-gradient-to-br from-gray-900 to-black">
{/* Back Content Container */}
<div className="relative w-full h-full p-6 flex flex-col justify-center text-white"> <div className="relative w-full h-full p-6 flex flex-col justify-center text-white">
{/* Included Value Section */} {/* Pricing Section */}
<div className="mb-4"> <div className="mb-4">
<div className="inline-flex items-center gap-2 bg-gradient-to-r from-green-500 to-emerald-600 text-white px-3 py-1.5 rounded-full text-sm font-medium mb-3"> <div className="inline-flex items-center gap-2 bg-gradient-to-r from-green-500 to-emerald-600 text-white px-3 py-1.5 rounded-full text-sm font-medium mb-3">
<CheckCircle className="w-4 h-4" /> <CheckCircle className="w-4 h-4" />
<span>Included Value</span> <span>Included Value</span>
</div> </div>
<div className="text-2xl font-bold mb-1">{attraction.includedValue}</div> <div className="text-2xl font-bold mb-1">
${attraction.ticketPriceAdult}
{attraction.ticketPriceChild && (
<span className="text-sm font-normal text-white/70 ml-2">
/ Child ${attraction.ticketPriceChild}
</span>
)}
</div>
<p className="text-white/80 text-sm"> <p className="text-white/80 text-sm">
{attraction.originalPrice === "Free" {attraction.isBookingRequired ? 'Booking required' : 'No booking required'}
? "Premium access included"
: "Save money with CityCard"}
</p> </p>
</div> </div>
{/* What's Included List */} {/* Inclusions List */}
{inclusions.length > 0 && (
<div className="mb-4"> <div className="mb-4">
<h4 className="font-semibold text-sm mb-3">What's Included:</h4> <h4 className="font-semibold text-sm mb-3">What's Included:</h4>
<div className="space-y-2"> <div className="space-y-2">
{attraction.perks.slice(0, 3).map((perk, perkIndex) => ( {inclusions.slice(0, 3).map((inc: any) => (
<div key={perkIndex} className="flex items-center gap-3 text-white/90"> <div key={inc.id} className="flex items-center gap-3 text-white/90">
<div className="w-6 h-6 rounded-full bg-white/20 backdrop-blur-sm flex items-center justify-center"> <div className="w-6 h-6 rounded-full bg-white/20 backdrop-blur-sm flex items-center justify-center flex-shrink-0">
<perk.icon className="w-3 h-3 text-white" /> <CheckCircle className="w-3 h-3 text-white" />
</div> </div>
<span className="text-sm">{perk.label}</span> <span className="text-sm">{inc.title}</span>
</div> </div>
))} ))}
</div> </div>
</div> </div>
)}
{/* Duration & Meta Info */} {/* Duration & Group Info */}
<div className="mb-4"> <div className="mb-4">
<div className="flex items-center gap-4 text-white/80 text-sm"> <div className="flex items-center gap-4 text-white/80 text-sm">
{attraction.durations && (
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<Clock className="w-4 h-4" /> <Clock className="w-4 h-4" />
<span>2-3 hours</span> <span>{attraction.durations} mins</span>
</div> </div>
)}
{attraction.groupSize && (
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<Users className="w-4 h-4" /> <Users className="w-4 h-4" />
<span>All ages</span> <span>Max {attraction.groupSize}</span>
</div> </div>
)}
{attraction.ageRange && (
<div className="flex items-center gap-1">
<Star className="w-4 h-4" />
<span>{attraction.ageRange}</span>
</div>
)}
</div> </div>
</div> </div>
{/* Footer Features */} {/* Categories */}
{attraction.categories?.length > 0 && (
<div className="flex flex-wrap gap-1 mb-4">
{attraction.categories.slice(0, 2).map((cat: any) => (
<span
key={cat.id}
className="text-xs bg-white/20 text-white/90 px-2 py-0.5 rounded-full"
>
{cat.categoryName}
</span>
))}
</div>
)}
{/* Footer */}
<div className="border-t border-white/20 pt-4"> <div className="border-t border-white/20 pt-4">
<div className="flex items-center justify-between text-white/80 text-xs"> <div className="flex items-center justify-between text-white/80 text-xs">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
@@ -261,10 +158,12 @@ export function MelbourneAttractions() {
</div> </div>
</motion.div> </motion.div>
); );
};
return ( return (
<section className="py-20 bg-gradient-to-br from-gray-50 to-white relative overflow-hidden"> <section className="py-20 bg-gradient-to-br from-gray-50 to-white relative overflow-hidden">
<div className="container mx-auto px-4"> <div className="container mx-auto px-4">
{/* Header */} {/* Header */}
<motion.div <motion.div
initial={{ opacity: 0, y: 30 }} initial={{ opacity: 0, y: 30 }}
@@ -276,19 +175,19 @@ export function MelbourneAttractions() {
<div className="inline-flex items-center gap-2 bg-gradient-to-r from-primary/10 to-secondary/10 px-4 py-2 rounded-full mb-6"> <div className="inline-flex items-center gap-2 bg-gradient-to-r from-primary/10 to-secondary/10 px-4 py-2 rounded-full mb-6">
<div className="w-2 h-2 bg-gradient-to-r from-primary to-secondary rounded-full"></div> <div className="w-2 h-2 bg-gradient-to-r from-primary to-secondary rounded-full"></div>
<span className="text-sm font-medium bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent"> <span className="text-sm font-medium bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent">
Melbourne Must-Sees {cityName} Must-Sees
</span> </span>
</div> </div>
<h2 className="heading-dynamic text-4xl md:text-5xl lg:text-6xl text-gray-900 mb-4"> <h2 className="heading-dynamic text-4xl md:text-5xl lg:text-6xl text-gray-900 mb-4">
<span className="font-light">Discover</span>{' '} <span className="font-light">Discover</span>{' '}
<span className="font-bold bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent italic"> <span className="font-bold bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent italic pr-1">
Melbourne's {cityName}'s
</span>{' '} </span>{' '}
<span className="font-normal">Best</span>{' '} <span className="font-normal">Best</span>{' '}
<span className="font-semibold text-emphasis">Experiences</span> <span className="font-semibold text-emphasis">Experiences</span>
</h2> </h2>
<p className="text-xl text-gray-600 max-w-3xl mx-auto"> <p className="text-xl text-gray-600 max-w-3xl mx-auto">
Discover Melbourne's iconic landmarks, vibrant culture, world-class dining, and hidden gems - all included with your Melbourne CityCard Discover {cityName}'s iconic landmarks, vibrant culture, world-class dining, and hidden gems all included with your {cityName} CityCard
</p> </p>
</motion.div> </motion.div>
@@ -300,23 +199,41 @@ export function MelbourneAttractions() {
viewport={{ once: true }} viewport={{ once: true }}
className="flex flex-wrap justify-center gap-3 mb-12" className="flex flex-wrap justify-center gap-3 mb-12"
> >
{categories.map((category, index) => ( {/* "All" button */}
<motion.button <motion.button
key={category}
initial={{ opacity: 0, scale: 0.8 }} initial={{ opacity: 0, scale: 0.8 }}
whileInView={{ opacity: 1, scale: 1 }} whileInView={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.3, delay: index * 0.05 }}
viewport={{ once: true }} viewport={{ once: true }}
onClick={() => setActiveCategory(category)} onClick={() => setSelectedCategoryId(null)}
whileHover={{ scale: 1.05 }} whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }} whileTap={{ scale: 0.95 }}
className={`px-6 py-4 h-14 rounded-2xl font-medium transition-all duration-300 ${ className={`px-6 py-4 h-14 rounded-2xl font-medium transition-all duration-300 ${
activeCategory === category selectedCategoryId === null
? 'bg-gradient-to-r from-primary to-secondary text-white shadow-xl shadow-primary/25 ring-2 ring-primary/20' ? 'bg-gradient-to-r from-primary to-secondary text-white shadow-xl shadow-primary/25 ring-2 ring-primary/20'
: 'bg-white/80 backdrop-blur-sm text-gray-700 hover:text-gray-900 hover:shadow-lg border border-gray-200/50 hover:border-primary/20 hover:bg-white' : 'bg-white/80 backdrop-blur-sm text-gray-700 hover:text-gray-900 hover:shadow-lg border border-gray-200/50 hover:border-primary/20 hover:bg-white'
}`} }`}
> >
{category} All
</motion.button>
{/* Dynamic category buttons from API */}
{apiCategories.map((category: any, index: number) => (
<motion.button
key={category.id}
initial={{ opacity: 0, scale: 0.8 }}
whileInView={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.3, delay: index * 0.05 }}
viewport={{ once: true }}
onClick={() => setSelectedCategoryId(category.id)}
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
className={`px-6 py-4 h-14 rounded-2xl font-medium transition-all duration-300 ${
selectedCategoryId === category.id
? 'bg-gradient-to-r from-primary to-secondary text-white shadow-xl shadow-primary/25 ring-2 ring-primary/20'
: 'bg-white/80 backdrop-blur-sm text-gray-700 hover:text-gray-900 hover:shadow-lg border border-gray-200/50 hover:border-primary/20 hover:bg-white'
}`}
>
{category.categoryName}
</motion.button> </motion.button>
))} ))}
</motion.div> </motion.div>
@@ -324,52 +241,44 @@ export function MelbourneAttractions() {
{/* Mobile Horizontal Carousel */} {/* Mobile Horizontal Carousel */}
<div className="block md:hidden mb-8"> <div className="block md:hidden mb-8">
<div className="relative"> <div className="relative">
{/* Scroll Container */}
<div className="flex gap-6 overflow-x-auto scrollbar-hide pb-4 px-4 -mx-4"> <div className="flex gap-6 overflow-x-auto scrollbar-hide pb-4 px-4 -mx-4">
{filteredAttractions.map((attraction, index) => ( {filteredAttractions.map((attraction: any, index: number) => (
<AttractionCard key={attraction.id} attraction={attraction} index={index} /> <AttractionCard key={attraction.id} attraction={attraction} index={index} />
))} ))}
</div> </div>
{/* Scroll Indicators */}
<div className="flex justify-center mt-6 gap-2"> <div className="flex justify-center mt-6 gap-2">
{Array.from({ length: Math.ceil(filteredAttractions.length / 2) }).map((_, index) => ( {Array.from({ length: Math.ceil(filteredAttractions.length / 2) }).map((_: any, index: number) => (
<div <div key={index} className="w-2 h-2 rounded-full bg-gray-300" />
key={index}
className="w-2 h-2 rounded-full bg-gray-300"
/>
))} ))}
</div> </div>
{/* Mobile Hint Text */}
<div className="text-center mt-4"> <div className="text-center mt-4">
<p className="text-sm text-gray-500"> <p className="text-sm text-gray-500">Swipe to explore more {cityName} attractions</p>
Swipe to explore more Melbourne attractions
</p>
</div> </div>
</div> </div>
</div> </div>
{/* Desktop Bento Grid */} {/* Desktop Bento Grid */}
<div className="hidden md:block w-full"> <div className="hidden md:block w-full">
{/* Top Row - 3 equal cards */}
<div className="grid grid-cols-3 gap-6"> <div className="grid grid-cols-3 gap-6">
{filteredAttractions.slice(0, 3).map((attraction, index) => ( {filteredAttractions.slice(0, 3).map((attraction: any, index: number) => (
<AttractionCard key={attraction.id} attraction={attraction} index={index} /> <AttractionCard key={attraction.id} attraction={attraction} index={index} />
))} ))}
</div> </div>
{/* Consistent Vertical Spacing */}
<div className="h-6"></div> <div className="h-6"></div>
{/* Bottom Row - 2 larger cards */}
<div className="grid grid-cols-2 gap-6"> <div className="grid grid-cols-2 gap-6">
{filteredAttractions.slice(3, 5).map((attraction, index) => ( {filteredAttractions.slice(3, 5).map((attraction: any, index: number) => (
<AttractionCard key={attraction.id} attraction={attraction} index={index + 3} /> <AttractionCard key={attraction.id} attraction={attraction} index={index + 3} />
))} ))}
</div> </div>
</div> </div>
{/* Empty State */}
{filteredAttractions.length === 0 && (
<div className="text-center py-16 text-gray-500">
<p className="text-lg">No attractions found for this category.</p>
</div>
)}
{/* Call to Action */} {/* Call to Action */}
<motion.div <motion.div
initial={{ opacity: 0, y: 30 }} initial={{ opacity: 0, y: 30 }}
@@ -382,15 +291,15 @@ export function MelbourneAttractions() {
whileHover={{ scale: 1.05, boxShadow: "0 20px 40px rgba(99,102,241,0.3)" }} whileHover={{ scale: 1.05, boxShadow: "0 20px 40px rgba(99,102,241,0.3)" }}
whileTap={{ scale: 0.95 }} whileTap={{ scale: 0.95 }}
className="relative bg-gradient-to-r from-primary to-secondary text-white py-4 px-12 rounded-lg text-lg shadow-xl transition-all duration-300 overflow-hidden group" className="relative bg-gradient-to-r from-primary to-secondary text-white py-4 px-12 rounded-lg text-lg shadow-xl transition-all duration-300 overflow-hidden group"
onClick={() => navigate('/passes')}
> >
<span className="relative z-10">Get Your Melbourne Card</span> <span className="relative z-10">Get Your {cityName} Card</span>
{/* Shine animation */}
<div className="absolute inset-0 opacity-30"> <div className="absolute inset-0 opacity-30">
<div className="h-full bg-gradient-to-r from-transparent via-white to-transparent animate-shine"></div> <div className="h-full bg-gradient-to-r from-transparent via-white to-transparent animate-shine"></div>
</div> </div>
</motion.button> </motion.button>
</motion.div> </motion.div>
</div> </div>
</section> </section>
); );

View File

@@ -2,6 +2,9 @@ import { motion } from 'motion/react';
import { ImageWithFallback } from './figma/ImageWithFallback'; import { ImageWithFallback } from './figma/ImageWithFallback';
import { Calendar, Clock, User, ArrowRight, Coffee, Camera, MapPin, Star } from 'lucide-react'; import { Calendar, Clock, User, ArrowRight, Coffee, Camera, MapPin, Star } from 'lucide-react';
import { Button } from './ui/button'; import { Button } from './ui/button';
import { useRef, useState } from "react";
import { useNavigate } from 'react-router-dom';
import { useGetBlogsForCityQuery } from '../Redux/services/blogs.service';
const blogPosts = [ const blogPosts = [
{ {
@@ -88,12 +91,31 @@ const categories = [
]; ];
export function MelbourneBlogs() { export function MelbourneBlogs() {
const sectionRef = useRef(null);
const navigate = useNavigate();
const cityId = localStorage.getItem('cityId');
const [categoryId, setCategoryId] = useState("");
const { data: blogsData, error, isLoading } = useGetBlogsForCityQuery({ cityId, categoryId });
const featuredPost = blogPosts.find(post => post.featured); const featuredPost = blogPosts.find(post => post.featured);
const regularPosts = blogPosts.filter(post => !post.featured); const regularPosts = blogPosts.filter(post => !post.featured);
const cityName = localStorage.getItem('cityName');
const baseUrl = import.meta.env.VITE_BASE_URL;
const blogss = blogsData?.blogs ?? [];
const categoriess = blogsData?.categories ?? []
const handleCategoryClick = (id: string) => {
// toggle logic: if already selected, reset to empty
setCategoryId(prev => (prev === id ? "" : id));
};
return ( return (
<section className="py-20 bg-gradient-to-br from-gray-50 via-white to-gray-50 relative overflow-hidden"> <section
{/* Background Pattern */} ref={sectionRef}
className="py-20 bg-gradient-to-br from-gray-50 via-white to-gray-50 relative overflow-hidden"
> {/* Background Pattern */}
<div className="absolute inset-0 opacity-[0.02]"> <div className="absolute inset-0 opacity-[0.02]">
<div className="absolute top-0 left-0 w-full h-full bg-gradient-to-br from-primary/20 via-secondary/20 to-primary/20"></div> <div className="absolute top-0 left-0 w-full h-full bg-gradient-to-br from-primary/20 via-secondary/20 to-primary/20"></div>
</div> </div>
@@ -110,21 +132,21 @@ export function MelbourneBlogs() {
<div className="inline-flex items-center gap-2 bg-gradient-to-r from-primary/10 to-secondary/10 px-4 py-2 rounded-full mb-6"> <div className="inline-flex items-center gap-2 bg-gradient-to-r from-primary/10 to-secondary/10 px-4 py-2 rounded-full mb-6">
<div className="w-2 h-2 bg-gradient-to-r from-primary to-secondary rounded-full"></div> <div className="w-2 h-2 bg-gradient-to-r from-primary to-secondary rounded-full"></div>
<span className="text-sm font-medium bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent"> <span className="text-sm font-medium bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent">
Melbourne Stories {cityName} Stories
</span> </span>
</div> </div>
<h2 className="font-merchant text-4xl md:text-5xl lg:text-6xl text-gray-900 mb-6"> <h2 className="font-merchant text-4xl md:text-5xl lg:text-6xl text-gray-900 mb-6">
<span className="font-normal">Melbourne</span>{' '} <span className="font-normal">{cityName}</span>{' '}
<span className="font-bold bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent italic"> <span className="font-bold bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent italic pr-2">
Blogs Blogs
</span> </span>
</h2> </h2>
<p className="text-xl text-gray-600 max-w-4xl mx-auto leading-relaxed"> <p className="text-xl text-gray-600 max-w-4xl mx-auto leading-relaxed">
Dive deep into Melbourne's rich cultural tapestry, from hidden laneway treasures to world-renowned Dive deep into {cityName}'s rich cultural tapestry, from hidden laneway treasures to world-renowned
coffee culture. Discover insider stories, local secrets, and expert guides to Australia's cultural capital coffee culture. Discover insider stories, local secrets, and expert guides to Australia's cultural capital
that will transform your Melbourne experience into an unforgettable journey. that will transform your {cityName} experience into an unforgettable journey.
</p> </p>
</motion.div> </motion.div>
@@ -136,29 +158,42 @@ export function MelbourneBlogs() {
viewport={{ once: true }} viewport={{ once: true }}
className="flex flex-wrap justify-center gap-3 mb-16" className="flex flex-wrap justify-center gap-3 mb-16"
> >
{categories.map((category, index) => (
<motion.button <motion.button
key={category.name} initial={{ opacity: 0, scale: 0.9 }}
whileInView={{ opacity: 1, scale: 1 }}
viewport={{ once: true }}
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
onClick={() => setCategoryId("")}
className={`cursor-pointer px-6 py-3 rounded-full font-medium shadow-lg hover:shadow-xl transition-all duration-300 group
${categoryId === "" ? "bg-gradient-to-r from-primary to-secondary text-white" : "bg-white text-gray-700"}`}
>
<span className="flex items-center gap-2">All</span>
</motion.button>
{categoriess.map((category: any, index) => (
<motion.button
key={category.id}
initial={{ opacity: 0, scale: 0.9 }} initial={{ opacity: 0, scale: 0.9 }}
whileInView={{ opacity: 1, scale: 1 }} whileInView={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.4, delay: 0.2 + index * 0.05 }} transition={{ duration: 0.4, delay: 0.2 + index * 0.05 }}
viewport={{ once: true }} viewport={{ once: true }}
whileHover={{ scale: 1.05 }} whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }} whileTap={{ scale: 0.95 }}
className={`px-6 py-3 rounded-full bg-gradient-to-r ${category.color} text-white font-medium shadow-lg hover:shadow-xl transition-all duration-300 group`} onClick={() => handleCategoryClick(category.id)}
className={`cursor-pointer px-6 py-3 rounded-full font-medium shadow-lg hover:shadow-xl transition-all duration-300 group
${categoryId === category.id ? "bg-gradient-to-r from-primary to-secondary text-white" : "bg-white text-gray-700"}`}
> >
<span className="flex items-center gap-2"> <span className="flex items-center gap-2">
{category.name} {category.categoryName}
<span className="text-xs bg-white/20 px-2 py-1 rounded-full group-hover:bg-white/30 transition-colors duration-200">
{category.count}
</span>
</span> </span>
</motion.button> </motion.button>
))} ))}
</motion.div> </motion.div>
{/* Featured Post */} {/* Featured Post */}
{featuredPost && ( {/* {featuredPost && (
<motion.div <motion.div
initial={{ opacity: 0, y: 30 }} initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
@@ -168,13 +203,13 @@ export function MelbourneBlogs() {
> >
</motion.div> </motion.div>
)} )} */}
{/* Regular Blog Posts Grid */} {/* Regular Blog Posts Grid */}
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8"> <div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
{regularPosts.map((post, index) => ( {blogss && blogss?.map((blog: any, index) => (
<motion.article <motion.article
key={post.id} key={blog.id}
initial={{ opacity: 0, y: 30 }} initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: 0.3 + index * 0.1 }} transition={{ duration: 0.5, delay: 0.3 + index * 0.1 }}
@@ -184,47 +219,52 @@ export function MelbourneBlogs() {
{/* Post Image */} {/* Post Image */}
<div className="relative overflow-hidden h-48"> <div className="relative overflow-hidden h-48">
<ImageWithFallback <ImageWithFallback
src={post.image} src={`${baseUrl}${blog?.coverImage}`}
alt={post.title} alt={blog?.blogTitle}
className="w-full h-full object-cover group-hover:scale-110 transition-transform duration-700" className="w-full h-full object-cover group-hover:scale-110 transition-transform duration-700"
/> />
<div className="absolute inset-0 bg-gradient-to-t from-black/20 via-transparent to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div> <div className="absolute inset-0 bg-gradient-to-t from-black/20 via-transparent to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300"></div>
{/* Category Badge */} {/* Category Badge */}
<div className="absolute top-4 left-4 bg-white/95 backdrop-blur-sm text-gray-900 px-3 py-1 rounded-full text-xs font-medium"> <div className="absolute top-4 left-4 bg-white/95 backdrop-blur-sm text-gray-900 px-3 py-1 rounded-full text-xs font-medium">
{post.category} {blog?.category?.categoryName}
</div> </div>
</div> </div>
{/* Post Content */} {/* Post Content */}
<div className="p-6 flex-1 flex flex-col justify-between"> <div className="p-6 flex-1 flex flex-col justify-between">
<div className="flex items-center gap-3 text-xs text-gray-500 mb-3"> <div className="flex items-center gap-3 text-xs text-gray-500 mb-3">
<div className="flex items-center gap-1"> {/* <div className="flex items-center gap-1">
<User className="w-3 h-3" /> <User className="w-3 h-3" />
{post.author} {blog?.author}
</div> </div> */}
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<Calendar className="w-3 h-3" /> <Calendar className="w-3 h-3" />
{post.date} {blog?.createdAt && new Date(blog.createdAt).toLocaleDateString(
'en-US',
{ month: 'short', day: 'numeric', year: 'numeric' }
)}
</div> </div>
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<Clock className="w-3 h-3" /> <Clock className="w-3 h-3" />
{post.readTime} 5 min read
</div> </div>
</div> </div>
<div className="flex-1 flex flex-col"> <div className="flex-1 flex flex-col">
<h3 className="font-merchant text-xl font-semibold text-gray-900 mb-3 leading-tight group-hover:text-primary transition-colors duration-200 line-clamp-2"> <h3 className="font-merchant text-xl font-semibold text-gray-900 mb-3 leading-tight group-hover:text-primary transition-colors duration-200 line-clamp-2">
{post.title} {blog?.blogTitle}
</h3> </h3>
<p className="text-gray-600 leading-relaxed mb-4 text-sm flex-1 line-clamp-3"> <p
{post.excerpt} className="text-gray-600 leading-relaxed mb-4 text-sm flex-1 line-clamp-3"
</p> dangerouslySetInnerHTML={{ __html: blog?.content }}
/>
{/* Tags */} {/* Tags */}
<div className="flex flex-wrap gap-1 mb-4"> {/* <div className="flex flex-wrap gap-1 mb-4">
{post.tags.slice(0, 2).map((tag, tagIndex) => ( {blog?.tags?.slice(0, 2).map((tag, tagIndex) => (
<span <span
key={tagIndex} key={tagIndex}
className="px-2 py-1 bg-gray-100 text-gray-600 rounded-full text-xs font-medium" className="px-2 py-1 bg-gray-100 text-gray-600 rounded-full text-xs font-medium"
@@ -232,12 +272,12 @@ export function MelbourneBlogs() {
{tag} {tag}
</span> </span>
))} ))}
{post.tags.length > 2 && ( {blog?.tags?.length > 2 && (
<span className="px-2 py-1 bg-gray-100 text-gray-600 rounded-full text-xs"> <span className="px-2 py-1 bg-gray-100 text-gray-600 rounded-full text-xs">
+{post.tags.length - 2} +{blog?.tags?.length - 2}
</span> </span>
)} )}
</div> </div> */}
</div> </div>
<div className="flex items-center justify-between mt-auto"> <div className="flex items-center justify-between mt-auto">
@@ -261,23 +301,30 @@ export function MelbourneBlogs() {
> >
<div className="bg-gradient-to-br from-primary/5 via-secondary/5 to-primary/5 rounded-3xl p-8 md:p-12 border border-gray-100"> <div className="bg-gradient-to-br from-primary/5 via-secondary/5 to-primary/5 rounded-3xl p-8 md:p-12 border border-gray-100">
<h3 className="font-merchant text-2xl md:text-3xl font-semibold text-gray-900 mb-4"> <h3 className="font-merchant text-2xl md:text-3xl font-semibold text-gray-900 mb-4">
Want to explore Melbourne yourself? Want to explore {cityName} yourself?
</h3> </h3>
<p className="text-gray-600 text-lg mb-8 max-w-2xl mx-auto"> <p className="text-gray-600 text-lg mb-8 max-w-2xl mx-auto">
Get your Melbourne CityCard and unlock access to all these incredible experiences and more. Get your {cityName} CityCard and unlock access to all these incredible experiences and more.
Start your adventure today with exclusive deals and insider access. Start your adventure today with exclusive deals and insider access.
</p> </p>
<div className="flex flex-col sm:flex-row gap-4 justify-center"> <div className="flex flex-col sm:flex-row gap-4 justify-center">
<Button <Button
className="bg-gradient-to-r from-primary to-secondary text-white font-semibold px-8 py-4 rounded-2xl hover:scale-105 transition-all duration-300 shadow-lg hover:shadow-xl" className="bg-gradient-to-r from-primary to-secondary text-white font-semibold px-8 py-4 rounded-2xl hover:scale-105 transition-all duration-300 shadow-lg hover:shadow-xl"
onClick={() => { navigate(`/${cityName.toLowerCase()}`), window.scrollTo({ top: 0, behavior: 'smooth' }); }}
> >
<MapPin className="w-5 h-5 mr-2" /> <MapPin className="w-5 h-5 mr-2" />
Explore Melbourne Explore {cityName}
</Button> </Button>
<Button <Button
variant="outline" variant="outline"
className="border-2 border-gray-300 text-gray-700 font-semibold px-8 py-4 rounded-2xl hover:border-primary hover:text-primary hover:scale-105 transition-all duration-300" className="border-2 border-gray-300 text-gray-700 font-semibold px-8 py-4 rounded-2xl hover:border-primary hover:text-primary hover:scale-105 transition-all duration-300"
onClick={() => {
sectionRef.current?.scrollIntoView({
behavior: "smooth",
block: "start",
});
}}
> >
<Coffee className="w-5 h-5 mr-2" /> <Coffee className="w-5 h-5 mr-2" />
View All Blogs View All Blogs

View File

@@ -1,54 +1,54 @@
import { useState } from 'react'; import { useEffect, useState } from 'react';
import { Check, X, Star, Users, MapPin, Calendar, Clock, Zap, Eye } from 'lucide-react'; import { Check, X, Star, Users, MapPin, Calendar, Clock, Zap, Eye } from 'lucide-react';
import { Button } from './ui/button'; import { Button } from './ui/button';
import { motion } from 'motion/react'; import { motion } from 'motion/react';
import { useNavigate } from 'react-router';
const cardOptions = [ // {
{ // id: 'selective',
id: 'selective', // name: 'Flexi Card',
name: 'Flexi Card', // subtitle: 'Pick 5-10 things to do from a choice of 102 attractions tours and activities',
subtitle: 'Pick 5-10 things to do from a choice of 102 attractions tours and activities', // priceRange: '$89-159',
priceRange: '$89-159', // duration: '3-7 days',
duration: '3-7 days', // popular: false,
popular: false, // color: 'from-blue-500 to-cyan-500',
color: 'from-blue-500 to-cyan-500', // features: {
features: { // passCategory: 'Selective Card',
passCategory: 'Selective Card', // accessToAttractions: true,
accessToAttractions: true, // entryToAttractions: true,
entryToAttractions: true, // accessToExperiences: true,
accessToExperiences: true, // entryToSites: true,
entryToSites: true, // accessToVenues: false,
accessToVenues: false, // entryToEvents: 'Pass Category',
entryToEvents: 'Pass Category', // accessToLocations: 'Pass Category',
accessToLocations: 'Pass Category', // entryToActivities: true,
entryToActivities: true, // accessToExhibits: true,
accessToExhibits: true, // entryToActivitiesSecond: true
entryToActivitiesSecond: true // }
} // },
}, // {
{ // id: 'unlimited',
id: 'unlimited', // name: 'Melbourne Unlimited Card',
name: 'Melbourne Unlimited Card', // subtitle: 'Pick 5-30 things to do from a choice of 102 attractions tours and activities',
subtitle: 'Pick 5-30 things to do from a choice of 102 attractions tours and activities', // priceRange: '$159-299',
priceRange: '$159-299', // duration: '3-7 days',
duration: '3-7 days', // popular: true,
popular: true, // color: 'from-purple-500 to-pink-500',
color: 'from-purple-500 to-pink-500', // features: {
features: { // passCategory: 'Pass Category',
passCategory: 'Pass Category', // accessToAttractions: true,
accessToAttractions: true, // entryToAttractions: true,
entryToAttractions: true, // accessToExperiences: true,
accessToExperiences: true, // entryToSites: true,
entryToSites: true, // accessToVenues: true,
accessToVenues: true, // entryToEvents: 'Pass Category',
entryToEvents: 'Pass Category', // accessToLocations: 'Pass Category',
accessToLocations: 'Pass Category', // entryToActivities: true,
entryToActivities: true, // accessToExhibits: true,
accessToExhibits: true, // entryToActivitiesSecond: true
entryToActivitiesSecond: true // }
} // }
} // ];
];
const features = [ const features = [
{ key: 'passCategory', label: 'Pass Category', icon: Star }, { key: 'passCategory', label: 'Pass Category', icon: Star },
@@ -71,10 +71,61 @@ const FeatureIcon = ({ feature }: { feature: typeof features[0] }) => {
interface MelbourneCardComparisonProps { interface MelbourneCardComparisonProps {
onCheckoutClick?: () => void; onCheckoutClick?: () => void;
cards: any[]
} }
export function MelbourneCardComparison({ onCheckoutClick }: MelbourneCardComparisonProps) { export function MelbourneCardComparison({ onCheckoutClick, cards }: MelbourneCardComparisonProps) {
const [selectedCard, setSelectedCard] = useState<string>('unlimited'); const [selectedCard, setSelectedCard] = useState<string>('unlimited');
const navigate = useNavigate();
const cityName=localStorage.getItem('cityName');
const cardOptions = [
{
id: cards[0]?.id,
name: cards[0]?.title,
subtitle: cards[0]?.description,
priceRange: `$${cards[0]?.adultPrice}`,
duration: '3-7 days',
popular: false,
color: 'from-blue-500 to-cyan-500',
features: {
passCategory: 'Selective Card',
accessToAttractions: true,
entryToAttractions: true,
accessToExperiences: true,
entryToSites: true,
accessToVenues: false,
entryToEvents: 'Pass Category',
accessToLocations: 'Pass Category',
entryToActivities: true,
accessToExhibits: true,
entryToActivitiesSecond: true
}
},
{
id: cards[1]?.id,
name: cards[1]?.title,
subtitle: cards[1]?.description,
priceRange: `$${cards[1]?.adultPrice}`,
duration: '3-7 days',
popular: true,
color: 'from-purple-500 to-pink-500',
features: {
passCategory: 'Pass Category',
accessToAttractions: true,
entryToAttractions: true,
accessToExperiences: true,
entryToSites: true,
accessToVenues: true,
entryToEvents: 'Pass Category',
accessToLocations: 'Pass Category',
entryToActivities: true,
accessToExhibits: true,
entryToActivitiesSecond: true
}
}
];
const renderFeatureValue = (value: boolean | string, cardId: string) => { const renderFeatureValue = (value: boolean | string, cardId: string) => {
if (typeof value === 'boolean') { if (typeof value === 'boolean') {
@@ -124,16 +175,25 @@ export function MelbourneCardComparison({ onCheckoutClick }: MelbourneCardCompar
</div> </div>
<h2 className="font-merchant text-4xl md:text-5xl lg:text-6xl text-gray-900 mb-6"> <h2 className="font-merchant text-4xl md:text-5xl lg:text-6xl text-gray-900 mb-6">
<span className="font-bold bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent italic"> <span className="font-bold bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent italic pr-2">
Buy Buy
</span>{' '} </span>{' '}
<span className="font-normal">Now</span> <span className="font-normal">Now</span>
</h2> </h2>
<p className="text-xl text-gray-600 max-w-4xl mx-auto leading-relaxed"> <p className="text-xl text-gray-600 max-w-4xl mx-auto leading-relaxed">
{cityName === 'Melbourne' && (
<span>
Melbourne is a must-visit cultural epicenter, and this spectacular trip unlocks Melbourne is a must-visit cultural epicenter, and this spectacular trip unlocks
your access around the city in one easy. Save over the cost of visiting Melbourne's your access around the city in one easy. Save over the cost of visiting Melbourne's
landmarks, have lunch at Phi Phi Leh, snorkel at Bamboo Island, and visit Monkey Beach. landmarks, have lunch at Phi Phi Leh, snorkel at Bamboo Island, and visit Monkey Beach.
</span>
)}
{cityName === 'Sydney' && (
<span>
Sydney is a dazzling harbor city that blends iconic landmarks with vibrant coastal escapes. This unforgettable trip gives you seamless access across Sydney in one easy pass. Save on the cost of visiting Sydneys worldfamous attractions, cruise past the Sydney Opera House and Harbour Bridge, relax on Bondi Beach, snorkel at Manly, and explore the wildlife at Taronga Zoo.
</span>
)}
</p> </p>
</motion.div> </motion.div>
@@ -151,8 +211,8 @@ export function MelbourneCardComparison({ onCheckoutClick }: MelbourneCardCompar
<div className="font-poppins text-xl font-semibold text-gray-900"> <div className="font-poppins text-xl font-semibold text-gray-900">
Features Features
</div> </div>
{cardOptions.map((card) => ( {cardOptions.map((card,index) => (
<div key={card.id} className="text-center"> <div key={card.id ?? index} className="text-center">
<div className="mb-2"> <div className="mb-2">
<div className="font-poppins font-semibold text-2xl" style={{ color: '#F95F62' }}>{card.name}</div> <div className="font-poppins font-semibold text-2xl" style={{ color: '#F95F62' }}>{card.name}</div>
</div> </div>
@@ -180,8 +240,8 @@ export function MelbourneCardComparison({ onCheckoutClick }: MelbourneCardCompar
<span className="font-medium text-gray-900">{feature.label}</span> <span className="font-medium text-gray-900">{feature.label}</span>
</div> </div>
{cardOptions.map((card) => ( {cardOptions.map((card, index) => (
<div key={card.id} className="text-center"> <div key={card.id ?? index} className="text-center">
{renderFeatureValue(card.features[feature.key as keyof typeof card.features], card.id)} {renderFeatureValue(card.features[feature.key as keyof typeof card.features], card.id)}
</div> </div>
))} ))}
@@ -197,17 +257,17 @@ export function MelbourneCardComparison({ onCheckoutClick }: MelbourneCardCompar
<div className="text-xs text-gray-500">Compare features above</div> <div className="text-xs text-gray-500">Compare features above</div>
</div> </div>
{cardOptions.map((card) => ( {cardOptions.map((card,index) => (
<motion.div key={card.id} className="text-center"> <motion.div key={card.id ?? index} className="text-center">
<div className="mb-4"> <div className="mb-4">
<div className="text-3xl font-bold text-gray-900">{card.priceRange}</div> <div className="text-3xl font-bold text-gray-900">{card.priceRange}</div>
<div className="text-sm text-gray-600">{card.duration}</div> <div className="text-sm text-gray-600">{card.duration}</div>
</div> </div>
<Button <Button
withShine={true} withShine={true}
className="w-full h-14 rounded-2xl text-white font-semibold text-lg hover:scale-105 transition-all duration-300 shadow-lg hover:shadow-xl" className="w-full h-14 rounded-2xl text-white font-semibold text-lg hover:scale-105 transition-all duration-300 shadow-lg hover:shadow-xl cursor-pointer"
style={{ backgroundColor: '#F95F62' }} style={{ backgroundColor: '#F95F62' }}
onClick={onCheckoutClick} onClick={()=>navigate("/passes")}
> >
Buy {card.name} Buy {card.name}
</Button> </Button>

View File

@@ -18,59 +18,61 @@ import {
AccordionTrigger, AccordionTrigger,
} from "./ui/accordion"; } from "./ui/accordion";
const cityName = localStorage.getItem('cityName') || 'the city';
const faqData = [ const faqData = [
{ // {
id: "refund", // id: "refund",
question: "Can I get a refund on my Melbourne CityCard?", // question: "Can I get a refund on my Melbourne CityCard?",
answer: "Yes, you can cancel your Melbourne CityCard and receive a full refund if you cancel at least 24 hours in advance of your selected start date. For cancellations within 24 hours, refunds are subject to our cancellation policy. Digital cards can be refunded instantly through your account.", // answer: "Yes, you can cancel your Melbourne CityCard and receive a full refund if you cancel at least 24 hours in advance of your selected start date. For cancellations within 24 hours, refunds are subject to our cancellation policy. Digital cards can be refunded instantly through your account.",
icon: CreditCard // icon: CreditCard
}, // },
{ {
id: "duration", id: "duration",
question: "How long is my Melbourne CityCard valid?", question: `How long is my ${cityName} CityCard valid?`,
answer: "Melbourne CityCards are available in 1, 2, 3, and 5-day options. Your card activates on the first attraction you visit and is valid for consecutive days only. The 5-day Melbourne Unlimited Card provides the best value for extended stays with access to over 40 premium attractions.", answer: `${cityName} CityCards are available in 1, 2, 3, and 5-day options. Your card activates on the first attraction you visit and is valid for consecutive days only. The 5-day ${cityName} Unlimited Card provides the best value for extended stays with access to over 40 premium attractions.`,
icon: Calendar icon: Calendar
}, },
{ {
id: "transportation", id: "transportation",
question: "Does the Melbourne CityCard include public transport?", question: `Does the ${cityName} CityCard include public transport?`,
answer: "The Melbourne Unlimited Card includes a complimentary Myki card loaded with travel credit for trams, trains, and buses within Melbourne's CBD and inner suburbs. The Selective Card focuses on attractions only, but we provide detailed transport guides for each venue.", answer: `The ${cityName} Unlimited Card includes a complimentary Myki card loaded with travel credit for trams, trains, and buses within ${cityName}'s CBD and inner suburbs. The Selective Card focuses on attractions only, but we provide detailed transport guides for each venue.`,
icon: Train icon: Train
}, },
{ {
id: "attractions", id: "attractions",
question: "What are the must-visit attractions included with my card?", question: `What are the must-visit attractions included with my ${cityName} CityCard?`,
answer: "Your Melbourne CityCard includes iconic experiences like Eureka Tower SkyDeck, Royal Botanic Gardens tours, Melbourne Zoo, SEA LIFE Melbourne Aquarium, and Melbourne Star observation wheel. Plus unique local experiences like laneways art tours, coffee culture walks, and rooftop dining discounts.", answer: `Your ${cityName} CityCard includes iconic experiences like Eureka Tower SkyDeck, Royal Botanic Gardens tours, ${cityName} Zoo, SEA LIFE ${cityName} Aquarium, and ${cityName} Star observation wheel. Plus unique local experiences like laneways art tours, coffee culture walks, and rooftop dining discounts.`,
icon: Camera icon: Camera
}, },
{ {
id: "best-time", id: "best-time",
question: "When is the best time to visit Melbourne?", question: `When is the best time to visit ${cityName}?`,
answer: "Melbourne is fantastic year-round! Spring (Sep-Nov) offers perfect weather and blooming gardens. Summer (Dec-Feb) brings outdoor festivals and rooftop season. Autumn (Mar-May) showcases beautiful foliage and harvest events. Winter (Jun-Aug) is ideal for cozy cafes, indoor attractions, and cultural experiences.", answer: `${cityName} is fantastic year-round! Spring (Sep-Nov) offers perfect weather and blooming gardens. Summer (Dec-Feb) brings outdoor festivals and rooftop season. Autumn (Mar-May) showcases beautiful foliage and harvest events. Winter (Jun-Aug) is ideal for cozy cafes, indoor attractions, and cultural experiences.`,
icon: Clock icon: Clock
}, },
{ {
id: "coffee-culture", id: "coffee-culture",
question: "How can I experience Melbourne's famous coffee culture?", question: `How can I experience ${cityName}'s famous coffee culture?`,
answer: "Your Melbourne CityCard includes guided coffee tours through famous laneways, visits to historic coffee roasters, and discounts at award-winning cafes. We've partnered with local baristas to offer exclusive tastings and behind-the-scenes experiences at Melbourne's most beloved coffee institutions.", answer: `Your ${cityName} CityCard includes guided coffee tours through famous laneways, visits to historic coffee roasters, and discounts at award-winning cafes. We've partnered with local baristas to offer exclusive tastings and behind-the-scenes experiences at ${cityName}'s most beloved coffee institutions.`,
icon: Coffee icon: Coffee
}, },
{ {
id: "group-bookings", id: "group-bookings",
question: "Do you offer group discounts for families or friends?", question: `Do you offer group discounts for families or friends?`,
answer: "Yes! Groups of 4+ receive automatic discounts, and families with children under 16 get special pricing. School groups and corporate bookings receive additional benefits. Contact our Melbourne team for custom packages that can include private tours and exclusive venue access.", answer: `Yes! Groups of 4+ receive automatic discounts, and families with children under 16 get special pricing. School groups and corporate bookings receive additional benefits. Contact our ${cityName} team for custom packages that can include private tours and exclusive venue access.`,
icon: Users icon: Users
}, },
{ {
id: "mobile-app", id: "mobile-app",
question: "Do I need the mobile app to use my Melbourne CityCard?", question: `Do I need the mobile app to use my ${cityName} CityCard?`,
answer: "While not required, our mobile app enhances your Melbourne experience with interactive maps, real-time attraction wait times, insider tips from locals, and the ability to skip lines at participating venues. Download it for offline access to your itinerary and exclusive app-only deals.", answer: `While not required, our mobile app enhances your ${cityName} experience with interactive maps, real-time attraction wait times, insider tips from locals, and the ability to skip lines at participating venues. Download it for offline access to your itinerary and exclusive app-only deals.`,
icon: Smartphone icon: Smartphone
}, },
{ {
id: "neighborhoods", id: "neighborhoods",
question: "Which Melbourne neighborhoods should I explore?", question: `Which ${cityName} neighborhoods should I explore?`,
answer: "Your CityCard provides access to experiences across Melbourne's diverse neighborhoods: Fitzroy for street art and vintage shopping, St. Kilda for beaches and nightlife, Southbank for dining and culture, CBD for iconic attractions, and Richmond for authentic Vietnamese food and shopping.", answer: `Your CityCard provides access to experiences across ${cityName}'s diverse neighborhoods: Fitzroy for street art and vintage shopping, St. Kilda for beaches and nightlife, Southbank for dining and culture, CBD for iconic attractions, and Richmond for authentic Vietnamese food and shopping.`,
icon: MapPin icon: MapPin
} }
]; ];
@@ -95,20 +97,20 @@ export function MelbourneFAQ() {
<div className="inline-flex items-center gap-2 bg-gradient-to-r from-primary/10 to-secondary/10 px-4 py-2 rounded-full mb-6"> <div className="inline-flex items-center gap-2 bg-gradient-to-r from-primary/10 to-secondary/10 px-4 py-2 rounded-full mb-6">
<HelpCircle className="w-4 h-4 text-primary" /> <HelpCircle className="w-4 h-4 text-primary" />
<span className="text-sm font-medium bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent"> <span className="text-sm font-medium bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent">
Melbourne Guide {cityName} Guide
</span> </span>
</div> </div>
<h2 className="font-merchant text-4xl md:text-5xl lg:text-6xl text-gray-900 mb-6"> <h2 className="font-merchant text-4xl md:text-5xl lg:text-6xl text-gray-900 mb-6">
<span className="font-normal">Frequently Asked</span>{' '} <span className="font-normal">Frequently Asked</span>{' '}
<span className="font-bold bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent italic"> <span className="pr-2 font-bold bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent italic">
Questions Questions
</span> </span>
</h2> </h2>
<p className="text-xl text-gray-600 max-w-4xl mx-auto leading-relaxed"> <p className="text-xl text-gray-600 max-w-4xl mx-auto leading-relaxed">
Everything you need to know about exploring Melbourne with your CityCard. From iconic attractions Everything you need to know about exploring {cityName} with your CityCard. From iconic attractions
to hidden local gems, we've got your Melbourne adventure covered. to hidden local gems, we've got your {cityName} adventure covered.
</p> </p>
</motion.div> </motion.div>
@@ -161,7 +163,7 @@ export function MelbourneFAQ() {
</div> </div>
{/* Call to Action */} {/* Call to Action */}
<motion.div {/* <motion.div
initial={{ opacity: 0, y: 30 }} initial={{ opacity: 0, y: 30 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.5 }} transition={{ duration: 0.6, delay: 0.5 }}
@@ -205,7 +207,7 @@ export function MelbourneFAQ() {
</motion.button> </motion.button>
</div> </div>
</div> </div>
</motion.div> </motion.div> */}
</div> </div>
</section> </section>
); );

View File

@@ -36,7 +36,7 @@ export function MelbourneTourOverview() {
} }
]; ];
const tourHighlights = [ const MelbourneTourHighlights = [
{ {
icon: Star, icon: Star,
text: "Experience the cultural capital's vibrant arts scene and hidden laneways", text: "Experience the cultural capital's vibrant arts scene and hidden laneways",
@@ -69,6 +69,43 @@ export function MelbourneTourOverview() {
} }
]; ];
const SydneyTourHighlights = [
{
icon: Star,
text: "Experience Sydneys iconic harbour lifestyle with Opera House and Harbour Bridge views",
color: "text-yellow-600"
},
{
icon: Coffee,
text: "Discover Sydneys vibrant café culture, waterfront dining, and buzzing food markets",
color: "text-amber-600"
},
{
icon: Camera,
text: "Capture panoramic views from Sydney Tower Eye and scenic harbour cruises",
color: "text-purple-600"
},
{
icon: Users,
text: "Enjoy beachside vibes at Bondi and Manly, plus lively nightlife in Darling Harbour",
color: "text-green-600"
},
{
icon: MapPin,
text: "Explore historic charm in The Rocks and coastal walks like Bondi to Coogee",
color: "text-blue-600"
},
{
icon: Calendar,
text: "Access year-round festivals, cultural events, and dynamic harbour celebrations",
color: "text-rose-600"
}
];
const cityName = localStorage.getItem("cityName")
const selectedHighlights = cityName === 'Melbourne' ? MelbourneTourHighlights : SydneyTourHighlights;
return ( return (
<section className="py-20 bg-gradient-to-br from-white via-gray-50/30 to-white relative overflow-hidden"> <section className="py-20 bg-gradient-to-br from-white via-gray-50/30 to-white relative overflow-hidden">
{/* Background Pattern */} {/* Background Pattern */}
@@ -88,8 +125,8 @@ export function MelbourneTourOverview() {
className="mb-16" className="mb-16"
> >
<h2 className="heading-dynamic font-merchant text-4xl md:text-5xl lg:text-6xl text-gray-900 mb-8"> <h2 className="heading-dynamic font-merchant text-4xl md:text-5xl lg:text-6xl text-gray-900 mb-8">
<span className="font-light">Melbourne</span>{' '} <span className="font-light">{cityName}</span>{' '}
<span className="font-bold bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent italic"> <span className="font-bold bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent italic pr-2">
Tour Tour
</span>{' '} </span>{' '}
<span className="font-normal">Overview</span> <span className="font-normal">Overview</span>
@@ -108,11 +145,20 @@ export function MelbourneTourOverview() {
viewport={{ once: true }} viewport={{ once: true }}
> >
<p className="text-lg md:text-xl text-gray-700 leading-relaxed"> <p className="text-lg md:text-xl text-gray-700 leading-relaxed">
{ cityName === 'Melbourne' && (
<span>
Melbourne is a must-visit cultural epicenter, and this spectacular experience unlocks Melbourne is a must-visit cultural epicenter, and this spectacular experience unlocks
your access around the city in one easy pass. Save over the cost of visiting Melbourne's your access around the city in one easy pass. Save over the cost of visiting Melbourne's
landmarks, explore famous laneways and street art, enjoy world-class dining in hidden bars, landmarks, explore famous laneways and street art, enjoy world-class dining in hidden bars,
and immerse yourself in the sports capital's vibrant atmosphere. From Royal Botanic Gardens and immerse yourself in the sports capital's vibrant atmosphere. From Royal Botanic Gardens
to Federation Square, hotel pickup and drop-off all included. to Federation Square, hotel pickup and drop-off all included.
</span>
)}
{ cityName === 'Sydney' && (
<span>
Sydney is a must-visit global destination, blending iconic landmarks with vibrant coastal charm, and this all-in-one experience gives you seamless access across the city. Save more while exploring Sydneys top attractions, wander through historic neighborhoods like The Rocks, admire world-famous sights such as the Sydney Opera House and Harbour Bridge, and relax along stunning beaches like Bondi and Manly. Enjoy diverse dining from waterfront restaurants to hidden cafés, soak in the lively cultural scene, and experience the energy of Australias most dynamic harbor cityall with convenient access to key locations and experiences included.
</span>
)}
</p> </p>
</motion.div> </motion.div>
@@ -226,7 +272,7 @@ export function MelbourneTourOverview() {
</h3> </h3>
<div className="space-y-6"> <div className="space-y-6">
{tourHighlights.map((highlight, index) => ( {selectedHighlights.map((highlight, index) => (
<motion.div <motion.div
key={index} key={index}
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}

View File

@@ -68,7 +68,7 @@ export function MobileAppSection() {
> >
<h1 className="font-merchant text-4xl lg:text-5xl xl:text-6xl leading-tight text-foreground"> <h1 className="font-merchant text-4xl lg:text-5xl xl:text-6xl leading-tight text-foreground">
<span className="font-normal">Access all your</span>{' '} <span className="font-normal">Access all your</span>{' '}
<span className="bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent font-bold italic"> <span className="bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent font-bold italic inline-block pr-2">
city cards city cards
</span> </span>
<br /> <br />

View File

@@ -2,13 +2,11 @@ import { useState, useEffect, useRef, forwardRef } from 'react';
import { Menu, X, ShoppingBag, ChevronDown, Globe, User, Settings, LogOut } from 'lucide-react'; import { Menu, X, ShoppingBag, ChevronDown, Globe, User, Settings, LogOut } from 'lucide-react';
import { motion, AnimatePresence } from 'motion/react'; import { motion, AnimatePresence } from 'motion/react';
import { Link, useLocation, useNavigate } from 'react-router-dom'; import { Link, useLocation, useNavigate } from 'react-router-dom';
import Frame1597884853 from '../imports/Frame1597884853';
import { Button } from './ui/button'; import { Button } from './ui/button';
import { ImageWithFallback } from './figma/ImageWithFallback'; import { ImageWithFallback } from './figma/ImageWithFallback';
import { CTAButton } from './CTAButton'; import { CTAButton } from './CTAButton';
import logoImage from '../assets/cit-logo.png'; import logoImage from '../assets/cit-logo.png';
import melbourneLogo from '../assets/melbourne-logo.png'; import { CitySelectionDialog, slugify } from './CitySelectionDialog';
import { CitySelectionDialog } from './CitySelectionDialog';
import { useAuth } from '../context/AuthContext'; import { useAuth } from '../context/AuthContext';
import { LoginModal } from './LoginModal'; import { LoginModal } from './LoginModal';
@@ -62,15 +60,12 @@ interface NavigationItem {
export default function Navbar({ export default function Navbar({
activeCity, activeCity,
onCityChange, onCityChange,
onSignInClick,
onSignOutClick,
isUserSignedIn = false, isUserSignedIn = false,
// user // user
}: NavbarProps) { }: NavbarProps) {
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false); const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
const [isScrolled, setIsScrolled] = useState(false); const [isScrolled, setIsScrolled] = useState(false);
const [activeLanguageDropdown, setActiveLanguageDropdown] = useState(false); const [activeLanguageDropdown, setActiveLanguageDropdown] = useState(false);
const [activeCartDropdown, setActiveCartDropdown] = useState(false);
const [activeUserDropdown, setActiveUserDropdown] = useState(false); const [activeUserDropdown, setActiveUserDropdown] = useState(false);
const [activeCityDropdown, setActiveCityDropdown] = useState(false); const [activeCityDropdown, setActiveCityDropdown] = useState(false);
const [isCityDialogOpen, setIsCityDialogOpen] = useState(false); const [isCityDialogOpen, setIsCityDialogOpen] = useState(false);
@@ -79,7 +74,6 @@ export default function Navbar({
const [dialogSource, setDialogSource] = useState<'navbar' | 'cta'>('navbar'); const [dialogSource, setDialogSource] = useState<'navbar' | 'cta'>('navbar');
const languageRef = useRef<HTMLDivElement>(null); const languageRef = useRef<HTMLDivElement>(null);
const cartRef = useRef<HTMLDivElement>(null);
const userRef = useRef<HTMLDivElement>(null); const userRef = useRef<HTMLDivElement>(null);
const cityRef = useRef<HTMLDivElement>(null); const cityRef = useRef<HTMLDivElement>(null);
@@ -89,7 +83,16 @@ export default function Navbar({
const [lastKnownCity, setLastKnownCity] = useState<'landing' | 'melbourne'>('landing'); const [lastKnownCity, setLastKnownCity] = useState<'landing' | 'melbourne'>('landing');
const [isLoginOpen, setLoginOpen] = useState(false); const [isLoginOpen, setLoginOpen] = useState(false);
const { user, login, logout } = useAuth(); // from AuthContext const { user, logout } = useAuth(); // from AuthContext
const cityLogo = sessionStorage.getItem("cityLogo")
const cityId = localStorage.getItem("cityId")
const cityName = localStorage.getItem("cityName")
// const citySelected = location.pathname.includes(slugify(cityName) || "")
const citySelected = sessionStorage.getItem("citySelected")
const baseUrl = import.meta.env.VITE_BASE_URL;
const protectedPaths = ["/passes", "/whats-included", "/", "/melbourne"]; const protectedPaths = ["/passes", "/whats-included", "/", "/melbourne"];
@@ -117,73 +120,75 @@ export default function Navbar({
melbourneLabel: 'How It Works' melbourneLabel: 'How It Works'
}, },
// Position 2 // Position 2
{
label: 'Magic Itinerary',
path: '/landing-magic-itinerary',
isShared: false
},
// Position 3 // Position 3
{ {
label: 'Whats Included', label: 'Whats Included',
path: '/whats-included', path: '/whats-included',
isShared: false isShared: false
}, },
// Position 4 - Shared item
{ {
label: 'Your Card', label: 'Magic Itinerary',
path: '/passes', path: '/landing-magic-itinerary',
isShared: true, isShared: false
landingLabel: 'Your Card',
melbourneLabel: 'Your Card'
}, },
// Position 5
{ {
label: 'FAQ', label: 'FAQ',
path: '/faq', path: '/faq',
isShared: false isShared: false
}, },
{
label: 'Your Postcard',
path: '/postcards',
isShared: true,
landingLabel: 'Your Postcard',
melbourneLabel: 'Your Postcard'
}
], ],
melbourne: [ citySelected: [
// Position 1 // Position 1
{ {
label: 'Attractions', label: 'Attractions',
path: '/attractions', path: `/attractions`,
isShared: false isShared: false
}, },
// Position 2 // Position 2
{ {
label: 'Magic Itinerary', label: 'Magic Itinerary',
path: '/magic-itinerary', path: `/magic-itinerary`,
isShared: false isShared: false
}, },
// Position 3 // Position 3
{ {
label: 'Super Savings', label: 'Super Savings',
path: '/super-savings', path: `/super-savings`,
isShared: false isShared: false
}, },
// Position 4 - Shared item // Position 4 - Shared item
{ {
label: 'How It Works', label: 'How It Works',
path: '/how-it-works', path: `/how-it-works`,
isShared: true, isShared: true,
landingLabel: 'Discover', landingLabel: 'Discover',
melbourneLabel: 'How It Works' melbourneLabel: 'How It Works'
}, },
// Position 5 - Shared item // Position 5 - Shared item
{ {
label: 'Your Card', label: 'Buy Cards',
path: '/passes', path: `/passes`,
isShared: true, isShared: true,
landingLabel: 'Your Card', landingLabel: 'Buy Cards',
melbourneLabel: 'Your Card' melbourneLabel: 'Buy Cards'
},
{
label: 'Your Postcard',
path: `/postcards`,
isShared: true,
landingLabel: 'Your Postcard',
melbourneLabel: 'Your Postcard'
} }
] ]
}; };
// Check if we're on landing page
const isLandingPage = location.pathname === '/';
// Restore from session on mount // Restore from session on mount
useEffect(() => { useEffect(() => {
const savedCity = sessionStorage.getItem('lastKnownCity'); const savedCity = sessionStorage.getItem('lastKnownCity');
@@ -220,20 +225,20 @@ export default function Navbar({
}, [location.pathname]); }, [location.pathname]);
// ✅ Determine which navbar to show // ✅ Determine which navbar to show
const getAutoNavigationSource = (): 'landing' | 'melbourne' => { const getAutoNavigationSource = () => {
const path = location.pathname; const path = location.pathname;
// Explicit routes // Explicit routes
if (path.startsWith('/melbourne')) return 'melbourne'; // if (path.startsWith('/melbourne')) return 'melbourne';
if (path === '/' || path.startsWith('/explore')) return 'landing'; if (path === '/' || path.startsWith('/explore')) return 'landing';
// Shared routes // Shared routes
if (['/passes', '/how-it-works'].includes(path)) { // if (['/passes', '/how-it-works'].includes(path)) {
return lastKnownCity; // ← remembers where user came from // return lastKnownCity; // ← remembers where user came from
} // }
// Fallback // Fallback
return lastKnownCity; return citySelected;
}; };
@@ -241,7 +246,7 @@ export default function Navbar({
const getNavigationItems = (): NavigationItem[] => { const getNavigationItems = (): NavigationItem[] => {
const currentSource = getAutoNavigationSource(); const currentSource = getAutoNavigationSource();
const items = currentSource === 'landing' ? const items = currentSource === 'landing' ?
navigationConfig.landing : navigationConfig.melbourne; navigationConfig.landing : navigationConfig.citySelected;
return items.map((item, index) => ({ return items.map((item, index) => ({
...item, ...item,
@@ -285,11 +290,11 @@ export default function Navbar({
setDialogSource('navbar'); setDialogSource('navbar');
}; };
const handleCitySelectFromNavbar = (cityId: string) => { const handleCitySelectFromNavbar = (cityName: string) => {
console.log('City selected from navbar:', cityId); console.log('City selected from navbar:', cityName);
onCityChange(cityId); onCityChange(cityName);
if (cityId.toLowerCase() === '1') { if (cityName.toLowerCase() === 'melbourne') {
setNavigationSource('melbourne'); setNavigationSource('melbourne');
navigate('/melbourne'); navigate('/melbourne');
} else { } else {
@@ -320,11 +325,11 @@ export default function Navbar({
handleCloseCityDialog(); handleCloseCityDialog();
}; };
const handleCitySelect = (cityId: string) => { const handleCitySelect = (cityName: string) => {
if (dialogSource === 'cta') { if (dialogSource === 'cta') {
handleCitySelectFromCTA(cityId); handleCitySelectFromCTA(cityName);
} else { } else {
handleCitySelectFromNavbar(cityId); handleCitySelectFromNavbar(cityName);
} }
}; };
@@ -350,34 +355,6 @@ export default function Navbar({
{ id: '2', name: 'Melbourne Premium Pass', price: '$129', quantity: 1 }, { id: '2', name: 'Melbourne Premium Pass', price: '$129', quantity: 1 },
]; ];
// Calculate cart total
const cartTotal = cartItems.reduce((total, item) => {
const price = parseFloat(item.price.replace('$', ''));
return total + (price * item.quantity);
}, 0);
// Cart dropdown items with proper navigation for checkout
const cartDropdownItems: DropdownItem[] = [
...cartItems.map(item => ({
id: item.id,
label: `${item.name} - ${item.price}`,
badge: `${item.quantity}x`
})),
{
id: 'total',
label: `Total: $${cartTotal.toFixed(2)}`,
icon: <ShoppingBag className="w-4 h-4" />
},
{
id: 'checkout',
label: 'Proceed to Checkout',
action: () => {
navigate('/checkout');
setActiveCartDropdown(false);
}
}
];
const closeMobileMenu = () => { const closeMobileMenu = () => {
setIsMobileMenuOpen(false); setIsMobileMenuOpen(false);
}; };
@@ -405,9 +382,9 @@ export default function Navbar({
}, []); }, []);
useEffect(() => { useEffect(() => {
if (activeCity.toLowerCase() === 'melbourne') { if (activeCity?.toLowerCase() === 'melbourne') {
setLastKnownCity('melbourne'); setLastKnownCity('melbourne');
} else if (activeCity.toLowerCase() === 'landing' || activeCity.toLowerCase() === 'landingpage') { } else if (activeCity?.toLowerCase() === 'landing' || activeCity?.toLowerCase() === 'landingpage') {
setLastKnownCity('landing'); setLastKnownCity('landing');
} }
}, [activeCity]); }, [activeCity]);
@@ -519,7 +496,7 @@ export default function Navbar({
<> <>
{/* Desktop Navbar - Enhanced Glassmorphism */} {/* Desktop Navbar - Enhanced Glassmorphism */}
<motion.nav <motion.nav
className="fixed top-0 left-0 right-0 z-50 hidden lg:block" className="fixed -top-1 left-0 right-0 z-50 hidden lg:block"
initial={{ y: -100, opacity: 0 }} initial={{ y: -100, opacity: 0 }}
animate={{ y: 0, opacity: 1 }} animate={{ y: 0, opacity: 1 }}
transition={{ duration: 0.6, ease: [0.25, 0.1, 0.25, 1], delay: 0.2 }} transition={{ duration: 0.6, ease: [0.25, 0.1, 0.25, 1], delay: 0.2 }}
@@ -545,21 +522,20 @@ export default function Navbar({
whileHover={{ scale: 1.05 }} whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }} whileTap={{ scale: 0.95 }}
> >
<Link to={currentSource === 'melbourne' ? '/melbourne' : '/'}> <Link to={citySelected ? `/${slugify(cityName)}` : '/'}>
<ImageWithFallback <ImageWithFallback
src={currentSource === 'melbourne' ? melbourneLogo : logoImage} src={citySelected ? `${baseUrl}${cityLogo}` : logoImage} alt={
alt={
currentSource === 'melbourne' currentSource === 'melbourne'
? 'Melbourne CityCards Logo' ? 'Melbourne CityCards Logo'
: 'CityCards Logo' : 'CityCards Logo'
} }
className="h-14 w-auto" className="h-17 w-auto"
/> />
</Link> </Link>
</motion.div> </motion.div>
<div className="absolute -translate-x-1/2 flex items-center gap-5" <div className="absolute -translate-x-1/2 flex items-center gap-5"
style={{ left: '45%', }} style={{ left: '42%', }}
> >
{/* Enhanced Navigation Items with source tracking */} {/* Enhanced Navigation Items with source tracking */}
{navigationItems.map((item) => { {navigationItems.map((item) => {
@@ -609,12 +585,13 @@ export default function Navbar({
onClick={handleOpenCityDialogFromNavbar} onClick={handleOpenCityDialogFromNavbar}
> >
<span> <span>
{!activeCity || activeCity === 'shared' {/* {!activeCity || activeCity === 'shared'
? 'City' ? 'City'
: ['landing', 'landingpage'].includes(activeCity.toLowerCase()) : ['landing', 'landingpage'].includes(activeCity.toLowerCase())
? 'City' ? 'City'
: activeCity.charAt(0).toUpperCase() + activeCity.slice(1) : activeCity.charAt(0).toUpperCase() + activeCity.slice(1)
} } */}
{citySelected ? cityName : "City"}
</span> </span>
<ChevronDown className="w-3.5 h-3.5" /> <ChevronDown className="w-3.5 h-3.5" />
@@ -637,7 +614,7 @@ export default function Navbar({
/> />
{/* Shopping Cart */} {/* Shopping Cart */}
<Dropdown {/* <Dropdown
ref={cartRef} ref={cartRef}
isOpen={activeCartDropdown} isOpen={activeCartDropdown}
onToggle={() => setActiveCartDropdown(prev => !prev)} onToggle={() => setActiveCartDropdown(prev => !prev)}
@@ -655,7 +632,8 @@ export default function Navbar({
</motion.div> </motion.div>
</div> </div>
} }
/> /> */}
<ShoppingBag className="w-6 h-6" onClick={() => navigate("/cart")} />
{/* Enhanced City Card Button with Source Tracking */} {/* Enhanced City Card Button with Source Tracking */}
<div className="flex items-center gap-3 pl-2"> <div className="flex items-center gap-3 pl-2">
@@ -671,7 +649,8 @@ export default function Navbar({
label: 'My Profile', label: 'My Profile',
icon: <User className="w-4 h-4" />, icon: <User className="w-4 h-4" />,
action: () => { action: () => {
navigate('/profile'); // navigate(citySelected ? `/${slugify(cityName)}/profile` : '/profile');
navigate(citySelected ? `/profile` : '/profile');
setActiveUserDropdown(false); setActiveUserDropdown(false);
} }
}, },
@@ -770,10 +749,11 @@ export default function Navbar({
trigger={ trigger={
<div className="flex items-center space-x-1 text-gray-700 hover:text-gray-900 text-sm font-medium transition-colors duration-200 cursor-pointer rounded-lg hover:bg-gray-50/50 px-2 py-1"> <div className="flex items-center space-x-1 text-gray-700 hover:text-gray-900 text-sm font-medium transition-colors duration-200 cursor-pointer rounded-lg hover:bg-gray-50/50 px-2 py-1">
<span> <span>
{activeCity && activeCity !== 'shared' ? {/* {activeCity && activeCity !== 'shared' ?
activeCity.charAt(0).toUpperCase() + activeCity.slice(1) : activeCity.charAt(0).toUpperCase() + activeCity.slice(1) :
currentSource === 'melbourne' ? 'Melbourne' : 'Select City' currentSource === 'melbourne' ? 'Melbourne' : 'Select City'
} } */}
{cityName ? cityName : "City"}
</span> </span>
<ChevronDown className={`w-3.5 h-3.5 transition-transform duration-200 ${activeCityDropdown ? 'rotate-180' : ''}`} /> <ChevronDown className={`w-3.5 h-3.5 transition-transform duration-200 ${activeCityDropdown ? 'rotate-180' : ''}`} />
</div> </div>

View File

@@ -3,6 +3,7 @@ import { motion, AnimatePresence } from 'motion/react';
import { Sparkles, MapPin, Calendar, Wand2, Clock } from 'lucide-react'; import { Sparkles, MapPin, Calendar, Wand2, Clock } from 'lucide-react';
// import { ImageWithFallback } from './figma/ImageWithFallback'; // import { ImageWithFallback } from './figma/ImageWithFallback';
import cityTourVideo from '../assets/citycards-vid.mp4'; import cityTourVideo from '../assets/citycards-vid.mp4';
import { useNavigate } from 'react-router-dom';
interface PersonalizedTourHeroProps { interface PersonalizedTourHeroProps {
onCreateItineraryClick?: () => void; onCreateItineraryClick?: () => void;
@@ -17,6 +18,7 @@ interface AttractionCard {
} }
export function PersonalizedTourHero({ onCreateItineraryClick }: PersonalizedTourHeroProps) { export function PersonalizedTourHero({ onCreateItineraryClick }: PersonalizedTourHeroProps) {
const navigate = useNavigate();
const attractionCards: AttractionCard[] = [ const attractionCards: AttractionCard[] = [
{ {
id: 1, id: 1,
@@ -71,6 +73,7 @@ export function PersonalizedTourHero({ onCreateItineraryClick }: PersonalizedTou
const nextCard = attractionCards[(currentCardIndex + 1) % attractionCards.length]; const nextCard = attractionCards[(currentCardIndex + 1) % attractionCards.length];
const thirdCard = attractionCards[(currentCardIndex + 2) % attractionCards.length]; const thirdCard = attractionCards[(currentCardIndex + 2) % attractionCards.length];
const cityName = localStorage.getItem("cityName")
return ( return (
<div className="relative w-full min-h-[90vh] overflow-hidden flex items-center bg-gradient-to-br from-orange-50 via-white to-rose-50"> <div className="relative w-full min-h-[90vh] overflow-hidden flex items-center bg-gradient-to-br from-orange-50 via-white to-rose-50">
{/* Gradient Background Elements */} {/* Gradient Background Elements */}
@@ -107,7 +110,7 @@ export function PersonalizedTourHero({ onCreateItineraryClick }: PersonalizedTou
> >
<Wand2 className="w-5 h-5 text-primary drop-shadow-lg" /> <Wand2 className="w-5 h-5 text-primary drop-shadow-lg" />
</motion.div> </motion.div>
<span className="font-poppins font-semibold text-gray-800">AI-Powered Planning</span> <span className="font-poppins font-semibold text-gray-800">Smart Planning</span>
<motion.div <motion.div
className="w-1.5 h-1.5 bg-primary rounded-full" className="w-1.5 h-1.5 bg-primary rounded-full"
animate={{ animate={{
@@ -121,20 +124,21 @@ export function PersonalizedTourHero({ onCreateItineraryClick }: PersonalizedTou
{/* Main Heading */} {/* Main Heading */}
<h1 className="font-poppins text-4xl sm:text-5xl md:text-6xl leading-tight mb-6"> <h1 className="font-poppins text-4xl sm:text-5xl md:text-6xl leading-tight mb-6">
<span className="font-light">Create Your</span>{' '} <span className="font-light">Create Your</span>{' '}
<span className="font-bold italic bg-gradient-to-r from-primary via-orange-500 to-rose-500 bg-clip-text text-transparent"> <span className="font-bold italic bg-gradient-to-r from-primary via-orange-500 to-rose-500
bg-clip-text text-transparent inline-block overflow-visible whitespace-nowrap pr-2">
Magic Itinerary Magic Itinerary
</span> </span>
</h1> </h1>
<p className="font-poppins text-lg md:text-xl font-normal leading-relaxed text-gray-600 mb-8"> <p className="font-poppins text-lg md:text-xl font-normal leading-relaxed text-gray-600 mb-8">
Let AI craft a personalized journey tailored to your interests, timeline, and travel style. Get the perfect itinerary in minutes. Craft a personalized journey tailored to your interests, timeline, and travel style. Get the perfect itinerary in minutes.
</p> </p>
{/* Quick Features */} {/* Quick Features */}
<div className="space-y-3 mb-8"> <div className="space-y-3 mb-8">
{[ {[
{ icon: <Sparkles className="w-5 h-5" />, text: 'AI-powered smart suggestions' }, { icon: <Sparkles className="w-5 h-5" />, text: 'Smart suggestions' },
{ icon: <MapPin className="w-5 h-5" />, text: '40+ top Melbourne attractions' }, { icon: <MapPin className="w-5 h-5" />, text: `40+ top ${cityName} attractions` },
{ icon: <Calendar className="w-5 h-5" />, text: 'Flexible & customizable plans' } { icon: <Calendar className="w-5 h-5" />, text: 'Flexible & customizable plans' }
].map((feature, index) => ( ].map((feature, index) => (
<motion.div <motion.div
@@ -162,18 +166,18 @@ export function PersonalizedTourHero({ onCreateItineraryClick }: PersonalizedTou
className="flex flex-col sm:flex-row gap-4 items-start sm:items-center" className="flex flex-col sm:flex-row gap-4 items-start sm:items-center"
> >
<button <button
onClick={onCreateItineraryClick} onClick={() => navigate('/create-itinerary')}
className="group relative px-8 py-4 rounded-lg flex items-center gap-2 overflow-hidden transition-all duration-300 hover:scale-105 shadow-lg hover:shadow-xl font-poppins font-semibold text-base text-white bg-gradient-to-r from-primary via-orange-500 to-rose-500 hover:from-primary/90 hover:via-orange-500/90 hover:to-rose-500/90" className="group cursor-pointer px-8 py-4 rounded-lg flex items-center gap-2 overflow-hidden transition-all duration-300 hover:scale-105 shadow-lg hover:shadow-xl font-poppins font-semibold text-base text-white bg-gradient-to-r from-primary via-orange-500 to-rose-500 hover:from-primary/90 hover:via-orange-500/90 hover:to-rose-500/90"
> >
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-white/20 to-transparent -translate-x-full group-hover:translate-x-full transition-transform duration-700" /> <div className="absolute inset-0 bg-gradient-to-r from-transparent via-white/20 to-transparent -translate-x-full group-hover:translate-x-full transition-transform duration-700" />
<Wand2 className="w-5 h-5 relative z-10 group-hover:rotate-12 transition-transform duration-300" /> <Wand2 className="w-5 h-5 relative z-10 group-hover:rotate-12 transition-transform duration-300" />
<span className="relative z-10">Create My Itinerary</span> <span className="relative z-10">Create My Itinerary</span>
</button> </button>
<p className="font-poppins text-sm text-gray-600 font-normal flex items-center gap-2"> {/* <p className="font-poppins text-sm text-gray-600 font-normal flex items-center gap-2">
<Sparkles className="w-4 h-4 text-primary" /> <Sparkles className="w-4 h-4 text-primary" />
<span>Free • Takes less than 2 minutes</span> <span>Free • Takes less than 2 minutes</span>
</p> </p> */}
</motion.div> </motion.div>
</motion.div> </motion.div>

View File

@@ -1,238 +0,0 @@
import React from 'react';
import { motion } from 'motion/react';
import { ArrowLeft, Camera, Edit3, Upload, Heart, Star, Download, Share2 } from 'lucide-react';
import { Button } from './ui/button';
import { Card, CardContent } from './ui/card';
import { Badge } from './ui/badge';
import Navbar from './Navbar';
// import SubNavbar from './SubNavbar';
import { Footer } from './Footer';
import { MobileAppSection } from './MobileAppSection';
import { EnhancedTestimonials } from './EnhancedTestimonials';
import { CustomPostcards } from './CustomPostcards';
import { HowItWorks } from './HowItWorks';
import { ImageWithFallback } from './figma/ImageWithFallback';
import { Layout } from '../Layout';
interface User {
email: string;
name: string;
}
interface PostCardsPageProps {
onBackClick: () => void;
onHomeClick: () => void;
onMelbourneClick: () => void;
onPassesClick: () => void;
onCheckoutClick: () => void;
onSignInClick: () => void;
onSignOutClick: () => void;
onAttractionsClick: () => void;
onBlogsClick: () => void;
onHowItWorksClick: () => void;
onFAQClick: () => void;
onPrivacyPolicyClick: () => void;
onAboutUsClick: () => void;
onProfileClick: () => void;
onCityCardsClick: () => void;
onMagicItineraryClick: () => void;
onPostCardsClick: () => void;
onOffersClick: () => void;
onContactUsClick?: () => void;
onEsimsClick?: () => void;
onHotelDiscountsClick?: () => void;
currentPage: string;
user: User | null;
}
export function PostCardsPage({
onBackClick,
onHomeClick,
onMelbourneClick,
onPassesClick,
onCheckoutClick,
onSignInClick,
onSignOutClick,
onAttractionsClick,
onBlogsClick,
onHowItWorksClick,
onFAQClick,
onPrivacyPolicyClick,
onAboutUsClick,
onProfileClick,
onCityCardsClick,
onMagicItineraryClick,
onPostCardsClick,
onOffersClick,
onContactUsClick,
onEsimsClick,
onHotelDiscountsClick,
currentPage,
user
}: PostCardsPageProps) {
return (
<div className="min-h-screen bg-background">
{/* Navbar */}
<Layout
activeCity="Landingpage"
onSignInClick={onSignInClick}
onSignOutClick={onSignOutClick}
user={user}
>
{/* Sub Navbar */}
{/* <SubNavbar
activeTab="postcards"
onCityCardsClick={onCityCardsClick}
onMagicItineraryClick={onMagicItineraryClick}
onPostCardsClick={onPostCardsClick}
onOffersClick={onOffersClick}
onEsimsClick={onEsimsClick}
onHotelDiscountsClick={onHotelDiscountsClick}
/> */}
{/* Hero Section */}
<section className="relative pt-52 pb-20 overflow-hidden">
{/* Background gradient */}
<div className="absolute inset-0 bg-gradient-to-br from-primary/5 via-secondary/5 to-background"></div>
<div className="container mx-auto px-4 relative z-10">
<motion.div
className="max-w-4xl mx-auto text-center"
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6 }}
>
<h1 className="font-merchant text-4xl md:text-5xl lg:text-6xl leading-tight mb-6">
<span className="font-light">Create Beautiful</span>{' '}
<span className="font-bold italic bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent">
Custom Postcards
</span>
</h1>
<p className="font-poppins text-xl leading-relaxed font-normal text-gray-600 mb-8 max-w-2xl mx-auto">
Transform your travel memories into stunning, personalized postcards with authentic handwritten messages.
Share your journey in a way that feels truly personal and meaningful.
</p>
<Button
onClick={onCheckoutClick}
className="bg-primary hover:bg-primary/90 text-white px-8 py-6 font-poppins font-semibold text-lg"
>
Start Creating Postcards
</Button>
</motion.div>
</div>
{/* Decorative elements */}
<div className="absolute top-20 left-10 w-20 h-20 bg-primary/10 rounded-full blur-xl"></div>
<div className="absolute bottom-20 right-10 w-32 h-32 bg-secondary/10 rounded-full blur-xl"></div>
</section>
{/* Reuse CustomPostcards Component */}
<CustomPostcards />
{/* How It Works Section */}
<HowItWorks />
{/* Features Section */}
{/* Gallery Section */}
<section className="py-16 bg-gradient-to-br from-amber-50 to-orange-50">
<div className="container mx-auto px-4">
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6, delay: 0.2 }}
className="text-center mb-12"
>
<h2 className="font-merchant text-3xl mb-4">Postcard Gallery</h2>
<p className="text-gray-600 font-poppins max-w-2xl mx-auto">
Get inspired by beautiful postcards created by our community of travelers
</p>
</motion.div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
{[
{
image: 'https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=400&h=600&fit=crop',
title: 'Tropical Paradise',
message: 'Greetings from paradise! The beaches here are absolutely breathtaking...',
location: 'Maldives'
},
{
image: 'https://images.unsplash.com/photo-1499856871958-5b9627545d1a?w=400&h=600&fit=crop',
title: 'City Adventures',
message: 'Having the most amazing time exploring this incredible city...',
location: 'Paris, France'
},
{
image: 'https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=400&h=600&fit=crop',
title: 'Mountain Views',
message: 'The views from up here are simply unbelievable. Wish you were here...',
location: 'Swiss Alps'
},
{
image: 'https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=400&h=600&fit=crop',
title: 'Cultural Journey',
message: 'Immersing myself in the rich culture and history of this amazing place...',
location: 'Kyoto, Japan'
},
{
image: 'https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=400&h=600&fit=crop',
title: 'Safari Adventure',
message: 'Just saw the most incredible wildlife! This experience is unforgettable...',
location: 'Kenya, Africa'
},
{
image: 'https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=400&h=600&fit=crop',
title: 'Island Escape',
message: 'Living the island life and loving every moment of this peaceful retreat...',
location: 'Santorini, Greece'
}
].map((postcard, index) => (
<motion.div
key={index}
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ duration: 0.6, delay: 0.3 + index * 0.1 }}
>
<Card className="hover:shadow-xl transition-all duration-300 transform hover:-translate-y-2">
<CardContent className="p-0">
<div className="relative">
<ImageWithFallback
src={postcard.image}
alt={postcard.title}
className="w-full h-48 object-cover rounded-t-lg"
/>
<Badge className="absolute top-3 left-3 bg-white/90 text-gray-700">
{postcard.location}
</Badge>
</div>
<div className="p-6">
<h3 className="font-merchant text-lg mb-2">{postcard.title}</h3>
<p className="text-gray-600 font-poppins text-sm italic">"{postcard.message}"</p>
</div>
</CardContent>
</Card>
</motion.div>
))}
</div>
</div>
</section>
{/* Ready to Explore Melbourne Section */}
{/* Mobile App Section */}
<MobileAppSection />
{/* Customer Reviews */}
<EnhancedTestimonials />
</Layout>
</div>
);
}

View File

@@ -0,0 +1,29 @@
// ProtectedRoute.tsx
import { useState } from 'react';
import { useAuth } from '../context/AuthContext';
import { LoginModal } from './LoginModal';
import { useNavigate } from 'react-router-dom';
interface ProtectedRouteProps {
children: React.ReactNode;
}
export function ProtectedRoute({ children }: ProtectedRouteProps) {
const { user } = useAuth();
const navigate = useNavigate();
const [isLoginOpen, setIsLoginOpen] = useState(!user);
if (!user) {
return (
<LoginModal
isOpen={isLoginOpen}
onClose={() => {
setIsLoginOpen(false);
navigate(-1);
}}
/>
);
}
return <>{children}</>;
}

View File

@@ -0,0 +1,306 @@
//RegisterPage.tsx
import { useState } from 'react';
import { Button } from './ui/button';
import { Input } from './ui/input';
import { useRegisterMutation } from '../Redux/services/auth.service';
import { toast } from 'sonner';
import { useAuth } from '../context/AuthContext';
import Navbar from './Navbar';
import { Footer } from './Footer';
import { useNavigate } from 'react-router-dom';
import { Label } from './ui/label';
import { useEffect } from 'react';
export default function RegisterPage() {
const { login, user } = useAuth();
const email = localStorage.getItem("userEmail")
const [formData, setFormData] = useState({
firstName: '',
lastName: '',
emailAddress: email ?? "",
isdCode: '',
mobileNumber: '',
address1: '',
address2: '',
city: '',
state: '',
country: '',
postalCode: ''
});
const [helperText, setHelperText] = useState('');
const [isLoading, setIsLoading] = useState(false);
const navigate = useNavigate()
useEffect(() => {
const pendingEmail = localStorage.getItem("userEmail");
if (user || !pendingEmail) {
navigate("/");
}
}, [user]);
const [register, { isLoading: isRegistering }] = useRegisterMutation();
const handleInputChange = (field: string, value: string) => {
setFormData(prev => ({ ...prev, [field]: value }));
};
const validateForm = () => {
// First Name
if (!formData.firstName.trim()) return toast.error('First name is required'), false;
if (!/^[A-Za-z]+$/.test(formData.firstName)) return toast.error('First name must contain only alphabets'), false;
if (formData.firstName.length < 2 || formData.firstName.length > 50) return toast.error('First name must be between 2 and 50 characters'), false;
// Last Name
if (!formData.lastName.trim()) return toast.error('Last name is required'), false;
if (!/^[A-Za-z]+$/.test(formData.lastName)) return toast.error('Last name must contain only alphabets'), false;
if (formData.lastName.length < 2 || formData.lastName.length > 50) return toast.error('Last name must be between 2 and 50 characters'), false;
// Email
if (!formData.emailAddress.includes('@')) return toast.error('Invalid email address'), false;
// ISD Code
if (!formData.isdCode.startsWith("+")) return toast.error("ISD code must start with '+'"), false;
if (!/^\+\d+$/.test(formData.isdCode)) return toast.error("ISD code must contain only numbers after '+'"), false;
// Mobile Number
if (!/^\d+$/.test(formData.mobileNumber)) return toast.error('Invalid mobile number'), false;
if (formData.mobileNumber.length < 7 || formData.mobileNumber.length > 15) return toast.error('Mobile number must be between 7 and 15 digits'), false;
// Address Line 1
if (!formData.address1.trim()) return toast.error('Address required'), false;
if (!/^[A-Za-z0-9\s]+$/.test(formData.address1)) return toast.error('Address must be alphanumeric'), false;
if (formData.address1.length < 5 || formData.address1.length > 100) return toast.error('Address must be between 5 and 100 characters'), false;
// City
if (!formData.city.trim()) return toast.error('City required'), false;
if (!/^[A-Za-z\s]+$/.test(formData.city)) return toast.error('City must contain only alphabets'), false;
if (formData.city.length < 2 || formData.city.length > 50) return toast.error('City must be between 2 and 50 characters'), false;
// State
if (!formData.state.trim()) return toast.error('State required'), false;
if (!/^[A-Za-z\s]+$/.test(formData.state)) return toast.error('State must contain only alphabets'), false;
if (formData.state.length < 2 || formData.state.length > 50) return toast.error('State must be between 2 and 50 characters'), false;
// Country
if (!formData.country.trim()) return toast.error('Country required'), false;
if (!/^[A-Za-z\s]+$/.test(formData.country)) return toast.error('Country must contain only alphabets'), false;
if (formData.country.length < 2 || formData.country.length > 50) return toast.error('Country must be between 2 and 50 characters'), false;
// Postal Code
if (!/^\d+$/.test(formData.postalCode)) return toast.error('Postal code should only contain numbers'), false;
if (formData.postalCode.length < 4 || formData.postalCode.length > 10) return toast.error('Postal code must be between 4 and 10 digits'), false;
return true;
};
const handleRegister = async () => {
if (!validateForm()) return;
setIsLoading(true);
setHelperText('');
try {
const response = await register(formData).unwrap();
toast.success('Registration successful!');
const userData = {
userId: response?.user?.id,
email: response?.email || formData.emailAddress,
name: response?.name || formData.emailAddress.split('@')[0].charAt(0).toUpperCase() + formData.emailAddress.split('@')[0].slice(1),
accessToken: response?.accessToken,
};
login(userData);
localStorage.removeItem("userEmail")
navigate("/")
} catch (err: any) {
const msg = err?.data?.message || 'Registration failed';
toast.error(msg);
setHelperText(msg);
} finally {
setIsLoading(false);
}
};
return (
<div className="min-h-screen flex flex-col bg-gray-50 w-full">
{/* Navbar */}
<Navbar activeCity="" />
{/* Main Content */}
<div className="flex-grow w-full px-6 md:px-10 py-10 mt-20">
<div className="w-full max-w-5xl mx-auto">
{/* Header */}
<div className="mb-8">
<h2 className="font-merchant text-3xl font-semibold text-gray-900 mb-2">
Create Account
</h2>
<p className="font-poppins text-gray-600">
Register to get started with City Cards
</p>
</div>
{/* Form Container */}
<div className="bg-white rounded-2xl border border-gray-200 p-8 space-y-8">
{/* Personal Info */}
<div className="space-y-4">
<h3 className="font-poppins font-semibold text-gray-800 text-lg">
Personal Information
</h3>
<div className="grid md:grid-cols-2 gap-6">
<div>
<Label htmlFor="firstName" className="font-poppins font-light">First Name</Label>
<Input
id="firstName"
value={formData.firstName}
onChange={(e) => handleInputChange('firstName', e.target.value)}
className="h-12 bg-gray-50 border-0 rounded-xl mt-1"
/>
</div>
<div>
<Label htmlFor="lastName" className="font-poppins font-light">Last Name</Label>
<Input
id="lastName"
value={formData.lastName}
onChange={(e) => handleInputChange('lastName', e.target.value)}
className="h-12 bg-gray-50 border-0 rounded-xl mt-1"
/>
</div>
</div>
<div>
<Label htmlFor="emailAddress" className="font-poppins font-light">Email Address</Label>
<Input
id="emailAddress"
type="email"
value={formData.emailAddress}
disabled
onChange={(e) => handleInputChange('emailAddress', e.target.value)}
className="h-12 bg-gray-50 border-0 rounded-xl mt-1"
/>
</div>
<div className="grid md:grid-cols-3 gap-6">
<div>
<Label htmlFor="isdCode" className="font-poppins font-light">ISD Code</Label>
<Input
id="isdCode"
placeholder="example: +91"
value={formData.isdCode}
onChange={(e) => handleInputChange('isdCode', e.target.value)}
className="h-12 bg-gray-50 border-0 rounded-xl mt-1"
/>
</div>
<div className="md:col-span-2">
<Label htmlFor="mobileNumber" className="font-poppins font-light">Mobile Number</Label>
<Input
id="mobileNumber"
value={formData.mobileNumber}
onChange={(e) => handleInputChange('mobileNumber', e.target.value)}
className="h-12 bg-gray-50 border-0 rounded-xl mt-1"
/>
</div>
</div>
</div>
{/* Address */}
<div className="space-y-4">
<h3 className="font-poppins font-semibold text-gray-800 text-lg">
Address Information
</h3>
<div>
<Label htmlFor="address1" className="font-poppins font-light">Address Line 1</Label>
<Input
id="address1"
value={formData.address1}
onChange={(e) => handleInputChange('address1', e.target.value)}
className="h-12 bg-gray-50 border-0 rounded-xl mt-1"
/>
</div>
<div>
<Label htmlFor="address2" className="font-poppins font-light">Address Line 2</Label>
<Input
id="address2"
value={formData.address2}
onChange={(e) => handleInputChange('address2', e.target.value)}
className="h-12 bg-gray-50 border-0 rounded-xl mt-1"
/>
</div>
<div className="grid md:grid-cols-2 gap-6">
<div>
<Label htmlFor="city" className="font-poppins font-light">City</Label>
<Input
id="city"
value={formData.city}
onChange={(e) => handleInputChange('city', e.target.value)}
className="h-12 bg-gray-50 border-0 rounded-xl mt-1"
/>
</div>
<div>
<Label htmlFor="state" className="font-poppins font-light">State</Label>
<Input
id="state"
value={formData.state}
onChange={(e) => handleInputChange('state', e.target.value)}
className="h-12 bg-gray-50 border-0 rounded-xl mt-1"
/>
</div>
</div>
<div className="grid md:grid-cols-2 gap-6">
<div>
<Label htmlFor="country" className="font-poppins font-light">Country</Label>
<Input
id="country"
value={formData.country}
onChange={(e) => handleInputChange('country', e.target.value)}
className="h-12 bg-gray-50 border-0 rounded-xl mt-1"
/>
</div>
<div>
<Label htmlFor="postalCode" className="font-poppins font-light">Postal Code</Label>
<Input
id="postalCode"
value={formData.postalCode}
onChange={(e) => handleInputChange('postalCode', e.target.value)}
className="h-12 bg-gray-50 border-0 rounded-xl mt-1"
/>
</div>
</div>
</div>
{helperText && (
<p className="text-sm text-red-500">{helperText}</p>
)}
<Button
onClick={handleRegister}
disabled={isLoading || isRegistering}
className="w-full cursor-pointer bg-gray-800 hover:bg-gray-900 md:px-10 h-12 text-white rounded-xl"
>
{isLoading || isRegistering ? 'Registering...' : 'Register'}
</Button>
</div>
</div>
</div>
{/* Footer */}
<div className="mt-auto">
<Footer />
</div>
</div>
);
}

View File

@@ -1,527 +0,0 @@
import { useState } from 'react';
import { motion } from 'motion/react';
import { ArrowLeft, Lock, Shield, CreditCard, Check, X, Tag } from 'lucide-react';
import { Button } from './ui/button';
import { Input } from './ui/input';
import { Label } from './ui/label';
import { Card, CardContent, CardHeader, CardTitle } from './ui/card';
import { Tabs, TabsContent, TabsList, TabsTrigger } from './ui/tabs';
import { Separator } from './ui/separator';
import { Badge } from './ui/badge';
import Navbar from './Navbar';
import { Footer } from './Footer';
interface User {
email: string;
name: string;
}
interface SecureCheckoutPageProps {
onBackClick: () => void;
onHomeClick: () => void;
onMelbourneClick: () => void;
onPassesClick: () => void;
onCheckoutClick: () => void;
onSignInClick: () => void;
onSignOutClick: () => void;
onAttractionsClick: () => void;
onBlogsClick: () => void;
onHowItWorksClick: () => void;
onFAQClick: () => void;
onPrivacyPolicyClick: () => void;
onAboutUsClick: () => void;
onProfileClick: () => void;
onCityCardsClick: () => void;
onMagicItineraryClick: () => void;
onPostCardsClick: () => void;
onOffersClick: () => void;
onContactUsClick?: () => void;
onEsimsClick?: () => void;
onHotelDiscountsClick?: () => void;
currentPage?: string;
user: User | null;
}
export function SecureCheckoutPage({
onBackClick,
onHomeClick,
onMelbourneClick,
onPassesClick,
onCheckoutClick,
onSignInClick,
onSignOutClick,
onAttractionsClick,
onBlogsClick,
onHowItWorksClick,
onFAQClick,
onPrivacyPolicyClick,
onAboutUsClick,
onProfileClick,
onCityCardsClick,
onMagicItineraryClick,
onPostCardsClick,
onOffersClick,
onContactUsClick,
onEsimsClick,
onHotelDiscountsClick,
currentPage,
user
}: SecureCheckoutPageProps) {
const [selectedTab, setSelectedTab] = useState('myself');
const [formData, setFormData] = useState({
firstName: 'Frank',
lastName: 'Adam S',
email: 'frank2023@mail.com',
phone: '',
travelDate: '',
billingAddress: '',
city: '',
country: 'England',
state: '',
zipCode: '000000'
});
const [discountCode, setDiscountCode] = useState('');
const [isEmailVerified, setIsEmailVerified] = useState(false);
const [showPaymentSuccess, setShowPaymentSuccess] = useState(false);
const orderSummary = {
item: 'Melbourne - Unlimited Card',
duration: '2 Days',
adults: 3,
kids: 3,
quantity: 1,
price: 49.80,
discount: 7.20,
tax: 2.24
};
const subtotal = orderSummary.price;
const discount = orderSummary.discount;
const total = subtotal - discount;
const handleInputChange = (field: string, value: string) => {
setFormData(prev => ({ ...prev, [field]: value }));
};
const handlePayment = () => {
setShowPaymentSuccess(true);
setTimeout(() => {
setShowPaymentSuccess(false);
onHomeClick();
}, 2000);
};
return (
<div className="min-h-screen bg-background">
<Navbar
onHomeClick={onHomeClick}
onMelbourneClick={onMelbourneClick}
onPassesClick={onPassesClick}
onCheckoutClick={onCheckoutClick}
onSignInClick={onSignInClick}
onSignOutClick={onSignOutClick}
onAttractionsClick={onAttractionsClick}
onBlogsClick={onBlogsClick}
onHowItWorksClick={onHowItWorksClick}
onFAQClick={onFAQClick}
onPrivacyPolicyClick={onPrivacyPolicyClick}
onAboutUsClick={onAboutUsClick}
onProfileClick={onProfileClick}
onCityCardsClick={onCityCardsClick}
onMagicItineraryClick={onMagicItineraryClick}
onPostCardsClick={onPostCardsClick}
onOffersClick={onOffersClick}
currentPage={currentPage}
isUserSignedIn={!!user}
user={user}
/>
<div className="container mx-auto px-4 py-12">
{/* Header */}
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
className="mb-8"
>
<Button
variant="ghost"
onClick={onBackClick}
className="mb-6 p-0 hover:bg-transparent"
>
<ArrowLeft className="w-5 h-5 mr-2" />
Back
</Button>
<div className="flex items-center gap-4 mb-2">
<h1 className="font-merchant text-3xl md:text-4xl leading-tight">
Secure <span className="text-primary">Checkout</span>
</h1>
<div className="flex items-center gap-2 text-green-600">
<Shield className="w-5 h-5" />
<span className="font-poppins text-sm font-medium">SSL Secured</span>
</div>
</div>
<p className="text-muted-foreground">
Complete your purchase securely. Your payment information is protected.
</p>
</motion.div>
<div className="grid lg:grid-cols-3 gap-8">
{/* Checkout Form */}
<motion.div
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
className="lg:col-span-2"
>
<Card className="shadow-lg border-0">
<CardHeader className="pb-6">
<Tabs value={selectedTab} onValueChange={setSelectedTab} className="w-full">
<TabsList className="grid w-fit grid-cols-2 bg-muted/50">
<TabsTrigger value="myself" className="px-6">For myself</TabsTrigger>
<TabsTrigger value="gift" className="px-6">To gift Someone</TabsTrigger>
</TabsList>
</Tabs>
</CardHeader>
<CardContent className="space-y-8">
{/* Personal Information */}
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.1 }}
className="space-y-6"
>
<div className="flex items-center gap-3">
<div className="w-8 h-8 bg-primary text-primary-foreground rounded-full flex items-center justify-center font-medium">
1
</div>
<h2 className="font-merchant text-xl font-medium leading-snug">Personal Information</h2>
</div>
<div className="grid md:grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="firstName">First Name</Label>
<Input
id="firstName"
value={formData.firstName}
onChange={(e) => handleInputChange('firstName', e.target.value)}
className="h-12"
/>
</div>
<div className="space-y-2">
<Label htmlFor="lastName">Last Name</Label>
<Input
id="lastName"
value={formData.lastName}
onChange={(e) => handleInputChange('lastName', e.target.value)}
className="h-12"
/>
</div>
</div>
<div className="space-y-2">
<Label htmlFor="email">Email Address</Label>
<div className="relative">
<Input
id="email"
type="email"
value={formData.email}
onChange={(e) => handleInputChange('email', e.target.value)}
className="h-12 pr-20"
/>
<Button
size="sm"
variant={isEmailVerified ? "default" : "outline"}
className="absolute right-2 top-1/2 -translate-y-1/2 h-8"
onClick={() => setIsEmailVerified(!isEmailVerified)}
>
{isEmailVerified ? (
<>
<Check className="w-3 h-3 mr-1" />
Verified
</>
) : (
'Verify'
)}
</Button>
</div>
</div>
<div className="grid md:grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="phone">Phone Number</Label>
<Input
id="phone"
type="tel"
placeholder="Enter mobile number"
value={formData.phone}
onChange={(e) => handleInputChange('phone', e.target.value)}
className="h-12"
/>
</div>
<div className="space-y-2">
<Label htmlFor="travelDate">Select Travel Date</Label>
<Input
id="travelDate"
type="date"
value={formData.travelDate}
onChange={(e) => handleInputChange('travelDate', e.target.value)}
className="h-12"
/>
</div>
</div>
</motion.div>
<Separator />
{/* Billing Information */}
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 0.2 }}
className="space-y-6"
>
<div className="flex items-center gap-3">
<div className="w-8 h-8 bg-primary text-primary-foreground rounded-full flex items-center justify-center font-medium">
2
</div>
<h2 className="font-merchant text-xl font-medium leading-snug">Billing Information</h2>
</div>
<div className="space-y-4">
<div className="space-y-2">
<Label htmlFor="billingAddress">Billing Address</Label>
<Input
id="billingAddress"
placeholder="Enter Address"
value={formData.billingAddress}
onChange={(e) => handleInputChange('billingAddress', e.target.value)}
className="h-12"
/>
</div>
<div className="grid md:grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="city">City</Label>
<Input
id="city"
placeholder="Enter City"
value={formData.city}
onChange={(e) => handleInputChange('city', e.target.value)}
className="h-12"
/>
</div>
<div className="space-y-2">
<Label htmlFor="country">Country</Label>
<Input
id="country"
value={formData.country}
onChange={(e) => handleInputChange('country', e.target.value)}
className="h-12"
/>
</div>
</div>
<div className="grid md:grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="state">State</Label>
<Input
id="state"
placeholder="State"
value={formData.state}
onChange={(e) => handleInputChange('state', e.target.value)}
className="h-12"
/>
</div>
<div className="space-y-2">
<Label htmlFor="zipCode">Zip Code</Label>
<Input
id="zipCode"
value={formData.zipCode}
onChange={(e) => handleInputChange('zipCode', e.target.value)}
className="h-12"
/>
</div>
</div>
</div>
</motion.div>
</CardContent>
</Card>
</motion.div>
{/* Order Summary */}
<motion.div
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
className="lg:col-span-1"
>
<Card className="shadow-lg border-0 sticky top-6">
<CardHeader>
<CardTitle className="flex items-center gap-2">
<CreditCard className="w-5 h-5" />
Order Summary
</CardTitle>
</CardHeader>
<CardContent className="space-y-6">
{/* Item Details */}
<div className="space-y-4">
<div className="flex justify-between items-start">
<div className="space-y-2">
<h3 className="font-poppins font-medium">{orderSummary.item}</h3>
<div className="flex items-center gap-2 text-sm text-muted-foreground">
<span>{orderSummary.duration}</span>
<span></span>
<span>Adults-{orderSummary.adults}</span>
<span></span>
<span>Kids-{orderSummary.kids}</span>
</div>
<div className="text-sm text-muted-foreground">
Qty: {orderSummary.quantity}
</div>
</div>
<div className="font-poppins text-lg font-medium">
${orderSummary.price.toFixed(2)}
</div>
</div>
</div>
<Separator />
{/* Discount Code */}
<div className="space-y-3">
<div className="flex gap-2">
<Input
placeholder="Gift or discount code"
value={discountCode}
onChange={(e) => setDiscountCode(e.target.value)}
className="flex-1"
/>
<Button variant="outline" className="px-6">
Apply
</Button>
</div>
</div>
<Separator />
{/* Price Breakdown */}
<div className="space-y-3">
<div className="flex justify-between">
<span>Subtotal</span>
<span>${subtotal.toFixed(2)}</span>
</div>
<div className="flex justify-between text-green-600">
<span>Discount</span>
<span>-${discount.toFixed(2)}</span>
</div>
</div>
<Separator />
{/* Total */}
<div className="space-y-2">
<div className="flex justify-between items-center">
<div>
<div className="font-medium">Total</div>
<div className="text-sm text-muted-foreground">
Including ${orderSummary.tax.toFixed(2)} in taxes
</div>
</div>
<div className="text-2xl font-bold text-primary">
${total.toFixed(2)}
</div>
</div>
</div>
{/* Payment Button */}
<Button
onClick={handlePayment}
className="w-full h-14 text-lg bg-gradient-to-r from-primary to-primary/90 hover:from-primary/90 hover:to-primary/80 relative overflow-hidden group"
disabled={showPaymentSuccess}
>
{showPaymentSuccess ? (
<motion.div
initial={{ scale: 0 }}
animate={{ scale: 1 }}
className="flex items-center gap-2"
>
<Check className="w-5 h-5" />
Payment Successful!
</motion.div>
) : (
<>
<Lock className="w-5 h-5 mr-2" />
Pay ${total.toFixed(2)}
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-white/20 to-transparent transform -skew-x-12 -translate-x-full group-hover:translate-x-full transition-transform duration-700" />
</>
)}
</Button>
{/* Security Notice */}
<div className="text-xs text-muted-foreground leading-relaxed">
Your personal data will be used to process your order, support your experience throughout this website, and for other purposes described in our privacy policy.
</div>
{/* Trust Badges */}
<div className="flex items-center justify-center gap-4 pt-4">
<div className="flex items-center gap-2 text-green-600">
<Shield className="w-4 h-4" />
<span className="text-xs">256-bit SSL</span>
</div>
<div className="flex items-center gap-2 text-blue-600">
<Lock className="w-4 h-4" />
<span className="text-xs">Secure Payment</span>
</div>
</div>
</CardContent>
</Card>
</motion.div>
</div>
</div>
<Footer
onHomeClick={onHomeClick}
onMelbourneClick={onMelbourneClick}
onPassesClick={onPassesClick}
onCheckoutClick={onCheckoutClick}
onSignInClick={onSignInClick}
onAttractionsClick={onAttractionsClick}
onBlogsClick={onBlogsClick}
onHowItWorksClick={onHowItWorksClick}
onFAQClick={onFAQClick}
onPrivacyPolicyClick={onPrivacyPolicyClick}
onAboutUsClick={onAboutUsClick}
onProfileClick={onProfileClick}
onCityCardsClick={onCityCardsClick}
onMagicItineraryClick={onMagicItineraryClick}
onPostCardsClick={onPostCardsClick}
onOffersClick={onOffersClick}
/>
{/* Success Overlay */}
{showPaymentSuccess && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
className="fixed inset-0 bg-black/50 backdrop-blur-sm z-50 flex items-center justify-center"
>
<motion.div
initial={{ scale: 0, rotate: 180 }}
animate={{ scale: 1, rotate: 0 }}
className="bg-white p-8 rounded-2xl shadow-2xl text-center max-w-sm mx-4"
>
<div className="w-16 h-16 bg-green-100 rounded-full flex items-center justify-center mx-auto mb-4">
<Check className="w-8 h-8 text-green-600" />
</div>
<h3 className="text-xl font-medium mb-2">Payment Successful!</h3>
<p className="text-muted-foreground">
Your Melbourne CityCard has been purchased successfully.
</p>
</motion.div>
</motion.div>
)}
</div>
);
}

View File

@@ -1,745 +0,0 @@
import { useState } from 'react';
import { motion } from 'motion/react';
import { ArrowLeft, Search, Filter, Star, MapPin, Clock, Tag, Heart, Share2, ChevronDown, ChevronRight, Check, Hotel, Plane, Building2, MapPinned, Home, Gift, Percent } from 'lucide-react';
import { Button } from './ui/button';
import { Input } from './ui/input';
import { Card, CardContent, CardHeader, CardTitle } from './ui/card';
import { Badge } from './ui/badge';
import { Separator } from './ui/separator';
import { Checkbox } from './ui/checkbox';
import Navbar from './Navbar';
import { Footer } from './Footer';
import { ImageWithFallback } from './figma/ImageWithFallback';
import { TrustSection } from './TrustSection';
import { MobileAppSection } from './MobileAppSection';
import { ReviewsSection } from './ReviewsSection';
import { TrustedCompanies } from './TrustedCompanies';
import { Layout } from '../Layout';
interface SuperSavingsPageProps {
onBackClick: () => void;
onHomeClick: () => void;
onMelbourneClick: () => void;
onPassesClick: () => void;
onCheckoutClick: () => void;
onSignInClick: () => void;
onSignOutClick: () => void;
onAttractionsClick: () => void;
onBlogsClick: () => void;
onHowItWorksClick: () => void;
onFAQClick: () => void;
onPrivacyPolicyClick: () => void;
onAboutUsClick: () => void;
onProfileClick: () => void;
onCityCardsClick: () => void;
onMagicItineraryClick: () => void;
onPostCardsClick: () => void;
onOffersClick: () => void;
onSuperSavingsClick: () => void;
onContactUsClick?: () => void;
onEsimsClick?: () => void;
onHotelDiscountsClick?: () => void;
fromSource?: 'products' | 'passes';
currentPage: string;
user?: { email: string; name: string; } | null;
}
// Mock super savings data
const savingsData = [
{
id: '1',
business: 'Grand Hotels Melbourne',
title: 'Up to 50% Off on luxury hotel stays across Melbourne',
discount: '50% OFF',
savedAmount: 'Save up to $300',
image: 'https://images.unsplash.com/photo-1566073771259-6a8506099945?w=400',
category: 'hotels'
},
{
id: '2',
business: 'Adventure Tours',
title: '40% Off on guided adventure tours and experiences',
discount: '40% OFF',
savedAmount: 'Save up to $150',
image: 'https://images.unsplash.com/photo-1571019613454-1cb2f99b2d8b?w=400',
category: 'tours'
},
{
id: '3',
business: 'Premium Spa & Wellness',
title: '45% Off on spa packages and wellness treatments',
discount: '45% OFF',
savedAmount: 'Save up to $200',
image: 'https://images.unsplash.com/photo-1544161515-4ab6ce6db874?w=400',
category: 'wellness'
},
{
id: '4',
business: 'Culinary Delights',
title: '35% Off on fine dining at Michelin-starred restaurants',
discount: '35% OFF',
savedAmount: 'Save up to $120',
image: 'https://images.unsplash.com/photo-1414235077428-338989a2e8c0?w=400',
category: 'dining'
},
{
id: '5',
business: 'Entertainment Pass',
title: '60% Off on theater shows and concert tickets',
discount: '60% OFF',
savedAmount: 'Save up to $250',
image: 'https://images.unsplash.com/photo-1514306191717-452ec28c7814?w=400',
category: 'entertainment'
},
{
id: '6',
business: 'Museum Pass',
title: '55% Off on museum entries and special exhibitions',
discount: '55% OFF',
savedAmount: 'Save up to $180',
image: 'https://images.unsplash.com/photo-1566127992631-137a642a90f4?w=400',
category: 'museums'
},
{
id: '7',
business: 'Luxury Shopping',
title: '30% Off on designer boutiques and luxury shopping',
discount: '30% OFF',
savedAmount: 'Save up to $500',
image: 'https://images.unsplash.com/photo-1441986300917-64674bd600d8?w=400',
category: 'shopping'
},
{
id: '8',
business: 'Water Sports',
title: '45% Off on water sports and beach activities',
discount: '45% OFF',
savedAmount: 'Save up to $175',
image: 'https://images.unsplash.com/photo-1476514525535-07fb3b4ae5f1?w=400',
category: 'sports'
},
{
id: '9',
business: 'Wine Tasting Tours',
title: '40% Off on wine country tours and tastings',
discount: '40% OFF',
savedAmount: 'Save up to $160',
image: 'https://images.unsplash.com/photo-1506377247377-2a5b3b417ebb?w=400',
category: 'tours'
},
{
id: '10',
business: 'Family Fun Parks',
title: '50% Off on family entertainment and theme parks',
discount: '50% OFF',
savedAmount: 'Save up to $220',
image: 'https://images.unsplash.com/photo-1524850011238-e3d235c7d4c9?w=400',
category: 'entertainment'
},
{
id: '11',
business: 'Boutique Stays',
title: '55% Off on boutique hotels and bed & breakfasts',
discount: '55% OFF',
savedAmount: 'Save up to $280',
image: 'https://images.unsplash.com/photo-1551882547-ff40c63fe5fa?w=400',
category: 'hotels'
},
{
id: '12',
business: 'Art Galleries',
title: '35% Off on contemporary art galleries and workshops',
discount: '35% OFF',
savedAmount: 'Save up to $140',
image: 'https://images.unsplash.com/photo-1561214115-f2f134cc4912?w=400',
category: 'museums'
},
{
id: '13',
business: 'Luxury Cruises',
title: '65% Off on harbor cruises and yacht experiences',
discount: '65% OFF',
savedAmount: 'Save up to $400',
image: 'https://images.unsplash.com/photo-1544551763-46a013bb70d5?w=400',
category: 'tours'
}
];
const filterCategories = [
{ value: 'hotels', label: 'Hotels', count: 2 },
{ value: 'tours', label: 'Tours', count: 3 },
{ value: 'wellness', label: 'Wellness', count: 1 },
{ value: 'dining', label: 'Dining', count: 1 },
{ value: 'entertainment', label: 'Entertainment', count: 2 },
{ value: 'museums', label: 'Museums', count: 2 },
{ value: 'shopping', label: 'Shopping', count: 1 },
{ value: 'sports', label: 'Sports', count: 1 }
];
// Categories data for the Super Savings Categories section
const categoriesData = [
{
icon: Hotel,
title: 'Luxury Hotels',
description: 'Premium stays at unbeatable prices',
savings: 'Up to 50% off',
color: 'from-primary to-primary/80'
},
{
icon: Plane,
title: 'Travel Tours',
description: 'Guided experiences worth your time',
savings: 'Up to 45% off',
color: 'from-primary to-primary/80'
},
{
icon: MapPinned,
title: 'Attractions',
description: 'Must-see landmarks and experiences',
savings: 'Up to 60% off',
color: 'from-primary to-primary/80'
},
{
icon: Building2,
title: 'Shopping',
description: 'Designer brands and local boutiques',
savings: 'Up to 35% off',
color: 'from-primary to-primary/80'
},
{
icon: Gift,
title: 'Wellness',
description: 'Spa treatments and relaxation',
savings: 'Up to 45% off',
color: 'from-primary to-primary/80'
}
];
export function SuperSavingsPage({
onBackClick,
onHomeClick,
onMelbourneClick,
onPassesClick,
onCheckoutClick,
onSignInClick,
onSignOutClick,
onAttractionsClick,
onBlogsClick,
onHowItWorksClick,
onFAQClick,
onPrivacyPolicyClick,
onAboutUsClick,
onProfileClick,
onCityCardsClick,
onMagicItineraryClick,
onPostCardsClick,
onOffersClick,
onSuperSavingsClick,
onContactUsClick,
onEsimsClick,
onHotelDiscountsClick,
fromSource = 'products',
currentPage,
user
}: SuperSavingsPageProps) {
const [searchQuery, setSearchQuery] = useState('');
const [selectedCategories, setSelectedCategories] = useState<string[]>([]);
const [currentPage_, setCurrentPage_] = useState(1);
const [showLoadMore, setShowLoadMore] = useState(true);
const toggleCategory = (category: string) => {
setSelectedCategories(prev =>
prev.includes(category)
? prev.filter(c => c !== category)
: [...prev, category]
);
};
const filteredSavings = savingsData.filter(saving => {
const matchesSearch = saving.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
saving.business.toLowerCase().includes(searchQuery.toLowerCase());
const matchesCategory = selectedCategories.length === 0 || selectedCategories.includes(saving.category);
return matchesSearch && matchesCategory;
});
const itemsPerPage = 12;
const displayedSavings = filteredSavings.slice(0, currentPage_ * itemsPerPage);
const hasMoreItems = filteredSavings.length > displayedSavings.length;
const handleLoadMore = () => {
setCurrentPage_(prev => prev + 1);
if (!hasMoreItems) setShowLoadMore(false);
};
// Show different layouts based on login state
if (!user) {
// Not logged in - show marketing/landing page
return (
<Layout
activeCity="Melbourne"
onSignInClick={onSignInClick}
onSignOutClick={onSignOutClick}
user={user}
>
<div className="min-h-screen bg-background">
{/* Hero Section */}
<section className="relative pt-52 pb-20 overflow-hidden">
{/* Background gradient */}
<div className="absolute inset-0 bg-gradient-to-br from-primary/5 via-secondary/5 to-background">
<ImageWithFallback
src="https://images.unsplash.com/photo-1758787397603-bbd85bd06a2c?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxNZWxib3VybmUlMjB0cmF2ZWwlMjBkZXN0aW5hdGlvbnxlbnwxfHx8fDE3NjI0MjQ3NTV8MA&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral"
alt="Melbourne Travel Destination"
className="w-full h-full object-cover opacity-20"
/>
<div className="absolute inset-0 bg-gradient-to-b from-white/40 via-white/60 to-white/90"></div>
</div>
<div className="container mx-auto px-4 relative z-10">
<motion.div
className="max-w-4xl mx-auto text-center"
initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6 }}
>
<h1 className="font-poppins text-4xl md:text-5xl lg:text-6xl leading-tight mb-6">
<span className="font-light">Unlock</span>{' '}
<span className="font-bold italic bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent">
Super Savings
</span>
</h1>
<p className="font-poppins text-lg md:text-xl leading-relaxed text-gray-600 mb-8 max-w-2xl mx-auto">
Experience incredible discounts up to 65% off on premium experiences, luxury stays, and unforgettable attractions.
</p>
<Button
onClick={onSignInClick}
className="bg-primary hover:bg-primary/90 text-white px-8 py-6 font-poppins font-semibold"
>
Start Saving Now
</Button>
</motion.div>
</div>
{/* Decorative elements */}
<div className="absolute top-20 left-10 w-20 h-20 bg-primary/10 rounded-full blur-xl"></div>
<div className="absolute bottom-20 right-10 w-32 h-32 bg-secondary/10 rounded-full blur-xl"></div>
</section>
{/* Trusted By Companies Section */}
<section className="py-12 bg-background">
<div className="container mx-auto px-4">
<div className="max-w-6xl mx-auto text-center">
<div className="mb-10">
<h2 className="font-poppins text-2xl md:text-3xl lg:text-4xl leading-tight mb-4">
<span>Trusted by the </span>
<span className="font-semibold bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent">world's best</span>
</h2>
<p className="font-poppins leading-relaxed text-muted-foreground max-w-2xl mx-auto">
Join thousands of savvy travelers enjoying massive savings on premium experiences
</p>
</div>
<TrustedCompanies />
</div>
</div>
</section>
{/* Featured Super Savings Section */}
<section className="py-20">
<div className="container mx-auto px-4">
<motion.div
className="text-center mb-12"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6 }}
>
<h2 className="font-poppins text-3xl md:text-4xl lg:text-5xl leading-tight mb-4">
<span className="font-light">Featured</span>{' '}
<span className="font-bold italic bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent">
Super Savings
</span>
</h2>
<p className="font-poppins leading-relaxed text-gray-600 max-w-2xl mx-auto">
Check out our biggest discounts and start saving on premium experiences
</p>
</motion.div>
<div className="container mx-auto px-4 pt-51 pb-16">
<div className="flex gap-8">
{/* Left Sidebar - Filters */}
<div className="w-64 flex-shrink-0">
<Card className="p-8 sticky top-48">
<div className="space-y-6">
{/* Search by header */}
<div className="flex items-center gap-4">
<div className="h-0 w-6 border-t-[3px] border-gray-800 rotate-90"></div>
<h3 className="font-poppins font-medium text-gray-800">Search by</h3>
</div>
{/* Filter categories */}
<div className="space-y-4">
{filterCategories.map(category => (
<div key={category.value} className="flex items-center gap-3">
<Checkbox
id={category.value}
checked={selectedCategories.includes(category.value)}
onCheckedChange={() => toggleCategory(category.value)}
className="border-gray-400"
/>
<label
htmlFor={category.value}
className="font-poppins text-sm text-gray-700 cursor-pointer flex-1"
>
{category.label} ({category.count})
</label>
</div>
))}
</div>
</div>
</Card>
</div>
{/* Main Content */}
<div className="flex-1">
{/* Breadcrumb */}
<div className="mb-8">
<p className="font-poppins text-sm text-gray-800">
{fromSource === 'passes' ? (
<>
<span>My Profile{'>'}My passes{'>'}</span>
<span className="font-semibold">Super Savings</span>
</>
) : (
<>
<span>Our Products{'>'}</span>
<span className="font-semibold">Super Savings</span>
</>
)}
</p>
</div>
{/* Header Section */}
<div className="mb-8">
<h1 className="font-poppins md:text-5xl font-medium text-gray-800 leading-tight text-[24px]">
Super Savings
</h1>
<p className="font-poppins text-gray-600 mt-2">
Exclusive discounts up to 65% off on premium experiences
</p>
</div>
{/* Savings Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-2 xl:grid-cols-3 gap-6 mb-16">
{displayedSavings.map((saving, index) => (
<motion.div
key={saving.id}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: index * 0.1 }}
>
<Card className="bg-white border border-gray-200 rounded-xl overflow-hidden h-full hover:shadow-lg transition-shadow duration-300 relative">
{/* Image */}
<div className="relative h-52 bg-gray-300">
<ImageWithFallback
src={saving.image}
alt={saving.title}
className="w-full h-full object-cover"
/>
<Button className="absolute bottom-4 right-3 bg-white rounded-full shadow-lg w-9 h-9 p-0 hover:bg-gray-100 transition-colors">
<Heart className="w-4 h-4 text-gray-800" />
</Button>
{/* Discount Badge */}
<div className="absolute top-4 left-4 bg-primary text-white px-3 py-1.5 rounded-lg">
<span className="font-poppins font-semibold text-sm">{saving.discount}</span>
</div>
</div>
<CardContent className="space-y-4 px-4 py-4">
{/* Business Name */}
<div className="flex items-center gap-2">
<div className="w-4 h-4 bg-gray-300 rounded"></div>
<span className="font-poppins text-sm text-gray-500">{saving.business}</span>
</div>
{/* Title */}
<h3 className="font-poppins font-medium text-gray-900 leading-relaxed min-h-[48px]">
{saving.title}
</h3>
{/* Saved Amount Display */}
<div className="bg-gradient-to-r from-primary/10 to-secondary/10 h-12 flex items-center justify-center rounded-lg">
<div className="flex items-center gap-2">
<Percent className="w-4 h-4 text-primary" />
<span className="font-poppins font-semibold text-primary">
{saving.savedAmount}
</span>
</div>
</div>
</CardContent>
</Card>
</motion.div>
))}
</div>
{/* Minimal Pagination */}
<div className="flex justify-center py-8">
<div className="flex items-center gap-2">
<Button
variant="outline"
size="sm"
className="w-8 h-8 p-0 font-poppins"
disabled={currentPage_ === 1}
>
<ChevronRight className="w-4 h-4 rotate-180" />
</Button>
<div className="flex items-center gap-1">
{[1, 2, 3].map((page) => (
<Button
key={page}
variant={currentPage_ === page ? "default" : "ghost"}
size="sm"
className={`w-8 h-8 p-0 font-poppins ${currentPage_ === page ? 'bg-primary hover:bg-primary/90' : ''}`}
onClick={() => setCurrentPage_(page)}
>
{page}
</Button>
))}
</div>
<Button
variant="outline"
size="sm"
className="w-8 h-8 p-0 font-poppins"
disabled={currentPage_ === 3}
>
<ChevronRight className="w-4 h-4" />
</Button>
</div>
</div>
</div>
</div>
</div>
<div className="text-center">
<Button
onClick={onSignInClick}
variant="outline"
className="font-poppins font-medium border-primary text-primary hover:bg-primary hover:text-white"
>
View All Super Savings
</Button>
</div>
</div>
</section>
{/* How It Works Section */}
<section className="py-24 relative overflow-hidden bg-[#FFF5F5]/50">
{/* Background decorative elements */}
<div className="absolute top-0 right-0 w-[600px] h-[600px] bg-primary/5 rounded-full blur-[100px] pointer-events-none -mr-40 -mt-40"></div>
<div className="absolute bottom-0 left-0 w-[400px] h-[400px] bg-primary/5 rounded-full blur-[80px] pointer-events-none -ml-20 -mb-20"></div>
<div className="container mx-auto px-4 relative z-10">
<motion.div
className="text-center mb-20"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.6 }}
>
<div className="flex items-center justify-center gap-3 mb-4">
<div className="h-px w-8 bg-primary"></div>
<span className="text-primary font-semibold tracking-widest uppercase text-xs font-poppins">Simple Process</span>
<div className="h-px w-8 bg-primary"></div>
</div>
<h2 className="font-merchant text-4xl md:text-5xl lg:text-6xl text-gray-900 mb-6">
Start Saving in <span className="text-primary italic">Minutes</span>
</h2>
<p className="font-poppins text-lg text-gray-500 font-light max-w-xl mx-auto leading-relaxed">
Your journey to smarter travel and bigger savings begins with three simple steps.
</p>
</motion.div>
<div className="relative max-w-6xl mx-auto">
{/* Connecting line for desktop */}
<div className="hidden md:block absolute top-12 left-[16%] right-[16%] h-0.5 border-t-2 border-dashed border-primary/20 z-0"></div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-12 md:gap-8">
{[
{
step: '01',
title: 'Unlock Access',
description: 'Get your CityCards pass to instantly activate membership perks.',
icon: MapPinned
},
{
step: '02',
title: 'Discover Deals',
description: 'Browse exclusive offers on hotels, flights, and experiences.',
icon: Search
},
{
step: '03',
title: 'Enjoy Savings',
description: 'Redeem discounts instantly and watch your travel budget grow.',
icon: Percent
}
].map((item, index) => (
<motion.div
key={item.step}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: index * 0.2 }}
className="relative z-10 group"
>
<div className="flex flex-col items-center text-center">
{/* Icon Container */}
<div className="relative mb-8">
<div className="w-24 h-24 rounded-[2rem] bg-white flex items-center justify-center shadow-[0_8px_30px_rgb(0,0,0,0.06)] group-hover:scale-110 group-hover:-rotate-3 transition-transform duration-300 border border-primary/10 relative overflow-hidden">
<div className="absolute inset-0 bg-primary/5 group-hover:bg-primary/10 transition-colors duration-300"></div>
<item.icon className="w-10 h-10 text-primary relative z-10" />
</div>
<div className="absolute -top-3 -right-3 w-8 h-8 rounded-full bg-primary text-white flex items-center justify-center font-bold font-poppins text-sm border-4 border-white shadow-md">
{index + 1}
</div>
</div>
<h3 className="font-merchant text-2xl text-gray-900 mb-3 group-hover:text-primary transition-colors">
{item.title}
</h3>
<p className="font-poppins text-gray-500 font-light leading-relaxed max-w-xs">
{item.description}
</p>
</div>
</motion.div>
))}
</div>
</div>
</div>
</section>
{/* Categories Section */}
<section className="py-24 bg-gray-50/50 relative overflow-hidden">
{/* Abstract Travel Patterns */}
<div className="absolute top-0 right-0 w-[800px] h-[800px] bg-gradient-to-bl from-primary/5 via-secondary/5 to-transparent rounded-full blur-3xl pointer-events-none -mr-40 -mt-40"></div>
<div className="container mx-auto px-4 relative z-10">
{/* Section Header */}
<div className="flex flex-col md:flex-row items-end justify-between mb-12 gap-6">
<motion.div
className="max-w-2xl"
initial={{ opacity: 0, x: -20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.6 }}
>
<div className="flex items-center gap-2 mb-4">
<div className="h-px w-8 bg-primary"></div>
<span className="text-primary font-semibold tracking-widest uppercase text-xs font-poppins">Explore Collections</span>
</div>
<h2 className="font-merchant text-4xl md:text-5xl lg:text-6xl text-gray-900 mb-4 leading-tight">
Curated for the <span className="text-primary italic">Modern Traveler</span>
</h2>
<p className="font-poppins text-lg text-gray-500 font-light max-w-lg leading-relaxed">
Discover exclusive savings across our most sought-after travel categories.
</p>
</motion.div>
<motion.div
initial={{ opacity: 0, x: 20 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.6 }}
className="hidden md:block"
>
<Button
onClick={onSignInClick}
variant="ghost"
className="group gap-2 text-gray-600 hover:text-primary font-poppins text-lg"
>
View All Categories
<span className="w-8 h-8 rounded-full bg-gray-100 flex items-center justify-center group-hover:bg-primary group-hover:text-white transition-all duration-300">
<ChevronRight className="w-4 h-4" />
</span>
</Button>
</motion.div>
</div>
{/* Bento Grid Layout */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{categoriesData.map((category, index) => (
<motion.div
key={category.title}
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4, delay: index * 0.1 }}
className={`${index === 0 ? 'md:col-span-2' : ''} group`}
>
<div
onClick={onSignInClick}
className={`
relative h-full bg-white rounded-[2rem] p-8 border border-gray-100
shadow-[0_4px_20px_-4px_rgba(0,0,0,0.05)] hover:shadow-[0_20px_40px_-10px_rgba(0,0,0,0.1)]
transition-all duration-500 cursor-pointer overflow-hidden flex flex-col justify-between
group-hover:border-primary/20
`}
>
{/* Background Gradient Hover */}
<div className={`absolute inset-0 bg-gradient-to-br ${category.color} opacity-0 group-hover:opacity-[0.03] transition-opacity duration-500`}></div>
{/* Large Watermark Icon for visual depth */}
<category.icon className="absolute -bottom-8 -right-8 w-48 h-48 text-gray-50 group-hover:text-primary/5 transition-colors duration-500 -rotate-12" />
<div className="relative z-10">
<div className="flex justify-between items-start mb-6">
<div className={`
w-14 h-14 rounded-2xl flex items-center justify-center shadow-sm transition-all duration-300
${index === 0 ? 'bg-primary text-white shadow-primary/20' : 'bg-gray-50 text-gray-600 group-hover:bg-primary group-hover:text-white'}
`}>
<category.icon className="w-7 h-7" />
</div>
<Badge className="bg-emerald-50 text-emerald-600 border-emerald-100 font-poppins font-medium px-3 py-1.5">
{category.savings}
</Badge>
</div>
<h3 className={`font-merchant text-gray-900 mb-2 group-hover:text-primary transition-colors ${index === 0 ? 'text-4xl' : 'text-2xl'}`}>
{category.title}
</h3>
<p className="font-poppins text-gray-500 font-light leading-relaxed max-w-md">
{category.description}
</p>
</div>
<div className="relative z-10 mt-8 flex items-center gap-2 text-primary font-poppins font-medium text-sm opacity-0 group-hover:opacity-100 transform translate-y-2 group-hover:translate-y-0 transition-all duration-300">
<span>Explore Deals</span>
<ArrowLeft className="w-4 h-4 rotate-180" />
</div>
</div>
</motion.div>
))}
</div>
{/* Mobile View All Button */}
<div className="mt-8 md:hidden text-center">
<Button
onClick={onSignInClick}
className="w-full bg-primary hover:bg-primary/90 text-white font-poppins font-semibold py-6 rounded-xl shadow-lg shadow-primary/20"
>
Browse All Categories
</Button>
</div>
</div>
</section>
{/* Access Your CityCards Section */}
<section className="py-20 bg-muted/30">
<MobileAppSection />
</section>
</div>
</Layout>
);
}
}

View File

@@ -151,7 +151,7 @@ export function TrustSection() {
<div className="text-center mb-16"> <div className="text-center mb-16">
<h2 className="font-merchant text-2xl md:text-3xl lg:text-4xl leading-tight mb-6 text-gray-900"> <h2 className="font-merchant text-2xl md:text-3xl lg:text-4xl leading-tight mb-6 text-gray-900">
<span className="font-light">What Our</span>{' '} <span className="font-light">What Our</span>{' '}
<span className="font-bold italic bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent"> <span className="font-bold italic bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent pr-1">
Travelers Travelers
</span>{' '} </span>{' '}
<span className="font-light">Say</span> <span className="font-light">Say</span>

View File

@@ -99,10 +99,13 @@ export function WhatsIncludedHero({ onCreateItineraryClick }: WhatsIncludedHeroP
className="text-center max-w-4xl" className="text-center max-w-4xl"
> >
{/* Main Heading */} {/* Main Heading */}
<h1 className="font-poppins text-4xl sm:text-5xl md:text-6xl leading-tight mb-6"> <h1 className="font-poppins text-4xl sm:text-5xl md:text-6xl w-full leading-tight mb-6">
<span className="font-light">One pass.</span>{' '} <span className="font-light">One CityCard</span>{' '}
<span className="font-bold italic bg-gradient-to-r from-primary via-orange-500 to-rose-500 bg-clip-text text-transparent"> <span className="font-bold italic pr-2 bg-gradient-to-r from-primary via-orange-500 to-rose-500 bg-clip-text text-transparent">
Everything you need Everything you
</span>{' '}
<span className="font-bold italic pr-2 bg-gradient-to-r from-primary via-orange-500 to-rose-500 bg-clip-text text-transparent">
need
</span>{' '} </span>{' '}
<span className="font-light">to explore.</span> <span className="font-light">to explore.</span>
</h1> </h1>

View File

@@ -1,9 +1,12 @@
// AuthContext.tsx
import React, { createContext, useContext, useEffect, useState } from 'react' import React, { createContext, useContext, useEffect, useState } from 'react'
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
interface User { interface User {
email: string; email: string;
name: string name: string;
accessToken:string;
userId:string;
} }
interface AuthContextType { interface AuthContextType {
@@ -29,11 +32,17 @@ export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
const login = (userData: User) => { const login = (userData: User) => {
setUser(userData) setUser(userData)
localStorage.setItem("user", JSON.stringify(userData)) localStorage.setItem("user", JSON.stringify(userData))
localStorage.setItem("accessToken", userData?.accessToken)
localStorage.setItem("userId", userData?.userId)
} }
const logout = () => { const logout = () => {
setUser(null) setUser(null)
localStorage.removeItem("user") localStorage.removeItem("user")
localStorage.removeItem("accessToken")
localStorage.removeItem("userId")
localStorage.removeItem("userEmail")
sessionStorage.removeItem("citySelected")
navigate("/") navigate("/")
} }

2
src/global.d.ts vendored
View File

@@ -32,3 +32,5 @@ declare module '*.mp4' {
const src: string; const src: string;
export default src; export default src;
} }
declare module "*.css";

View File

@@ -1,345 +0,0 @@
# Image Reference Guidelines
**IMPORTANT**: When I provide an image for reference, it is for design reference only. Do NOT use that image inside any image section in the code. The image is provided to help understand the layout, styling, and visual direction - not to be embedded as an actual image in the application.
# CityCards Typography Guidelines
**Project**: Typography Guidelines for CityCards Travel Website
## Font System
### Primary Font
- **Poppins**: Used for all text including headings (H1H6), body text, buttons, labels, and forms - Clean, readable, and consistent throughout
### Font Weight Scale & Usage
- **font-light (300)**: Hero headings only - For creating dynamic contrast in H1/H2
- **font-normal (400)**: Standard body text - Default weight for paragraphs
- **font-medium (500)**: Buttons, navigation links, subtle emphasis
- **font-semibold (600)**: Section headings, primary buttons
- **font-bold (700)**: Hero keywords, strong emphasis
## Heading Typography Specifications
### H1 - Hero/Main Page Headings
- **Font**: Poppins
- **Size**: `text-5xl md:text-6xl lg:text-7xl` (48px/60px/72px - targeting ~64px)
- **Line Height**: `leading-tight`
- **Pattern**: Dynamic multi-weight with gradient/italic accents (max 2 emphasis styles)
```jsx
<h1 className="font-poppins text-5xl md:text-6xl lg:text-7xl leading-tight">
<span className="font-light">Discover</span>{' '}
<span className="font-bold italic bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent">
Melbourne's
</span>{' '}
<span className="font-normal">Best Experiences</span>
</h1>
```
### H2 - Section Headings
- **Font**: Poppins
- **Size**: `text-2xl md:text-3xl lg:text-4xl` (24px/36px/48px)
- **Line Height**: `leading-tight`
- **Pattern**: Mixed weights with gradient emphasis
```jsx
<h2 className="font-poppins text-2xl md:text-3xl lg:text-4xl leading-tight">
<span className="font-light">Explore</span>{' '}
<span className="font-bold italic bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent">
Amazing
</span>{' '}
<span className="font-normal">Cities</span>
</h2>
```
### H3 - Subsection Headings
- **Font**: Poppins
- **Size**: `text-xl md:text-2xl` (24px/30px)
- **Line Height**: `leading-snug`
- **Weight**: `font-semibold`
```jsx
<h3 className="font-poppins text-xl md:text-2xl leading-snug font-semibold">
Feature Title
</h3>
```
### H4 - Component Headings
- **Font**: Poppins
- **Size**: `text-lg md:text-xl` (20px/24px)
- **Line Height**: `leading-snug`
- **Weight**: `font-medium` or `font-semibold`
```jsx
<h4 className="font-poppins text-lg md:text-xl leading-snug font-medium">
Component Heading
</h4>
```
### H5 - Card/Item Titles
- **Font**: Poppins
- **Size**: `text-lg` (18px)
- **Line Height**: `leading-snug`
- **Weight**: `font-medium`
```jsx
<h5 className="font-poppins text-lg leading-snug font-medium">
Card Title
</h5>
```
### H6 - Small Headings
- **Font**: Poppins
- **Size**: `text-base` (16px)
- **Line Height**: `leading-snug`
- **Weight**: `font-medium`
```jsx
<h6 className="font-poppins text-base leading-snug font-medium">
Small Heading
</h6>
```
## Body Typography Specifications
### Large Body Text
- **Font**: Poppins
- **Size**: `text-xl` (20px)
- **Line Height**: `leading-relaxed`
- **Weight**: `font-normal`
```jsx
<p className="font-poppins text-xl leading-relaxed font-normal">
Large descriptive text for important sections
</p>
```
### Regular Body Text
- **Font**: Poppins
- **Size**: `text-base` (16px)
- **Line Height**: `leading-relaxed`
- **Weight**: `font-normal`
```jsx
<p className="font-poppins text-base leading-relaxed font-normal">
Regular body text content
</p>
```
### Small Body Text
- **Font**: Poppins
- **Size**: `text-sm` (14px)
- **Line Height**: `leading-relaxed`
- **Weight**: `font-normal` or `font-light`
```jsx
<p className="font-poppins text-sm leading-relaxed font-normal">
Caption or meta information
</p>
```
## Interactive Element Typography
### Buttons
- **Font**: Poppins
- **Primary Weight**: `font-semibold`
- **Secondary Weight**: `font-medium`
- **Min Size**: 16px
```jsx
// Primary Button
<Button className="font-poppins font-semibold">
Primary Action
</Button>
// Secondary Button
<Button className="font-poppins font-medium">
Secondary Action
</Button>
```
### Navigation Links
- **Font**: Poppins
- **Weight**: `font-medium`
- **Size**: `text-base` (16px)
```jsx
<a className="font-poppins font-medium text-base">
Navigation Link
</a>
```
### Form Labels
- **Font**: Poppins
- **Weight**: `font-light` or `font-normal`
- **Size**: `text-sm` or `text-base` (14px/16px)
```jsx
<Label className="font-poppins font-light text-sm">
Form Label
</Label>
```
### Form Inputs
- **Font**: Poppins
- **Weight**: `font-normal`
- **Size**: `text-base` (16px)
```jsx
<Input className="font-poppins font-normal text-base" />
```
## Accessibility Standards
### Text Size Requirements
- **Minimum Text Size**: 14px
- **Interactive Minimum Size**: 16px
- **Contrast**: WCAG AA or higher
- **Heading Hierarchy**: Maintain semantic order (H1 → H2 → H3 etc.)
### Implementation Requirements
```jsx
// Always include explicit font and size classes to override defaults
<p className="font-poppins text-base font-normal leading-relaxed">
Content with explicit styling
</p>
```
## Typography Rules
### DO ✅
- Use Poppins for all text (headings and body)
- Apply max 2 emphasis styles per heading
- Use gradient effects sparingly
- Keep line-heights consistent
- Always specify explicit font classes to override component defaults
### DON'T ❌
- Don't use font-light in small text
- Don't mix more than 3 weights in one heading
- Don't go below 14px for captions
- Don't override font sizes without Tailwind classes
- Don't break semantic heading hierarchy
## Implementation Guidelines
### Component Styling Override
**IMPORTANT**: Always explicitly set typography classes to override component defaults:
```jsx
// ✅ CORRECT - Explicit typography classes
<Card>
<CardHeader>
<CardTitle className="font-poppins text-xl font-semibold">
Card Title
</CardTitle>
</CardHeader>
<CardContent>
<p className="font-poppins text-base font-normal leading-relaxed">
Card content with explicit styling
</p>
</CardContent>
</Card>
// ❌ INCORRECT - Relying on defaults
<Card>
<CardHeader>
<CardTitle>Card Title</CardTitle>
</CardHeader>
<CardContent>
<p>Card content without explicit styling</p>
</CardContent>
</Card>
```
### Dynamic Heading Patterns
```jsx
// Pattern 1: Light → Bold (H1/H2 only)
<h1 className="font-poppins text-4xl md:text-5xl lg:text-6xl leading-tight">
<span className="font-light">Discover</span>{' '}
<span className="font-bold italic bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent">
Amazing
</span>{' '}
<span className="font-normal">Destinations</span>
</h1>
// Pattern 2: Normal → Semibold (H3/H4)
<h3 className="font-poppins text-xl md:text-2xl leading-snug">
<span className="font-normal">Experience</span>{' '}
<span className="font-semibold">Melbourne's Culture</span>
</h3>
```
### Responsive Typography
```jsx
// Mobile-first responsive scaling
<h1 className="font-poppins text-3xl sm:text-4xl md:text-5xl lg:text-6xl leading-tight">
Responsive Heading
</h1>
<p className="font-poppins text-sm sm:text-base md:text-lg leading-relaxed font-normal">
Responsive body text
</p>
```
**Add your own guidelines here**
<!--
System Guidelines
Use this file to provide the AI with rules and guidelines you want it to follow.
This template outlines a few examples of things you can add. You can add your own sections and format it to suit your needs
TIP: More context isn't always better. It can confuse the LLM. Try and add the most important rules you need
# General guidelines
Any general rules you want the AI to follow.
For example:
* Only use absolute positioning when necessary. Opt for responsive and well structured layouts that use flexbox and grid by default
* Refactor code as you go to keep code clean
* Keep file sizes small and put helper functions and components in their own files.
--------------
# Design system guidelines
Rules for how the AI should make generations look like your company's design system
Additionally, if you select a design system to use in the prompt box, you can reference
your design system's components, tokens, variables and components.
For example:
* Use a base font-size of 14px
* Date formats should always be in the format “Jun 10”
* The bottom toolbar should only ever have a maximum of 4 items
* Never use the floating action button with the bottom toolbar
* Chips should always come in sets of 3 or more
* Don't use a dropdown if there are 2 or fewer options
You can also create sub sections and add more specific details
For example:
## Button
The Button component is a fundamental interactive element in our design system, designed to trigger actions or navigate
users through the application. It provides visual feedback and clear affordances to enhance user experience.
### Usage
Buttons should be used for important actions that users need to take, such as form submissions, confirming choices,
or initiating processes. They communicate interactivity and should have clear, action-oriented labels.
### Variants
* Primary Button
* Purpose : Used for the main action in a section or page
* Visual Style : Bold, filled with the primary brand color
* Usage : One primary button per section to guide users toward the most important action
* Secondary Button
* Purpose : Used for alternative or supporting actions
* Visual Style : Outlined with the primary color, transparent background
* Usage : Can appear alongside a primary button for less important actions
* Tertiary Button
* Purpose : Used for the least important actions
* Visual Style : Text-only with no border, using primary color
* Usage : For actions that should be available but not emphasized
-->

View File

@@ -0,0 +1,916 @@
import svgPaths from "./svg-adqhdgpsh";
import imgPhoto from "figma:asset/895585e16a5ef99513fed52e650cc187c5d00f0a.png";
import imgRectangle36 from "figma:asset/c878c3c798a222b8d2bc4e2bd140d905882d2175.png";
import imgRectangle37 from "figma:asset/a125d02d1ff3e548286c149140a80da8c96f2b93.png";
function Confetti() {
return (
<div className="absolute h-[600.579px] left-0 top-[729.79px] w-[392px]" data-name="Confetti">
<svg className="absolute block inset-0 size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 32 32">
<g id="Confetti" opacity="0">
<path d={svgPaths.p30846ac0} fill="var(--fill-0, #F266FF)" id="Vector" />
<path d={svgPaths.p1af8f150} id="Vector_2" stroke="var(--stroke-0, #A1FF66)" strokeLinecap="round" strokeWidth="3" />
<path d={svgPaths.p2e546900} fill="var(--fill-0, #66FFA8)" id="Vector_3" />
<path d={svgPaths.p2a600580} id="Vector_4" stroke="var(--stroke-0, #8C66FF)" strokeLinecap="round" strokeWidth="4" />
<path d={svgPaths.p3b167700} fill="var(--fill-0, #66D9FF)" id="Vector_5" />
<path d={svgPaths.p114f0d00} id="Vector_6" stroke="var(--stroke-0, #66CCFF)" strokeLinecap="round" strokeWidth="4" />
<path d={svgPaths.p6b82b00} fill="var(--fill-0, #7D66FF)" id="Vector_7" />
<path d={svgPaths.p3a1b1dc0} id="Vector_8" stroke="var(--stroke-0, #E066FF)" strokeLinecap="round" strokeWidth="3" />
<path d={svgPaths.p1f72dd00} id="Vector_9" stroke="var(--stroke-0, #66FFED)" strokeLinecap="round" strokeWidth="4" />
<path d={svgPaths.p31508a60} id="Vector_10" stroke="var(--stroke-0, #FFC766)" strokeLinecap="round" strokeWidth="4" />
<path d={svgPaths.p3e65c400} id="Vector_11" stroke="var(--stroke-0, #FF6E66)" strokeLinecap="round" strokeWidth="3" />
<path d={svgPaths.p1ca023c0} id="Vector_12" stroke="var(--stroke-0, #E8FF66)" strokeLinecap="round" strokeWidth="4" />
<path d={svgPaths.p36217800} fill="var(--fill-0, #DEFF66)" id="Vector_13" />
<path d={svgPaths.p13f60480} id="Vector_14" stroke="var(--stroke-0, #66FFE8)" strokeLinecap="round" strokeWidth="4" />
<path d={svgPaths.p125d3600} fill="var(--fill-0, #A6FF66)" id="Vector_15" />
<path d={svgPaths.p10f7c300} fill="var(--fill-0, #85FF66)" id="Vector_16" />
<path d={svgPaths.p365e9600} fill="var(--fill-0, #66FFBF)" id="Vector_17" />
<path d={svgPaths.p1617b600} fill="var(--fill-0, #66FFB5)" id="Vector_18" />
<path d={svgPaths.p2f540d00} fill="var(--fill-0, #D966FF)" id="Vector_19" />
<path d={svgPaths.p26750500} fill="var(--fill-0, #66FF7F)" id="Vector_20" />
<path d={svgPaths.pca2f800} id="Vector_21" stroke="var(--stroke-0, #FFB866)" strokeLinecap="round" strokeWidth="4" />
<path d={svgPaths.p130d1600} fill="var(--fill-0, #B8FF66)" id="Vector_22" />
<path d={svgPaths.p112ca500} id="Vector_23" stroke="var(--stroke-0, #66FFC9)" strokeLinecap="round" strokeWidth="4" />
<path d={svgPaths.p30587f00} id="Vector_24" stroke="var(--stroke-0, #FF66B5)" strokeLinecap="round" strokeWidth="3" />
<path d={svgPaths.p2c53d00} fill="var(--fill-0, #FFB866)" id="Vector_25" />
<path d={svgPaths.p245b5500} fill="var(--fill-0, #FFDB66)" id="Vector_26" />
<path d={svgPaths.p13ac0f00} fill="var(--fill-0, #D4FF66)" id="Vector_27" />
<path d={svgPaths.p2d198000} id="Vector_28" stroke="var(--stroke-0, #66FF96)" strokeLinecap="round" strokeWidth="4" />
<path d={svgPaths.pc00e050} id="Vector_29" stroke="var(--stroke-0, #66FFC2)" strokeLinecap="round" strokeWidth="4" />
<path d={svgPaths.p1ae38580} id="Vector_30" stroke="var(--stroke-0, #66FF7D)" strokeLinecap="round" strokeWidth="3" />
<path d={svgPaths.p1d9db000} fill="var(--fill-0, #FFB366)" id="Vector_31" />
<path d={svgPaths.pcd62e8} id="Vector_32" stroke="var(--stroke-0, #F266FF)" strokeLinecap="round" strokeWidth="4" />
<path d={svgPaths.pa248b00} fill="var(--fill-0, #6699FF)" id="Vector_33" />
<path d={svgPaths.p23dc0000} id="Vector_34" stroke="var(--stroke-0, #FF7366)" strokeLinecap="round" strokeWidth="4" />
<path d={svgPaths.p21984100} fill="var(--fill-0, #FF6669)" id="Vector_35" />
<path d={svgPaths.p315a1e30} fill="var(--fill-0, #FF8266)" id="Vector_36" />
<path d={svgPaths.p36d2ba80} id="Vector_37" stroke="var(--stroke-0, #E366FF)" strokeLinecap="round" strokeWidth="4" />
<path d={svgPaths.pee931c0} fill="var(--fill-0, #FF66E8)" id="Vector_38" />
<path d={svgPaths.p23d70180} fill="var(--fill-0, #66FF8C)" id="Vector_39" />
<path d={svgPaths.p3ba87480} fill="var(--fill-0, #66FFF5)" id="Vector_40" />
<path d="M14.4383 38.0174L0 4.01739" id="Vector_41" stroke="var(--stroke-0, #FF7566)" strokeLinecap="round" strokeWidth="3" />
<path d={svgPaths.p1d1bf900} id="Vector_42" stroke="var(--stroke-0, #8AFF66)" strokeLinecap="round" strokeWidth="4" />
<path d={svgPaths.p39b4f00} fill="var(--fill-0, #ED66FF)" id="Vector_43" />
<path d={svgPaths.p2369e2c0} id="Vector_44" stroke="var(--stroke-0, #FF6682)" strokeLinecap="round" strokeWidth="4" />
<path d={svgPaths.p21a2df00} fill="var(--fill-0, #FF66BD)" id="Vector_45" />
<path d={svgPaths.p245cc9b0} fill="var(--fill-0, #66FFF2)" id="Vector_46" />
<path d={svgPaths.p293abff0} fill="var(--fill-0, #FFB866)" id="Vector_47" />
<path d={svgPaths.p28906180} fill="var(--fill-0, #66E6FF)" id="Vector_48" />
<path d={svgPaths.p3282b480} fill="var(--fill-0, #FF66ED)" id="Vector_49" />
<path d={svgPaths.p3d2fe400} id="Vector_50" stroke="var(--stroke-0, #667AFF)" strokeLinecap="round" strokeWidth="4" />
<path d={svgPaths.p1fd17c80} fill="var(--fill-0, #FF6666)" id="Vector_51" />
<path d={svgPaths.p1ada2c00} fill="var(--fill-0, #FF667A)" id="Vector_52" />
<path d={svgPaths.p55e1500} id="Vector_53" stroke="var(--stroke-0, #66D1FF)" strokeLinecap="round" strokeWidth="4" />
<path d={svgPaths.p9491b80} fill="var(--fill-0, #FF7566)" id="Vector_54" />
<path d={svgPaths.p394df1c0} fill="var(--fill-0, #91FF66)" id="Vector_55" />
<path d={svgPaths.p39dd4ef0} fill="var(--fill-0, #CCFF66)" id="Vector_56" />
<path d={svgPaths.p38fea80} fill="var(--fill-0, #CCFF66)" id="Vector_57" />
<path d={svgPaths.p751e500} fill="var(--fill-0, #66FFA8)" id="Vector_58" />
<path d={svgPaths.p3ae84670} fill="var(--fill-0, #75FF66)" id="Vector_59" />
<path d={svgPaths.p195a2f80} fill="var(--fill-0, #FF66EB)" id="Vector_60" />
<path d={svgPaths.p1c14be00} fill="var(--fill-0, #66FF9E)" id="Vector_61" />
<path d={svgPaths.p302dfa00} fill="var(--fill-0, #FF6682)" id="Vector_62" />
<path d={svgPaths.p8f80e00} fill="var(--fill-0, #F266FF)" id="Vector_63" />
<path d={svgPaths.p37f77100} id="Vector_64" stroke="var(--stroke-0, #66FFEB)" strokeLinecap="round" strokeWidth="3" />
<path d={svgPaths.p31b9a200} fill="var(--fill-0, #BAFF66)" id="Vector_65" />
<path d={svgPaths.p1cfa9a70} fill="var(--fill-0, #66FFB0)" id="Vector_66" />
<path d={svgPaths.p273a0500} fill="var(--fill-0, #66FFF7)" id="Vector_67" />
<path d={svgPaths.p1621380} fill="var(--fill-0, #F766FF)" id="Vector_68" />
<path d={svgPaths.p32b87800} id="Vector_69" stroke="var(--stroke-0, #FF66ED)" strokeLinecap="round" strokeWidth="4" />
<path d={svgPaths.p9a6ab80} id="Vector_70" stroke="var(--stroke-0, #96FF66)" strokeLinecap="round" strokeWidth="4" />
<path d={svgPaths.p1261c6f0} fill="var(--fill-0, #FF66E8)" id="Vector_71" />
<path d={svgPaths.p31d95b00} fill="var(--fill-0, #6670FF)" id="Vector_72" />
<path d={svgPaths.p670ab80} fill="var(--fill-0, #66FF7F)" id="Vector_73" />
<path d={svgPaths.p20991b80} fill="var(--fill-0, #DE66FF)" id="Vector_74" />
<path d={svgPaths.pb0da100} id="Vector_75" stroke="var(--stroke-0, #BA66FF)" strokeLinecap="round" strokeWidth="3" />
<path d={svgPaths.p2dc248a0} id="Vector_76" stroke="var(--stroke-0, #FFAD66)" strokeLinecap="round" strokeWidth="3" />
<path d={svgPaths.p1b50ff00} fill="var(--fill-0, #8CFF66)" id="Vector_77" />
<path d={svgPaths.p33b66680} fill="var(--fill-0, #99FF66)" id="Vector_78" />
<path d={svgPaths.p1f83a980} fill="var(--fill-0, #66FFFF)" id="Vector_79" />
<path d="M361.68 6.01758L392 22.0176" id="Vector_80" stroke="var(--stroke-0, #FF66A8)" strokeLinecap="round" strokeWidth="4" />
</g>
</svg>
</div>
);
}
function Icon() {
return (
<div className="relative shrink-0 size-[15.984px]" data-name="Icon">
<svg className="absolute block inset-0 size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 15.9836 15.9836">
<g id="Icon">
<path d={svgPaths.pabdaa80} id="Vector" stroke="var(--stroke-0, #4A5565)" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.33197" />
<path d={svgPaths.p23a159d0} id="Vector_2" stroke="var(--stroke-0, #4A5565)" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.33197" />
<path d={svgPaths.p5521d80} id="Vector_3" stroke="var(--stroke-0, #4A5565)" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.33197" />
<path d={svgPaths.p6cb7a00} id="Vector_4" stroke="var(--stroke-0, #4A5565)" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.33197" />
</g>
</svg>
</div>
);
}
function CompletionScreen() {
return (
<div className="h-[42px] relative rounded-[36829000px] shrink-0 w-full" data-name="CompletionScreen">
<div aria-hidden="true" className="absolute border-[#e5e7eb] border-[1.098px] border-solid inset-0 pointer-events-none rounded-[36829000px]" />
<div className="flex flex-row items-center justify-center size-full">
<div className="bg-clip-padding border-0 border-[transparent] border-solid content-stretch flex gap-[8px] items-center justify-center px-[111px] py-[12px] relative size-full">
<Icon />
<p className="font-['Poppins:Medium',sans-serif] leading-[18px] not-italic relative shrink-0 text-[#4a5565] text-[16px] whitespace-nowrap">Start Over</p>
</div>
</div>
</div>
);
}
function Frame() {
return (
<div className="bg-[#f95f62] h-[42px] relative rounded-[38px] shrink-0 w-full">
<div className="flex flex-row items-center justify-center size-full">
<div className="bg-clip-padding border-0 border-[transparent] border-solid content-stretch flex items-center justify-center pl-[8px] pr-[12px] py-[12px] relative size-full">
<p className="font-['Poppins:Medium',sans-serif] leading-[18px] not-italic relative shrink-0 text-[16px] text-white whitespace-nowrap">Get My Trip Plan</p>
</div>
</div>
</div>
);
}
function Container() {
return (
<div className="absolute content-stretch flex flex-col gap-[12px] items-start justify-center left-[20.46px] opacity-0 top-[186px] w-[336px]" data-name="Container">
<CompletionScreen />
<Frame />
</div>
);
}
function HeaderText() {
return (
<div className="absolute content-stretch flex flex-col gap-[8px] items-start left-[20.46px] not-italic opacity-0 text-center top-[65px] w-[352.015px]" data-name="header text">
<p className="font-['Poppins:Medium',sans-serif] leading-[0] min-w-full relative shrink-0 text-[#101828] text-[24px] tracking-[-0.96px] w-[min-content]">
<span className="font-['Poppins:Regular',sans-serif] leading-[1.3] text-[rgba(16,24,40,0.6)]">Your</span>
<span className="leading-[1.3]">{` `}</span>
<span className="leading-[1.3] text-[#f95f62]">Magic Itinerary</span>
<span className="leading-[1.3]">{` `}</span>
<span className="font-['Poppins:Regular',sans-serif] leading-[1.3] text-[rgba(16,24,40,0.6)]">is</span>
<span className="leading-[1.3]">{` `}</span>
<span className="leading-[1.3] text-[#f95f62]">Ready</span>
<span className="leading-[1.3]">{``}</span>
</p>
<p className="font-['Poppins:Regular',sans-serif] leading-[1.6] relative shrink-0 text-[14px] text-[rgba(0,0,0,0.6)] w-[336px]">{`We've got everything we need to plan your perfect trip`}</p>
</div>
);
}
function Frame37() {
return (
<div className="absolute content-stretch flex flex-col font-['Poppins:SemiBold',sans-serif] gap-[6px] items-start leading-[0] left-[19.5px] not-italic text-[#101828] text-center top-[82px] w-[351.979px]">
<div className="flex flex-col h-[21.107px] justify-center relative shrink-0 text-[24px] w-full">
<p className="leading-[32px]">{`Your `}</p>
</div>
<div className="flex flex-col h-[21.107px] justify-center relative shrink-0 text-[0px] w-full">
<p className="text-[24px]">
<span className="leading-[32px] text-[#f95f62]">Melbourne</span>
<span className="leading-[32px]">{` `}</span>
<span className="leading-[32px] text-[#f95f62]">Magic Itinerary</span>
</p>
</div>
</div>
);
}
function Photo() {
return (
<div className="flex-[1_0_0] min-h-px min-w-px relative rounded-[2px] w-full" data-name="photo">
<img alt="" className="absolute inset-0 max-w-none object-cover pointer-events-none rounded-[2px] size-full" src={imgPhoto} />
</div>
);
}
function OuterFrame() {
return (
<div className="bg-white content-stretch flex flex-col items-center overflow-clip relative self-stretch shrink-0 w-[86px]" data-name="outer frame">
<Photo />
</div>
);
}
function Group1() {
return (
<div className="absolute inset-[11.11%]">
<div className="absolute inset-[-0.71%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 14.2 14.2">
<g id="Group 1000002093">
<path d={svgPaths.p1aba0880} fill="var(--fill-0, #F95F62)" id="Vector" />
<path d={svgPaths.p1fa04100} fill="var(--fill-0, #F95F62)" id="Vector_2" />
<path d={svgPaths.pe6ea010} fill="var(--fill-0, #F95F62)" id="Vector_3" />
<path d={svgPaths.pbdbaa00} fill="var(--fill-0, #F95F62)" id="Vector_4" />
<path d={svgPaths.p18d2ab00} fill="var(--fill-0, #F95F62)" id="Vector_5" />
<path d={svgPaths.p16eea180} fill="var(--fill-0, #F95F62)" id="Vector_6" />
<path clipRule="evenodd" d={svgPaths.p3e80b480} fill="var(--fill-0, #F95F62)" fillRule="evenodd" id="Vector_7" stroke="var(--stroke-0, white)" strokeWidth="0.2" />
</g>
</svg>
</div>
</div>
);
}
function Frame35() {
return (
<div className="relative shrink-0 size-[18px]">
<Group1 />
</div>
);
}
function Frame30() {
return (
<div className="content-center flex flex-wrap gap-[8px] items-center relative shrink-0">
<Frame35 />
<p className="font-['Poppins:Medium',sans-serif] leading-[18px] not-italic relative shrink-0 text-[#f95f62] text-[16px] whitespace-nowrap">3 Days</p>
</div>
);
}
function Frame36() {
return (
<div className="relative shrink-0 size-[18px]">
<svg className="absolute block inset-0 size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 18 18">
<g id="Frame 1597882141">
<path d={svgPaths.pcef7b00} fill="var(--fill-0, #F95F62)" id="Vector" stroke="var(--stroke-0, #F95F62)" strokeWidth="0.1" />
</g>
</svg>
</div>
);
}
function Frame31() {
return (
<div className="content-center flex flex-wrap gap-[8px] items-center relative shrink-0">
<Frame36 />
<p className="font-['Poppins:Medium',sans-serif] leading-[18px] not-italic relative shrink-0 text-[#f95f62] text-[16px] whitespace-nowrap">12 stops</p>
</div>
);
}
function Overview1() {
return (
<div className="content-stretch flex flex-col gap-[8px] items-start relative shrink-0 w-[237.5px]" data-name="overview">
<Frame30 />
<Frame31 />
</div>
);
}
function Frame32() {
return (
<div className="content-stretch flex flex-col gap-[8px] items-start relative shrink-0 w-full">
<div className="flex flex-col font-['Poppins:Regular',sans-serif] h-[9.672px] justify-center leading-[0] min-w-full not-italic relative shrink-0 text-[10px] text-[rgba(0,0,0,0.8)] uppercase w-[min-content]">
<p className="leading-[13.852px]">TRIP DETAILS:</p>
</div>
<Overview1 />
</div>
);
}
function Group() {
return (
<div className="absolute inset-[12.5%_5.95%_8.33%_8.34%]" data-name="Group">
<div className="absolute inset-[-5.26%_0_0_0]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 12 11.6667">
<g id="Group">
<path d={svgPaths.p4d87d80} fill="var(--fill-0, black)" fillOpacity="0.8" id="Vector" />
<path d={svgPaths.p3d2bae00} id="Vector_2" stroke="var(--stroke-0, #999999)" strokeLinecap="round" strokeWidth="1.16667" />
</g>
</svg>
</div>
</div>
);
}
function LetsIconsDateFill() {
return (
<div className="overflow-clip relative shrink-0 size-[14px]" data-name="lets-icons:date-fill">
<Group />
</div>
);
}
function Adults() {
return (
<div className="content-stretch flex gap-[8px] h-[16px] items-center justify-center relative shrink-0" data-name="adults">
<LetsIconsDateFill />
<p className="font-['Poppins:Regular',sans-serif] leading-[14px] not-italic relative shrink-0 text-[11px] text-[rgba(0,0,0,0.8)] tracking-[0.06px] whitespace-nowrap">22/02/2025</p>
</div>
);
}
function RiParentFill() {
return (
<div className="relative shrink-0 size-[14px]" data-name="ri:parent-fill">
<svg className="absolute block inset-0 size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 14 14">
<g id="ri:parent-fill">
<path d={svgPaths.p7670300} fill="var(--fill-0, black)" fillOpacity="0.8" id="Vector" />
</g>
</svg>
</div>
);
}
function Adults1() {
return (
<div className="content-stretch flex gap-[8px] h-[16px] items-center justify-center relative shrink-0" data-name="adults">
<RiParentFill />
<p className="font-['Poppins:Regular',sans-serif] leading-[14px] not-italic relative shrink-0 text-[11px] text-[rgba(0,0,0,0.8)] tracking-[0.06px] whitespace-nowrap">3 adults</p>
</div>
);
}
function FaSolidChild() {
return (
<div className="h-[14px] relative shrink-0 w-[10.5px]" data-name="fa-solid:child">
<svg className="absolute block inset-0 size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 10.5 14">
<g clipPath="url(#clip0_9924_1087)" id="fa-solid:child">
<path d={svgPaths.p2e64dc00} fill="var(--fill-0, black)" fillOpacity="0.8" id="Vector" />
</g>
<defs>
<clipPath id="clip0_9924_1087">
<rect fill="white" height="14" width="10.5" />
</clipPath>
</defs>
</svg>
</div>
);
}
function Kids() {
return (
<div className="content-stretch flex gap-[8px] h-[18.667px] items-center relative shrink-0" data-name="kids">
<FaSolidChild />
<p className="font-['Poppins:Regular',sans-serif] leading-[14px] not-italic relative shrink-0 text-[11px] text-[rgba(0,0,0,0.8)] tracking-[0.06px] whitespace-nowrap">3 kids</p>
</div>
);
}
function Frame21() {
return (
<div className="content-center flex flex-wrap gap-[12px] items-center relative shrink-0 w-full">
<Adults />
<Adults1 />
<Kids />
</div>
);
}
function Frame28() {
return (
<div className="content-stretch flex flex-col gap-[12px] items-start relative shrink-0 w-[237.5px]">
<Frame32 />
<Frame21 />
</div>
);
}
function Frame29() {
return (
<div className="content-stretch flex gap-[8px] items-start relative shrink-0 w-full">
<OuterFrame />
<Frame28 />
</div>
);
}
function MaterialSymbolsShare() {
return (
<div className="relative shrink-0 size-[16px]" data-name="material-symbols:share">
<svg className="absolute block inset-0 size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 16 16">
<g id="material-symbols:share">
<path d={svgPaths.p31f8ed00} fill="var(--fill-0, #F95F62)" id="Vector" />
</g>
</svg>
</div>
);
}
function Frame9() {
return (
<div className="bg-[rgba(249,95,98,0)] flex-[1_0_0] h-[44px] min-h-px min-w-px relative rounded-[8px]">
<div className="flex flex-row items-center justify-center overflow-clip rounded-[inherit] size-full">
<div className="content-stretch flex gap-[8px] items-center justify-center p-[8px] relative size-full">
<MaterialSymbolsShare />
<p className="font-['Poppins:Regular',sans-serif] leading-[16px] not-italic relative shrink-0 text-[#f95f62] text-[12px] whitespace-nowrap">Share</p>
</div>
</div>
<div aria-hidden="true" className="absolute border border-[#f95f62] border-solid inset-0 pointer-events-none rounded-[8px]" />
</div>
);
}
function DownloadMinimalisticSvgrepoCom() {
return (
<div className="relative shrink-0 size-[16px]" data-name="download-minimalistic_svgrepo.com">
<svg className="absolute block inset-0 size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 16 16">
<g id="download-minimalistic_svgrepo.com">
<path d={svgPaths.p13f2e80} id="Vector" stroke="var(--stroke-0, #F95F62)" strokeLinecap="round" strokeLinejoin="round" />
<path d={svgPaths.p21309b00} id="Vector_2" stroke="var(--stroke-0, #F95F62)" strokeLinecap="round" strokeLinejoin="round" />
</g>
</svg>
</div>
);
}
function Frame10() {
return (
<div className="bg-[rgba(249,95,98,0)] flex-[1_0_0] h-[44px] min-h-px min-w-px relative rounded-[8px]">
<div className="flex flex-row items-center justify-center overflow-clip rounded-[inherit] size-full">
<div className="content-stretch flex gap-[8px] items-center justify-center p-[8px] relative size-full">
<DownloadMinimalisticSvgrepoCom />
<p className="font-['Poppins:Regular',sans-serif] leading-[16px] not-italic relative shrink-0 text-[#f95f62] text-[12px] whitespace-nowrap">Download</p>
</div>
</div>
<div aria-hidden="true" className="absolute border border-[#f95f62] border-solid inset-0 pointer-events-none rounded-[8px]" />
</div>
);
}
function Frame11() {
return (
<div className="content-stretch flex gap-[12px] items-center relative shrink-0 w-full">
<Frame9 />
<Frame10 />
</div>
);
}
function Overview() {
return (
<div className="bg-white content-stretch flex flex-col gap-[8px] items-center p-[8px] relative rounded-[6px] shadow-[0px_23.087px_46.175px_0px_rgba(209,213,220,0.5)] shrink-0 w-[352px]" data-name="Overview">
<Frame29 />
<div className="h-0 relative shrink-0 w-full">
<div className="absolute inset-[-1px_0_0_0]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 336 1">
<line id="Line 631" stroke="var(--stroke-0, black)" strokeLinecap="round" strokeOpacity="0.04" x1="0.5" x2="335.5" y1="0.5" y2="0.5" />
</svg>
</div>
</div>
<Frame11 />
</div>
);
}
function Frame4() {
return (
<div className="bg-[#fee7e7] h-[50px] overflow-clip relative rounded-[100px] shrink-0 w-[353px]">
<div className="absolute bg-white content-stretch flex items-center justify-center left-[4px] overflow-clip px-[24px] py-[12px] rounded-[100px] top-[4px] w-[155px]" data-name="Button">
<p className="font-['Poppins:Medium',sans-serif] leading-[18px] not-italic relative shrink-0 text-[#2a2a2a] text-[16px] whitespace-nowrap">Daily View</p>
</div>
<div className="absolute content-stretch flex items-center justify-center left-[163px] overflow-clip px-[24px] py-[12px] rounded-[100px] top-[4px] w-[174px]" data-name="Button">
<p className="font-['Poppins:Medium',sans-serif] leading-[18px] not-italic relative shrink-0 text-[#2a2a2a] text-[16px] whitespace-nowrap">Summary</p>
</div>
</div>
);
}
function MaterialSymbolsChevronRightRounded() {
return (
<div className="absolute left-[328px] size-[24px] top-[15px]" data-name="material-symbols:chevron-right-rounded">
<svg className="absolute block inset-0 size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 24 24">
<g id="material-symbols:chevron-right-rounded">
<rect fill="var(--fill-0, #F95F62)" fillOpacity="0.04" height="23" rx="11.5" width="23" x="0.5" y="0.5" />
<rect height="23" rx="11.5" stroke="var(--stroke-0, #F95F62)" width="23" x="0.5" y="0.5" />
<path d={svgPaths.p149b8d00} fill="var(--fill-0, #F95F62)" id="Vector" />
</g>
</svg>
</div>
);
}
function Frame5() {
return (
<div className="h-[50px] overflow-clip relative shrink-0 w-[353px]">
<p className="absolute font-['Poppins:Regular',sans-serif] leading-[22px] left-0 not-italic text-[#f95f62] text-[18px] top-[16px] tracking-[-0.72px] whitespace-nowrap">Day 1</p>
<p className="absolute font-['Poppins:Regular',sans-serif] leading-[22px] left-[75px] not-italic text-[18px] text-[rgba(0,0,0,0.8)] top-[16px] tracking-[-0.72px] whitespace-nowrap">Day 2</p>
<p className="absolute font-['Poppins:Regular',sans-serif] leading-[22px] left-[155px] not-italic text-[18px] text-[rgba(0,0,0,0.8)] top-[16px] tracking-[-0.72px] whitespace-nowrap">Day 3</p>
<p className="absolute font-['Poppins:Regular',sans-serif] leading-[22px] left-[235px] not-italic text-[18px] text-[rgba(0,0,0,0.8)] top-[16px] tracking-[-0.72px] whitespace-nowrap">Day 4</p>
<p className="absolute font-['Poppins:Regular',sans-serif] leading-[22px] left-[316px] not-italic text-[18px] text-[rgba(0,0,0,0.4)] top-[16px] tracking-[-0.72px] whitespace-nowrap">D</p>
<p className="absolute font-['Poppins:Medium',sans-serif] leading-[18px] left-[363.33px] not-italic text-[#8e8e8e] text-[16px] top-[16px] whitespace-nowrap">Day 3</p>
<p className="absolute font-['Poppins:Medium',sans-serif] leading-[18px] left-[456.33px] not-italic text-[#8e8e8e] text-[16px] top-[16px] whitespace-nowrap">Day 4</p>
<div className="absolute h-0 left-0 top-[45px] w-[43px]">
<div className="absolute inset-[-0.5px_0]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 43 1">
<path d="M0 0.5H43" id="Vector 31" stroke="var(--stroke-0, #F95F62)" />
</svg>
</div>
</div>
<div className="absolute h-0 left-[6.92px] top-[45px] w-[393px]">
<div className="absolute inset-[-0.5px_0]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 393 1">
<path d="M0 0.5H393" id="Vector 31" stroke="var(--stroke-0, #F95F62)" strokeOpacity="0.12" />
</svg>
</div>
</div>
<MaterialSymbolsChevronRightRounded />
</div>
);
}
function SelectionTabs() {
return (
<div className="relative shrink-0 w-full" data-name="selection tabs">
<div className="flex flex-col items-center size-full">
<div className="content-stretch flex flex-col gap-[16px] items-center px-[20px] relative size-full">
<Frame4 />
<Frame5 />
</div>
</div>
</div>
);
}
function Frame27() {
return (
<div className="content-stretch flex items-center justify-center px-[12px] relative rounded-[24px] shrink-0">
<p className="font-['Poppins:Regular',sans-serif] leading-[1.6] not-italic relative shrink-0 text-[14px] text-[rgba(0,0,0,0.8)] whitespace-nowrap">09:00 AM</p>
</div>
);
}
function Time() {
return (
<div className="content-stretch flex flex-col gap-[8px] items-center justify-center relative shrink-0 w-[352.888px]" data-name="time">
<Frame27 />
</div>
);
}
function Frame2() {
return (
<div className="content-stretch flex flex-col gap-[2px] items-start not-italic relative shrink-0 w-full">
<p className="font-['Poppins:SemiBold',sans-serif] leading-[22px] relative shrink-0 text-[#2a2a2a] text-[16px] whitespace-nowrap">Melbourne Zoo</p>
<p className="font-['Poppins:Regular',sans-serif] leading-[16px] relative shrink-0 text-[12px] text-[rgba(0,0,0,0.6)] w-[358px]">Elliott Avenue, Parkville, Victoria 3052</p>
</div>
);
}
function Frame1() {
return <div className="absolute h-[16px] left-[290px] top-[218px] w-[67px]" />;
}
function Cards() {
return (
<div className="content-stretch flex flex-col gap-[6px] items-start relative shrink-0 w-full" data-name="Cards">
<div className="h-[211px] relative rounded-[8px] shrink-0 w-full">
<div className="absolute inset-0 overflow-hidden pointer-events-none rounded-[8px]">
<img alt="" className="absolute h-[250.95%] left-0 max-w-none top-[-41.17%] w-full" src={imgRectangle36} />
</div>
</div>
<Frame2 />
<Frame1 />
</div>
);
}
function Frame13() {
return (
<div className="bg-[rgba(254,231,231,0.6)] content-stretch flex items-center justify-center px-[12px] py-[6px] relative rounded-[100px] shrink-0">
<div aria-hidden="true" className="absolute border border-[#bb474a] border-solid inset-0 pointer-events-none rounded-[100px]" />
<p className="font-['Poppins:Regular',sans-serif] leading-[16px] not-italic relative shrink-0 text-[#bb474a] text-[12px] whitespace-nowrap">Food</p>
</div>
);
}
function Frame16() {
return (
<div className="bg-[rgba(254,231,231,0.6)] content-stretch flex items-center justify-center px-[12px] py-[6px] relative rounded-[100px] shrink-0">
<div aria-hidden="true" className="absolute border border-[#bb474a] border-solid inset-0 pointer-events-none rounded-[100px]" />
<p className="font-['Poppins:Regular',sans-serif] leading-[16px] not-italic relative shrink-0 text-[#bb474a] text-[12px] whitespace-nowrap">Photography</p>
</div>
);
}
function Frame17() {
return (
<div className="bg-[rgba(254,231,231,0.6)] content-stretch flex items-center justify-center px-[12px] py-[6px] relative rounded-[100px] shrink-0">
<div aria-hidden="true" className="absolute border border-[#bb474a] border-solid inset-0 pointer-events-none rounded-[100px]" />
<p className="font-['Poppins:Regular',sans-serif] leading-[16px] not-italic relative shrink-0 text-[#bb474a] text-[12px] whitespace-nowrap">Culture</p>
</div>
);
}
function Frame18() {
return (
<div className="bg-[rgba(254,231,231,0.6)] content-stretch flex items-center justify-center px-[12px] py-[6px] relative rounded-[100px] shrink-0">
<div aria-hidden="true" className="absolute border border-[#bb474a] border-solid inset-0 pointer-events-none rounded-[100px]" />
<p className="font-['Poppins:Regular',sans-serif] leading-[16px] not-italic relative shrink-0 text-[#bb474a] text-[12px] whitespace-nowrap">Souvenirs</p>
</div>
);
}
function Frame22() {
return (
<div className="content-center flex flex-wrap gap-[8px] items-center relative shrink-0 w-full">
<Frame13 />
<Frame16 />
<Frame17 />
<Frame18 />
</div>
);
}
function MdiLocation() {
return (
<div className="relative shrink-0 size-[24px]" data-name="mdi:location">
<svg className="absolute block inset-0 size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 24 24">
<g id="mdi:location">
<path d={svgPaths.p3aac8400} fill="var(--fill-0, white)" id="Vector" />
</g>
</svg>
</div>
);
}
function Frame6() {
return (
<div className="absolute bg-[#f95f62] content-stretch flex gap-[10px] items-center justify-center left-[12px] px-[8px] py-[4px] rounded-[100px] top-[11px]">
<MdiLocation />
<div className="flex flex-col font-['Poppins:Regular',sans-serif] justify-end leading-[0] not-italic relative shrink-0 text-[14px] text-white whitespace-nowrap">
<p className="leading-[1.6]">Get Directions</p>
</div>
</div>
);
}
function Frame23() {
return (
<div className="content-stretch flex flex-col gap-[12px] items-start relative shrink-0 w-[353px]">
<Cards />
<Frame22 />
<ul className="block font-['Poppins:Regular',sans-serif] leading-[0] list-disc min-w-full not-italic relative shrink-0 text-[12px] text-[rgba(0,0,0,0.8)] w-[min-content]">
<li className="mb-0 ms-[18px]">
<span className="leading-[20px]">Discover various species of wildlife only present in this zoo!</span>
</li>
<li className="mb-0 ms-[18px]">
<span className="leading-[20px]">{`Try the amazing food from our local produce `}</span>
</li>
<li className="ms-[18px]">
<span className="leading-[20px]">Shop for souvenirs that makes you trip a memorable one</span>
</li>
</ul>
<Frame6 />
</div>
);
}
function Frame34() {
return (
<div className="content-stretch flex flex-col gap-[16px] items-start relative shrink-0">
<Time />
<Frame23 />
</div>
);
}
function Frame26() {
return (
<div className="content-stretch flex items-center justify-center px-[12px] relative rounded-[24px] shrink-0">
<p className="font-['Poppins:Regular',sans-serif] leading-[1.6] not-italic relative shrink-0 text-[14px] text-[rgba(0,0,0,0.8)] whitespace-nowrap">02:00 PM</p>
</div>
);
}
function Frame3() {
return (
<div className="content-stretch flex flex-col gap-[2px] items-start not-italic relative shrink-0 w-full">
<p className="font-['Poppins:SemiBold',sans-serif] leading-[22px] relative shrink-0 text-[#2a2a2a] text-[16px] whitespace-nowrap">Melbourne Skydeck</p>
<p className="font-['Poppins:Regular',sans-serif] leading-[16px] min-w-full relative shrink-0 text-[12px] text-[rgba(0,0,0,0.6)] w-[min-content]">South Bank, just across the Yarra River from Melbournes CBD</p>
</div>
);
}
function Cards1() {
return (
<div className="content-stretch flex flex-col gap-[6px] items-start relative shrink-0 w-full" data-name="Cards">
<div className="h-[211px] relative rounded-[8px] shrink-0 w-full">
<img alt="" className="absolute inset-0 max-w-none object-cover pointer-events-none rounded-[8px] size-full" src={imgRectangle37} />
</div>
<Frame3 />
</div>
);
}
function MdiLocation1() {
return (
<div className="relative shrink-0 size-[24px]" data-name="mdi:location">
<svg className="absolute block inset-0 size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 24 24">
<g id="mdi:location">
<path d={svgPaths.p3aac8400} fill="var(--fill-0, white)" id="Vector" />
</g>
</svg>
</div>
);
}
function Frame7() {
return (
<div className="absolute bg-[#f95f62] content-stretch flex gap-[10px] items-center justify-center left-[12px] px-[8px] py-[4px] rounded-[100px] top-[10.58px]">
<MdiLocation1 />
<div className="flex flex-col font-['Poppins:Regular',sans-serif] justify-end leading-[0] not-italic relative shrink-0 text-[14px] text-white whitespace-nowrap">
<p className="leading-[1.6]">Get Directions</p>
</div>
</div>
);
}
function Frame14() {
return (
<div className="bg-[rgba(254,231,231,0.6)] content-stretch flex items-center justify-center px-[12px] py-[6px] relative rounded-[100px] shrink-0">
<div aria-hidden="true" className="absolute border border-[#bb474a] border-solid inset-0 pointer-events-none rounded-[100px]" />
<p className="font-['Poppins:Regular',sans-serif] leading-[16px] not-italic relative shrink-0 text-[#bb474a] text-[12px] whitespace-nowrap">Food</p>
</div>
);
}
function Frame19() {
return (
<div className="bg-[rgba(254,231,231,0.6)] content-stretch flex items-center justify-center px-[12px] py-[6px] relative rounded-[100px] shrink-0">
<div aria-hidden="true" className="absolute border border-[#bb474a] border-solid inset-0 pointer-events-none rounded-[100px]" />
<p className="font-['Poppins:Regular',sans-serif] leading-[16px] not-italic relative shrink-0 text-[#bb474a] text-[12px] whitespace-nowrap">Photography</p>
</div>
);
}
function Frame20() {
return (
<div className="bg-[rgba(254,231,231,0.6)] content-stretch flex items-center justify-center px-[12px] py-[6px] relative rounded-[100px] shrink-0">
<div aria-hidden="true" className="absolute border border-[#bb474a] border-solid inset-0 pointer-events-none rounded-[100px]" />
<p className="font-['Poppins:Regular',sans-serif] leading-[16px] not-italic relative shrink-0 text-[#bb474a] text-[12px] whitespace-nowrap">Souvenirs</p>
</div>
);
}
function Frame25() {
return (
<div className="content-center flex flex-wrap gap-[8px] items-center relative shrink-0 w-full">
<Frame14 />
<Frame19 />
<Frame20 />
</div>
);
}
function Frame24() {
return (
<div className="content-stretch flex flex-col gap-[12px] items-start relative shrink-0 w-[353px]">
<Cards1 />
<Frame7 />
<Frame25 />
<ul className="block font-['Poppins:Regular',sans-serif] leading-[0] list-disc min-w-full not-italic relative shrink-0 text-[12px] text-[rgba(0,0,0,0.8)] w-[min-content]">
<li className="mb-0 ms-[18px]">
<span className="leading-[20px]">Take in the breathtaking views from the summit overlooking Melbourne.</span>
</li>
<li className="mb-0 ms-[18px]">
<span className="leading-[20px]">Try out the infamous sky dining experience</span>
</li>
<li className="ms-[18px]">
<span className="leading-[20px]">Have a peek at the award winning voyager theatre</span>
</li>
</ul>
</div>
);
}
function Frame33() {
return (
<div className="content-stretch flex flex-col gap-[16px] items-center relative shrink-0">
<Frame26 />
<Frame24 />
</div>
);
}
function Frame8() {
return (
<div className="content-stretch flex flex-col gap-[24px] items-center relative shrink-0">
<Frame34 />
<Frame33 />
</div>
);
}
function ItineraryDetails() {
return (
<div className="content-stretch flex flex-col gap-[24px] h-[1417px] items-center relative shrink-0" data-name="itinerary details">
<SelectionTabs />
<Frame8 />
</div>
);
}
function Content() {
return (
<div className="absolute content-stretch flex flex-col gap-[24px] h-[1673px] items-center left-0 top-[172px] w-[392px]" data-name="content">
<Overview />
<ItineraryDetails />
</div>
);
}
function ArrowRightLine() {
return (
<div className="relative size-[24px]" data-name="arrow-right-line">
<svg className="absolute block inset-0 size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 24 24">
<g id="arrow-right-line">
<path d={svgPaths.p15a24580} fill="var(--fill-0, #212121)" fillOpacity="0.4" id="Vector" />
</g>
</svg>
</div>
);
}
function Frame15() {
return (
<div className="content-stretch flex gap-[8px] items-center py-[12px] relative shrink-0 w-[76px]">
<div className="flex items-center justify-center relative shrink-0">
<div className="flex-none rotate-180">
<ArrowRightLine />
</div>
</div>
<p className="font-['Poppins:Regular',sans-serif] leading-[16px] not-italic relative shrink-0 text-[12px] text-black whitespace-nowrap">Back</p>
</div>
);
}
function Arrow() {
return (
<div className="absolute h-[3.274px] left-[10.64px] top-[11.11px] w-[3.293px]" data-name="arrow">
<div className="absolute inset-[0_-3.43%_-8.67%_-8.62%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 3.69019 3.55896">
<g id="arrow">
<path d={svgPaths.p1a652670} fill="var(--stroke-0, #F95F62)" id="Line 13" />
<path d={svgPaths.p28638300} fill="var(--stroke-0, #F95F62)" id="Line 14" />
<path d={svgPaths.p2ce32bc0} fill="var(--stroke-0, #F95F62)" id="Line 15" />
</g>
</svg>
</div>
</div>
);
}
function Logo() {
return (
<div className="absolute contents left-[4.57px] top-[4px]" data-name="logo">
<div className="-translate-y-1/2 absolute flex flex-col font-['Chillax:Medium',sans-serif] h-[14.097px] justify-center leading-[0] left-[4.57px] not-italic text-[#f95f62] text-[19.657px] top-[12.95px] tracking-[-1.1794px] w-[13.265px]">
<p className="leading-[normal]">C</p>
</div>
<div className="absolute left-[6.36px] size-[1.329px] top-[5.24px]">
<svg className="absolute block inset-0 size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 1.3292 1.3292">
<circle cx="0.6646" cy="0.6646" fill="var(--fill-0, #F95F62)" id="Ellipse 4" r="0.6646" />
</svg>
</div>
<div className="absolute h-[1.411px] left-[8.64px] top-[4px] w-[1.329px]">
<svg className="absolute block inset-0 size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 1.3292 1.41095">
<ellipse cx="0.6646" cy="0.705473" fill="var(--fill-0, #F95F62)" id="Ellipse 5" rx="0.6646" ry="0.705473" />
</svg>
</div>
<Arrow />
</div>
);
}
function Frame12() {
return (
<div className="bg-[rgba(249,95,98,0.12)] content-stretch flex items-center justify-center relative rounded-[62px] shrink-0 size-[24px]">
<Logo />
</div>
);
}
function Header() {
return (
<div className="absolute content-stretch flex items-center justify-between left-[20px] top-[18px] w-[351.968px]" data-name="header">
<Frame15 />
<Frame12 />
</div>
);
}
function DietQuestion() {
return (
<div className="absolute bg-white h-[1445px] left-0 overflow-clip top-0 w-[393px]" data-name="DietQuestion">
<Confetti />
<Container />
<HeaderText />
<Frame37 />
<Content />
<Header />
</div>
);
}
export default function MagicItinDay() {
return (
<div className="bg-white relative size-full" data-name="magic itin - day 5">
<DietQuestion />
</div>
);
}

View File

@@ -0,0 +1,996 @@
import svgPaths from "./svg-8l5dq4lsp8";
import imgPhoto from "figma:asset/895585e16a5ef99513fed52e650cc187c5d00f0a.png";
function Confetti() {
return (
<div className="absolute h-[600.579px] left-0 top-[729.79px] w-[392px]" data-name="Confetti">
<svg className="absolute block inset-0 size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 32 32">
<g id="Confetti" opacity="0">
<path d={svgPaths.p30846ac0} fill="var(--fill-0, #F266FF)" id="Vector" />
<path d={svgPaths.p1af8f150} id="Vector_2" stroke="var(--stroke-0, #A1FF66)" strokeLinecap="round" strokeWidth="3" />
<path d={svgPaths.p2e546900} fill="var(--fill-0, #66FFA8)" id="Vector_3" />
<path d={svgPaths.p2a600580} id="Vector_4" stroke="var(--stroke-0, #8C66FF)" strokeLinecap="round" strokeWidth="4" />
<path d={svgPaths.p3b167700} fill="var(--fill-0, #66D9FF)" id="Vector_5" />
<path d={svgPaths.p114f0d00} id="Vector_6" stroke="var(--stroke-0, #66CCFF)" strokeLinecap="round" strokeWidth="4" />
<path d={svgPaths.p6b82b00} fill="var(--fill-0, #7D66FF)" id="Vector_7" />
<path d={svgPaths.p3a1b1dc0} id="Vector_8" stroke="var(--stroke-0, #E066FF)" strokeLinecap="round" strokeWidth="3" />
<path d={svgPaths.p1f72dd00} id="Vector_9" stroke="var(--stroke-0, #66FFED)" strokeLinecap="round" strokeWidth="4" />
<path d={svgPaths.p31508a60} id="Vector_10" stroke="var(--stroke-0, #FFC766)" strokeLinecap="round" strokeWidth="4" />
<path d={svgPaths.p3e65c400} id="Vector_11" stroke="var(--stroke-0, #FF6E66)" strokeLinecap="round" strokeWidth="3" />
<path d={svgPaths.p1ca023c0} id="Vector_12" stroke="var(--stroke-0, #E8FF66)" strokeLinecap="round" strokeWidth="4" />
<path d={svgPaths.p36217800} fill="var(--fill-0, #DEFF66)" id="Vector_13" />
<path d={svgPaths.p13f60480} id="Vector_14" stroke="var(--stroke-0, #66FFE8)" strokeLinecap="round" strokeWidth="4" />
<path d={svgPaths.p125d3600} fill="var(--fill-0, #A6FF66)" id="Vector_15" />
<path d={svgPaths.p10f7c300} fill="var(--fill-0, #85FF66)" id="Vector_16" />
<path d={svgPaths.p365e9600} fill="var(--fill-0, #66FFBF)" id="Vector_17" />
<path d={svgPaths.p1617b600} fill="var(--fill-0, #66FFB5)" id="Vector_18" />
<path d={svgPaths.p2f540d00} fill="var(--fill-0, #D966FF)" id="Vector_19" />
<path d={svgPaths.p26750500} fill="var(--fill-0, #66FF7F)" id="Vector_20" />
<path d={svgPaths.pca2f800} id="Vector_21" stroke="var(--stroke-0, #FFB866)" strokeLinecap="round" strokeWidth="4" />
<path d={svgPaths.p130d1600} fill="var(--fill-0, #B8FF66)" id="Vector_22" />
<path d={svgPaths.p112ca500} id="Vector_23" stroke="var(--stroke-0, #66FFC9)" strokeLinecap="round" strokeWidth="4" />
<path d={svgPaths.p30587f00} id="Vector_24" stroke="var(--stroke-0, #FF66B5)" strokeLinecap="round" strokeWidth="3" />
<path d={svgPaths.p2c53d00} fill="var(--fill-0, #FFB866)" id="Vector_25" />
<path d={svgPaths.p245b5500} fill="var(--fill-0, #FFDB66)" id="Vector_26" />
<path d={svgPaths.p13ac0f00} fill="var(--fill-0, #D4FF66)" id="Vector_27" />
<path d={svgPaths.p2d198000} id="Vector_28" stroke="var(--stroke-0, #66FF96)" strokeLinecap="round" strokeWidth="4" />
<path d={svgPaths.pc00e050} id="Vector_29" stroke="var(--stroke-0, #66FFC2)" strokeLinecap="round" strokeWidth="4" />
<path d={svgPaths.p1ae38580} id="Vector_30" stroke="var(--stroke-0, #66FF7D)" strokeLinecap="round" strokeWidth="3" />
<path d={svgPaths.p1d9db000} fill="var(--fill-0, #FFB366)" id="Vector_31" />
<path d={svgPaths.pcd62e8} id="Vector_32" stroke="var(--stroke-0, #F266FF)" strokeLinecap="round" strokeWidth="4" />
<path d={svgPaths.pa248b00} fill="var(--fill-0, #6699FF)" id="Vector_33" />
<path d={svgPaths.p23dc0000} id="Vector_34" stroke="var(--stroke-0, #FF7366)" strokeLinecap="round" strokeWidth="4" />
<path d={svgPaths.p21984100} fill="var(--fill-0, #FF6669)" id="Vector_35" />
<path d={svgPaths.p315a1e30} fill="var(--fill-0, #FF8266)" id="Vector_36" />
<path d={svgPaths.p36d2ba80} id="Vector_37" stroke="var(--stroke-0, #E366FF)" strokeLinecap="round" strokeWidth="4" />
<path d={svgPaths.pee931c0} fill="var(--fill-0, #FF66E8)" id="Vector_38" />
<path d={svgPaths.p23d70180} fill="var(--fill-0, #66FF8C)" id="Vector_39" />
<path d={svgPaths.p3ba87480} fill="var(--fill-0, #66FFF5)" id="Vector_40" />
<path d="M14.4383 38.0174L0 4.01739" id="Vector_41" stroke="var(--stroke-0, #FF7566)" strokeLinecap="round" strokeWidth="3" />
<path d={svgPaths.p1d1bf900} id="Vector_42" stroke="var(--stroke-0, #8AFF66)" strokeLinecap="round" strokeWidth="4" />
<path d={svgPaths.p39b4f00} fill="var(--fill-0, #ED66FF)" id="Vector_43" />
<path d={svgPaths.p2369e2c0} id="Vector_44" stroke="var(--stroke-0, #FF6682)" strokeLinecap="round" strokeWidth="4" />
<path d={svgPaths.p21a2df00} fill="var(--fill-0, #FF66BD)" id="Vector_45" />
<path d={svgPaths.p245cc9b0} fill="var(--fill-0, #66FFF2)" id="Vector_46" />
<path d={svgPaths.p293abff0} fill="var(--fill-0, #FFB866)" id="Vector_47" />
<path d={svgPaths.p28906180} fill="var(--fill-0, #66E6FF)" id="Vector_48" />
<path d={svgPaths.p3282b480} fill="var(--fill-0, #FF66ED)" id="Vector_49" />
<path d={svgPaths.p3d2fe400} id="Vector_50" stroke="var(--stroke-0, #667AFF)" strokeLinecap="round" strokeWidth="4" />
<path d={svgPaths.p1fd17c80} fill="var(--fill-0, #FF6666)" id="Vector_51" />
<path d={svgPaths.p1ada2c00} fill="var(--fill-0, #FF667A)" id="Vector_52" />
<path d={svgPaths.p55e1500} id="Vector_53" stroke="var(--stroke-0, #66D1FF)" strokeLinecap="round" strokeWidth="4" />
<path d={svgPaths.p9491b80} fill="var(--fill-0, #FF7566)" id="Vector_54" />
<path d={svgPaths.p394df1c0} fill="var(--fill-0, #91FF66)" id="Vector_55" />
<path d={svgPaths.p39dd4ef0} fill="var(--fill-0, #CCFF66)" id="Vector_56" />
<path d={svgPaths.p38fea80} fill="var(--fill-0, #CCFF66)" id="Vector_57" />
<path d={svgPaths.p751e500} fill="var(--fill-0, #66FFA8)" id="Vector_58" />
<path d={svgPaths.p3ae84670} fill="var(--fill-0, #75FF66)" id="Vector_59" />
<path d={svgPaths.p195a2f80} fill="var(--fill-0, #FF66EB)" id="Vector_60" />
<path d={svgPaths.p1c14be00} fill="var(--fill-0, #66FF9E)" id="Vector_61" />
<path d={svgPaths.p302dfa00} fill="var(--fill-0, #FF6682)" id="Vector_62" />
<path d={svgPaths.p8f80e00} fill="var(--fill-0, #F266FF)" id="Vector_63" />
<path d={svgPaths.p37f77100} id="Vector_64" stroke="var(--stroke-0, #66FFEB)" strokeLinecap="round" strokeWidth="3" />
<path d={svgPaths.p31b9a200} fill="var(--fill-0, #BAFF66)" id="Vector_65" />
<path d={svgPaths.p1cfa9a70} fill="var(--fill-0, #66FFB0)" id="Vector_66" />
<path d={svgPaths.p273a0500} fill="var(--fill-0, #66FFF7)" id="Vector_67" />
<path d={svgPaths.p1621380} fill="var(--fill-0, #F766FF)" id="Vector_68" />
<path d={svgPaths.p32b87800} id="Vector_69" stroke="var(--stroke-0, #FF66ED)" strokeLinecap="round" strokeWidth="4" />
<path d={svgPaths.p9a6ab80} id="Vector_70" stroke="var(--stroke-0, #96FF66)" strokeLinecap="round" strokeWidth="4" />
<path d={svgPaths.p1261c6f0} fill="var(--fill-0, #FF66E8)" id="Vector_71" />
<path d={svgPaths.p31d95b00} fill="var(--fill-0, #6670FF)" id="Vector_72" />
<path d={svgPaths.p670ab80} fill="var(--fill-0, #66FF7F)" id="Vector_73" />
<path d={svgPaths.p20991b80} fill="var(--fill-0, #DE66FF)" id="Vector_74" />
<path d={svgPaths.pb0da100} id="Vector_75" stroke="var(--stroke-0, #BA66FF)" strokeLinecap="round" strokeWidth="3" />
<path d={svgPaths.p2dc248a0} id="Vector_76" stroke="var(--stroke-0, #FFAD66)" strokeLinecap="round" strokeWidth="3" />
<path d={svgPaths.p1b50ff00} fill="var(--fill-0, #8CFF66)" id="Vector_77" />
<path d={svgPaths.p33b66680} fill="var(--fill-0, #99FF66)" id="Vector_78" />
<path d={svgPaths.p1f83a980} fill="var(--fill-0, #66FFFF)" id="Vector_79" />
<path d="M361.68 6.01758L392 22.0176" id="Vector_80" stroke="var(--stroke-0, #FF66A8)" strokeLinecap="round" strokeWidth="4" />
</g>
</svg>
</div>
);
}
function Icon() {
return (
<div className="relative shrink-0 size-[15.984px]" data-name="Icon">
<svg className="absolute block inset-0 size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 15.9836 15.9836">
<g id="Icon">
<path d={svgPaths.pabdaa80} id="Vector" stroke="var(--stroke-0, #4A5565)" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.33197" />
<path d={svgPaths.p23a159d0} id="Vector_2" stroke="var(--stroke-0, #4A5565)" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.33197" />
<path d={svgPaths.p5521d80} id="Vector_3" stroke="var(--stroke-0, #4A5565)" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.33197" />
<path d={svgPaths.p6cb7a00} id="Vector_4" stroke="var(--stroke-0, #4A5565)" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.33197" />
</g>
</svg>
</div>
);
}
function CompletionScreen() {
return (
<div className="h-[42px] relative rounded-[36829000px] shrink-0 w-full" data-name="CompletionScreen">
<div aria-hidden="true" className="absolute border-[#e5e7eb] border-[1.098px] border-solid inset-0 pointer-events-none rounded-[36829000px]" />
<div className="flex flex-row items-center justify-center size-full">
<div className="bg-clip-padding border-0 border-[transparent] border-solid content-stretch flex gap-[8px] items-center justify-center px-[111px] py-[12px] relative size-full">
<Icon />
<p className="font-['Poppins:Medium',sans-serif] leading-[18px] not-italic relative shrink-0 text-[#4a5565] text-[16px] whitespace-nowrap">Start Over</p>
</div>
</div>
</div>
);
}
function Frame() {
return (
<div className="bg-[#f95f62] h-[42px] relative rounded-[38px] shrink-0 w-full">
<div className="flex flex-row items-center justify-center size-full">
<div className="bg-clip-padding border-0 border-[transparent] border-solid content-stretch flex items-center justify-center pl-[8px] pr-[12px] py-[12px] relative size-full">
<p className="font-['Poppins:Medium',sans-serif] leading-[18px] not-italic relative shrink-0 text-[16px] text-white whitespace-nowrap">Get My Trip Plan</p>
</div>
</div>
</div>
);
}
function Container() {
return (
<div className="absolute content-stretch flex flex-col gap-[12px] items-start justify-center left-[20.46px] opacity-0 top-[186px] w-[336px]" data-name="Container">
<CompletionScreen />
<Frame />
</div>
);
}
function HeaderText() {
return (
<div className="absolute content-stretch flex flex-col gap-[8px] items-start left-[20.46px] not-italic opacity-0 text-center top-[65px] w-[352.015px]" data-name="header text">
<p className="font-['Poppins:Medium',sans-serif] leading-[0] min-w-full relative shrink-0 text-[#101828] text-[24px] tracking-[-0.96px] w-[min-content]">
<span className="font-['Poppins:Regular',sans-serif] leading-[1.3] text-[rgba(16,24,40,0.6)]">Your</span>
<span className="leading-[1.3]">{` `}</span>
<span className="leading-[1.3] text-[#f95f62]">Magic Itinerary</span>
<span className="leading-[1.3]">{` `}</span>
<span className="font-['Poppins:Regular',sans-serif] leading-[1.3] text-[rgba(16,24,40,0.6)]">is</span>
<span className="leading-[1.3]">{` `}</span>
<span className="leading-[1.3] text-[#f95f62]">Ready</span>
<span className="leading-[1.3]">{``}</span>
</p>
<p className="font-['Poppins:Regular',sans-serif] leading-[1.6] relative shrink-0 text-[14px] text-[rgba(0,0,0,0.6)] w-[336px]">{`We've got everything we need to plan your perfect trip`}</p>
</div>
);
}
function Frame35() {
return (
<div className="absolute content-stretch flex flex-col font-['Poppins:SemiBold',sans-serif] gap-[6px] items-start leading-[0] left-[20.46px] not-italic text-[#101828] text-center top-[89px] w-[351.979px]">
<div className="flex flex-col h-[21.107px] justify-center relative shrink-0 text-[24px] w-full">
<p className="leading-[32px]">{`Your `}</p>
</div>
<div className="flex flex-col h-[21.107px] justify-center relative shrink-0 text-[0px] w-full">
<p className="text-[24px]">
<span className="leading-[32px] text-[#f95f62]">Melbourne</span>
<span className="leading-[32px]">{` `}</span>
<span className="leading-[32px] text-[#f95f62]">Magic Itinerary</span>
</p>
</div>
</div>
);
}
function Photo() {
return (
<div className="flex-[1_0_0] min-h-px min-w-px relative rounded-[2px] w-full" data-name="photo">
<img alt="" className="absolute inset-0 max-w-none object-cover pointer-events-none rounded-[2px] size-full" src={imgPhoto} />
</div>
);
}
function OuterFrame() {
return (
<div className="bg-white content-stretch flex flex-col items-center overflow-clip relative self-stretch shrink-0 w-[86px]" data-name="outer frame">
<Photo />
</div>
);
}
function Group5() {
return (
<div className="absolute inset-[11.11%]">
<div className="absolute inset-[-0.71%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 14.2 14.2">
<g id="Group 1000002093">
<path d={svgPaths.p1aba0880} fill="var(--fill-0, #F95F62)" id="Vector" />
<path d={svgPaths.p1fa04100} fill="var(--fill-0, #F95F62)" id="Vector_2" />
<path d={svgPaths.pe6ea010} fill="var(--fill-0, #F95F62)" id="Vector_3" />
<path d={svgPaths.pbdbaa00} fill="var(--fill-0, #F95F62)" id="Vector_4" />
<path d={svgPaths.p18d2ab00} fill="var(--fill-0, #F95F62)" id="Vector_5" />
<path d={svgPaths.p16eea180} fill="var(--fill-0, #F95F62)" id="Vector_6" />
<path clipRule="evenodd" d={svgPaths.p3e80b480} fill="var(--fill-0, #F95F62)" fillRule="evenodd" id="Vector_7" stroke="var(--stroke-0, white)" strokeWidth="0.2" />
</g>
</svg>
</div>
</div>
);
}
function Frame31() {
return (
<div className="relative shrink-0 size-[18px]">
<Group5 />
</div>
);
}
function Frame28() {
return (
<div className="content-center flex flex-wrap gap-[8px] items-center relative shrink-0">
<Frame31 />
<p className="font-['Poppins:Medium',sans-serif] leading-[18px] not-italic relative shrink-0 text-[#f95f62] text-[16px] whitespace-nowrap">3 Days</p>
</div>
);
}
function Frame32() {
return (
<div className="relative shrink-0 size-[18px]">
<svg className="absolute block inset-0 size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 18 18">
<g id="Frame 1597882141">
<path d={svgPaths.pcef7b00} fill="var(--fill-0, #F95F62)" id="Vector" stroke="var(--stroke-0, #F95F62)" strokeWidth="0.1" />
</g>
</svg>
</div>
);
}
function Frame29() {
return (
<div className="content-center flex flex-wrap gap-[8px] items-center relative shrink-0">
<Frame32 />
<p className="font-['Poppins:Medium',sans-serif] leading-[18px] not-italic relative shrink-0 text-[#f95f62] text-[16px] whitespace-nowrap">12 stops</p>
</div>
);
}
function Overview1() {
return (
<div className="content-stretch flex flex-col gap-[8px] items-start relative shrink-0 w-[237.5px]" data-name="overview">
<Frame28 />
<Frame29 />
</div>
);
}
function Frame30() {
return (
<div className="content-stretch flex flex-col gap-[8px] items-start relative shrink-0 w-full">
<div className="flex flex-col font-['Poppins:Regular',sans-serif] h-[9.672px] justify-center leading-[0] min-w-full not-italic relative shrink-0 text-[10px] text-[rgba(0,0,0,0.8)] uppercase w-[min-content]">
<p className="leading-[13.852px]">TRIP DETAILS:</p>
</div>
<Overview1 />
</div>
);
}
function Group() {
return (
<div className="absolute inset-[12.5%_5.95%_8.33%_8.34%]" data-name="Group">
<div className="absolute inset-[-5.26%_0_0_0]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 12 11.6667">
<g id="Group">
<path d={svgPaths.p4d87d80} fill="var(--fill-0, black)" fillOpacity="0.8" id="Vector" />
<path d={svgPaths.p3d2bae00} id="Vector_2" stroke="var(--stroke-0, #999999)" strokeLinecap="round" strokeWidth="1.16667" />
</g>
</svg>
</div>
</div>
);
}
function LetsIconsDateFill() {
return (
<div className="overflow-clip relative shrink-0 size-[14px]" data-name="lets-icons:date-fill">
<Group />
</div>
);
}
function Adults() {
return (
<div className="content-stretch flex gap-[8px] h-[16px] items-center justify-center relative shrink-0" data-name="adults">
<LetsIconsDateFill />
<p className="font-['Poppins:Regular',sans-serif] leading-[14px] not-italic relative shrink-0 text-[11px] text-[rgba(0,0,0,0.8)] tracking-[0.06px] whitespace-nowrap">22/02/2025</p>
</div>
);
}
function RiParentFill() {
return (
<div className="relative shrink-0 size-[14px]" data-name="ri:parent-fill">
<svg className="absolute block inset-0 size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 14 14">
<g id="ri:parent-fill">
<path d={svgPaths.p7670300} fill="var(--fill-0, black)" fillOpacity="0.8" id="Vector" />
</g>
</svg>
</div>
);
}
function Adults1() {
return (
<div className="content-stretch flex gap-[8px] h-[16px] items-center justify-center relative shrink-0" data-name="adults">
<RiParentFill />
<p className="font-['Poppins:Regular',sans-serif] leading-[14px] not-italic relative shrink-0 text-[11px] text-[rgba(0,0,0,0.8)] tracking-[0.06px] whitespace-nowrap">3 adults</p>
</div>
);
}
function FaSolidChild() {
return (
<div className="h-[14px] relative shrink-0 w-[10.5px]" data-name="fa-solid:child">
<svg className="absolute block inset-0 size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 10.5 14">
<g clipPath="url(#clip0_9924_1087)" id="fa-solid:child">
<path d={svgPaths.p2e64dc00} fill="var(--fill-0, black)" fillOpacity="0.8" id="Vector" />
</g>
<defs>
<clipPath id="clip0_9924_1087">
<rect fill="white" height="14" width="10.5" />
</clipPath>
</defs>
</svg>
</div>
);
}
function Kids() {
return (
<div className="content-stretch flex gap-[8px] h-[18.667px] items-center relative shrink-0" data-name="kids">
<FaSolidChild />
<p className="font-['Poppins:Regular',sans-serif] leading-[14px] not-italic relative shrink-0 text-[11px] text-[rgba(0,0,0,0.8)] tracking-[0.06px] whitespace-nowrap">3 kids</p>
</div>
);
}
function Frame9() {
return (
<div className="content-center flex flex-wrap gap-[12px] items-center relative shrink-0 w-full">
<Adults />
<Adults1 />
<Kids />
</div>
);
}
function Frame26() {
return (
<div className="content-stretch flex flex-col gap-[12px] items-start relative shrink-0 w-[237.5px]">
<Frame30 />
<Frame9 />
</div>
);
}
function Frame27() {
return (
<div className="content-stretch flex gap-[8px] items-start relative shrink-0 w-full">
<OuterFrame />
<Frame26 />
</div>
);
}
function MaterialSymbolsShare() {
return (
<div className="relative shrink-0 size-[16px]" data-name="material-symbols:share">
<svg className="absolute block inset-0 size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 16 16">
<g id="material-symbols:share">
<path d={svgPaths.p31f8ed00} fill="var(--fill-0, #F95F62)" id="Vector" />
</g>
</svg>
</div>
);
}
function Frame3() {
return (
<div className="bg-[rgba(249,95,98,0)] flex-[1_0_0] h-[44px] min-h-px min-w-px relative rounded-[8px]">
<div className="flex flex-row items-center justify-center overflow-clip rounded-[inherit] size-full">
<div className="content-stretch flex gap-[8px] items-center justify-center p-[8px] relative size-full">
<MaterialSymbolsShare />
<p className="font-['Poppins:Regular',sans-serif] leading-[16px] not-italic relative shrink-0 text-[#f95f62] text-[12px] whitespace-nowrap">Share</p>
</div>
</div>
<div aria-hidden="true" className="absolute border border-[#f95f62] border-solid inset-0 pointer-events-none rounded-[8px]" />
</div>
);
}
function DownloadMinimalisticSvgrepoCom() {
return (
<div className="relative shrink-0 size-[16px]" data-name="download-minimalistic_svgrepo.com">
<svg className="absolute block inset-0 size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 16 16">
<g id="download-minimalistic_svgrepo.com">
<path d={svgPaths.p13f2e80} id="Vector" stroke="var(--stroke-0, #F95F62)" strokeLinecap="round" strokeLinejoin="round" />
<path d={svgPaths.p21309b00} id="Vector_2" stroke="var(--stroke-0, #F95F62)" strokeLinecap="round" strokeLinejoin="round" />
</g>
</svg>
</div>
);
}
function Frame4() {
return (
<div className="bg-[rgba(249,95,98,0)] flex-[1_0_0] h-[44px] min-h-px min-w-px relative rounded-[8px]">
<div className="flex flex-row items-center justify-center overflow-clip rounded-[inherit] size-full">
<div className="content-stretch flex gap-[8px] items-center justify-center p-[8px] relative size-full">
<DownloadMinimalisticSvgrepoCom />
<p className="font-['Poppins:Regular',sans-serif] leading-[16px] not-italic relative shrink-0 text-[#f95f62] text-[12px] whitespace-nowrap">Download</p>
</div>
</div>
<div aria-hidden="true" className="absolute border border-[#f95f62] border-solid inset-0 pointer-events-none rounded-[8px]" />
</div>
);
}
function Frame5() {
return (
<div className="content-stretch flex gap-[12px] items-center relative shrink-0 w-full">
<Frame3 />
<Frame4 />
</div>
);
}
function Overview() {
return (
<div className="bg-white content-stretch flex flex-col gap-[8px] items-center p-[8px] relative rounded-[6px] shadow-[0px_23.087px_46.175px_0px_rgba(209,213,220,0.5)] shrink-0 w-[352px]" data-name="Overview">
<Frame27 />
<div className="h-0 relative shrink-0 w-full">
<div className="absolute inset-[-1px_0_0_0]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 336 1">
<line id="Line 631" stroke="var(--stroke-0, black)" strokeLinecap="round" strokeOpacity="0.04" x1="0.5" x2="335.5" y1="0.5" y2="0.5" />
</svg>
</div>
</div>
<Frame5 />
</div>
);
}
function Frame1() {
return (
<div className="bg-[#fee7e7] h-[50px] overflow-clip relative rounded-[100px] shrink-0 w-[353px]">
<div className="absolute bg-white content-stretch flex items-center justify-center left-[182.5px] overflow-clip px-[24px] py-[12px] rounded-[100px] top-[3.66px] w-[167px]" data-name="Button">
<p className="font-['Poppins:Medium',sans-serif] leading-[18px] not-italic relative shrink-0 text-[#2a2a2a] text-[16px] whitespace-nowrap">Summary</p>
</div>
<div className="absolute content-stretch flex items-center justify-center left-[4px] overflow-clip px-[24px] py-[12px] rounded-[100px] top-[4px] w-[174px]" data-name="Button">
<p className="font-['Poppins:Medium',sans-serif] leading-[18px] not-italic relative shrink-0 text-[#2a2a2a] text-[16px] whitespace-nowrap">Daily View</p>
</div>
</div>
);
}
function Frame33() {
return (
<div className="content-stretch flex items-center relative shrink-0">
<p className="font-['Poppins:Regular',sans-serif] leading-[18px] not-italic relative shrink-0 text-[#212121] text-[16px] whitespace-nowrap">Day 1:</p>
</div>
);
}
function Group1() {
return (
<div className="absolute inset-[12.5%_8.33%_8.33%_8.33%]" data-name="Group">
<div className="absolute inset-[-5.26%_0_0_0]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 11.6667 11.6667">
<g id="Group">
<path d={svgPaths.p34b84f00} fill="var(--fill-0, #F95F62)" id="Vector" />
<path d={svgPaths.p219a1600} id="Vector_2" stroke="var(--stroke-0, #F95F62)" strokeLinecap="round" strokeWidth="1.16667" />
</g>
</svg>
</div>
</div>
);
}
function LetsIconsDateFill1() {
return (
<div className="relative shrink-0 size-[14px]" data-name="lets-icons:date-fill">
<Group1 />
</div>
);
}
function Adults2() {
return (
<div className="content-stretch flex gap-[4px] h-[16px] items-center justify-center relative shrink-0" data-name="adults">
<LetsIconsDateFill1 />
<p className="font-['Poppins:Regular',sans-serif] leading-[16px] not-italic relative shrink-0 text-[#f95f62] text-[12px] whitespace-nowrap">21/02/2025</p>
</div>
);
}
function Frame10() {
return (
<div className="content-stretch flex items-center justify-between relative shrink-0 w-full">
<Frame33 />
<Adults2 />
</div>
);
}
function Frame11() {
return (
<div className="bg-[#fee7e7] relative rounded-[5px] shrink-0 w-full">
<div className="flex flex-row items-center size-full">
<div className="content-stretch flex items-center justify-between px-[14px] py-[10px] relative size-full">
<p className="font-['Poppins:Regular',sans-serif] leading-[1.6] not-italic relative shrink-0 text-[#212121] text-[14px] whitespace-nowrap">09:00 am: Melbourne Zoo</p>
<div className="flex h-[5px] items-center justify-center relative shrink-0 w-[10px]" style={{ "--transform-inner-width": "1185", "--transform-inner-height": "21" } as React.CSSProperties}>
<div className="-scale-y-100 flex-none rotate-90">
<div className="h-[10px] relative w-[5px]">
<div className="absolute inset-[-6.25%_-12.5%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 6.25 11.25">
<path d={svgPaths.pec20f00} id="Vector 796" stroke="var(--stroke-0, black)" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.25" />
</svg>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
}
function Frame15() {
return (
<div className="content-stretch flex items-center justify-between relative shrink-0 w-[303px]">
<p className="font-['Poppins:Regular',sans-serif] leading-[1.6] not-italic relative shrink-0 text-[#212121] text-[14px] whitespace-nowrap">01:00 pm: Melbourne Skydeck</p>
<div className="flex h-[5px] items-center justify-center relative shrink-0 w-[10px]" style={{ "--transform-inner-width": "1185", "--transform-inner-height": "21" } as React.CSSProperties}>
<div className="-scale-y-100 flex-none rotate-90">
<div className="h-[10px] relative w-[5px]">
<div className="absolute inset-[-6.25%_-12.5%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 6.25 11.25">
<path d={svgPaths.pec20f00} id="Vector 796" stroke="var(--stroke-0, black)" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.25" />
</svg>
</div>
</div>
</div>
</div>
</div>
);
}
function Group2() {
return (
<div className="absolute inset-[8.33%_12.5%_0.78%_12.5%]" data-name="Group">
<svg className="absolute block inset-0 size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 12 14.5427">
<g id="Group">
<g id="Vector" />
<path clipRule="evenodd" d={svgPaths.p31a57b00} fill="var(--fill-0, white)" fillRule="evenodd" id="Vector_2" />
</g>
</svg>
</div>
);
}
function MingcuteLocationLine() {
return (
<div className="overflow-clip relative shrink-0 size-[16px]" data-name="mingcute:location-line">
<Group2 />
</div>
);
}
function Frame7() {
return (
<div className="bg-[#f95f62] content-stretch flex gap-[6px] items-center justify-center px-[12px] py-[8px] relative rounded-[100px] shrink-0">
<MingcuteLocationLine />
<p className="font-['Poppins:Regular',sans-serif] leading-[14px] not-italic relative shrink-0 text-[11px] text-white tracking-[0.06px] whitespace-nowrap">Get directions</p>
</div>
);
}
function Frame14() {
return (
<div className="bg-[#fee7e7] relative rounded-[5px] shrink-0 w-full">
<div className="flex flex-col justify-center size-full">
<div className="content-stretch flex flex-col gap-[10px] items-start justify-center px-[14px] py-[10px] relative size-full">
<Frame15 />
<ul className="block font-['Poppins:Regular',sans-serif] leading-[0] list-disc min-w-full not-italic relative shrink-0 text-[12px] text-[rgba(0,0,0,0.8)] w-[min-content]">
<li className="mb-0 ms-[18px]">
<span className="leading-[20px]">Take in the breathtaking views from the summit overlooking Melbourne.</span>
</li>
<li className="mb-0 ms-[18px]">
<span className="leading-[20px]">Try out the infamous sky dining experience</span>
</li>
<li className="ms-[18px]">
<span className="leading-[20px]">Have a peek at the award winning voyager theatre</span>
</li>
</ul>
<Frame7 />
</div>
</div>
</div>
);
}
function Frame13() {
return (
<div className="content-stretch flex flex-col gap-[10px] items-start relative shrink-0 w-full">
<Frame11 />
<Frame14 />
</div>
);
}
function Frame12() {
return (
<div className="relative rounded-[10px] shrink-0 w-full">
<div aria-hidden="true" className="absolute border border-[#f95f62] border-solid inset-0 pointer-events-none rounded-[10px]" />
<div className="content-stretch flex flex-col gap-[22px] items-start px-[8px] py-[12px] relative size-full">
<Frame10 />
<Frame13 />
</div>
</div>
);
}
function Frame34() {
return (
<div className="content-stretch flex items-center relative shrink-0">
<p className="font-['Poppins:Regular',sans-serif] leading-[18px] not-italic relative shrink-0 text-[#212121] text-[16px] whitespace-nowrap">Day 2:</p>
</div>
);
}
function Group3() {
return (
<div className="absolute inset-[12.5%_8.33%_8.33%_8.33%]" data-name="Group">
<div className="absolute inset-[-5.26%_0_0_0]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 11.6667 11.6667">
<g id="Group">
<path d={svgPaths.p34b84f00} fill="var(--fill-0, #F95F62)" id="Vector" />
<path d={svgPaths.p219a1600} id="Vector_2" stroke="var(--stroke-0, #F95F62)" strokeLinecap="round" strokeWidth="1.16667" />
</g>
</svg>
</div>
</div>
);
}
function LetsIconsDateFill2() {
return (
<div className="relative shrink-0 size-[14px]" data-name="lets-icons:date-fill">
<Group3 />
</div>
);
}
function Adults3() {
return (
<div className="content-stretch flex gap-[4px] h-[16px] items-center justify-center relative shrink-0" data-name="adults">
<LetsIconsDateFill2 />
<p className="font-['Poppins:Regular',sans-serif] leading-[16px] not-italic relative shrink-0 text-[#f95f62] text-[12px] whitespace-nowrap">21/02/2025</p>
</div>
);
}
function Frame17() {
return (
<div className="content-stretch flex items-center justify-between relative shrink-0 w-full">
<Frame34 />
<Adults3 />
</div>
);
}
function Frame19() {
return (
<div className="bg-[#fee7e7] relative rounded-[5px] shrink-0 w-full">
<div className="flex flex-row items-center size-full">
<div className="content-stretch flex items-center justify-between px-[14px] py-[10px] relative size-full">
<p className="font-['Poppins:Regular',sans-serif] leading-[1.6] not-italic relative shrink-0 text-[#212121] text-[14px] whitespace-nowrap">09:00 am: Royal Botanic Gardens Victoria</p>
<div className="flex h-[5px] items-center justify-center relative shrink-0 w-[10px]" style={{ "--transform-inner-width": "1185", "--transform-inner-height": "21" } as React.CSSProperties}>
<div className="-scale-y-100 flex-none rotate-90">
<div className="h-[10px] relative w-[5px]">
<div className="absolute inset-[-6.25%_-12.5%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 6.25 11.25">
<path d={svgPaths.pec20f00} id="Vector 796" stroke="var(--stroke-0, black)" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.25" />
</svg>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
}
function Frame20() {
return (
<div className="bg-[#fee7e7] relative rounded-[5px] shrink-0 w-full">
<div className="flex flex-row items-center size-full">
<div className="content-stretch flex items-center justify-between px-[14px] py-[10px] relative size-full">
<p className="font-['Poppins:Regular',sans-serif] leading-[1.6] not-italic relative shrink-0 text-[#212121] text-[14px] whitespace-nowrap">01:00 pm: National Gallery of Victoria</p>
<div className="flex h-[5px] items-center justify-center relative shrink-0 w-[10px]" style={{ "--transform-inner-width": "1185", "--transform-inner-height": "21" } as React.CSSProperties}>
<div className="-scale-y-100 flex-none rotate-90">
<div className="h-[10px] relative w-[5px]">
<div className="absolute inset-[-6.25%_-12.5%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 6.25 11.25">
<path d={svgPaths.pec20f00} id="Vector 796" stroke="var(--stroke-0, black)" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.25" />
</svg>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
}
function Frame18() {
return (
<div className="content-stretch flex flex-col gap-[10px] items-start relative shrink-0 w-full">
<Frame19 />
<Frame20 />
</div>
);
}
function Frame16() {
return (
<div className="relative rounded-[10px] shrink-0 w-full">
<div aria-hidden="true" className="absolute border border-[rgba(249,95,98,0.12)] border-solid inset-0 pointer-events-none rounded-[10px]" />
<div className="content-stretch flex flex-col gap-[22px] items-start px-[8px] py-[12px] relative size-full">
<Frame17 />
<Frame18 />
</div>
</div>
);
}
function Frame36() {
return (
<div className="content-stretch flex items-center relative shrink-0">
<p className="font-['Poppins:Regular',sans-serif] leading-[18px] not-italic relative shrink-0 text-[#212121] text-[16px] whitespace-nowrap">Day 3:</p>
</div>
);
}
function Group4() {
return (
<div className="absolute inset-[12.5%_8.33%_8.33%_8.33%]" data-name="Group">
<div className="absolute inset-[-5.26%_0_0_0]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 11.6667 11.6667">
<g id="Group">
<path d={svgPaths.p34b84f00} fill="var(--fill-0, #F95F62)" id="Vector" />
<path d={svgPaths.p219a1600} id="Vector_2" stroke="var(--stroke-0, #F95F62)" strokeLinecap="round" strokeWidth="1.16667" />
</g>
</svg>
</div>
</div>
);
}
function LetsIconsDateFill3() {
return (
<div className="relative shrink-0 size-[14px]" data-name="lets-icons:date-fill">
<Group4 />
</div>
);
}
function Adults4() {
return (
<div className="content-stretch flex gap-[4px] h-[16px] items-center justify-center relative shrink-0" data-name="adults">
<LetsIconsDateFill3 />
<p className="font-['Poppins:Regular',sans-serif] leading-[16px] not-italic relative shrink-0 text-[#f95f62] text-[12px] whitespace-nowrap">21/02/2025</p>
</div>
);
}
function Frame22() {
return (
<div className="content-stretch flex items-center justify-between relative shrink-0 w-full">
<Frame36 />
<Adults4 />
</div>
);
}
function Frame24() {
return (
<div className="bg-[#fee7e7] relative rounded-[5px] shrink-0 w-full">
<div className="flex flex-row items-center size-full">
<div className="content-stretch flex items-center justify-between px-[14px] py-[10px] relative size-full">
<p className="font-['Poppins:Regular',sans-serif] leading-[1.6] not-italic relative shrink-0 text-[#212121] text-[14px] whitespace-nowrap">09:00 am: Queens Victoria Market</p>
<div className="flex h-[5px] items-center justify-center relative shrink-0 w-[10px]" style={{ "--transform-inner-width": "1185", "--transform-inner-height": "21" } as React.CSSProperties}>
<div className="-scale-y-100 flex-none rotate-90">
<div className="h-[10px] relative w-[5px]">
<div className="absolute inset-[-6.25%_-12.5%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 6.25 11.25">
<path d={svgPaths.pec20f00} id="Vector 796" stroke="var(--stroke-0, black)" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.25" />
</svg>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
}
function Frame25() {
return (
<div className="bg-[#fee7e7] relative rounded-[5px] shrink-0 w-full">
<div className="flex flex-row items-center size-full">
<div className="content-stretch flex items-center justify-between px-[14px] py-[10px] relative size-full">
<p className="font-['Poppins:Regular',sans-serif] leading-[1.6] not-italic relative shrink-0 text-[#212121] text-[14px] whitespace-nowrap">{`04:30 pm: St Kilda Beach & Pier`}</p>
<div className="flex h-[5px] items-center justify-center relative shrink-0 w-[10px]" style={{ "--transform-inner-width": "1185", "--transform-inner-height": "21" } as React.CSSProperties}>
<div className="-scale-y-100 flex-none rotate-90">
<div className="h-[10px] relative w-[5px]">
<div className="absolute inset-[-6.25%_-12.5%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 6.25 11.25">
<path d={svgPaths.pec20f00} id="Vector 796" stroke="var(--stroke-0, black)" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.25" />
</svg>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
);
}
function Frame23() {
return (
<div className="content-stretch flex flex-col gap-[10px] items-start relative shrink-0 w-full">
<Frame24 />
<Frame25 />
</div>
);
}
function Frame21() {
return (
<div className="relative rounded-[10px] shrink-0 w-full">
<div aria-hidden="true" className="absolute border border-[rgba(249,95,98,0.12)] border-solid inset-0 pointer-events-none rounded-[10px]" />
<div className="content-stretch flex flex-col gap-[22px] items-start px-[8px] py-[12px] relative size-full">
<Frame22 />
<Frame23 />
</div>
</div>
);
}
function Frame2() {
return (
<div className="content-stretch flex flex-col gap-[24px] items-center relative shrink-0 w-[353px]">
<Frame12 />
<Frame16 />
<Frame21 />
</div>
);
}
function ItineraryDetails() {
return (
<div className="content-stretch flex flex-col gap-[24px] h-[1019px] items-center relative shrink-0 w-full" data-name="itinerary details">
<Frame1 />
<Frame2 />
</div>
);
}
function Content() {
return (
<div className="absolute content-stretch flex flex-col gap-[24px] h-[1237px] items-center left-0 top-[172px] w-[392px]" data-name="content">
<Overview />
<ItineraryDetails />
</div>
);
}
function ArrowRightLine() {
return (
<div className="relative size-[24px]" data-name="arrow-right-line">
<svg className="absolute block inset-0 size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 24 24">
<g id="arrow-right-line">
<path d={svgPaths.p15a24580} fill="var(--fill-0, #212121)" fillOpacity="0.4" id="Vector" />
</g>
</svg>
</div>
);
}
function Frame8() {
return (
<div className="content-stretch flex gap-[8px] items-center py-[12px] relative shrink-0 w-[76px]">
<div className="flex items-center justify-center relative shrink-0">
<div className="flex-none rotate-180">
<ArrowRightLine />
</div>
</div>
<p className="font-['Poppins:Regular',sans-serif] leading-[16px] not-italic relative shrink-0 text-[12px] text-black whitespace-nowrap">Back</p>
</div>
);
}
function Arrow() {
return (
<div className="absolute h-[3.274px] left-[10.64px] top-[11.11px] w-[3.293px]" data-name="arrow">
<div className="absolute inset-[0_-3.43%_-8.67%_-8.62%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 3.69019 3.55896">
<g id="arrow">
<path d={svgPaths.p1a652670} fill="var(--stroke-0, #F95F62)" id="Line 13" />
<path d={svgPaths.p28638300} fill="var(--stroke-0, #F95F62)" id="Line 14" />
<path d={svgPaths.p2ce32bc0} fill="var(--stroke-0, #F95F62)" id="Line 15" />
</g>
</svg>
</div>
</div>
);
}
function Logo() {
return (
<div className="absolute contents left-[4.57px] top-[4px]" data-name="logo">
<div className="-translate-y-1/2 absolute flex flex-col font-['Chillax:Medium',sans-serif] h-[14.097px] justify-center leading-[0] left-[4.57px] not-italic text-[#f95f62] text-[19.657px] top-[12.95px] tracking-[-1.1794px] w-[13.265px]">
<p className="leading-[normal]">C</p>
</div>
<div className="absolute left-[6.36px] size-[1.329px] top-[5.24px]">
<svg className="absolute block inset-0 size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 1.3292 1.3292">
<circle cx="0.6646" cy="0.6646" fill="var(--fill-0, #F95F62)" id="Ellipse 4" r="0.6646" />
</svg>
</div>
<div className="absolute h-[1.411px] left-[8.64px] top-[4px] w-[1.329px]">
<svg className="absolute block inset-0 size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 1.3292 1.41095">
<ellipse cx="0.6646" cy="0.705473" fill="var(--fill-0, #F95F62)" id="Ellipse 5" rx="0.6646" ry="0.705473" />
</svg>
</div>
<Arrow />
</div>
);
}
function Frame6() {
return (
<div className="bg-[rgba(249,95,98,0.12)] content-stretch flex items-center justify-center relative rounded-[62px] shrink-0 size-[24px]">
<Logo />
</div>
);
}
function Header() {
return (
<div className="absolute content-stretch flex items-center justify-between left-[20px] top-[18px] w-[351.968px]" data-name="header">
<Frame8 />
<Frame6 />
</div>
);
}
function DietQuestion() {
return (
<div className="absolute bg-white h-[1163px] left-0 overflow-clip top-0 w-[393px]" data-name="DietQuestion">
<Confetti />
<Container />
<HeaderText />
<Frame35 />
<Content />
<Header />
</div>
);
}
export default function MagicItinSummaryTab() {
return (
<div className="bg-white relative size-full" data-name="magic itin - summary tab">
<DietQuestion />
</div>
);
}

View File

@@ -0,0 +1,13 @@
import svgPaths from "./svg-wjtpqfkmjn";
export default function NounRelax() {
return (
<div className="relative size-full" data-name="noun-relax-6597055 1">
<svg className="absolute block inset-0 size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 100 100">
<g id="noun-relax-6597055 1">
<path d={svgPaths.p1bfdb500} fill="var(--fill-0, #F95F62)" id="Vector" stroke="var(--stroke-0, #FFF3F3)" strokeWidth="0.4" />
</g>
</svg>
</div>
);
}

View File

@@ -0,0 +1,32 @@
import svgPaths from "./svg-3e945qfv3w";
function Group() {
return (
<div className="absolute inset-[19%_8.76%_19%_8.75%]">
<div className="absolute inset-[-0.16%_0_-0.16%_-0.12%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 82.6407 62.2">
<g id="Group 1000002086">
<path d={svgPaths.p30d09400} fill="var(--fill-0, #F95F62)" id="Vector" stroke="var(--stroke-0, #FFF3F3)" strokeWidth="0.1" />
<g id="Vector_2">
<path d={svgPaths.p187f9700} fill="var(--fill-0, #F95F62)" />
<path clipRule="evenodd" d={svgPaths.p3eab2600} fill="var(--fill-0, #F95F62)" fillRule="evenodd" />
<path clipRule="evenodd" d={svgPaths.p3c4db480} fill="var(--fill-0, #F95F62)" fillRule="evenodd" />
<path clipRule="evenodd" d={svgPaths.p35fc7b00} fill="var(--fill-0, #F95F62)" fillRule="evenodd" />
<path clipRule="evenodd" d={svgPaths.p2c937b40} fill="var(--fill-0, #F95F62)" fillRule="evenodd" />
<path clipRule="evenodd" d={svgPaths.p2dd59000} fill="var(--fill-0, #F95F62)" fillRule="evenodd" />
<path d={svgPaths.p18421100} stroke="var(--stroke-0, #FFF3F3)" strokeWidth="0.2" />
</g>
</g>
</svg>
</div>
</div>
);
}
export default function NounRelax() {
return (
<div className="overflow-clip relative rounded-[19.048px] size-full" data-name="noun-relax-6597055 1">
<Group />
</div>
);
}

View File

@@ -0,0 +1,13 @@
import svgPaths from "./svg-nvjqsh4m9x";
export default function NounRelax() {
return (
<div className="relative size-full" data-name="noun-relax-6597055 1">
<svg className="absolute block inset-0 size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 62 62">
<g id="noun-relax-6597055 1">
<path d={svgPaths.pf613c00} fill="var(--fill-0, #F95F62)" id="Vector" />
</g>
</svg>
</div>
);
}

View File

@@ -0,0 +1,13 @@
import svgPaths from "./svg-rpecqp6li2";
export default function NounRelax() {
return (
<div className="relative size-full" data-name="noun-relax-6597055 1">
<svg className="absolute block inset-0 size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 62 62">
<g id="noun-relax-6597055 1">
<path d={svgPaths.p32a3dd80} fill="var(--fill-0, #F95F62)" id="Vector" />
</g>
</svg>
</div>
);
}

View File

@@ -0,0 +1,29 @@
import svgPaths from "./svg-u2edxj8dia";
function Group() {
return (
<div className="absolute inset-[18.8%_13.05%]">
<div className="absolute inset-[-0.52%_-0.44%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 46.2181 39.09">
<g id="Group 1000002089">
<path d={svgPaths.p1d2c1e00} fill="var(--fill-0, #F95F62)" id="Vector" />
<path d={svgPaths.p11d0e900} fill="var(--fill-0, #F95F62)" id="Vector_2" />
<path d={svgPaths.p17868d00} fill="var(--fill-0, #F95F62)" id="Vector_3" />
<path d={svgPaths.p1cd27a80} fill="var(--fill-0, #F95F62)" id="Vector_4" />
<path d={svgPaths.p1df26700} fill="var(--fill-0, #F95F62)" id="Vector_5" />
<path d={svgPaths.p15b7cd00} fill="var(--fill-0, #F95F62)" id="Vector_6" />
<path d={svgPaths.p18f61d00} fill="var(--fill-0, #F95F62)" id="Vector_7" />
</g>
</svg>
</div>
</div>
);
}
export default function NounRelax() {
return (
<div className="overflow-clip relative rounded-[8px] size-full" data-name="noun-relax-6597055 1">
<Group />
</div>
);
}

View File

@@ -0,0 +1,13 @@
import svgPaths from "./svg-kj92221mds";
export default function NounRelax() {
return (
<div className="relative size-full" data-name="noun-relax-6597055 1">
<svg className="absolute block inset-0 size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 62 62">
<g id="noun-relax-6597055 1">
<path d={svgPaths.p2c67c800} fill="var(--fill-0, #F95F62)" id="Vector" />
</g>
</svg>
</div>
);
}

View File

@@ -0,0 +1,31 @@
import svgPaths from "./svg-qafz9g9knb";
function Group() {
return (
<div className="absolute inset-[18.8%_20.75%_18.8%_20.74%]">
<div className="absolute inset-[-0.13%_-0.14%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 36.3759 38.7903">
<g id="Group 1000002088">
<path d={svgPaths.p25c1fb00} fill="var(--fill-0, #F95F62)" id="Vector" />
<path d={svgPaths.p1315a800} fill="var(--fill-0, #F95F62)" id="Vector_2" />
<path d={svgPaths.p5c4670} fill="var(--fill-0, #F95F62)" id="Vector_3" />
<path d={svgPaths.p27c0e980} fill="var(--fill-0, #F95F62)" id="Vector_4" />
<path d={svgPaths.p175044f0} fill="var(--fill-0, #F95F62)" id="Vector_5" />
<path d={svgPaths.p1b76b0f0} fill="var(--fill-0, #F95F62)" id="Vector_6" />
<path d={svgPaths.p6408e00} fill="var(--fill-0, #F95F62)" id="Vector_7" />
<path d={svgPaths.p35ead8c0} fill="var(--fill-0, #F95F62)" id="Vector_8" />
<path d={svgPaths.p8ca8800} fill="var(--fill-0, #F95F62)" id="Vector_9" />
</g>
</svg>
</div>
</div>
);
}
export default function NounRelax() {
return (
<div className="overflow-clip relative rounded-[8px] size-full" data-name="noun-relax-6597055 1">
<Group />
</div>
);
}

View File

@@ -0,0 +1,553 @@
import svgPaths from "./svg-qnagtgq03x";
function Group1() {
return (
<div className="absolute inset-[59.5%_33.65%_22.05%_65.27%]" data-name="Group">
<div className="absolute inset-[-0.44%_-7.45%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 0.771057 11.5419">
<g id="Group">
<path d={svgPaths.p1b540400} fill="var(--fill-0, #F95F62)" id="Vector" stroke="var(--stroke-0, #F95F62)" strokeWidth="0.1" />
</g>
</svg>
</div>
</div>
);
}
function Group2() {
return (
<div className="absolute inset-[23.07%_37.26%_18.8%_45.55%]" data-name="Group">
<div className="absolute inset-[-0.14%_-0.47%_-0.14%_-0.46%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 10.7594 36.1445">
<g id="Group">
<path d={svgPaths.p3450f000} fill="var(--fill-0, #F95F62)" id="Vector" stroke="var(--stroke-0, #F95F62)" strokeWidth="0.1" />
</g>
</svg>
</div>
</div>
);
}
function Group3() {
return (
<div className="absolute inset-[23.07%_34.68%_50.35%_58.86%]" data-name="Group">
<div className="absolute inset-[-0.3%_-1.24%_-0.3%_-1.26%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 4.10206 16.5821">
<g id="Group">
<path d={svgPaths.p1b618330} fill="var(--fill-0, #F95F62)" id="Vector" stroke="var(--stroke-0, #F95F62)" strokeWidth="0.1" />
</g>
</svg>
</div>
</div>
);
}
function Group4() {
return (
<div className="absolute inset-[18.87%_38.46%_75.85%_50.36%]" data-name="Group">
<div className="absolute inset-[-1.53%_-0.72%_-1.52%_-0.73%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 7.02982 3.37534">
<g id="Group">
<path d={svgPaths.p35daba00} fill="var(--fill-0, #F95F62)" id="Vector" stroke="var(--stroke-0, #F95F62)" strokeWidth="0.1" />
</g>
</svg>
</div>
</div>
);
}
function Group5() {
return (
<div className="absolute inset-[52.94%_36.78%_45.98%_60.13%]" data-name="Group">
<div className="absolute inset-[-7.45%_-2.61%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 2.01627 0.77106">
<g id="Group">
<path d={svgPaths.p2f242100} fill="var(--fill-0, #F95F62)" id="Vector" stroke="var(--stroke-0, #F95F62)" strokeWidth="0.1" />
</g>
</svg>
</div>
</div>
);
}
function Group6() {
return (
<div className="absolute inset-[52.94%_42.21%_45.98%_45.71%]" data-name="Group">
<div className="absolute inset-[-7.45%_-0.67%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 7.5846 0.77106">
<g id="Group">
<path d={svgPaths.p21290430} fill="var(--fill-0, #F95F62)" id="Vector" stroke="var(--stroke-0, #F95F62)" strokeWidth="0.1" />
</g>
</svg>
</div>
</div>
);
}
function Group7() {
return (
<div className="absolute inset-[67.24%_33.65%_31.68%_45.71%]" data-name="Group">
<div className="absolute inset-[-7.44%_-0.39%_-7.47%_-0.39%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 12.8961 0.771057">
<g id="Group">
<path d={svgPaths.p2f0cc900} fill="var(--fill-0, #F95F62)" id="Vector" stroke="var(--stroke-0, #F95F62)" strokeWidth="0.1" />
</g>
</svg>
</div>
</div>
);
}
function Group9() {
return (
<div className="absolute inset-[45.7%_19.68%_35.61%_63.58%]" data-name="Group">
<div className="absolute inset-[-0.43%_-0.48%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 10.476 11.6873">
<g id="Group">
<path d={svgPaths.p3401a40} fill="var(--fill-0, #F95F62)" id="Vector" stroke="var(--stroke-0, #F95F62)" strokeWidth="0.1" />
</g>
</svg>
</div>
</div>
);
}
function Group10() {
return (
<div className="absolute inset-[63.31%_27.51%_21.53%_71.41%]" data-name="Group">
<div className="absolute inset-[-0.53%_-7.51%_-0.53%_-7.39%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 0.771057 9.5045">
<g id="Group">
<path d={svgPaths.p206c6580} fill="var(--fill-0, #F95F62)" id="Vector" stroke="var(--stroke-0, #F95F62)" strokeWidth="0.1" />
</g>
</svg>
</div>
</div>
);
}
function Group11() {
return (
<div className="absolute inset-[77.62%_20.78%_18.8%_64.72%]" data-name="Group">
<div className="absolute inset-[-2.25%_-0.56%_-2.25%_-0.55%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 9.09225 2.32273">
<g id="Group">
<g id="Vector">
<path d={svgPaths.p1dc35100} fill="var(--fill-0, #F95F62)" />
<path d={svgPaths.p3ce0580} stroke="var(--stroke-0, #F95F62)" strokeWidth="0.1" />
</g>
</g>
</svg>
</div>
</div>
);
}
function Group12() {
return (
<div className="absolute inset-[54.66%_19.94%_41.64%_63.89%]" data-name="Group">
<div className="absolute inset-[-2.18%_-0.5%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 10.1247 2.39298">
<g id="Group">
<path d={svgPaths.p2051aa80} fill="var(--fill-0, #F95F62)" id="Vector" stroke="var(--stroke-0, #F95F62)" strokeWidth="0.1" />
</g>
</svg>
</div>
</div>
);
}
function Group8() {
return (
<div className="absolute contents inset-[45.7%_19.68%_18.8%_63.58%]" data-name="Group">
<Group9 />
<Group10 />
<Group11 />
<Group12 />
</div>
);
}
function Group14() {
return (
<div className="absolute inset-[45.49%_63.28%_50.09%_19.68%]" data-name="Group">
<div className="absolute inset-[-1.83%_-0.47%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 10.6625 2.83753">
<g id="Group">
<path d={svgPaths.p3c314100} fill="var(--fill-0, #F95F62)" id="Vector" stroke="var(--stroke-0, #F95F62)" strokeWidth="0.1" />
</g>
</svg>
</div>
</div>
);
}
function Group15() {
return (
<div className="absolute inset-[48.83%_65.61%_36.3%_22.01%]" data-name="Group">
<div className="absolute inset-[-0.54%_-0.65%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 7.77719 9.32281">
<g id="Group">
<path d={svgPaths.p332aed00} fill="var(--fill-0, #F95F62)" id="Vector" stroke="var(--stroke-0, #F95F62)" strokeWidth="0.1" />
</g>
</svg>
</div>
</div>
);
}
function Group16() {
return (
<div className="absolute inset-[62.4%_69.7%_24.8%_26.1%]" data-name="Group">
<div className="absolute inset-[-0.63%_-1.92%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 2.70671 8.03156">
<g id="Group">
<path d={svgPaths.p34c8570} fill="var(--fill-0, #F95F62)" id="Vector" stroke="var(--stroke-0, #F95F62)" strokeWidth="0.1" />
</g>
</svg>
</div>
</div>
);
}
function Group17() {
return (
<div className="absolute inset-[52.66%_65.61%_46.25%_22.01%]" data-name="Group">
<div className="absolute inset-[-7.45%_-0.65%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 7.77598 0.771057">
<g id="Group">
<path d={svgPaths.p35856480} fill="var(--fill-0, #F95F62)" id="Vector" stroke="var(--stroke-0, #F95F62)" strokeWidth="0.1" />
</g>
</svg>
</div>
</div>
);
}
function Group18() {
return (
<div className="absolute inset-[55.02%_70.37%_43.9%_22.01%]" data-name="Group">
<div className="absolute inset-[-7.45%_-1.06%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 4.82648 0.77106">
<g id="Group">
<path d={svgPaths.p119fe300} fill="var(--fill-0, #F95F62)" id="Vector" stroke="var(--stroke-0, #F95F62)" strokeWidth="0.1" />
</g>
</svg>
</div>
</div>
);
}
function Group19() {
return (
<div className="absolute inset-[55.02%_65.61%_43.9%_31.59%]" data-name="Group">
<div className="absolute inset-[-7.45%_-2.88%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 1.83821 0.77106">
<g id="Group">
<path d={svgPaths.p1a7ca200} fill="var(--fill-0, #F95F62)" id="Vector" stroke="var(--stroke-0, #F95F62)" strokeWidth="0.1" />
</g>
</svg>
</div>
</div>
);
}
function Group20() {
return (
<div className="absolute inset-[32.48%_67.46%_53.42%_23.85%]" data-name="Group">
<div className="absolute inset-[-0.57%_-0.94%_-0.57%_-0.92%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 5.48785 8.83708">
<g id="Group">
<path d={svgPaths.p39705400} fill="var(--fill-0, #F95F62)" id="Vector" stroke="var(--stroke-0, #F95F62)" strokeWidth="0.1" />
</g>
</svg>
</div>
</div>
);
}
function Group21() {
return (
<div className="absolute inset-[29.67%_71.26%_66.43%_27.66%]" data-name="Group">
<div className="absolute inset-[-2.07%_-7.4%_-2.06%_-7.5%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 0.771058 2.51654">
<g id="Group">
<path d={svgPaths.p2c63c800} fill="var(--fill-0, #F95F62)" id="Vector" stroke="var(--stroke-0, #F95F62)" strokeWidth="0.1" />
</g>
</svg>
</div>
</div>
);
}
function Group22() {
return (
<div className="absolute inset-[74.12%_64.91%_18.8%_21.31%]" data-name="Group">
<div className="absolute inset-[-1.14%_-0.59%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 8.63964 4.49337">
<g id="Group">
<path d={svgPaths.p1aa58800} fill="var(--fill-0, #F95F62)" id="Vector" stroke="var(--stroke-0, #F95F62)" strokeWidth="0.1" />
</g>
</svg>
</div>
</div>
);
}
function Group23() {
return (
<div className="absolute inset-[77.66%_75.18%_21.26%_21.5%]" data-name="Group">
<div className="absolute inset-[-7.45%_-2.44%_-7.45%_-2.41%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 2.16405 0.771057">
<g id="Group">
<path d={svgPaths.p3263c200} fill="var(--fill-0, #F95F62)" id="Vector" stroke="var(--stroke-0, #F95F62)" strokeWidth="0.1" />
</g>
</svg>
</div>
</div>
);
}
function Group24() {
return (
<div className="absolute inset-[77.66%_65.09%_21.26%_26.65%]" data-name="Group">
<div className="absolute inset-[-7.45%_-0.98%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 5.22015 0.771057">
<g id="Group">
<path d={svgPaths.p32a21d80} fill="var(--fill-0, #F95F62)" id="Vector" stroke="var(--stroke-0, #F95F62)" strokeWidth="0.1" />
</g>
</svg>
</div>
</div>
);
}
function Group25() {
return (
<div className="absolute inset-[18.8%_69.1%_71.62%_25.5%]" data-name="Group">
<div className="absolute inset-[-0.84%_-1.5%_-0.84%_-1.49%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 3.4456 6.03777">
<g id="Group">
<path d={svgPaths.p200bb800} fill="var(--fill-0, #F95F62)" id="Vector" stroke="var(--stroke-0, #F95F62)" strokeWidth="0.1" />
</g>
</svg>
</div>
</div>
);
}
function Group13() {
return (
<div className="absolute contents inset-[18.8%_63.28%_18.8%_19.68%]" data-name="Group">
<Group14 />
<Group15 />
<Group16 />
<Group17 />
<Group18 />
<Group19 />
<Group20 />
<Group21 />
<Group22 />
<Group23 />
<Group24 />
<Group25 />
</div>
);
}
function Group27() {
return (
<div className="absolute inset-[48.83%_52.92%_50.09%_37.3%]" data-name="Group">
<div className="absolute inset-[-7.47%_-0.83%_-7.44%_-0.82%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 6.16132 0.77106">
<g id="Group">
<path d={svgPaths.p2ceb9900} fill="var(--fill-0, #F95F62)" id="Vector" stroke="var(--stroke-0, #F95F62)" strokeWidth="0.1" />
</g>
</svg>
</div>
</div>
);
}
function Group28() {
return (
<div className="absolute inset-[45.49%_51.06%_53.42%_37.3%]" data-name="Group">
<div className="absolute inset-[-7.45%_-0.69%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 7.3169 0.771057">
<g id="Group">
<path d={svgPaths.p5cab80} fill="var(--fill-0, #F95F62)" id="Vector" stroke="var(--stroke-0, #F95F62)" strokeWidth="0.1" />
</g>
</svg>
</div>
</div>
);
}
function Group29() {
return (
<div className="absolute inset-[48.83%_52.34%_36.3%_38.35%]" data-name="Group">
<div className="absolute inset-[-0.54%_-0.86%_-0.54%_-0.87%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 5.87061 9.32281">
<g id="Group">
<path d={svgPaths.p2040a800} fill="var(--fill-0, #F95F62)" id="Vector" stroke="var(--stroke-0, #F95F62)" strokeWidth="0.1" />
</g>
</svg>
</div>
</div>
);
}
function Group30() {
return (
<div className="absolute inset-[62.4%_53.37%_24.8%_42.44%]" data-name="Group">
<div className="absolute inset-[-0.63%_-1.92%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 2.69823 8.03156">
<g id="Group">
<path d={svgPaths.p15afe300} fill="var(--fill-0, #F95F62)" id="Vector" stroke="var(--stroke-0, #F95F62)" strokeWidth="0.1" />
</g>
</svg>
</div>
</div>
);
}
function Group31() {
return (
<div className="absolute inset-[52.66%_56.01%_46.25%_38.35%]" data-name="Group">
<div className="absolute inset-[-7.45%_-1.43%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 3.59701 0.771057">
<g id="Group">
<path d={svgPaths.p20329b80} fill="var(--fill-0, #F95F62)" id="Vector" stroke="var(--stroke-0, #F95F62)" strokeWidth="0.1" />
</g>
</svg>
</div>
</div>
);
}
function Group32() {
return (
<div className="absolute inset-[55.02%_53.36%_43.9%_38.35%]" data-name="Group">
<div className="absolute inset-[-7.45%_-0.97%_-7.45%_-0.98%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 5.23953 0.77106">
<g id="Group">
<path d={svgPaths.pcabba00} fill="var(--fill-0, #F95F62)" id="Vector" stroke="var(--stroke-0, #F95F62)" strokeWidth="0.1" />
</g>
</svg>
</div>
</div>
);
}
function Group33() {
return (
<div className="absolute inset-[32.48%_51.12%_53.42%_40.19%]" data-name="Group">
<div className="absolute inset-[-0.57%_-0.92%_-0.57%_-0.94%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 5.48785 8.83708">
<g id="Group">
<path d={svgPaths.p88545c0} fill="var(--fill-0, #F95F62)" id="Vector" stroke="var(--stroke-0, #F95F62)" strokeWidth="0.1" />
</g>
</svg>
</div>
</div>
);
}
function Group34() {
return (
<div className="absolute inset-[29.67%_54.92%_66.43%_44%]" data-name="Group">
<div className="absolute inset-[-2.07%_-7.45%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 0.77106 2.51654">
<g id="Group">
<path d={svgPaths.p2b2e2f00} fill="var(--fill-0, #F95F62)" id="Vector" stroke="var(--stroke-0, #F95F62)" strokeWidth="0.1" />
</g>
</svg>
</div>
</div>
);
}
function Group35() {
return (
<div className="absolute inset-[74.12%_53.36%_18.8%_37.65%]" data-name="Group">
<div className="absolute inset-[-1.14%_-0.9%_-1.14%_-0.89%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 5.67317 4.49337">
<g id="Group">
<path d={svgPaths.p37797c00} fill="var(--fill-0, #F95F62)" id="Vector" stroke="var(--stroke-0, #F95F62)" strokeWidth="0.1" />
</g>
</svg>
</div>
</div>
);
}
function Group36() {
return (
<div className="absolute inset-[77.66%_53.18%_21.26%_37.83%]" data-name="Group">
<div className="absolute inset-[-7.45%_-0.9%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 5.67196 0.771057">
<g id="Group">
<path d={svgPaths.pbe42b80} fill="var(--fill-0, #F95F62)" id="Vector" stroke="var(--stroke-0, #F95F62)" strokeWidth="0.1" />
</g>
</svg>
</div>
</div>
);
}
function Group37() {
return (
<div className="absolute inset-[18.8%_52.76%_71.62%_41.84%]" data-name="Group">
<div className="absolute inset-[-0.84%_-1.5%_-0.84%_-1.49%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 3.4456 6.03777">
<g id="Group">
<path d={svgPaths.p26102900} fill="var(--fill-0, #F95F62)" id="Vector" stroke="var(--stroke-0, #F95F62)" strokeWidth="0.1" />
</g>
</svg>
</div>
</div>
);
}
function Group26() {
return (
<div className="absolute contents inset-[18.8%_51.06%_18.8%_37.3%]" data-name="Group">
<Group27 />
<Group28 />
<Group29 />
<Group30 />
<Group31 />
<Group32 />
<Group33 />
<Group34 />
<Group35 />
<Group36 />
<Group37 />
</div>
);
}
function Group() {
return (
<div className="absolute contents inset-[18.8%_19.68%]" data-name="Group">
<Group1 />
<Group2 />
<Group3 />
<Group4 />
<Group5 />
<Group6 />
<Group7 />
<Group8 />
<Group13 />
<Group26 />
</div>
);
}
export default function NounRelax() {
return (
<div className="overflow-clip relative rounded-[8px] size-full" data-name="noun-relax-6597055 1">
<Group />
</div>
);
}

View File

@@ -0,0 +1,15 @@
import svgPaths from "./svg-bfd6jik9me";
export default function NounRelax() {
return (
<div className="relative size-full" data-name="noun-relax-6597055 1">
<svg className="absolute block inset-0 size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 62 62">
<g id="noun-relax-6597055 1">
<g id="Vector">
<path clipRule="evenodd" d={svgPaths.p9958380} fill="var(--fill-0, #F95F62)" fillRule="evenodd" />
</g>
</g>
</svg>
</div>
);
}

View File

@@ -0,0 +1,28 @@
import svgPaths from "./svg-jhl6jizeq8";
function Group() {
return (
<div className="absolute inset-[18.8%_7.64%]">
<div className="absolute inset-[-0.87%_-0.64%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 53.2001 39.3637">
<g id="Group 1000002091">
<path d={svgPaths.p1f010b80} fill="var(--fill-0, #F95F62)" id="Vector" />
<path d={svgPaths.p3d025700} fill="var(--fill-0, #F95F62)" id="Vector_2" />
<path d={svgPaths.p14022570} fill="var(--fill-0, #F95F62)" id="Vector_3" />
<path d={svgPaths.p6506f00} fill="var(--fill-0, #F95F62)" id="Vector_4" />
<path d={svgPaths.p294a4200} fill="var(--fill-0, #F95F62)" id="Vector_5" />
<path d={svgPaths.p13d26c70} fill="var(--fill-0, #F95F62)" id="Vector_6" />
</g>
</svg>
</div>
</div>
);
}
export default function NounRelax() {
return (
<div className="overflow-clip relative rounded-[8px] size-full" data-name="noun-relax-6597055 1">
<Group />
</div>
);
}

View File

@@ -0,0 +1,34 @@
import svgPaths from "./svg-70pmm871bv";
function Group() {
return (
<div className="absolute inset-[18.8%_20.26%]">
<div className="absolute inset-[-0.52%_-0.54%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 37.279 39.09">
<g id="Group 1000002090">
<path d={svgPaths.p2d70fa80} fill="var(--fill-0, #F95F62)" id="Vector" />
<path d={svgPaths.p17231c80} fill="var(--fill-0, #F95F62)" id="Vector_2" />
<path d={svgPaths.p2009e400} fill="var(--fill-0, #F95F62)" id="Vector_3" />
<path d={svgPaths.p7bc2000} fill="var(--fill-0, #F95F62)" id="Vector_4" />
<path d={svgPaths.pbcf8200} fill="var(--fill-0, #F95F62)" id="Vector_5" />
<path d={svgPaths.p1e3ba380} fill="var(--fill-0, #F95F62)" id="Vector_6" />
<path d={svgPaths.p34abd3f0} fill="var(--fill-0, #F95F62)" id="Vector_7" />
<path d={svgPaths.p3f923a00} fill="var(--fill-0, #F95F62)" id="Vector_8" />
<path d={svgPaths.p1b51ae80} fill="var(--fill-0, #F95F62)" id="Vector_9" />
<path d={svgPaths.pfd3f900} fill="var(--fill-0, #F95F62)" id="Vector_10" />
<path d={svgPaths.p3bcea400} fill="var(--fill-0, #F95F62)" id="Vector_11" />
<path d={svgPaths.p1c5d9f00} fill="var(--fill-0, #F95F62)" id="Vector_12" />
</g>
</svg>
</div>
</div>
);
}
export default function NounRelax() {
return (
<div className="overflow-clip relative rounded-[8px] size-full" data-name="noun-relax-6597055 1">
<Group />
</div>
);
}

View File

@@ -0,0 +1,28 @@
import svgPaths from "./svg-5q6zbixxri";
function Group() {
return (
<div className="absolute inset-[18.8%_16.69%]">
<div className="absolute inset-[-0.39%_-0.36%]">
<svg className="block size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 41.6008 38.99">
<g id="Group 1000002092">
<path d={svgPaths.p19680b80} fill="var(--fill-0, #F95F62)" id="Vector" />
<path d={svgPaths.p1ff00f00} fill="var(--fill-0, #F95F62)" id="Vector_2" />
<path d={svgPaths.p24992980} fill="var(--fill-0, #F95F62)" id="Vector_3" />
<path d={svgPaths.p37896700} fill="var(--fill-0, #F95F62)" id="Vector_4" />
<path d={svgPaths.p2c621c00} fill="var(--fill-0, #F95F62)" id="Vector_5" />
<path d={svgPaths.p351aec00} fill="var(--fill-0, #F95F62)" id="Vector_6" />
</g>
</svg>
</div>
</div>
);
}
export default function NounRelax() {
return (
<div className="overflow-clip relative rounded-[8px] size-full" data-name="noun-relax-6597055 1">
<Group />
</div>
);
}

View File

@@ -0,0 +1,13 @@
import svgPaths from "./svg-s8leuc872s";
export default function NounRelax() {
return (
<div className="relative size-full" data-name="noun-relax-6597055 1">
<svg className="absolute block inset-0 size-full" fill="none" preserveAspectRatio="none" viewBox="0 0 62 62">
<g id="noun-relax-6597055 1">
<path clipRule="evenodd" d={svgPaths.pc549500} fill="var(--fill-0, #F95F62)" fillRule="evenodd" id="Vector" />
</g>
</svg>
</div>
);
}

BIN
src/imports/image-1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
src/imports/image-10.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

BIN
src/imports/image-2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
src/imports/image-3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
src/imports/image-4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
src/imports/image-5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

BIN
src/imports/image-6.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

BIN
src/imports/image-7.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
src/imports/image-8.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

BIN
src/imports/image-9.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
src/imports/image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,3 @@
export default {
pc3f4d00: "M44.3466 22.4327C44.4716 22.4411 44.5949 22.4461 44.72 22.4461C46.0236 22.4461 47.2689 21.981 48.2608 21.1158C49.346 20.1689 49.9978 18.8536 50.0962 17.4166C50.1928 15.9796 49.7261 14.5893 48.7776 13.5024C47.8307 12.4155 46.5154 11.7654 45.0784 11.667C43.6447 11.5703 42.2511 12.0371 41.1642 12.9856C40.3257 13.7191 39.7489 14.671 39.4838 15.7296C39.0037 15.4378 38.4486 15.2728 37.8768 15.2728H26.4243C25.1957 15.2728 24.0405 15.7512 23.1703 16.6197L20.368 19.422C19.1494 20.6406 19.0561 22.5527 20.158 23.7745C20.7331 24.413 21.5216 24.7781 22.3784 24.7997C23.232 24.8297 24.0505 24.493 24.6489 23.8945L27.1545 21.389H33.1859L21.2268 33.3468L17.8527 29.9777C17.256 29.3809 16.4274 29.0558 15.5839 29.0725C14.7271 29.0959 13.9385 29.4593 13.3634 30.0977C12.2632 31.3197 12.3549 33.2318 13.5735 34.4503L18.7462 39.623C19.4247 40.3015 20.3199 40.6716 21.2751 40.6716H21.3535C22.3387 40.6499 23.2489 40.2398 23.9124 39.5163L27.7732 35.3537L32.2875 40.1531L32.2991 47.1131C32.2991 48.8368 33.5861 50.2538 35.2281 50.3405C35.2848 50.3438 35.3415 50.3455 35.3965 50.3455C36.1933 50.3455 36.9435 50.0471 37.5236 49.4953C38.1371 48.9135 38.4872 48.095 38.4872 47.2481L38.4738 40.0265C38.4738 38.4595 37.8821 36.9692 36.8068 35.8306L33.8362 32.6816L37.377 29.0475V30.3711C37.377 32.9317 39.4608 35.0155 42.0214 35.0155H46.9526C47.7978 35.0155 48.6146 34.6638 49.1964 34.052C49.7865 33.4302 50.0866 32.615 50.0433 31.7565C49.9566 30.1145 48.5396 28.8275 46.8159 28.8275H43.5685V22.3195C43.8219 22.3745 44.0803 22.4178 44.3454 22.4361L44.3466 22.4327ZM40.6591 16.773C40.7324 15.6911 41.2225 14.7026 42.0394 13.9891C42.7862 13.3373 43.7231 12.9872 44.7033 12.9872C44.7967 12.9872 44.89 12.9906 44.9851 12.9972C46.067 13.0706 47.0555 13.5607 47.769 14.3792C48.4825 15.1961 48.8342 16.2429 48.7609 17.3248C48.6875 18.4084 48.1974 19.3969 47.3805 20.1104C46.5637 20.8239 45.5235 21.174 44.4333 21.1039C42.1994 20.9522 40.5041 19.0102 40.6574 16.7762L40.6591 16.773ZM22.9319 38.6095C22.5135 39.0646 21.9434 39.323 21.3249 39.3363C20.7398 39.3463 20.1263 39.1162 19.6879 38.6795L14.5152 33.5067C13.81 32.8016 13.7367 31.673 14.3518 30.9895C14.6802 30.6261 15.1286 30.4177 15.6154 30.4044C16.0988 30.3977 16.5623 30.5744 16.9073 30.9195L20.7532 34.7603C21.0132 35.0204 21.435 35.0204 21.6967 34.7603L25.4875 30.9695C25.4692 32.1598 25.8976 33.3567 26.7811 34.2969L26.8578 34.3786L22.9319 38.6095ZM32.429 33.1282L35.8381 36.7423C36.6783 37.6342 37.1417 38.7994 37.1417 40.0247L37.1551 47.2463C37.1551 47.7347 36.96 48.1882 36.6066 48.5233C36.2532 48.86 35.7864 49.0284 35.2997 49.0034C34.3811 48.955 33.6343 48.1048 33.6343 47.1063L33.621 39.8813C33.621 39.7112 33.556 39.5495 33.4409 39.4262L27.7531 33.3798C26.4811 32.0279 26.5128 29.9407 27.8248 28.6287L35.2632 21.1903C35.4532 21.0003 35.5116 20.7135 35.4082 20.4635C35.3049 20.2134 35.0615 20.0517 34.7914 20.0517H26.8746C26.6979 20.0517 26.5279 20.1217 26.4028 20.2468L23.7022 22.9474C23.3555 23.2924 22.8721 23.4692 22.4103 23.4625C21.9236 23.4491 21.4735 23.2424 21.1467 22.879C20.5316 22.1955 20.605 21.0653 21.3101 20.3618L24.1124 17.5595C24.7292 16.9427 25.551 16.6026 26.4229 16.6026H37.8754C38.4289 16.6026 38.9573 16.8677 39.289 17.3111C39.3007 17.3278 39.3157 17.3311 39.3291 17.3411C39.4341 19.2666 40.5593 20.9519 42.2047 21.8204C42.1713 22.0838 42.0597 22.3305 41.8713 22.5239L32.439 32.2045C32.189 32.4612 32.1857 32.868 32.4307 33.128L32.429 33.1282ZM46.8172 30.1575C47.8141 30.1575 48.6643 30.9044 48.7126 31.8229C48.7393 32.313 48.5676 32.7764 48.2309 33.1298C47.8941 33.4832 47.4407 33.6783 46.9539 33.6783H42.0227C40.1973 33.6783 38.712 32.193 38.712 30.3676V27.6753L42.2328 24.0612V29.4892C42.2328 29.8576 42.5312 30.156 42.8996 30.156H46.8138L46.8172 30.1575ZM31.4522 48.3281C31.4522 48.6965 31.1538 48.9949 30.7854 48.9949H20.0665C19.698 48.9949 19.3996 48.6965 19.3996 48.3281C19.3996 47.9597 19.698 47.6613 20.0665 47.6613H30.7854C31.1538 47.6613 31.4522 47.9597 31.4522 48.3281ZM16.2822 48.3281C16.2822 48.6965 15.9838 48.9949 15.6154 48.9949H12.8848C12.5164 48.9949 12.218 48.6949 12.218 48.3281C12.218 47.9597 12.5164 47.6613 12.8848 47.6613H15.6154C15.9838 47.6613 16.2822 47.9614 16.2822 48.3281ZM27.121 43.9974C26.7526 43.9974 26.4542 43.699 26.4542 43.3305C26.4542 42.9621 26.7526 42.6637 27.121 42.6637H29.8383C30.2067 42.6637 30.5051 42.9621 30.5051 43.3305C30.5051 43.699 30.2067 43.9974 29.8383 43.9974H27.121ZM15.7287 42.6637H24.0022C24.3707 42.6637 24.6691 42.9621 24.6691 43.3305C24.6691 43.699 24.3707 43.9974 24.0022 43.9974H15.7287C15.3602 43.9974 15.0618 43.699 15.0618 43.3305C15.0618 42.9621 15.3602 42.6637 15.7287 42.6637ZM12.558 38.9996C12.1896 38.9996 11.8912 38.7012 11.8912 38.3328C11.8912 37.9644 12.1896 37.666 12.558 37.666H15.0668C15.4353 37.666 15.7337 37.9644 15.7337 38.3328C15.7337 38.7012 15.4353 38.9996 15.0668 38.9996H12.558Z",
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,37 @@
export default {
p119fe300: "M4.44095 0.72106H0.385528C0.2002 0.72106 0.05 0.57086 0.05 0.385531C0.05 0.200203 0.2002 0.05 0.385528 0.05H4.44095C4.62628 0.05 4.77648 0.200203 4.77648 0.385531C4.77648 0.57086 4.62628 0.72106 4.44095 0.72106Z",
p15afe300: "M2.31149 7.98157H0.385531C0.200203 7.98157 0.05 7.83136 0.05 7.64603V0.385528C0.05 0.2002 0.200203 0.05 0.385531 0.05C0.57086 0.05 0.72106 0.2002 0.72106 0.385528V7.31051H2.3127C2.49803 7.31051 2.64823 7.46071 2.64823 7.64603C2.64702 7.83136 2.49682 7.98157 2.31149 7.98157Z",
p1a7ca200: "M1.45268 0.72106H0.385528C0.2002 0.72106 0.05 0.57086 0.05 0.385531C0.05 0.200203 0.2002 0.05 0.385528 0.05H1.45268C1.63801 0.05 1.78821 0.200203 1.78821 0.385531C1.78821 0.57086 1.6368 0.72106 1.45268 0.72106Z",
p1aa58800: "M8.25411 4.44337H0.38553C0.200202 4.44337 0.05 4.29317 0.05 4.10784V3.18362C0.05 2.50408 0.391585 1.87906 0.965739 1.51325L3.17272 0.103296C3.22602 0.0693796 3.28901 0.05 3.3532 0.05H5.28764C5.35184 0.05 5.41362 0.0681683 5.46813 0.103296L7.6739 1.51203C8.24684 1.87784 8.58964 2.50287 8.58964 3.18241V4.10784C8.58964 4.29317 8.43944 4.44337 8.25411 4.44337ZM0.721058 3.77352H7.91979V3.18362C7.91979 2.73302 7.69328 2.31997 7.31414 2.07771L5.18953 0.719847H3.45132L1.32671 2.0765C0.947571 2.31876 0.721058 2.73302 0.721058 3.18241V3.77352Z",
p1b540400: "M0.385528 11.4919C0.2002 11.4919 0.05 11.3417 0.05 11.1564V0.385528C0.05 0.2002 0.2002 0.05 0.385528 0.05C0.570857 0.05 0.721057 0.2002 0.721057 0.385528V11.1564C0.721057 11.3417 0.570857 11.4919 0.385528 11.4919Z",
p1b618330: "M3.71538 16.5321C3.54822 16.5321 3.40287 16.4061 3.38349 16.2353C3.31565 15.6539 2.75603 14.634 2.48713 14.1434C2.43504 14.0489 2.39143 13.9678 2.35752 13.9048L1.62711 12.5142C1.11109 11.5331 0.716213 10.4902 0.449728 9.41332C0.184454 8.33769 0.05 7.22935 0.05 6.12101V0.385528C0.05 0.2002 0.2002 0.05 0.385528 0.05C0.570857 0.05 0.721057 0.2002 0.721057 0.385528V6.12101C0.721057 8.23109 1.23949 10.3339 2.22064 12.2029L2.95105 13.5935C2.98134 13.6516 3.02494 13.7304 3.07461 13.8212C3.38591 14.3893 3.96612 15.4456 4.0497 16.1578C4.0715 16.3419 3.93947 16.5079 3.75535 16.5297C3.74203 16.5309 3.72871 16.5321 3.71538 16.5321Z",
p1dc35100: "M8.5434 2.27273H0.54885C0.306591 2.27273 0.105517 2.10678 0.0594875 1.86937C0.0134583 1.63195 0.13822 1.40302 0.363521 1.31338L2.56202 0.432771C3.19553 0.178399 3.86295 0.05 4.54613 0.05C5.2293 0.05 5.89672 0.178399 6.53023 0.432771L8.72873 1.31338C8.95403 1.40302 9.07879 1.63195 9.03276 1.86937C8.98673 2.10678 8.78566 2.27273 8.5434 2.27273ZM1.44157 1.60288H7.64946L6.2807 1.05416C5.16146 0.605984 3.92958 0.605984 2.81155 1.05416L1.44157 1.60288Z",
p200bb800: "M1.72401 5.98777C0.801003 5.98777 0.05 5.23677 0.05 4.31376C0.05 3.90677 0.203834 2.90018 0.453361 1.98444C0.593871 1.46722 0.746495 1.04932 0.903963 0.74165C0.992388 0.570857 1.26008 0.05 1.7228 0.05C2.18551 0.05 2.45321 0.569646 2.54163 0.74165C2.70031 1.04811 2.85173 1.466 2.99224 1.98444C3.24176 2.90018 3.3956 3.90677 3.3956 4.31376C3.39802 5.23677 2.64702 5.98777 1.72401 5.98777ZM1.72401 0.731959C1.68283 0.764664 1.57987 0.868835 1.43814 1.17893C1.31096 1.45753 1.18256 1.84151 1.06628 2.28847C0.839765 3.15818 0.719846 4.02305 0.719846 4.31376C0.719846 4.86732 1.17045 5.31671 1.7228 5.31671C2.27636 5.31671 2.72575 4.86611 2.72575 4.31376C2.72575 4.02305 2.60583 3.15818 2.37932 2.28847C2.26304 1.84151 2.13464 1.45753 2.00745 1.17893C1.86937 0.868835 1.76641 0.764664 1.72401 0.731959Z",
p20329b80: "M3.21148 0.721057H0.38553C0.200202 0.721057 0.05 0.570857 0.05 0.385528C0.05 0.2002 0.200202 0.05 0.38553 0.05H3.21148C3.39681 0.05 3.54701 0.2002 3.54701 0.385528C3.5458 0.570857 3.3956 0.721057 3.21148 0.721057Z",
p2040a800: "M3.88738 9.27281C2.86263 9.27281 1.89844 8.87308 1.17408 8.14872C0.449728 7.42437 0.05 6.46018 0.05 5.43542V0.385531C0.05 0.200203 0.200202 0.05 0.38553 0.05H5.48509C5.67041 0.05 5.82061 0.200203 5.82061 0.385531C5.82061 0.57086 5.67041 0.72106 5.48509 0.72106H0.721058V5.43542C0.721058 7.18211 2.14191 8.60296 3.8886 8.60296C4.23987 8.60296 4.55481 8.56904 4.75225 8.5109C4.9291 8.4576 5.11564 8.55935 5.16894 8.7362C5.22223 8.91305 5.12049 9.09959 4.94364 9.15289C4.60932 9.25221 4.18536 9.27281 3.88738 9.27281Z",
p2051aa80: "M7.44815 2.34298C6.38826 2.34298 5.59729 1.86937 4.89958 1.45147C4.26971 1.07476 3.67375 0.718635 2.93849 0.718635C2.12571 0.718635 1.53097 0.933034 1.17485 1.11231C0.807824 1.29763 0.624917 1.47691 0.622495 1.47933C0.491675 1.61015 0.279697 1.61136 0.148877 1.48175C0.0180568 1.35093 0.0168461 1.13896 0.146455 1.00814C0.185216 0.969375 1.11792 0.05 2.93728 0.05C3.85787 0.05 4.56163 0.471533 5.24238 0.878528C5.92676 1.28795 6.57238 1.67435 7.44693 1.67435C8.99739 1.67435 9.44436 0.962105 9.448 0.954837C9.53884 0.793735 9.74355 0.736805 9.90465 0.828864C10.0658 0.919711 10.1227 1.12442 10.0306 1.28552C10.0137 1.3158 9.85136 1.58835 9.44315 1.84756C8.92593 2.17583 8.25487 2.34298 7.44815 2.34298Z",
p206c6580: "M0.385528 9.4545C0.2002 9.4545 0.05 9.3043 0.05 9.11897V0.385528C0.05 0.2002 0.2002 0.05 0.385528 0.05C0.570857 0.05 0.721057 0.2002 0.721057 0.385528V9.11897C0.719845 9.30551 0.570857 9.4545 0.385528 9.4545Z",
p21290430: "M7.19907 0.72106H0.385528C0.2002 0.72106 0.05 0.57086 0.05 0.385531C0.05 0.200203 0.2002 0.05 0.385528 0.05H7.19907C7.3844 0.05 7.5346 0.200203 7.5346 0.385531C7.53338 0.57086 7.3844 0.72106 7.19907 0.72106Z",
p26102900: "M1.72401 5.98777C0.801003 5.98777 0.05 5.23677 0.05 4.31376C0.05 3.90677 0.203835 2.90018 0.453362 1.98444C0.593872 1.46722 0.746495 1.04932 0.903963 0.74165C0.992388 0.570857 1.26009 0.05 1.7228 0.05C2.18552 0.05 2.45321 0.569646 2.54163 0.74165C2.70031 1.04811 2.85173 1.466 2.99224 1.98444C3.24177 2.90018 3.3956 3.90677 3.3956 4.31376C3.39802 5.23677 2.64702 5.98777 1.72401 5.98777ZM1.72401 0.731959C1.68283 0.764664 1.57987 0.868835 1.43815 1.17893C1.31096 1.45753 1.18256 1.84151 1.06628 2.28847C0.839767 3.15818 0.719847 4.02305 0.719847 4.31376C0.719847 4.86732 1.17045 5.31671 1.7228 5.31671C2.27636 5.31671 2.72575 4.86611 2.72575 4.31376C2.72575 4.02305 2.60583 3.15818 2.37932 2.28847C2.26304 1.84151 2.13464 1.45753 2.00746 1.17893C1.86937 0.868835 1.76641 0.764664 1.72401 0.731959Z",
p2b2e2f00: "M0.385528 2.46654C0.2002 2.46654 0.05 2.31633 0.05 2.13101V0.385528C0.05 0.2002 0.2002 0.05 0.385528 0.05C0.570857 0.05 0.72106 0.2002 0.72106 0.385528V2.13101C0.72106 2.31754 0.570857 2.46654 0.385528 2.46654Z",
p2c63c800: "M0.385528 2.46654C0.2002 2.46654 0.05 2.31633 0.05 2.13101V0.385528C0.05 0.2002 0.2002 0.05 0.385528 0.05C0.570857 0.05 0.721058 0.2002 0.721058 0.385528V2.13101C0.721058 2.31754 0.570857 2.46654 0.385528 2.46654Z",
p2ceb9900: "M5.7758 0.72106H0.38553C0.200202 0.72106 0.05 0.57086 0.05 0.385531C0.05 0.200203 0.200202 0.05 0.38553 0.05H5.7758C5.96112 0.05 6.11132 0.200203 6.11132 0.385531C6.11132 0.57086 5.96112 0.72106 5.7758 0.72106Z",
p2f0cc900: "M12.5106 0.721057H0.385528C0.2002 0.721057 0.05 0.570857 0.05 0.385528C0.05 0.2002 0.2002 0.05 0.385528 0.05H12.5106C12.6959 0.05 12.8461 0.2002 12.8461 0.385528C12.8461 0.572068 12.6959 0.721057 12.5106 0.721057Z",
p2f242100: "M1.63074 0.72106H0.385528C0.2002 0.72106 0.05 0.57086 0.05 0.385531C0.05 0.200203 0.2002 0.05 0.385528 0.05H1.63074C1.81607 0.05 1.96627 0.200203 1.96627 0.385531C1.96506 0.57086 1.81607 0.72106 1.63074 0.72106Z",
p3263c200: "M1.77852 0.721057H0.385528C0.2002 0.721057 0.05 0.570857 0.05 0.385528C0.05 0.2002 0.2002 0.05 0.385528 0.05H1.77852C1.96385 0.05 2.11405 0.2002 2.11405 0.385528C2.11405 0.570857 1.96385 0.721057 1.77852 0.721057Z",
p32a21d80: "M4.83462 0.721057H0.38553C0.200202 0.721057 0.05 0.570857 0.05 0.385528C0.05 0.2002 0.200202 0.05 0.38553 0.05H4.83462C5.01995 0.05 5.17015 0.2002 5.17015 0.385528C5.17015 0.570857 5.01995 0.721057 4.83462 0.721057Z",
p332aed00: "M3.88738 9.27281C2.86263 9.27281 1.89844 8.87308 1.17408 8.14872C0.449727 7.42437 0.05 6.46018 0.05 5.43542V0.385531C0.05 0.200203 0.2002 0.05 0.385528 0.05C0.570857 0.05 0.721057 0.200203 0.721057 0.385531V5.43542C0.721057 7.18211 2.14191 8.60296 3.8886 8.60296C5.63528 8.60296 7.05613 7.18211 7.05613 5.43542V0.385531C7.05613 0.200203 7.20633 0.05 7.39166 0.05C7.57699 0.05 7.72719 0.200203 7.72719 0.385531V5.43542C7.72719 6.46018 7.32746 7.42437 6.60311 8.14872C5.87633 8.87429 4.91214 9.27281 3.88738 9.27281Z",
p3401a40: "M5.23919 11.6373C4.52695 11.6373 3.83772 11.4592 3.18968 11.1067C2.57071 10.7712 2.01714 10.2915 1.54232 9.68344C0.579336 8.44791 0.05 6.81024 0.05 5.07203C0.05 4.27137 0.161437 3.3532 0.362512 2.48592C0.516346 1.82334 0.794945 0.889428 1.24676 0.201412C1.30853 0.106931 1.41392 0.05 1.52657 0.05H8.94939C9.06204 0.05 9.16742 0.106931 9.2292 0.201412C9.68101 0.889428 9.95961 1.82334 10.1134 2.48592C10.3157 3.3532 10.426 4.27137 10.426 5.07203C10.426 6.81024 9.89541 8.44791 8.93364 9.68344C8.45881 10.2927 7.90525 10.7712 7.28628 11.1067C6.63945 11.458 5.95022 11.6373 5.23919 11.6373ZM1.71432 0.719847C1.06749 1.82697 0.721057 3.75293 0.721057 5.07203C0.721057 8.32194 2.74877 10.9662 5.2404 10.9662C7.73204 10.9662 9.75974 8.32194 9.75974 5.07203C9.75974 3.75414 9.4121 1.82697 8.76648 0.719847H1.71432Z",
p3450f000: "M10.3739 36.0945H2.62037C1.20315 36.0945 0.05 34.9414 0.05 33.5242V20.6663C0.05 19.4404 0.198989 18.2146 0.492123 17.0239C0.785257 15.8332 1.22375 14.6788 1.79427 13.5935L2.52468 12.2029C3.50583 10.3351 4.02426 8.2323 4.02426 6.12101V0.385528C4.02426 0.2002 4.17446 0.05 4.35979 0.05C4.54512 0.05 4.69532 0.2002 4.69532 0.385528V6.12101C4.69532 7.22935 4.56087 8.33769 4.29559 9.41332C4.03032 10.4889 3.63422 11.5331 3.11821 12.5142L2.3878 13.9048C1.29642 15.9822 0.719847 18.32 0.719847 20.665V33.5217C0.719847 34.5695 1.5726 35.4223 2.62037 35.4223H10.3739C10.5592 35.4223 10.7094 35.5725 10.7094 35.7578C10.7094 35.9431 10.558 36.0945 10.3739 36.0945Z",
p34c8570: "M2.31997 7.98157H0.38553C0.200202 7.98157 0.05 7.83136 0.05 7.64603V0.385528C0.05 0.2002 0.200202 0.05 0.38553 0.05C0.570858 0.05 0.721058 0.2002 0.721058 0.385528V7.31051H1.98565V0.385528C1.98565 0.2002 2.13585 0.05 2.32118 0.05C2.50651 0.05 2.65671 0.2002 2.65671 0.385528V7.64603C2.65429 7.83136 2.5053 7.98157 2.31997 7.98157Z",
p35856480: "M7.39045 0.721057H0.385528C0.2002 0.721057 0.05 0.570857 0.05 0.385528C0.05 0.2002 0.2002 0.05 0.385528 0.05H7.39045C7.57578 0.05 7.72598 0.2002 7.72598 0.385528C7.72477 0.570857 7.57457 0.721057 7.39045 0.721057Z",
p35daba00: "M6.04349 3.32534H3.89102C3.70569 3.32534 3.55549 3.17514 3.55549 2.98981C3.55549 2.80449 3.70569 2.65429 3.89102 2.65429H6.04349C6.19006 2.65429 6.30877 2.53558 6.30877 2.38901V0.986332C6.30877 0.839765 6.19006 0.721057 6.04349 0.721057H0.986332C0.839765 0.721057 0.72106 0.839765 0.72106 0.986332V2.38901C0.72106 2.53558 0.839765 2.65429 0.986332 2.65429H2.17461C2.35994 2.65429 2.51014 2.80449 2.51014 2.98981C2.51014 3.17514 2.35994 3.32534 2.17461 3.32534H0.986332C0.47032 3.32534 0.05 2.90502 0.05 2.38901V0.986332C0.05 0.47032 0.47032 0.05 0.986332 0.05H6.04349C6.5595 0.05 6.97982 0.47032 6.97982 0.986332V2.38901C6.97982 2.90502 6.5595 3.32534 6.04349 3.32534Z",
p37797c00: "M4.31982 4.44337H0.38553C0.200202 4.44337 0.05 4.29317 0.05 4.10784V3.18362C0.05 2.50408 0.391585 1.87906 0.965739 1.51325L3.17272 0.103296C3.22602 0.0693795 3.28901 0.05 3.3532 0.05H5.28764C5.47297 0.05 5.62317 0.2002 5.62317 0.385528C5.62317 0.570857 5.47297 0.721057 5.28764 0.721057H3.45132L1.32671 2.0765C0.94757 2.31876 0.721058 2.73302 0.721058 3.18241V3.77231H4.31982C4.50515 3.77231 4.65535 3.92251 4.65535 4.10784C4.65535 4.29317 4.50515 4.44337 4.31982 4.44337Z",
p39705400: "M5.10353 8.78708C4.9182 8.78708 4.768 8.63688 4.768 8.45155V0.721058H0.721058V8.45155C0.721058 8.63688 0.570858 8.78708 0.38553 8.78708C0.200202 8.78708 0.05 8.63688 0.05 8.45155V0.385528C0.05 0.2002 0.200202 0.05 0.38553 0.05H5.10231C5.28764 0.05 5.43784 0.2002 5.43784 0.385528V8.45155C5.43906 8.63688 5.28886 8.78708 5.10353 8.78708Z",
p3c314100: "M9.24373 2.78753H4.81524C4.62991 2.78753 4.47971 2.63733 4.47971 2.452C4.47971 2.26667 4.62991 2.11647 4.81524 2.11647H9.24373C9.62893 2.11647 9.94144 1.80275 9.94144 1.41876C9.94144 1.03357 9.62772 0.721057 9.24373 0.721057H1.41876C1.03357 0.721057 0.721058 1.03478 0.721058 1.41876C0.721058 1.80396 1.03478 2.11647 1.41876 2.11647H2.93409C3.11942 2.11647 3.26962 2.26667 3.26962 2.452C3.26962 2.63733 3.11942 2.78753 2.93409 2.78753H1.41876C0.664127 2.78753 0.05 2.1734 0.05 1.41876C0.05 0.664127 0.664127 0.05 1.41876 0.05H9.24373C9.99837 0.05 10.6125 0.664127 10.6125 1.41876C10.6125 2.1734 9.99837 2.78753 9.24373 2.78753Z",
p3ce0580: "M2.56202 0.432771L0.363521 1.31338C0.13822 1.40302 0.0134583 1.63195 0.0594875 1.86937C0.105517 2.10678 0.306591 2.27273 0.54885 2.27273H8.5434C8.78566 2.27273 8.98673 2.10678 9.03276 1.86937C9.07879 1.63195 8.95403 1.40302 8.72873 1.31338L6.53023 0.432771C5.89672 0.178399 5.2293 0.05 4.54613 0.05C3.86295 0.05 3.19553 0.178399 2.56202 0.432771ZM2.56202 0.432771L2.68679 0.744073M1.44157 1.60288H7.64946L6.2807 1.05416C5.16146 0.605984 3.92958 0.605984 2.81155 1.05416L1.44157 1.60288Z",
p5cab80: "M6.93137 0.721057H0.385528C0.2002 0.721057 0.05 0.570857 0.05 0.385528C0.05 0.2002 0.2002 0.05 0.385528 0.05H6.93137C7.1167 0.05 7.2669 0.2002 7.2669 0.385528C7.2669 0.570857 7.1167 0.721057 6.93137 0.721057Z",
p88545c0: "M5.10353 8.78708C4.9182 8.78708 4.768 8.63688 4.768 8.45155V0.721058H0.721058V8.45155C0.721058 8.63688 0.570857 8.78708 0.385528 8.78708C0.2002 8.78708 0.05 8.63688 0.05 8.45155V0.385528C0.05 0.2002 0.2002 0.05 0.385528 0.05H5.10231C5.28764 0.05 5.43784 0.2002 5.43784 0.385528V8.45155C5.43905 8.63688 5.28886 8.78708 5.10353 8.78708Z",
pbe42b80: "M5.28643 0.721057H0.385528C0.2002 0.721057 0.05 0.570857 0.05 0.385528C0.05 0.2002 0.2002 0.05 0.385528 0.05H5.28643C5.47176 0.05 5.62196 0.2002 5.62196 0.385528C5.62075 0.570857 5.47055 0.721057 5.28643 0.721057Z",
pcabba00: "M4.854 0.72106H0.38553C0.200202 0.72106 0.05 0.57086 0.05 0.385531C0.05 0.200203 0.200202 0.05 0.38553 0.05H4.854C5.03933 0.05 5.18953 0.200203 5.18953 0.385531C5.18953 0.57086 5.03933 0.72106 4.854 0.72106Z",
}

View File

@@ -0,0 +1,4 @@
export default {
p1054ae00: "M53.2116 48.9121H38.9889L40.9593 33.2206L48.4109 24.4796C49.5519 23.1182 49.5376 21.1299 48.3751 19.7867L43.109 13.7681C42.86 13.4743 42.5018 13.2952 42.1184 13.2665C41.7333 13.2396 41.3553 13.3686 41.0669 13.6247L40.6012 14.0546C40.6621 13.5854 40.4884 13.116 40.1355 12.8008L39.2399 12.0126C38.6685 11.5092 37.8033 11.5397 37.2695 12.0842L31.7526 17.7447C31.7472 18.7048 31.5143 19.6506 31.0719 20.5032L38.3084 13.0874L39.204 13.8756L32.5049 22.4736C32.367 22.658 32.3276 22.8981 32.3974 23.1184C32.4333 23.2259 33.4005 26.0919 37.9144 27.5607C37.8427 28.3489 37.6636 29.2087 37.2695 29.4236C36.9238 29.6206 36.8038 30.063 37.0009 30.4087C37.1979 30.7545 37.6403 30.8745 37.986 30.6774C39.1324 30.0326 39.3115 28.2055 39.3474 27.3457L44.542 22.6886C45.0776 22.2175 45.185 21.4222 44.7927 20.8257L41.2102 15.3803L42.0342 14.6638L47.3003 20.7182C48.0186 21.5188 48.0348 22.7297 47.3362 23.5483L39.7415 32.4687C39.6484 32.569 39.5874 32.6926 39.5624 32.827L37.5562 48.9123H35.335V34.26C35.3332 33.9483 35.1308 33.6725 34.8334 33.5793C34.7618 33.5793 29.4238 31.8955 26.4147 26.6651C26.2947 26.4573 26.0798 26.323 25.8415 26.3069L14.1625 25.6262V24.4798L24.3727 23.8349H24.5877L24.731 23.7633C23.7135 23.6128 22.7535 23.2062 21.9366 22.5811L14.0549 23.0827C13.3062 23.1382 12.7276 23.7651 12.7294 24.5157V25.6621C12.7276 26.4269 13.3277 27.0575 14.0908 27.0951L25.3755 27.7041C27.3745 30.9211 30.3678 33.3985 33.9019 34.7618V48.9125H8.78843C8.39256 48.9125 8.07193 49.2331 8.07193 49.629C8.07193 50.0248 8.39256 50.3455 8.78843 50.3455H53.2114C53.6073 50.3455 53.9279 50.0248 53.9279 49.629C53.9279 49.2331 53.6075 48.9121 53.2116 48.9121ZM43.6103 21.6493L38.4873 26.2349C35.4421 25.3035 34.2957 23.6913 33.9017 23.0107L39.7767 15.416C39.7803 15.6973 39.8681 15.9713 40.0275 16.2042L43.6103 21.6493Z",
p365acf00: "M20.9685 17.7085C20.9685 19.5929 22.1024 21.291 23.8434 22.011C25.5846 22.7329 27.5872 22.3335 28.9179 21.0008C30.2505 19.6699 30.65 17.6673 29.9281 15.9264C29.208 14.1853 27.51 13.0514 25.6255 13.0514C23.0533 13.0514 20.9685 15.1364 20.9685 17.7085ZM28.8502 17.7085C28.8502 19.0125 28.0639 20.1875 26.8602 20.6873C25.6547 21.1871 24.2683 20.9112 23.3457 19.9887C22.4232 19.0663 22.1474 17.6798 22.6472 16.4743C23.1469 15.2706 24.322 14.4842 25.626 14.4842C26.4876 14.4753 27.3169 14.8102 27.9295 15.4157C28.5421 16.0211 28.886 16.8469 28.886 17.7085H28.8502Z",
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,9 @@
export default {
p11d0e900: "M0.884818 27.4327C1.10144 27.7782 1.3664 28.0915 1.67254 28.3636C1.6994 28.1094 1.74773 27.8588 1.81576 27.6117C1.88737 27.2949 1.98405 26.9834 2.1022 26.6808C1.60091 25.8931 1.81576 24.819 2.20962 24.0671C2.66792 23.1702 2.58556 22.0907 1.99479 21.2743C1.37535 20.2825 1.66181 18.9792 2.63925 18.3384C4.03564 17.3716 5.3604 17.6223 6.57779 19.1261C6.73175 19.3391 6.86243 19.5665 6.97164 19.8064V19.878C7.58031 21.1312 10.0509 21.525 11.6619 21.6682C11.6279 21.1902 11.6386 20.7104 11.6977 20.236C10.0506 20.0928 8.47529 19.6989 8.22464 19.2335V19.1619C8.07963 18.8343 7.89882 18.521 7.68756 18.231C6.04052 16.1543 3.85645 15.7246 1.85157 17.121C0.884834 17.7297 -0.583147 19.5199 0.705822 21.9547L0.777431 22.0621C1.06387 22.447 1.10504 22.9608 0.884847 23.3869C0.204547 24.8191 0.168724 26.3228 0.884818 27.4327Z",
p15b7cd00: "M41.0576 28.8649C41.0576 28.9007 39.8761 31.8725 35.0424 33.6986C34.7023 33.8096 34.4964 34.1533 34.5591 34.506C34.6217 34.8587 34.935 35.1093 35.2931 35.095H35.5437C40.986 33.0184 42.3467 29.5812 42.4182 29.4377C42.5202 29.2551 42.5381 29.0385 42.4665 28.8434C42.3949 28.6465 42.241 28.4925 42.0458 28.4191C41.8525 28.3439 41.6341 28.36 41.4515 28.4585C41.2689 28.5587 41.1382 28.7324 41.0934 28.9365L41.0576 28.8649Z",
p17868d00: "M9.19108 25.2484H9.11947C8.79544 25.1177 8.46066 25.0229 8.11694 24.962C5.5748 24.5323 3.74879 25.6781 3.17601 27.9696C2.88957 29.0437 2.99699 31.2636 5.5033 32.1229H5.64652C6.08155 32.1945 6.43425 32.5186 6.54163 32.9464C6.93548 34.4144 7.90223 35.4886 9.11956 35.8108C9.47045 35.9039 9.83029 35.9522 10.1937 35.954C10.815 35.9576 11.4236 35.784 11.9481 35.4527C13.3803 34.5576 13.81 32.8032 13.9174 32.3019C13.9998 31.8526 14.0248 31.3961 13.989 30.9413C13.9711 30.3702 14.2074 29.8189 14.6335 29.4376L16.8176 27.3251C17.8398 27.9552 19.0178 28.29 20.219 28.2918C21.2788 28.2882 22.3225 28.0304 23.2624 27.5399L23.9427 27.1819C24.981 26.609 25.6971 26.2151 27.2725 26.1793C28.0173 26.1614 28.7602 26.2223 29.4924 26.3584C31.3901 26.6806 36.152 26.9312 39.6969 22.4915C45.0677 15.7242 43.5638 9.06465 40.3056 5.05453C38.0195 2.2886 34.7148 0.559249 31.1396 0.256588C27.3085 -0.0656522 20.4342 0.722036 17.6413 9.74477V9.85218C17.4265 11.3202 16.5313 15.366 15.135 16.5837C14.7447 16.9131 14.4063 17.2998 14.1324 17.7294C13.1442 19.2708 12.8703 21.1631 13.3805 22.9211L13.2731 22.9927C11.6261 24.3533 9.72812 25.4632 9.19108 25.2484ZM15.3135 18.4454C15.5104 18.1375 15.7521 17.86 16.0296 17.6219C17.9989 15.8675 18.8582 10.8904 19.0014 10.103C21.4003 2.47647 26.8425 1.33072 30.996 1.68911C34.197 1.95406 37.1547 3.50439 39.1955 5.98567C42.0956 9.53034 43.4204 15.5096 38.5868 21.6325C37.5574 22.9412 36.1879 23.9437 34.6286 24.5309C33.0711 25.118 31.3811 25.2667 29.7431 24.9623C28.9053 24.8137 28.0531 24.7529 27.201 24.7833C25.8064 24.8173 24.4458 25.2255 23.2624 25.9648L22.5821 26.3229C20.4482 27.44 17.8254 26.9172 16.2805 25.0697L15.8867 24.5684L15.7076 24.3178C13.7384 21.8473 14.6335 19.5918 15.3138 18.446L15.3135 18.4454ZM14.5974 25.1769L14.7048 25.356C14.8588 25.5815 15.0253 25.7964 15.2061 26.0004L15.6716 26.5017L13.6307 28.471C12.929 29.1405 12.5512 30.0804 12.5924 31.0489C12.6228 31.3837 12.6121 31.7202 12.5566 32.0514C12.4134 32.8033 11.9837 33.8059 11.2318 34.2713C10.7127 34.5775 10.0843 34.6437 9.5132 34.4504C8.68968 34.2355 8.1526 33.3404 7.93779 32.5885C7.68895 31.6308 6.90662 30.9039 5.93273 30.7267C4.21411 30.118 4.50054 28.6142 4.57213 28.292C4.96598 26.6808 6.0759 26.0363 7.90195 26.3227L8.61805 26.5375H8.68966C10.2293 27.1104 12.8788 25.0695 13.9528 24.1744C14.1425 24.5253 14.3575 24.8601 14.5974 25.1769Z",
p18f61d00: "M32.4646 34.2716C32.0529 34.4291 31.6017 34.4542 31.1757 34.3432C30.7997 34.2143 30.3915 34.4148 30.2626 34.7908C30.1337 35.1667 30.3342 35.5749 30.7102 35.7038C31.5122 35.9526 32.3769 35.9007 33.1449 35.5606C33.5012 35.3833 33.6444 34.9501 33.4671 34.5938C33.2899 34.2376 32.8567 34.0944 32.5004 34.2716L32.4646 34.2716Z",
p1cd27a80: "M36.0448 21.489C36.3527 21.7343 36.7985 21.6859 37.0474 21.3816C40.6636 16.8344 40.0907 13.1464 40.0549 13.0034C40.046 12.7957 39.9493 12.6042 39.7882 12.4735C39.6271 12.3446 39.4158 12.2909 39.2135 12.3267C39.0095 12.3643 38.8304 12.486 38.7248 12.6651C38.6192 12.8423 38.5941 13.0571 38.6586 13.2541C38.6586 13.2899 39.1599 16.4407 35.9374 20.4865C35.6922 20.7944 35.7405 21.2401 36.0448 21.489Z",
p1d2c1e00: "M45.9985 25.5706C46.1471 22.7689 45.4489 19.9848 43.9934 17.5861C43.8019 18.184 43.5745 18.7694 43.3131 19.3405C44.2494 21.2525 44.6809 23.3721 44.5663 25.4991C44.2799 30.2971 41.1291 35.9539 33.1803 37.2788C28.1674 38.1381 25.1601 35.0589 24.1216 33.6625C23.5952 32.9572 22.9955 32.3091 22.3314 31.7291C20.8276 30.4401 19.7534 30.1895 18.5002 29.903L17.7484 29.724C17.4154 29.6363 17.0913 29.5163 16.7816 29.366L15.7075 30.4043C16.2338 30.7176 16.7995 30.9592 17.3903 31.1204L18.178 31.3352C19.3953 31.6216 20.1831 31.8365 21.4004 32.8748C21.984 33.3904 22.5121 33.9668 22.9758 34.5934C24.05 35.9898 26.8427 38.89 31.4617 38.89C32.1098 38.8846 32.756 38.8238 33.3952 38.711C42.239 37.2072 45.7121 30.9414 45.9985 25.5706Z",
p1df26700: "M32.6077 23.6372C33.4473 23.6103 34.2493 23.2792 34.8634 22.7063C35.0693 22.4145 35.0263 22.0135 34.7649 21.77C34.5035 21.5265 34.1025 21.514 33.825 21.7396C33.4831 22.0314 33.057 22.2068 32.6077 22.2408C32.2121 22.2408 31.8916 22.5613 31.8916 22.9569C31.8916 23.3526 32.2121 23.673 32.6077 23.673L32.6077 23.6372Z",
}

Some files were not shown because too many files have changed in this diff Show More