@travories/frontend-sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +20 -0
- package/README.md +193 -0
- package/dist/index.d.mts +633 -0
- package/dist/index.d.ts +633 -0
- package/dist/index.js +2406 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2373 -0
- package/dist/index.mjs.map +1 -0
- package/dist/styles.css +2 -0
- package/package.json +95 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,2373 @@
|
|
|
1
|
+
import { createContext, useState, useRef, useEffect, useContext, useMemo } from 'react';
|
|
2
|
+
import { Icon } from '@iconify/react';
|
|
3
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
4
|
+
|
|
5
|
+
// src/api/http.ts
|
|
6
|
+
var TravoriesApiError = class extends Error {
|
|
7
|
+
constructor(message, status, url, body) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.name = "TravoriesApiError";
|
|
10
|
+
this.status = status;
|
|
11
|
+
this.url = url;
|
|
12
|
+
this.body = body;
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
var HttpClient = class {
|
|
16
|
+
constructor(config) {
|
|
17
|
+
if (!config.baseUrl) {
|
|
18
|
+
throw new Error("[TravoriesClient] baseUrl is required");
|
|
19
|
+
}
|
|
20
|
+
this.baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
21
|
+
this.apiKey = config.apiKey;
|
|
22
|
+
this.authToken = config.authToken;
|
|
23
|
+
this.defaultHeaders = config.defaultHeaders ?? {};
|
|
24
|
+
this.fetchImpl = config.fetchImpl ?? (typeof fetch !== "undefined" ? fetch.bind(globalThis) : void 0);
|
|
25
|
+
if (!this.fetchImpl) {
|
|
26
|
+
throw new Error(
|
|
27
|
+
"[TravoriesClient] no `fetch` available. Pass `fetchImpl` (e.g. node-fetch) in the config."
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
async resolveAuthToken() {
|
|
32
|
+
if (!this.authToken) return void 0;
|
|
33
|
+
if (typeof this.authToken === "function") {
|
|
34
|
+
const v = this.authToken();
|
|
35
|
+
return v instanceof Promise ? await v : v;
|
|
36
|
+
}
|
|
37
|
+
return this.authToken;
|
|
38
|
+
}
|
|
39
|
+
async request(path, options = {}) {
|
|
40
|
+
const url = path.startsWith("http") ? path : `${this.baseUrl}/${path.replace(/^\//, "")}`;
|
|
41
|
+
const token = await this.resolveAuthToken();
|
|
42
|
+
const headers = {
|
|
43
|
+
"Content-Type": "application/json",
|
|
44
|
+
Accept: "application/json",
|
|
45
|
+
...this.defaultHeaders,
|
|
46
|
+
...options.headers ?? {},
|
|
47
|
+
...this.apiKey ? { "x-api-key": this.apiKey } : {},
|
|
48
|
+
...token ? { Authorization: `Bearer ${token}` } : {}
|
|
49
|
+
};
|
|
50
|
+
const init = {
|
|
51
|
+
method: options.method ?? "GET",
|
|
52
|
+
headers,
|
|
53
|
+
signal: options.signal
|
|
54
|
+
};
|
|
55
|
+
if (options.body !== void 0) {
|
|
56
|
+
init.body = typeof options.body === "string" ? options.body : JSON.stringify(options.body);
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
const res = await this.fetchImpl(url, init);
|
|
60
|
+
if (!res.ok) {
|
|
61
|
+
const text = await res.text().catch(() => "");
|
|
62
|
+
if (options.silent) {
|
|
63
|
+
console.error(`[TravoriesClient] ${res.status} ${url}`);
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
throw new TravoriesApiError(
|
|
67
|
+
`Travories API error ${res.status} for ${url}`,
|
|
68
|
+
res.status,
|
|
69
|
+
url,
|
|
70
|
+
text
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
if (res.status === 204) return null;
|
|
74
|
+
return await res.json();
|
|
75
|
+
} catch (err) {
|
|
76
|
+
if (options.silent) {
|
|
77
|
+
console.error(`[TravoriesClient] fetch failed for ${url}:`, err);
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
throw err;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// src/api/packages.ts
|
|
86
|
+
var normalizeImage = (img) => ({
|
|
87
|
+
id: img?.id ?? "",
|
|
88
|
+
key: img?.key ?? "",
|
|
89
|
+
type: img?.type ?? "image",
|
|
90
|
+
alt: img?.alt ?? "",
|
|
91
|
+
preview: img?.preview ?? null,
|
|
92
|
+
url: {
|
|
93
|
+
original: img?.url?.original ?? null,
|
|
94
|
+
hero: img?.url?.hero ?? null,
|
|
95
|
+
hero_fit: img?.url?.hero_fit ?? null,
|
|
96
|
+
section: img?.url?.section ?? null,
|
|
97
|
+
portrait: img?.url?.portrait ?? null,
|
|
98
|
+
general: img?.url?.general ?? null,
|
|
99
|
+
jpeg: img?.url?.jpeg ?? null
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
var normalizeHomeCard = (raw) => ({
|
|
103
|
+
id: raw?.id ?? "",
|
|
104
|
+
title: raw?.title ?? "",
|
|
105
|
+
slug: raw?.slug ?? "",
|
|
106
|
+
agencySlug: raw?.agencySlug ?? "",
|
|
107
|
+
totalDays: Number(raw?.totalDays ?? 0),
|
|
108
|
+
minPrice: Number(raw?.minPrice ?? 0),
|
|
109
|
+
maxPrice: Number(raw?.maxPrice ?? 0),
|
|
110
|
+
rating: Number(raw?.rating ?? 0),
|
|
111
|
+
packageCategory: raw?.packageCategory ?? "",
|
|
112
|
+
thumbnail: normalizeImage(raw?.thumbnail)
|
|
113
|
+
});
|
|
114
|
+
var normalizePackage = (raw) => ({
|
|
115
|
+
id: raw.id,
|
|
116
|
+
title: raw.title,
|
|
117
|
+
packageSlug: raw.packageSlug,
|
|
118
|
+
slug: raw.slug,
|
|
119
|
+
packageType: raw.packageType,
|
|
120
|
+
description: raw.description,
|
|
121
|
+
difficulty: raw.difficulty,
|
|
122
|
+
packageFlexibility: raw.packageFlexibility,
|
|
123
|
+
packageCategory: raw.packageCategory,
|
|
124
|
+
totalDays: raw.totalDays,
|
|
125
|
+
media: (raw.media ?? []).map(normalizeImage),
|
|
126
|
+
thumbnail: raw.thumbnail ? normalizeImage(raw.thumbnail) : null,
|
|
127
|
+
days: raw.days ?? [],
|
|
128
|
+
prices: raw.prices ?? [],
|
|
129
|
+
discounts: raw.discounts ?? [],
|
|
130
|
+
thingsIncluded: raw.thingsIncluded ?? [],
|
|
131
|
+
thingsExcluded: raw.thingsExcluded ?? [],
|
|
132
|
+
thingsToPack: raw.thingsToPack ?? [],
|
|
133
|
+
rating: raw.rating ?? 0,
|
|
134
|
+
maxAllowedPeople: raw.maxAllowedPeople ?? 10,
|
|
135
|
+
minAllowedPeople: raw.minAllowedPeople ?? 1
|
|
136
|
+
});
|
|
137
|
+
var PackagesResource = class {
|
|
138
|
+
constructor(http) {
|
|
139
|
+
this.http = http;
|
|
140
|
+
}
|
|
141
|
+
/** Fetch a single package by its public slug. Returns null on 404 / network error in silent mode. */
|
|
142
|
+
async getBySlug(slug, options) {
|
|
143
|
+
if (!slug) throw new Error("[TravoriesClient] getBySlug requires a slug");
|
|
144
|
+
const raw = await this.http.request(`/agency-package/by-slug/${slug}`, {
|
|
145
|
+
silent: true,
|
|
146
|
+
...options
|
|
147
|
+
});
|
|
148
|
+
if (!raw) return null;
|
|
149
|
+
return normalizePackage(raw);
|
|
150
|
+
}
|
|
151
|
+
/** Fetch host (agency) information for the package. */
|
|
152
|
+
async getHost(slug, options) {
|
|
153
|
+
if (!slug) throw new Error("[TravoriesClient] getHost requires a slug");
|
|
154
|
+
return this.http.request(`/agency-info/by-package-slug/${slug}`, {
|
|
155
|
+
silent: true,
|
|
156
|
+
...options
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
/** Fetch major attractions tied to the package. */
|
|
160
|
+
async getMajorAttractions(slug, options) {
|
|
161
|
+
if (!slug) throw new Error("[TravoriesClient] getMajorAttractions requires a slug");
|
|
162
|
+
return this.http.request(`/major-attractions/package/${slug}`, {
|
|
163
|
+
silent: true,
|
|
164
|
+
...options
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Fetch the four home/landing sections in one request:
|
|
169
|
+
* popular, topRated, trending, moreToExplore.
|
|
170
|
+
* Each item is a compact card (id, title, slug, thumbnail, price range, rating).
|
|
171
|
+
*/
|
|
172
|
+
async getHomeSections(options) {
|
|
173
|
+
const raw = await this.http.request(`/agency-package/home/sections`, {
|
|
174
|
+
silent: true,
|
|
175
|
+
...options
|
|
176
|
+
});
|
|
177
|
+
if (!raw) return null;
|
|
178
|
+
return {
|
|
179
|
+
popular: (raw.popular ?? []).map(normalizeHomeCard),
|
|
180
|
+
topRated: (raw.topRated ?? []).map(normalizeHomeCard),
|
|
181
|
+
trending: (raw.trending ?? []).map(normalizeHomeCard),
|
|
182
|
+
moreToExplore: (raw.moreToExplore ?? []).map(normalizeHomeCard)
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Fetch per-day GeoJSON routes for the package. Used by the map on the
|
|
187
|
+
* itinerary section to draw actual road/trail paths between stops.
|
|
188
|
+
*/
|
|
189
|
+
async getRoutes(slug, options) {
|
|
190
|
+
if (!slug) throw new Error("[TravoriesClient] getRoutes requires a slug");
|
|
191
|
+
return this.http.request(`/agency-package/routes/${slug}`, {
|
|
192
|
+
silent: true,
|
|
193
|
+
...options
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Convenience: fetch the package + host + attractions + per-day routes in
|
|
198
|
+
* parallel. This is the payload `<PackageDetailView />` consumes.
|
|
199
|
+
*/
|
|
200
|
+
async getBundle(slug, options) {
|
|
201
|
+
const [pkg, hostRes, attractionsRes, routesRes] = await Promise.all([
|
|
202
|
+
this.getBySlug(slug, options),
|
|
203
|
+
this.getHost(slug, options),
|
|
204
|
+
this.getMajorAttractions(slug, options),
|
|
205
|
+
this.getRoutes(slug, options)
|
|
206
|
+
]);
|
|
207
|
+
if (!pkg) return null;
|
|
208
|
+
return {
|
|
209
|
+
pkg,
|
|
210
|
+
host: hostRes?.host ?? null,
|
|
211
|
+
attractions: attractionsRes?.data ?? [],
|
|
212
|
+
routes: routesRes?.days ?? []
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
// src/client/TravoriesClient.ts
|
|
218
|
+
var TravoriesClient = class {
|
|
219
|
+
constructor(config) {
|
|
220
|
+
/** Shortcut for `client.packages.getBySlug(slug)`. */
|
|
221
|
+
this.getPackageBySlug = (slug) => this.packages.getBySlug(slug);
|
|
222
|
+
/** Shortcut for `client.packages.getBundle(slug)` — package + host + attractions in one call. */
|
|
223
|
+
this.getPackageBundle = (slug) => this.packages.getBundle(slug);
|
|
224
|
+
this.http = new HttpClient(config);
|
|
225
|
+
this.packages = new PackagesResource(this.http);
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
function createTravoriesClient(config) {
|
|
229
|
+
return new TravoriesClient(config);
|
|
230
|
+
}
|
|
231
|
+
function usePackageBySlug(client, slug) {
|
|
232
|
+
const [data, setData] = useState(null);
|
|
233
|
+
const [loading, setLoading] = useState(Boolean(slug));
|
|
234
|
+
const [error, setError] = useState(null);
|
|
235
|
+
const reqId = useRef(0);
|
|
236
|
+
const run = () => {
|
|
237
|
+
if (!slug) {
|
|
238
|
+
setData(null);
|
|
239
|
+
setLoading(false);
|
|
240
|
+
setError(null);
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
const myReq = ++reqId.current;
|
|
244
|
+
setLoading(true);
|
|
245
|
+
setError(null);
|
|
246
|
+
client.packages.getBySlug(slug).then((res) => {
|
|
247
|
+
if (myReq !== reqId.current) return;
|
|
248
|
+
setData(res);
|
|
249
|
+
}).catch((err) => {
|
|
250
|
+
if (myReq !== reqId.current) return;
|
|
251
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
252
|
+
}).finally(() => {
|
|
253
|
+
if (myReq !== reqId.current) return;
|
|
254
|
+
setLoading(false);
|
|
255
|
+
});
|
|
256
|
+
};
|
|
257
|
+
useEffect(() => {
|
|
258
|
+
run();
|
|
259
|
+
return () => {
|
|
260
|
+
reqId.current++;
|
|
261
|
+
};
|
|
262
|
+
}, [client, slug]);
|
|
263
|
+
return { data, loading, error, refetch: run };
|
|
264
|
+
}
|
|
265
|
+
function usePackageBundle(client, slug) {
|
|
266
|
+
const [data, setData] = useState(null);
|
|
267
|
+
const [loading, setLoading] = useState(Boolean(slug));
|
|
268
|
+
const [error, setError] = useState(null);
|
|
269
|
+
const reqId = useRef(0);
|
|
270
|
+
const run = () => {
|
|
271
|
+
if (!slug) {
|
|
272
|
+
setData(null);
|
|
273
|
+
setLoading(false);
|
|
274
|
+
setError(null);
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
const myReq = ++reqId.current;
|
|
278
|
+
setLoading(true);
|
|
279
|
+
setError(null);
|
|
280
|
+
client.packages.getBundle(slug).then((res) => {
|
|
281
|
+
if (myReq !== reqId.current) return;
|
|
282
|
+
setData(res);
|
|
283
|
+
}).catch((err) => {
|
|
284
|
+
if (myReq !== reqId.current) return;
|
|
285
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
286
|
+
}).finally(() => {
|
|
287
|
+
if (myReq !== reqId.current) return;
|
|
288
|
+
setLoading(false);
|
|
289
|
+
});
|
|
290
|
+
};
|
|
291
|
+
useEffect(() => {
|
|
292
|
+
run();
|
|
293
|
+
return () => {
|
|
294
|
+
reqId.current++;
|
|
295
|
+
};
|
|
296
|
+
}, [client, slug]);
|
|
297
|
+
return { data, loading, error, refetch: run };
|
|
298
|
+
}
|
|
299
|
+
function useHomeSections(client) {
|
|
300
|
+
const [data, setData] = useState(null);
|
|
301
|
+
const [loading, setLoading] = useState(Boolean(client));
|
|
302
|
+
const [error, setError] = useState(null);
|
|
303
|
+
const reqId = useRef(0);
|
|
304
|
+
const run = () => {
|
|
305
|
+
if (!client) {
|
|
306
|
+
setData(null);
|
|
307
|
+
setLoading(false);
|
|
308
|
+
setError(null);
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
const myReq = ++reqId.current;
|
|
312
|
+
setLoading(true);
|
|
313
|
+
setError(null);
|
|
314
|
+
client.packages.getHomeSections().then((res) => {
|
|
315
|
+
if (myReq !== reqId.current) return;
|
|
316
|
+
setData(res);
|
|
317
|
+
}).catch((err) => {
|
|
318
|
+
if (myReq !== reqId.current) return;
|
|
319
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
320
|
+
}).finally(() => {
|
|
321
|
+
if (myReq !== reqId.current) return;
|
|
322
|
+
setLoading(false);
|
|
323
|
+
});
|
|
324
|
+
};
|
|
325
|
+
useEffect(() => {
|
|
326
|
+
run();
|
|
327
|
+
return () => {
|
|
328
|
+
reqId.current++;
|
|
329
|
+
};
|
|
330
|
+
}, [client]);
|
|
331
|
+
return { data, loading, error, refetch: run };
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// src/utils/format.ts
|
|
335
|
+
function pickImageUrl(img) {
|
|
336
|
+
if (!img?.url) return "";
|
|
337
|
+
return img.url.hero || img.url.general || img.url.hero_fit || img.url.section || img.url.portrait || img.url.original || img.url.jpeg || "";
|
|
338
|
+
}
|
|
339
|
+
function startingPrice(prices) {
|
|
340
|
+
if (!prices || prices.length === 0) return 0;
|
|
341
|
+
return prices.reduce(
|
|
342
|
+
(min, p) => p.pricePerPerson > 0 && p.pricePerPerson < min ? p.pricePerPerson : min,
|
|
343
|
+
prices[0].pricePerPerson
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
function formatCurrency(amount, currency = "USD") {
|
|
347
|
+
try {
|
|
348
|
+
return new Intl.NumberFormat("en-US", {
|
|
349
|
+
style: "currency",
|
|
350
|
+
currency,
|
|
351
|
+
maximumFractionDigits: 0
|
|
352
|
+
}).format(amount);
|
|
353
|
+
} catch {
|
|
354
|
+
return `${currency} ${amount}`;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
function formatRating(rating) {
|
|
358
|
+
if (!rating || rating <= 0) return "New";
|
|
359
|
+
return rating.toFixed(1);
|
|
360
|
+
}
|
|
361
|
+
var PackageCard = ({
|
|
362
|
+
pkg,
|
|
363
|
+
currency = "USD",
|
|
364
|
+
onSelect,
|
|
365
|
+
href,
|
|
366
|
+
openInNewTab = false,
|
|
367
|
+
showHeart = true,
|
|
368
|
+
liked,
|
|
369
|
+
onLike
|
|
370
|
+
}) => {
|
|
371
|
+
const img = pickImageUrl(pkg.thumbnail);
|
|
372
|
+
const rating = Math.round(pkg.rating ?? 0);
|
|
373
|
+
const price = pkg.minPrice || pkg.maxPrice || 0;
|
|
374
|
+
const [localLiked, setLocalLiked] = useState(Boolean(liked));
|
|
375
|
+
const isLiked = liked ?? localLiked;
|
|
376
|
+
const toggleLike = (e) => {
|
|
377
|
+
e.preventDefault();
|
|
378
|
+
e.stopPropagation();
|
|
379
|
+
const next = !isLiked;
|
|
380
|
+
if (onLike) onLike(pkg, next);
|
|
381
|
+
else setLocalLiked(next);
|
|
382
|
+
};
|
|
383
|
+
const handleAnchorClick = (e) => {
|
|
384
|
+
if (e.metaKey || e.ctrlKey || e.button === 1) return;
|
|
385
|
+
if (openInNewTab) return;
|
|
386
|
+
if (onSelect) {
|
|
387
|
+
e.preventDefault();
|
|
388
|
+
onSelect(pkg);
|
|
389
|
+
}
|
|
390
|
+
};
|
|
391
|
+
return /* @__PURE__ */ jsxs("div", { className: "relative w-[300px] md:w-[320px] shrink-0 transition-all duration-300 ease-out hover:-translate-y-1 cursor-pointer", children: [
|
|
392
|
+
href ? /* @__PURE__ */ jsx(
|
|
393
|
+
"a",
|
|
394
|
+
{
|
|
395
|
+
href,
|
|
396
|
+
target: openInNewTab ? "_blank" : void 0,
|
|
397
|
+
rel: openInNewTab ? "noopener noreferrer" : void 0,
|
|
398
|
+
onClick: handleAnchorClick,
|
|
399
|
+
className: "absolute inset-0 z-0 rounded-lg",
|
|
400
|
+
"aria-label": openInNewTab ? `${pkg.title} (opens in a new tab)` : pkg.title
|
|
401
|
+
}
|
|
402
|
+
) : /* @__PURE__ */ jsx(
|
|
403
|
+
"button",
|
|
404
|
+
{
|
|
405
|
+
type: "button",
|
|
406
|
+
onClick: () => onSelect?.(pkg),
|
|
407
|
+
className: "absolute inset-0 z-0 rounded-lg bg-transparent border-0 cursor-pointer p-0",
|
|
408
|
+
"aria-label": pkg.title
|
|
409
|
+
}
|
|
410
|
+
),
|
|
411
|
+
/* @__PURE__ */ jsxs("div", { className: "relative z-10 pointer-events-none", children: [
|
|
412
|
+
/* @__PURE__ */ jsxs("div", { className: "relative border border-[#E0E4E8] overflow-hidden rounded-xl h-44 md:h-48 bg-[#F0EEF4]", children: [
|
|
413
|
+
img ? /* @__PURE__ */ jsx(
|
|
414
|
+
"img",
|
|
415
|
+
{
|
|
416
|
+
src: img,
|
|
417
|
+
alt: pkg.thumbnail?.alt || pkg.title,
|
|
418
|
+
loading: "lazy",
|
|
419
|
+
className: "absolute inset-0 w-full h-full object-cover"
|
|
420
|
+
}
|
|
421
|
+
) : /* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center justify-center text-primary-normal/40 text-xs", children: "No image" }),
|
|
422
|
+
/* @__PURE__ */ jsx(
|
|
423
|
+
"div",
|
|
424
|
+
{
|
|
425
|
+
className: "absolute inset-0 rounded-xl",
|
|
426
|
+
style: { background: "rgba(0, 0, 0, 0.18)" }
|
|
427
|
+
}
|
|
428
|
+
),
|
|
429
|
+
pkg.packageCategory && /* @__PURE__ */ jsx("div", { className: "absolute top-3 left-3 z-10", children: /* @__PURE__ */ jsx(
|
|
430
|
+
"div",
|
|
431
|
+
{
|
|
432
|
+
className: "h-[26px] w-fit min-w-[70px] rounded-full flex items-center justify-center px-3 box-border text-white",
|
|
433
|
+
style: {
|
|
434
|
+
background: "rgba(0, 0, 0, 0.45)",
|
|
435
|
+
backdropFilter: "blur(18px)",
|
|
436
|
+
WebkitBackdropFilter: "blur(18px)"
|
|
437
|
+
},
|
|
438
|
+
children: /* @__PURE__ */ jsx("span", { className: "text-[11px] font-medium tracking-[-0.02em] whitespace-nowrap", children: pkg.packageCategory })
|
|
439
|
+
}
|
|
440
|
+
) }),
|
|
441
|
+
showHeart && /* @__PURE__ */ jsx(
|
|
442
|
+
"button",
|
|
443
|
+
{
|
|
444
|
+
type: "button",
|
|
445
|
+
onClick: toggleLike,
|
|
446
|
+
"aria-pressed": isLiked,
|
|
447
|
+
"aria-label": isLiked ? "Remove from wishlist" : "Save to wishlist",
|
|
448
|
+
className: "absolute top-3 right-3 z-10 w-9 h-9 rounded-full flex items-center justify-center pointer-events-auto transition-transform hover:scale-110",
|
|
449
|
+
style: {
|
|
450
|
+
background: "rgba(0, 0, 0, 0.45)",
|
|
451
|
+
backdropFilter: "blur(18px)",
|
|
452
|
+
WebkitBackdropFilter: "blur(18px)"
|
|
453
|
+
},
|
|
454
|
+
children: /* @__PURE__ */ jsx(
|
|
455
|
+
Icon,
|
|
456
|
+
{
|
|
457
|
+
icon: isLiked ? "mdi:heart" : "mdi:heart-outline",
|
|
458
|
+
className: `w-5 h-5 ${isLiked ? "text-red-500" : "text-white"}`
|
|
459
|
+
}
|
|
460
|
+
)
|
|
461
|
+
}
|
|
462
|
+
)
|
|
463
|
+
] }),
|
|
464
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-3 relative w-full", children: [
|
|
465
|
+
/* @__PURE__ */ jsx("h3", { className: "text-primary-normal text-base md:text-lg font-semibold line-clamp-2 mr-2 w-auto sm:w-full leading-snug tracking-[-0.01em]", children: pkg.title || "Unknown Destination" }),
|
|
466
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 mt-1", children: [
|
|
467
|
+
pkg.totalDays > 0 && /* @__PURE__ */ jsxs("p", { className: "text-secondary-normal-hover text-sm md:text-base font-light truncate", children: [
|
|
468
|
+
pkg.totalDays,
|
|
469
|
+
" ",
|
|
470
|
+
pkg.totalDays === 1 ? "Day" : "Days"
|
|
471
|
+
] }),
|
|
472
|
+
/* @__PURE__ */ jsx("div", { className: "w-1 h-1 rounded-full bg-gray-20" }),
|
|
473
|
+
rating > 0 ? /* @__PURE__ */ jsx("div", { className: "flex gap-x-0.5 flex-shrink-0", children: Array.from({ length: 5 }).map((_, i) => /* @__PURE__ */ jsx(
|
|
474
|
+
Icon,
|
|
475
|
+
{
|
|
476
|
+
icon: "material-symbols:star",
|
|
477
|
+
className: `text-sm md:text-base ${i < rating ? "text-primary-normal" : "text-gray-300"}`
|
|
478
|
+
},
|
|
479
|
+
i
|
|
480
|
+
)) }) : /* @__PURE__ */ jsx("span", { className: "text-xs md:text-sm text-primary-normal font-medium", children: "N/A" })
|
|
481
|
+
] }),
|
|
482
|
+
/* @__PURE__ */ jsxs("p", { className: "text-secondary text-sm sm:text-base font-light truncate mt-1", children: [
|
|
483
|
+
"Starting from ",
|
|
484
|
+
formatCurrency(price, currency)
|
|
485
|
+
] })
|
|
486
|
+
] })
|
|
487
|
+
] })
|
|
488
|
+
] });
|
|
489
|
+
};
|
|
490
|
+
var PackageCard_default = PackageCard;
|
|
491
|
+
var PackagesCarousel = ({
|
|
492
|
+
title,
|
|
493
|
+
subtitle,
|
|
494
|
+
packages,
|
|
495
|
+
currency = "USD",
|
|
496
|
+
onSelect,
|
|
497
|
+
hrefFor,
|
|
498
|
+
openInNewTab,
|
|
499
|
+
hideWhenEmpty = true
|
|
500
|
+
}) => {
|
|
501
|
+
const scrollRef = useRef(null);
|
|
502
|
+
if (hideWhenEmpty && (!packages || packages.length === 0)) return null;
|
|
503
|
+
const scrollBy = (dir) => {
|
|
504
|
+
const node = scrollRef.current;
|
|
505
|
+
if (!node) return;
|
|
506
|
+
node.scrollBy({ left: dir * 320 * 2, behavior: "smooth" });
|
|
507
|
+
};
|
|
508
|
+
return /* @__PURE__ */ jsxs("section", { className: "w-full py-6 md:py-8", children: [
|
|
509
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-end justify-between gap-4 mb-4 md:mb-6 px-1", children: [
|
|
510
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
|
|
511
|
+
/* @__PURE__ */ jsx("h2", { className: "text-primary-normal text-xl md:text-2xl font-semibold tracking-[-0.04em]", children: title }),
|
|
512
|
+
subtitle && /* @__PURE__ */ jsx("p", { className: "text-secondary-normal-hover text-sm md:text-base mt-1", children: subtitle })
|
|
513
|
+
] }),
|
|
514
|
+
/* @__PURE__ */ jsxs("div", { className: "hidden sm:flex items-center gap-2", children: [
|
|
515
|
+
/* @__PURE__ */ jsx(
|
|
516
|
+
"button",
|
|
517
|
+
{
|
|
518
|
+
type: "button",
|
|
519
|
+
onClick: () => scrollBy(-1),
|
|
520
|
+
"aria-label": "Scroll left",
|
|
521
|
+
className: "w-10 h-10 rounded-full bg-primary-background hover:bg-primary-light-hover flex items-center justify-center transition-colors",
|
|
522
|
+
children: /* @__PURE__ */ jsx(Icon, { icon: "lucide:arrow-left", className: "w-4 h-4 text-primary-normal" })
|
|
523
|
+
}
|
|
524
|
+
),
|
|
525
|
+
/* @__PURE__ */ jsx(
|
|
526
|
+
"button",
|
|
527
|
+
{
|
|
528
|
+
type: "button",
|
|
529
|
+
onClick: () => scrollBy(1),
|
|
530
|
+
"aria-label": "Scroll right",
|
|
531
|
+
className: "w-10 h-10 rounded-full bg-primary-background hover:bg-primary-light-hover flex items-center justify-center transition-colors",
|
|
532
|
+
children: /* @__PURE__ */ jsx(Icon, { icon: "lucide:arrow-right", className: "w-4 h-4 text-primary-normal" })
|
|
533
|
+
}
|
|
534
|
+
)
|
|
535
|
+
] })
|
|
536
|
+
] }),
|
|
537
|
+
/* @__PURE__ */ jsx(
|
|
538
|
+
"div",
|
|
539
|
+
{
|
|
540
|
+
ref: scrollRef,
|
|
541
|
+
className: "flex gap-4 overflow-x-auto hide-scrollbar scroll-smooth -mx-1 px-1 pb-2 snap-x snap-mandatory",
|
|
542
|
+
children: packages.map((pkg) => /* @__PURE__ */ jsx("div", { className: "snap-start", children: /* @__PURE__ */ jsx(
|
|
543
|
+
PackageCard_default,
|
|
544
|
+
{
|
|
545
|
+
pkg,
|
|
546
|
+
currency,
|
|
547
|
+
onSelect,
|
|
548
|
+
href: hrefFor?.(pkg),
|
|
549
|
+
openInNewTab
|
|
550
|
+
}
|
|
551
|
+
) }, pkg.id))
|
|
552
|
+
}
|
|
553
|
+
)
|
|
554
|
+
] });
|
|
555
|
+
};
|
|
556
|
+
var PackagesCarousel_default = PackagesCarousel;
|
|
557
|
+
var DEFAULT_TITLES = {
|
|
558
|
+
popular: "Popular Nepal Trekking Packages",
|
|
559
|
+
trending: "Trending Right Now",
|
|
560
|
+
topRated: "Top Rated Packages",
|
|
561
|
+
moreToExplore: "More to Explore"
|
|
562
|
+
};
|
|
563
|
+
var DEFAULT_ORDER = [
|
|
564
|
+
"popular",
|
|
565
|
+
"trending",
|
|
566
|
+
"topRated",
|
|
567
|
+
"moreToExplore"
|
|
568
|
+
];
|
|
569
|
+
function HomeSections({
|
|
570
|
+
client,
|
|
571
|
+
initialSections,
|
|
572
|
+
currency = "USD",
|
|
573
|
+
onSelect,
|
|
574
|
+
hrefFor,
|
|
575
|
+
openInNewTab,
|
|
576
|
+
titles,
|
|
577
|
+
order,
|
|
578
|
+
className,
|
|
579
|
+
renderLoading,
|
|
580
|
+
renderError
|
|
581
|
+
}) {
|
|
582
|
+
const effectiveOrder = order ?? DEFAULT_ORDER;
|
|
583
|
+
if (!initialSections && !client) {
|
|
584
|
+
throw new Error(
|
|
585
|
+
"[HomeSections] Provide either `initialSections` or `client`."
|
|
586
|
+
);
|
|
587
|
+
}
|
|
588
|
+
const remote = useHomeSections(initialSections ? null : client ?? null);
|
|
589
|
+
const sections = initialSections ?? remote.data;
|
|
590
|
+
const loading = !initialSections && Boolean(client) && remote.loading;
|
|
591
|
+
const error = !initialSections && Boolean(client) ? remote.error : null;
|
|
592
|
+
const mergedTitles = { ...DEFAULT_TITLES, ...titles ?? {} };
|
|
593
|
+
if (loading) {
|
|
594
|
+
return /* @__PURE__ */ jsx("div", { className: `tvr-package-detail ${className ?? ""}`.trim(), children: /* @__PURE__ */ jsxs("div", { className: "mx-auto max-w-[1440px] px-6 md:px-12 py-8", children: [
|
|
595
|
+
renderLoading ? renderLoading() : /* @__PURE__ */ jsx(SkeletonCarousel, {}),
|
|
596
|
+
renderLoading ? null : /* @__PURE__ */ jsx(SkeletonCarousel, {})
|
|
597
|
+
] }) });
|
|
598
|
+
}
|
|
599
|
+
if (error) {
|
|
600
|
+
return /* @__PURE__ */ jsx("div", { className: `tvr-package-detail ${className ?? ""}`.trim(), children: /* @__PURE__ */ jsx("div", { className: "mx-auto max-w-[1440px] px-6 md:px-12 py-16 text-center text-red-600 text-sm", children: renderError ? renderError(error) : `Failed to load: ${error.message}` }) });
|
|
601
|
+
}
|
|
602
|
+
if (!sections) {
|
|
603
|
+
return null;
|
|
604
|
+
}
|
|
605
|
+
return /* @__PURE__ */ jsx("div", { className: `tvr-package-detail ${className ?? ""}`.trim(), children: /* @__PURE__ */ jsx("div", { className: "mx-auto max-w-[1440px] px-6 md:px-12 py-4 md:py-6", children: effectiveOrder.map((key) => /* @__PURE__ */ jsx(
|
|
606
|
+
PackagesCarousel_default,
|
|
607
|
+
{
|
|
608
|
+
title: mergedTitles[key],
|
|
609
|
+
packages: sections[key] ?? [],
|
|
610
|
+
currency,
|
|
611
|
+
onSelect,
|
|
612
|
+
hrefFor,
|
|
613
|
+
openInNewTab
|
|
614
|
+
},
|
|
615
|
+
key
|
|
616
|
+
)) }) });
|
|
617
|
+
}
|
|
618
|
+
function SkeletonCarousel() {
|
|
619
|
+
return /* @__PURE__ */ jsxs("div", { className: "py-6 md:py-8", children: [
|
|
620
|
+
/* @__PURE__ */ jsx("div", { className: "h-7 w-72 bg-gray-200 rounded mb-4 animate-pulse" }),
|
|
621
|
+
/* @__PURE__ */ jsx("div", { className: "flex gap-4 overflow-hidden", children: Array.from({ length: 4 }).map((_, i) => /* @__PURE__ */ jsx(
|
|
622
|
+
"div",
|
|
623
|
+
{
|
|
624
|
+
className: "w-[300px] shrink-0 rounded-2xl bg-gray-100 animate-pulse",
|
|
625
|
+
style: { height: 360 }
|
|
626
|
+
},
|
|
627
|
+
i
|
|
628
|
+
)) })
|
|
629
|
+
] });
|
|
630
|
+
}
|
|
631
|
+
var HomeSectionsContext = createContext(null);
|
|
632
|
+
function HomeSectionsProvider({
|
|
633
|
+
client,
|
|
634
|
+
initialSections,
|
|
635
|
+
children
|
|
636
|
+
}) {
|
|
637
|
+
if (!initialSections && !client) {
|
|
638
|
+
throw new Error(
|
|
639
|
+
"[HomeSectionsProvider] Provide either `initialSections` or `client`."
|
|
640
|
+
);
|
|
641
|
+
}
|
|
642
|
+
const remote = useHomeSections(initialSections ? null : client ?? null);
|
|
643
|
+
const value = {
|
|
644
|
+
data: initialSections ?? remote.data,
|
|
645
|
+
loading: !initialSections && remote.loading,
|
|
646
|
+
error: !initialSections ? remote.error : null,
|
|
647
|
+
refetch: remote.refetch
|
|
648
|
+
};
|
|
649
|
+
return /* @__PURE__ */ jsx(HomeSectionsContext.Provider, { value, children });
|
|
650
|
+
}
|
|
651
|
+
function useHomeSectionsContext() {
|
|
652
|
+
const ctx = useContext(HomeSectionsContext);
|
|
653
|
+
if (!ctx) {
|
|
654
|
+
throw new Error(
|
|
655
|
+
"useHomeSectionsContext must be used inside <HomeSectionsProvider>."
|
|
656
|
+
);
|
|
657
|
+
}
|
|
658
|
+
return ctx;
|
|
659
|
+
}
|
|
660
|
+
var DEFAULT_TITLES2 = {
|
|
661
|
+
popular: "Popular Nepal Trekking Packages",
|
|
662
|
+
trending: "Trending Right Now",
|
|
663
|
+
topRated: "Top Rated Packages",
|
|
664
|
+
moreToExplore: "More to Explore"
|
|
665
|
+
};
|
|
666
|
+
var PackagesSection = ({
|
|
667
|
+
section,
|
|
668
|
+
title,
|
|
669
|
+
subtitle,
|
|
670
|
+
currency,
|
|
671
|
+
onSelect,
|
|
672
|
+
hrefFor,
|
|
673
|
+
openInNewTab,
|
|
674
|
+
hideWhenLoading = false,
|
|
675
|
+
hideWhenEmpty = true
|
|
676
|
+
}) => {
|
|
677
|
+
const { data, loading, error } = useHomeSectionsContext();
|
|
678
|
+
const packages = data?.[section] ?? [];
|
|
679
|
+
if (loading) {
|
|
680
|
+
if (hideWhenLoading) return null;
|
|
681
|
+
return /* @__PURE__ */ jsx("div", { className: "tvr-package-detail", children: /* @__PURE__ */ jsx("div", { className: "mx-auto max-w-[1440px] px-6 md:px-12", children: /* @__PURE__ */ jsx(SkeletonCarousel2, { title: title ?? DEFAULT_TITLES2[section] }) }) });
|
|
682
|
+
}
|
|
683
|
+
if (error) {
|
|
684
|
+
console.error(`[PackagesSection:${section}]`, error);
|
|
685
|
+
return null;
|
|
686
|
+
}
|
|
687
|
+
if (hideWhenEmpty && packages.length === 0) return null;
|
|
688
|
+
return /* @__PURE__ */ jsx("div", { className: "tvr-package-detail", children: /* @__PURE__ */ jsx("div", { className: "mx-auto max-w-[1440px] px-6 md:px-12", children: /* @__PURE__ */ jsx(
|
|
689
|
+
PackagesCarousel_default,
|
|
690
|
+
{
|
|
691
|
+
title: title ?? DEFAULT_TITLES2[section],
|
|
692
|
+
subtitle,
|
|
693
|
+
packages,
|
|
694
|
+
currency,
|
|
695
|
+
onSelect,
|
|
696
|
+
hrefFor,
|
|
697
|
+
openInNewTab,
|
|
698
|
+
hideWhenEmpty
|
|
699
|
+
}
|
|
700
|
+
) }) });
|
|
701
|
+
};
|
|
702
|
+
function SkeletonCarousel2({ title }) {
|
|
703
|
+
return /* @__PURE__ */ jsxs("div", { className: "py-6 md:py-8", children: [
|
|
704
|
+
/* @__PURE__ */ jsx("div", { className: "text-primary-normal text-xl md:text-2xl font-semibold tracking-[-0.04em] mb-4", children: title }),
|
|
705
|
+
/* @__PURE__ */ jsx("div", { className: "flex gap-4 overflow-hidden", children: Array.from({ length: 4 }).map((_, i) => /* @__PURE__ */ jsx(
|
|
706
|
+
"div",
|
|
707
|
+
{
|
|
708
|
+
className: "w-[300px] md:w-[320px] shrink-0 rounded-xl bg-gray-100 animate-pulse",
|
|
709
|
+
style: { height: 280 }
|
|
710
|
+
},
|
|
711
|
+
i
|
|
712
|
+
)) })
|
|
713
|
+
] });
|
|
714
|
+
}
|
|
715
|
+
var PackagesSection_default = PackagesSection;
|
|
716
|
+
var initialOptions = [
|
|
717
|
+
{ id: "Adult", name: "Adult", count: 1, ageRange: { min: 18, max: 64 }, countInTotal: true },
|
|
718
|
+
{ id: "Senior Citizen", name: "Senior Citizen", count: 0, ageRange: { min: 65, max: 100 }, countInTotal: true },
|
|
719
|
+
{ id: "Children", name: "Children", count: 0, ageRange: { min: 4, max: 17 }, countInTotal: true },
|
|
720
|
+
{ id: "Infant", name: "Infant", count: 0, ageRange: { min: 0, max: 3 }, countInTotal: false }
|
|
721
|
+
];
|
|
722
|
+
var getPluralizedName = (name, count) => {
|
|
723
|
+
const map = {
|
|
724
|
+
Adult: ["Adult", "Adults"],
|
|
725
|
+
"Senior Citizen": ["Senior Citizen", "Senior Citizens"],
|
|
726
|
+
Children: ["Child", "Children"],
|
|
727
|
+
Infant: ["Infant", "Infants"]
|
|
728
|
+
};
|
|
729
|
+
const [s, p] = map[name] || [name, name];
|
|
730
|
+
return count <= 1 ? s : p;
|
|
731
|
+
};
|
|
732
|
+
var BookingCardLite = ({
|
|
733
|
+
DescriptionData,
|
|
734
|
+
currency = "USD",
|
|
735
|
+
onReserve
|
|
736
|
+
}) => {
|
|
737
|
+
const prices = DescriptionData.Price ?? [];
|
|
738
|
+
const discounts = DescriptionData.Discounts ?? [];
|
|
739
|
+
const totalDays = DescriptionData.TotalDays || 1;
|
|
740
|
+
const [options, setOptions] = useState(initialOptions);
|
|
741
|
+
const [arrival, setArrival] = useState("");
|
|
742
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
743
|
+
const [isPriceDialogOpen, setIsPriceDialogOpen] = useState(false);
|
|
744
|
+
const dropdownRef = useRef(null);
|
|
745
|
+
const departure = useMemo(() => {
|
|
746
|
+
if (!arrival) return "";
|
|
747
|
+
const d = new Date(arrival);
|
|
748
|
+
if (isNaN(d.getTime())) return "";
|
|
749
|
+
d.setDate(d.getDate() + Math.max(0, totalDays - 1));
|
|
750
|
+
return d.toISOString().split("T")[0];
|
|
751
|
+
}, [arrival, totalDays]);
|
|
752
|
+
useEffect(() => {
|
|
753
|
+
const onDocClick = (e) => {
|
|
754
|
+
if (dropdownRef.current && !dropdownRef.current.contains(e.target)) {
|
|
755
|
+
setIsOpen(false);
|
|
756
|
+
}
|
|
757
|
+
};
|
|
758
|
+
document.addEventListener("mousedown", onDocClick);
|
|
759
|
+
return () => document.removeEventListener("mousedown", onDocClick);
|
|
760
|
+
}, []);
|
|
761
|
+
const allPeopleCount = useMemo(
|
|
762
|
+
() => options.filter((o) => o.countInTotal).reduce((s, o) => s + o.count, 0),
|
|
763
|
+
[options]
|
|
764
|
+
);
|
|
765
|
+
const totalPeople = useMemo(() => options.reduce((s, o) => s + o.count, 0), [options]);
|
|
766
|
+
const currentPriceGroup = useMemo(() => {
|
|
767
|
+
if (prices.length === 0) return null;
|
|
768
|
+
const sorted = [...prices].sort((a, b) => a.groupSizeMin - b.groupSizeMin);
|
|
769
|
+
if (allPeopleCount === 0) return sorted[0];
|
|
770
|
+
return sorted.find(
|
|
771
|
+
(p) => allPeopleCount >= p.groupSizeMin && allPeopleCount <= p.groupSizeMax
|
|
772
|
+
) ?? sorted[sorted.length - 1];
|
|
773
|
+
}, [prices, allPeopleCount]);
|
|
774
|
+
const minPriceGroup = useMemo(() => {
|
|
775
|
+
if (prices.length === 0) return null;
|
|
776
|
+
return [...prices].sort((a, b) => a.pricePerPerson - b.pricePerPerson)[0];
|
|
777
|
+
}, [prices]);
|
|
778
|
+
const currentPrice = currentPriceGroup?.pricePerPerson ?? 0;
|
|
779
|
+
const maxPrice = useMemo(
|
|
780
|
+
() => prices.reduce((m, p) => Math.max(m, p.pricePerPerson), 0),
|
|
781
|
+
[prices]
|
|
782
|
+
);
|
|
783
|
+
const minPrice = minPriceGroup?.pricePerPerson ?? 0;
|
|
784
|
+
const categoryPrices = useMemo(() => {
|
|
785
|
+
const result = {};
|
|
786
|
+
for (const opt of options) {
|
|
787
|
+
if (!opt.ageRange) {
|
|
788
|
+
result[opt.id] = currentPrice;
|
|
789
|
+
continue;
|
|
790
|
+
}
|
|
791
|
+
const matched = discounts.find(
|
|
792
|
+
(d) => opt.ageRange.min >= d.ageMin && opt.ageRange.min <= d.ageMax || opt.ageRange.max >= d.ageMin && opt.ageRange.max <= d.ageMax
|
|
793
|
+
);
|
|
794
|
+
const pct = matched?.discountPercentage ?? 0;
|
|
795
|
+
result[opt.id] = Math.max(0, currentPrice * (1 - pct / 100));
|
|
796
|
+
}
|
|
797
|
+
return result;
|
|
798
|
+
}, [options, currentPrice, discounts]);
|
|
799
|
+
const costBreakdown = useMemo(
|
|
800
|
+
() => options.filter((o) => o.count > 0).map((o) => {
|
|
801
|
+
const discountedPrice = categoryPrices[o.id] ?? currentPrice;
|
|
802
|
+
const discountPercentage = currentPrice > 0 ? (currentPrice - discountedPrice) / currentPrice * 100 : 0;
|
|
803
|
+
return {
|
|
804
|
+
category: o.name,
|
|
805
|
+
count: o.count,
|
|
806
|
+
discountedPrice,
|
|
807
|
+
discountPercentage
|
|
808
|
+
};
|
|
809
|
+
}),
|
|
810
|
+
[options, categoryPrices, currentPrice]
|
|
811
|
+
);
|
|
812
|
+
const totalCost = useMemo(
|
|
813
|
+
() => costBreakdown.reduce((s, x) => s + x.discountedPrice * x.count, 0),
|
|
814
|
+
[costBreakdown]
|
|
815
|
+
);
|
|
816
|
+
const updateTraveler = (id, inc) => setOptions(
|
|
817
|
+
(prev) => prev.map(
|
|
818
|
+
(o) => o.id === id ? { ...o, count: Math.max(0, o.count + (inc ? 1 : -1)) } : o
|
|
819
|
+
)
|
|
820
|
+
);
|
|
821
|
+
const handleReserve = () => {
|
|
822
|
+
if (!onReserve) return;
|
|
823
|
+
if (allPeopleCount === 0) return;
|
|
824
|
+
onReserve({
|
|
825
|
+
arrival,
|
|
826
|
+
departure,
|
|
827
|
+
travelers: options.filter((o) => o.count > 0),
|
|
828
|
+
totalCost,
|
|
829
|
+
pricePerPerson: currentPrice
|
|
830
|
+
});
|
|
831
|
+
};
|
|
832
|
+
return /* @__PURE__ */ jsxs("div", { className: "w-full sm:w-full lg:w-[520px] h-fit border rounded-xl shadow-md px-4 sm:px-6 bg-white flex flex-col gap-6 py-8 sm:py-10", children: [
|
|
833
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
|
|
834
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 md:gap-3", children: [
|
|
835
|
+
/* @__PURE__ */ jsx("div", { className: "text-lg md:text-xl lg:text-2xl font-medium text-primary-normal flex flex-col gap-2 pb-2 border-b border-[#E0E4E8]", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center lg:justify-between justify-center gap-1", children: [
|
|
836
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
|
|
837
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm lg:text-base font-medium text-secondary-normal-hover tracking-[-0.02em] mr-2", children: "Price For Single Adult" }),
|
|
838
|
+
currentPriceGroup?.isNegotiable && /* @__PURE__ */ jsx("span", { className: "text-xs bg-green-100 text-green-800 px-2 py-1 rounded-full", children: "Negotiable" }),
|
|
839
|
+
/* @__PURE__ */ jsx(
|
|
840
|
+
"button",
|
|
841
|
+
{
|
|
842
|
+
type: "button",
|
|
843
|
+
onClick: () => setIsPriceDialogOpen(true),
|
|
844
|
+
className: "ml-2 p-1 h-6 w-6 flex items-center justify-center rounded hover:bg-gray-100 transition-colors",
|
|
845
|
+
"aria-label": "Show pricing details",
|
|
846
|
+
children: /* @__PURE__ */ jsx(Icon, { icon: "lucide:info", className: "w-4 h-4 text-gray-500" })
|
|
847
|
+
}
|
|
848
|
+
)
|
|
849
|
+
] }),
|
|
850
|
+
/* @__PURE__ */ jsx("div", { className: "text-right", children: /* @__PURE__ */ jsxs("p", { className: "text-lg md:text-xl font-medium tracking-[-0.04em] text-primary-normal", children: [
|
|
851
|
+
formatCurrency(
|
|
852
|
+
totalCost && totalPeople > 0 ? categoryPrices["Adult"] ?? currentPrice : currentPrice,
|
|
853
|
+
currency
|
|
854
|
+
),
|
|
855
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm", children: "/-" })
|
|
856
|
+
] }) })
|
|
857
|
+
] }) }),
|
|
858
|
+
/* @__PURE__ */ jsx("div", { className: "w-full h-[2px] bg-primary-background/10" }),
|
|
859
|
+
/* @__PURE__ */ jsxs("div", { className: "grid lg:grid-cols-2 md:grid-cols-1 sm:grid-cols-1 gap-4 mt-4", children: [
|
|
860
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
861
|
+
/* @__PURE__ */ jsx(
|
|
862
|
+
"label",
|
|
863
|
+
{
|
|
864
|
+
htmlFor: "tvr-arrival",
|
|
865
|
+
className: "text-sm md:text-base text-secondary-normal-hover",
|
|
866
|
+
children: "Arrival Date"
|
|
867
|
+
}
|
|
868
|
+
),
|
|
869
|
+
/* @__PURE__ */ jsx(
|
|
870
|
+
"input",
|
|
871
|
+
{
|
|
872
|
+
id: "tvr-arrival",
|
|
873
|
+
type: "date",
|
|
874
|
+
value: arrival,
|
|
875
|
+
onChange: (e) => setArrival(e.target.value),
|
|
876
|
+
className: "w-full max-h-10 h-10 mt-1 px-4 text-sm md:text-base rounded-xl border text-primary-normal focus:outline-none focus:ring-1 focus:ring-primary-normal-active"
|
|
877
|
+
}
|
|
878
|
+
),
|
|
879
|
+
departure && /* @__PURE__ */ jsxs("p", { className: "text-xs text-secondary-normal-hover mt-1", children: [
|
|
880
|
+
"Returns: ",
|
|
881
|
+
departure
|
|
882
|
+
] })
|
|
883
|
+
] }),
|
|
884
|
+
/* @__PURE__ */ jsxs("div", { className: "relative", ref: dropdownRef, children: [
|
|
885
|
+
/* @__PURE__ */ jsx("label", { className: "text-sm md:text-base text-secondary-normal-hover", children: "Number of people" }),
|
|
886
|
+
/* @__PURE__ */ jsxs(
|
|
887
|
+
"button",
|
|
888
|
+
{
|
|
889
|
+
type: "button",
|
|
890
|
+
onClick: () => setIsOpen((v) => !v),
|
|
891
|
+
className: "w-full max-h-10 h-10 overflow-y-scroll mt-1 flex justify-between px-4 items-center text-sm md:text-base rounded-xl border text-primary-normal focus:outline-none focus:ring-1 focus:ring-primary-normal-active",
|
|
892
|
+
children: [
|
|
893
|
+
options.some((o) => o.count > 0) ? options.filter((o) => o.count > 0).map((o) => `${o.count} ${getPluralizedName(o.name, o.count)}`).join(", ") : "Eg : 2 Adults",
|
|
894
|
+
/* @__PURE__ */ jsx(
|
|
895
|
+
Icon,
|
|
896
|
+
{
|
|
897
|
+
icon: isOpen ? "lucide:chevron-up" : "lucide:chevron-down",
|
|
898
|
+
className: "w-4 h-4"
|
|
899
|
+
}
|
|
900
|
+
)
|
|
901
|
+
]
|
|
902
|
+
}
|
|
903
|
+
),
|
|
904
|
+
isOpen && /* @__PURE__ */ jsx("div", { className: "absolute z-10 sm:min-w-[360px] md:min-w-[400px] bg-white border rounded-xl shadow-lg mt-2 left-0 sm:left-auto sm:-ml-[200px]", children: options.map((option) => {
|
|
905
|
+
const price = categoryPrices[option.id] ?? currentPrice;
|
|
906
|
+
const discountPercentage = currentPrice > 0 ? (currentPrice - price) / currentPrice * 100 : 0;
|
|
907
|
+
return /* @__PURE__ */ jsxs(
|
|
908
|
+
"div",
|
|
909
|
+
{
|
|
910
|
+
className: "py-3 border-b last:border-b-0 hover:bg-primary-light/30 transition-colors flex justify-between items-center gap-3 px-4 sm:px-5",
|
|
911
|
+
children: [
|
|
912
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-4", children: [
|
|
913
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
|
|
914
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs md:text-lg font-medium text-secondary-normal-hover leading-tight", children: getPluralizedName(option.name, option.count || 1) }),
|
|
915
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs md:text-sm text-gray-500 whitespace-nowrap leading-tight mt-0.5", children: option.ageRange && `( Ages ${option.ageRange.min}-${option.ageRange.max} )` })
|
|
916
|
+
] }),
|
|
917
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-col items-start whitespace-nowrap", children: Math.round(discountPercentage) === 100 ? /* @__PURE__ */ jsx("span", { className: "text-green-600 text-xs font-medium text-start w-full", children: "(FREE)" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
918
|
+
maxPrice !== price && /* @__PURE__ */ jsxs("span", { className: "text-xs line-through text-gray-400", children: [
|
|
919
|
+
formatCurrency(maxPrice, currency),
|
|
920
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px]", children: "pp" })
|
|
921
|
+
] }),
|
|
922
|
+
/* @__PURE__ */ jsxs("p", { className: "text-sm md:text-base font-semibold text-primary-normal leading-tight", children: [
|
|
923
|
+
formatCurrency(price, currency),
|
|
924
|
+
/* @__PURE__ */ jsx("span", { className: "text-[10px] md:text-xs", children: "pp" })
|
|
925
|
+
] }),
|
|
926
|
+
Math.round(discountPercentage) > 0 && /* @__PURE__ */ jsxs("span", { className: "text-xs text-green-600", children: [
|
|
927
|
+
"(",
|
|
928
|
+
discountPercentage.toFixed(0),
|
|
929
|
+
"% off)"
|
|
930
|
+
] })
|
|
931
|
+
] }) })
|
|
932
|
+
] }) }),
|
|
933
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
934
|
+
/* @__PURE__ */ jsx(
|
|
935
|
+
"button",
|
|
936
|
+
{
|
|
937
|
+
type: "button",
|
|
938
|
+
onClick: () => updateTraveler(option.id, false),
|
|
939
|
+
className: "w-8 h-8 md:w-9 md:h-9 border rounded-full text-primary-normal flex items-center justify-center hover:bg-primary-light/40 transition-colors",
|
|
940
|
+
"aria-label": `Remove one ${option.name}`,
|
|
941
|
+
children: "-"
|
|
942
|
+
}
|
|
943
|
+
),
|
|
944
|
+
/* @__PURE__ */ jsx("h1", { className: "text-base text-primary-normal w-5 text-center", children: option.count }),
|
|
945
|
+
/* @__PURE__ */ jsx(
|
|
946
|
+
"button",
|
|
947
|
+
{
|
|
948
|
+
type: "button",
|
|
949
|
+
onClick: () => updateTraveler(option.id, true),
|
|
950
|
+
className: "w-8 h-8 md:w-9 md:h-9 border rounded-full text-primary-normal flex items-center justify-center hover:bg-primary-light/40 transition-colors",
|
|
951
|
+
"aria-label": `Add one ${option.name}`,
|
|
952
|
+
children: "+"
|
|
953
|
+
}
|
|
954
|
+
)
|
|
955
|
+
] })
|
|
956
|
+
]
|
|
957
|
+
},
|
|
958
|
+
option.id
|
|
959
|
+
);
|
|
960
|
+
}) })
|
|
961
|
+
] })
|
|
962
|
+
] }),
|
|
963
|
+
allPeopleCount > 0 && /* @__PURE__ */ jsxs("div", { className: "p-2 mt-4 mb-2 rounded-lg hidden lg:block text-sm md:text-base text-gray-600 border border-secondary/50", children: [
|
|
964
|
+
costBreakdown.map((item, idx) => /* @__PURE__ */ jsxs("span", { children: [
|
|
965
|
+
item.count,
|
|
966
|
+
" ",
|
|
967
|
+
getPluralizedName(item.category, item.count),
|
|
968
|
+
" \xD7",
|
|
969
|
+
" ",
|
|
970
|
+
formatCurrency(item.discountedPrice, currency),
|
|
971
|
+
item.discountedPrice === 0 && /* @__PURE__ */ jsx("span", { className: "text-green-600 text-xs ml-1", children: "(FREE)" }),
|
|
972
|
+
item.discountPercentage > 0 && /* @__PURE__ */ jsxs("span", { className: "text-xs text-green-600 ml-1", children: [
|
|
973
|
+
item.discountPercentage.toFixed(0),
|
|
974
|
+
"% discount applied"
|
|
975
|
+
] }),
|
|
976
|
+
idx !== costBreakdown.length - 1 && " + "
|
|
977
|
+
] }, item.category)),
|
|
978
|
+
/* @__PURE__ */ jsxs("div", { className: "pt-2 flex gap-4 items-center font-semibold text-primary-normal", children: [
|
|
979
|
+
/* @__PURE__ */ jsx("span", { children: "Total :" }),
|
|
980
|
+
/* @__PURE__ */ jsx("span", { className: "text-primary-normal text-xl font-bold", children: formatCurrency(totalCost, currency) })
|
|
981
|
+
] })
|
|
982
|
+
] })
|
|
983
|
+
] }),
|
|
984
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-3", children: [
|
|
985
|
+
/* @__PURE__ */ jsx(
|
|
986
|
+
"button",
|
|
987
|
+
{
|
|
988
|
+
type: "button",
|
|
989
|
+
onClick: handleReserve,
|
|
990
|
+
disabled: allPeopleCount === 0 || !onReserve,
|
|
991
|
+
className: "w-full py-2 md:py-3 bg-primary-normal text-white font-semibold rounded-lg hover:bg-primary-normal-hover transition text-sm md:text-base disabled:opacity-60 disabled:cursor-not-allowed",
|
|
992
|
+
children: allPeopleCount === 0 ? "Reserve Now" : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
993
|
+
"Book Now - ",
|
|
994
|
+
formatCurrency(totalCost, currency)
|
|
995
|
+
] })
|
|
996
|
+
}
|
|
997
|
+
),
|
|
998
|
+
/* @__PURE__ */ jsxs("p", { className: "text-center text-xs md:text-sm text-secondary-normal-hover mt-2 md:mt-3 pb-4 tracking-[-0.02em]", children: [
|
|
999
|
+
"*Price Varies According to",
|
|
1000
|
+
/* @__PURE__ */ jsx("span", { className: "text-primary-normal text-sm md:text-sm", children: " age group " }),
|
|
1001
|
+
"and",
|
|
1002
|
+
/* @__PURE__ */ jsxs("span", { className: "text-primary-normal text-sm md:text-sm", children: [
|
|
1003
|
+
" ",
|
|
1004
|
+
"number of people.",
|
|
1005
|
+
" "
|
|
1006
|
+
] })
|
|
1007
|
+
] })
|
|
1008
|
+
] })
|
|
1009
|
+
] }),
|
|
1010
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-0 justify-center p-4 border-2 rounded-lg", children: [
|
|
1011
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0 font-medium", children: [
|
|
1012
|
+
/* @__PURE__ */ jsx("span", { className: "text-primary-normal text-lg md:text-lg tracking-[-0.02em]", children: formatCurrency(minPrice, currency) }),
|
|
1013
|
+
/* @__PURE__ */ jsx("span", { className: "text-secondary-normal-hover text-sm md:text-sm ml-2", children: "Per Person" })
|
|
1014
|
+
] }),
|
|
1015
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col sm:items-start md:items-center sm:flex-row whitespace-nowrap text-xs md:text-sm", children: [
|
|
1016
|
+
/* @__PURE__ */ jsxs("span", { className: "text-secondary-normal-hover", children: [
|
|
1017
|
+
"Best Price for group of ",
|
|
1018
|
+
minPriceGroup?.groupSizeMin,
|
|
1019
|
+
" -",
|
|
1020
|
+
" ",
|
|
1021
|
+
minPriceGroup?.groupSizeMax,
|
|
1022
|
+
" people"
|
|
1023
|
+
] }),
|
|
1024
|
+
/* @__PURE__ */ jsx("span", { className: "text-primary-normal sm:ml-2", children: "(Lowest Price)" })
|
|
1025
|
+
] })
|
|
1026
|
+
] }),
|
|
1027
|
+
isPriceDialogOpen && /* @__PURE__ */ jsx(
|
|
1028
|
+
"div",
|
|
1029
|
+
{
|
|
1030
|
+
className: "fixed inset-0 z-[1000] bg-black/50 flex items-center justify-center p-4",
|
|
1031
|
+
onClick: () => setIsPriceDialogOpen(false),
|
|
1032
|
+
children: /* @__PURE__ */ jsxs(
|
|
1033
|
+
"div",
|
|
1034
|
+
{
|
|
1035
|
+
className: "bg-white rounded-2xl border border-[#E0E4E8] max-w-md w-full p-6 max-h-[80vh] overflow-y-auto",
|
|
1036
|
+
onClick: (e) => e.stopPropagation(),
|
|
1037
|
+
children: [
|
|
1038
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between mb-4", children: [
|
|
1039
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1040
|
+
/* @__PURE__ */ jsx("h3", { className: "text-primary-normal text-lg font-semibold", children: "Pricing Details" }),
|
|
1041
|
+
/* @__PURE__ */ jsx("p", { className: "text-secondary-normal-hover text-sm mt-1", children: "Prices vary based on group size and age. All prices are per person." })
|
|
1042
|
+
] }),
|
|
1043
|
+
/* @__PURE__ */ jsx(
|
|
1044
|
+
"button",
|
|
1045
|
+
{
|
|
1046
|
+
type: "button",
|
|
1047
|
+
onClick: () => setIsPriceDialogOpen(false),
|
|
1048
|
+
className: "text-gray-500 hover:bg-gray-100 rounded p-1",
|
|
1049
|
+
"aria-label": "Close",
|
|
1050
|
+
children: /* @__PURE__ */ jsx(Icon, { icon: "lucide:x", className: "w-5 h-5" })
|
|
1051
|
+
}
|
|
1052
|
+
)
|
|
1053
|
+
] }),
|
|
1054
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
1055
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1056
|
+
/* @__PURE__ */ jsx("h4", { className: "font-semibold mb-2 text-primary-normal", children: "Group Size Pricing" }),
|
|
1057
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-2", children: prices.map((p, i) => /* @__PURE__ */ jsxs(
|
|
1058
|
+
"div",
|
|
1059
|
+
{
|
|
1060
|
+
className: "flex justify-between items-center p-2 bg-primary-background/10 rounded border border-primary-border/20",
|
|
1061
|
+
children: [
|
|
1062
|
+
/* @__PURE__ */ jsxs("span", { className: "text-sm text-secondary-normal-hover", children: [
|
|
1063
|
+
"Group of ",
|
|
1064
|
+
p.groupSizeMin,
|
|
1065
|
+
" - ",
|
|
1066
|
+
p.groupSizeMax,
|
|
1067
|
+
" people"
|
|
1068
|
+
] }),
|
|
1069
|
+
/* @__PURE__ */ jsxs("div", { className: "font-medium text-primary-normal text-sm", children: [
|
|
1070
|
+
formatCurrency(p.pricePerPerson, currency),
|
|
1071
|
+
p.isNegotiable && /* @__PURE__ */ jsx("span", { className: "text-xs text-green-600 ml-1", children: "(Negotiable)" })
|
|
1072
|
+
] })
|
|
1073
|
+
]
|
|
1074
|
+
},
|
|
1075
|
+
i
|
|
1076
|
+
)) })
|
|
1077
|
+
] }),
|
|
1078
|
+
discounts.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
|
|
1079
|
+
/* @__PURE__ */ jsx("h4", { className: "font-semibold mb-2 text-primary-normal", children: "Age-Based Discounts" }),
|
|
1080
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-1 text-sm text-secondary-normal-hover", children: discounts.map((d, i) => /* @__PURE__ */ jsxs("div", { children: [
|
|
1081
|
+
"Ages ",
|
|
1082
|
+
d.ageMin,
|
|
1083
|
+
" - ",
|
|
1084
|
+
d.ageMax,
|
|
1085
|
+
": ",
|
|
1086
|
+
d.discountPercentage,
|
|
1087
|
+
"% discount"
|
|
1088
|
+
] }, i)) })
|
|
1089
|
+
] })
|
|
1090
|
+
] })
|
|
1091
|
+
]
|
|
1092
|
+
}
|
|
1093
|
+
)
|
|
1094
|
+
}
|
|
1095
|
+
)
|
|
1096
|
+
] });
|
|
1097
|
+
};
|
|
1098
|
+
var BookingCardLite_default = BookingCardLite;
|
|
1099
|
+
var DAY_COLORS = [
|
|
1100
|
+
"#8b5cf6",
|
|
1101
|
+
// violet
|
|
1102
|
+
"#f59e0b",
|
|
1103
|
+
// amber
|
|
1104
|
+
"#14b8a6",
|
|
1105
|
+
// teal
|
|
1106
|
+
"#6366f1",
|
|
1107
|
+
// indigo
|
|
1108
|
+
"#f97316",
|
|
1109
|
+
// orange
|
|
1110
|
+
"#84cc16",
|
|
1111
|
+
// lime
|
|
1112
|
+
"#ec4899",
|
|
1113
|
+
// pink
|
|
1114
|
+
"#0ea5e9"
|
|
1115
|
+
// sky
|
|
1116
|
+
];
|
|
1117
|
+
var dayColor = (n) => DAY_COLORS[((n ?? 1) - 1) % DAY_COLORS.length];
|
|
1118
|
+
function PackageMap({
|
|
1119
|
+
locations,
|
|
1120
|
+
dayNumber,
|
|
1121
|
+
routes,
|
|
1122
|
+
className
|
|
1123
|
+
}) {
|
|
1124
|
+
const [Leaflet, setLeaflet] = useState(null);
|
|
1125
|
+
useEffect(() => {
|
|
1126
|
+
let cancelled = false;
|
|
1127
|
+
(async () => {
|
|
1128
|
+
const [rl, leaflet] = await Promise.all([
|
|
1129
|
+
import('react-leaflet'),
|
|
1130
|
+
import('leaflet')
|
|
1131
|
+
]);
|
|
1132
|
+
await import('leaflet/dist/leaflet.css').catch(
|
|
1133
|
+
() => void 0
|
|
1134
|
+
);
|
|
1135
|
+
if (cancelled) return;
|
|
1136
|
+
const Lns = leaflet.default ?? leaflet;
|
|
1137
|
+
delete Lns.Icon.Default.prototype._getIconUrl;
|
|
1138
|
+
Lns.Icon.Default.mergeOptions({
|
|
1139
|
+
iconRetinaUrl: "https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-icon-2x.png",
|
|
1140
|
+
iconUrl: "https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-icon.png",
|
|
1141
|
+
shadowUrl: "https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/images/marker-shadow.png"
|
|
1142
|
+
});
|
|
1143
|
+
setLeaflet({
|
|
1144
|
+
MapContainer: rl.MapContainer,
|
|
1145
|
+
TileLayer: rl.TileLayer,
|
|
1146
|
+
Marker: rl.Marker,
|
|
1147
|
+
Popup: rl.Popup,
|
|
1148
|
+
Polyline: rl.Polyline,
|
|
1149
|
+
useMap: rl.useMap,
|
|
1150
|
+
L: Lns
|
|
1151
|
+
});
|
|
1152
|
+
})();
|
|
1153
|
+
return () => {
|
|
1154
|
+
cancelled = true;
|
|
1155
|
+
};
|
|
1156
|
+
}, []);
|
|
1157
|
+
const valid = useMemo(
|
|
1158
|
+
() => (locations || []).filter(
|
|
1159
|
+
(l) => l?.coord && Number.isFinite(l.coord[0]) && Number.isFinite(l.coord[1])
|
|
1160
|
+
),
|
|
1161
|
+
[locations]
|
|
1162
|
+
);
|
|
1163
|
+
if (valid.length === 0) {
|
|
1164
|
+
return /* @__PURE__ */ jsx(
|
|
1165
|
+
"div",
|
|
1166
|
+
{
|
|
1167
|
+
className: `h-full w-full flex items-center justify-center bg-slate-100 text-slate-500 rounded-lg border border-[#E0E4E8] text-sm ${className ?? ""}`,
|
|
1168
|
+
children: "No locations available"
|
|
1169
|
+
}
|
|
1170
|
+
);
|
|
1171
|
+
}
|
|
1172
|
+
if (!Leaflet) {
|
|
1173
|
+
return /* @__PURE__ */ jsx(
|
|
1174
|
+
"div",
|
|
1175
|
+
{
|
|
1176
|
+
className: `h-full w-full flex items-center justify-center bg-slate-100 text-slate-500 rounded-lg border border-[#E0E4E8] text-sm animate-pulse ${className ?? ""}`,
|
|
1177
|
+
children: "Loading map\u2026"
|
|
1178
|
+
}
|
|
1179
|
+
);
|
|
1180
|
+
}
|
|
1181
|
+
const { MapContainer, TileLayer, Marker, Popup, Polyline, useMap, L } = Leaflet;
|
|
1182
|
+
const color = dayColor(dayNumber);
|
|
1183
|
+
const lats = valid.map((l) => l.coord[0]);
|
|
1184
|
+
const lngs = valid.map((l) => l.coord[1]);
|
|
1185
|
+
const center = [
|
|
1186
|
+
(Math.min(...lats) + Math.max(...lats)) / 2,
|
|
1187
|
+
(Math.min(...lngs) + Math.max(...lngs)) / 2
|
|
1188
|
+
];
|
|
1189
|
+
const hasRealRoutes = (routes?.length ?? 0) > 0;
|
|
1190
|
+
const FitBounds = () => {
|
|
1191
|
+
const map = useMap();
|
|
1192
|
+
useEffect(() => {
|
|
1193
|
+
const points = valid.map((l) => l.coord);
|
|
1194
|
+
if (hasRealRoutes) {
|
|
1195
|
+
for (const line of routes) {
|
|
1196
|
+
for (const p of line) points.push(p);
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
if (!points.length) return;
|
|
1200
|
+
const b = L.latLngBounds(points.map((p) => L.latLng(p[0], p[1])));
|
|
1201
|
+
const padding = [60, 60];
|
|
1202
|
+
map.flyToBounds(b, {
|
|
1203
|
+
paddingTopLeft: padding,
|
|
1204
|
+
paddingBottomRight: padding,
|
|
1205
|
+
duration: 1.2,
|
|
1206
|
+
easeLinearity: 0.5
|
|
1207
|
+
});
|
|
1208
|
+
}, [
|
|
1209
|
+
dayNumber,
|
|
1210
|
+
valid.length,
|
|
1211
|
+
valid.map((l) => `${l.coord[0]},${l.coord[1]}`).join("|"),
|
|
1212
|
+
hasRealRoutes,
|
|
1213
|
+
routes?.length
|
|
1214
|
+
]);
|
|
1215
|
+
return null;
|
|
1216
|
+
};
|
|
1217
|
+
return /* @__PURE__ */ jsx(
|
|
1218
|
+
"div",
|
|
1219
|
+
{
|
|
1220
|
+
className: `h-full w-full overflow-hidden rounded-lg border border-[#E0E4E8] ${className ?? ""}`,
|
|
1221
|
+
style: { minHeight: 500 },
|
|
1222
|
+
children: /* @__PURE__ */ jsxs(
|
|
1223
|
+
MapContainer,
|
|
1224
|
+
{
|
|
1225
|
+
center,
|
|
1226
|
+
zoom: 8,
|
|
1227
|
+
scrollWheelZoom: false,
|
|
1228
|
+
style: { height: "100%", width: "100%" },
|
|
1229
|
+
children: [
|
|
1230
|
+
/* @__PURE__ */ jsx(
|
|
1231
|
+
TileLayer,
|
|
1232
|
+
{
|
|
1233
|
+
attribution: '\xA9 <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
|
|
1234
|
+
url: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
|
1235
|
+
}
|
|
1236
|
+
),
|
|
1237
|
+
/* @__PURE__ */ jsx(FitBounds, {}),
|
|
1238
|
+
hasRealRoutes ? routes.map((line, i) => /* @__PURE__ */ jsx(
|
|
1239
|
+
Polyline,
|
|
1240
|
+
{
|
|
1241
|
+
positions: line,
|
|
1242
|
+
pathOptions: { color, weight: 4, opacity: 0.9 }
|
|
1243
|
+
},
|
|
1244
|
+
`route-${dayNumber}-${i}`
|
|
1245
|
+
)) : valid.length > 1 && /* @__PURE__ */ jsx(
|
|
1246
|
+
Polyline,
|
|
1247
|
+
{
|
|
1248
|
+
positions: valid.map((l) => l.coord),
|
|
1249
|
+
pathOptions: {
|
|
1250
|
+
color,
|
|
1251
|
+
weight: 3,
|
|
1252
|
+
opacity: 0.6,
|
|
1253
|
+
dashArray: "6 8"
|
|
1254
|
+
// dashed fallback so it reads as "no real route"
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
),
|
|
1258
|
+
valid.map((loc, i) => /* @__PURE__ */ jsx(Marker, { position: loc.coord, children: /* @__PURE__ */ jsx(Popup, { children: /* @__PURE__ */ jsxs("div", { style: { fontFamily: "inherit" }, children: [
|
|
1259
|
+
/* @__PURE__ */ jsx("strong", { style: { color }, children: loc.displayName }),
|
|
1260
|
+
dayNumber !== void 0 && /* @__PURE__ */ jsxs("div", { style: { fontSize: 11, color: "#64748b", marginTop: 4 }, children: [
|
|
1261
|
+
"Day ",
|
|
1262
|
+
dayNumber
|
|
1263
|
+
] })
|
|
1264
|
+
] }) }) }, `${dayNumber}-${i}-${loc.coord[0]}-${loc.coord[1]}`))
|
|
1265
|
+
]
|
|
1266
|
+
}
|
|
1267
|
+
)
|
|
1268
|
+
}
|
|
1269
|
+
);
|
|
1270
|
+
}
|
|
1271
|
+
var DifficultyProgressBar = ({ level }) => {
|
|
1272
|
+
const normalizedLevel = (level || "").toLowerCase();
|
|
1273
|
+
let width = "30%";
|
|
1274
|
+
let colorClass = "bg-green-500";
|
|
1275
|
+
let label = "Easy";
|
|
1276
|
+
if (normalizedLevel.includes("moderate") || normalizedLevel.includes("medium")) {
|
|
1277
|
+
width = "70%";
|
|
1278
|
+
colorClass = "bg-secondary";
|
|
1279
|
+
label = "Moderate";
|
|
1280
|
+
} else if (normalizedLevel.includes("challenging") || normalizedLevel.includes("challenge")) {
|
|
1281
|
+
width = "100%";
|
|
1282
|
+
colorClass = "bg-orange-500";
|
|
1283
|
+
label = "Challenging";
|
|
1284
|
+
} else if (normalizedLevel.includes("extreme") || normalizedLevel.includes("hard")) {
|
|
1285
|
+
width = "100%";
|
|
1286
|
+
colorClass = "bg-primary-dark";
|
|
1287
|
+
label = "Extreme";
|
|
1288
|
+
} else {
|
|
1289
|
+
width = "30%";
|
|
1290
|
+
colorClass = "bg-green-500";
|
|
1291
|
+
label = level || "Easy";
|
|
1292
|
+
}
|
|
1293
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 w-full", children: [
|
|
1294
|
+
/* @__PURE__ */ jsx("div", { className: "w-[80px] h-1.5 bg-gray-200 rounded-full overflow-hidden shrink-0", children: /* @__PURE__ */ jsx(
|
|
1295
|
+
"div",
|
|
1296
|
+
{
|
|
1297
|
+
className: `h-full ${colorClass} transition-all duration-500`,
|
|
1298
|
+
style: { width }
|
|
1299
|
+
}
|
|
1300
|
+
) }),
|
|
1301
|
+
/* @__PURE__ */ jsxs("span", { className: "text-sm md:text-base text-[#47586E] whitespace-nowrap", children: [
|
|
1302
|
+
"Difficulty: ",
|
|
1303
|
+
label
|
|
1304
|
+
] })
|
|
1305
|
+
] });
|
|
1306
|
+
};
|
|
1307
|
+
var getStopStyles = (label) => {
|
|
1308
|
+
if (label === "Start") {
|
|
1309
|
+
return {
|
|
1310
|
+
labelClass: "text-emerald-600",
|
|
1311
|
+
dotClass: "bg-emerald-500 border-emerald-500",
|
|
1312
|
+
lineClass: "bg-emerald-300"
|
|
1313
|
+
};
|
|
1314
|
+
}
|
|
1315
|
+
if (label === "End") {
|
|
1316
|
+
return {
|
|
1317
|
+
labelClass: "text-[#3b82f6]",
|
|
1318
|
+
dotClass: "bg-[#3b82f6] border-[#3b82f6]",
|
|
1319
|
+
lineClass: "bg-[#3b82f6] opacity-30"
|
|
1320
|
+
};
|
|
1321
|
+
}
|
|
1322
|
+
return {
|
|
1323
|
+
labelClass: "text-[#8b5cf6]",
|
|
1324
|
+
dotClass: "bg-[#8b5cf6] border-[#8b5cf6]",
|
|
1325
|
+
lineClass: "bg-[#8b5cf6] opacity-30"
|
|
1326
|
+
};
|
|
1327
|
+
};
|
|
1328
|
+
var Itinerary = ({ Description, routes, currency = "USD", onReserveClick }) => {
|
|
1329
|
+
const [selectedDay, setSelectedDay] = useState(1);
|
|
1330
|
+
const [descriptionExpanded, setDescriptionExpanded] = useState(false);
|
|
1331
|
+
const scrollRef = useRef(null);
|
|
1332
|
+
const detailCardRef = useRef(null);
|
|
1333
|
+
const hasUserSelectedDayRef = useRef(false);
|
|
1334
|
+
const routesByDay = useMemo(() => {
|
|
1335
|
+
const map = /* @__PURE__ */ new Map();
|
|
1336
|
+
for (const day of routes ?? []) {
|
|
1337
|
+
const lines = [];
|
|
1338
|
+
for (const r of day.routes ?? []) {
|
|
1339
|
+
let geom = r.geometry;
|
|
1340
|
+
if (typeof geom === "string") {
|
|
1341
|
+
try {
|
|
1342
|
+
geom = JSON.parse(geom);
|
|
1343
|
+
} catch {
|
|
1344
|
+
continue;
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
if (!geom?.coordinates || !Array.isArray(geom.coordinates)) continue;
|
|
1348
|
+
const flipped = geom.coordinates.map(
|
|
1349
|
+
(c) => Array.isArray(c) && Number.isFinite(c[0]) && Number.isFinite(c[1]) ? [c[1], c[0]] : null
|
|
1350
|
+
).filter(Boolean);
|
|
1351
|
+
if (flipped.length >= 2) lines.push(flipped);
|
|
1352
|
+
}
|
|
1353
|
+
if (lines.length) map.set(day.dayNumber, lines);
|
|
1354
|
+
}
|
|
1355
|
+
return map;
|
|
1356
|
+
}, [routes]);
|
|
1357
|
+
const dayItems = useMemo(() => {
|
|
1358
|
+
if (!Description?.Days?.length) return [];
|
|
1359
|
+
return Description.Days.map((day, index) => {
|
|
1360
|
+
const actualDayNumber = day?.DayNumber ?? day?.dayNumber ?? index + 1;
|
|
1361
|
+
const flattenedLocs = [];
|
|
1362
|
+
(day?.locations || []).forEach((item) => {
|
|
1363
|
+
if (item?.locations && Array.isArray(item.locations)) {
|
|
1364
|
+
flattenedLocs.push(...item.locations);
|
|
1365
|
+
} else {
|
|
1366
|
+
flattenedLocs.push(item);
|
|
1367
|
+
}
|
|
1368
|
+
});
|
|
1369
|
+
const locationsData = flattenedLocs.map((loc, idx) => {
|
|
1370
|
+
const lat = Number(loc?.lat ?? loc?.coord?.[0]);
|
|
1371
|
+
const lng = Number(loc?.lng ?? loc?.coord?.[1]);
|
|
1372
|
+
if (isNaN(lat) || isNaN(lng)) return null;
|
|
1373
|
+
return {
|
|
1374
|
+
coord: [lat, lng],
|
|
1375
|
+
displayName: loc?.name || loc?.displayName || `Location ${loc?.Sequence || loc?.sequence || idx + 1}`,
|
|
1376
|
+
sequence: loc?.sequence || loc?.position || idx + 1
|
|
1377
|
+
};
|
|
1378
|
+
}).filter(Boolean);
|
|
1379
|
+
const locationName = locationsData.length > 0 ? locationsData.length > 1 ? `${locationsData[0].displayName} - ${locationsData[locationsData.length - 1].displayName}` : locationsData[0].displayName : "";
|
|
1380
|
+
return {
|
|
1381
|
+
key: `Day ${actualDayNumber}`,
|
|
1382
|
+
data: {
|
|
1383
|
+
title: locationName || `Location ${actualDayNumber}`,
|
|
1384
|
+
locationsData,
|
|
1385
|
+
duration: `${day?.travelDuration || day?.travelDurationHours || "N/A"} hrs`,
|
|
1386
|
+
difficulty: day?.difficulty || "N/A",
|
|
1387
|
+
transportation: day?.transportation?.join(", ") || "N/A",
|
|
1388
|
+
food: Array.isArray(day?.foodService) ? day.foodService.join(", ") : day?.foodService || "N/A",
|
|
1389
|
+
accommodation: day?.accommodation || "N/A",
|
|
1390
|
+
content: day?.description || "No description available",
|
|
1391
|
+
activities: day?.activities || []
|
|
1392
|
+
}
|
|
1393
|
+
};
|
|
1394
|
+
});
|
|
1395
|
+
}, [Description?.Days]);
|
|
1396
|
+
useEffect(() => {
|
|
1397
|
+
if (scrollRef.current) {
|
|
1398
|
+
const activeTab = scrollRef.current.children[selectedDay - 1];
|
|
1399
|
+
if (activeTab) {
|
|
1400
|
+
const containerWidth = scrollRef.current.offsetWidth;
|
|
1401
|
+
const tabWidth = activeTab.offsetWidth;
|
|
1402
|
+
const tabLeft = activeTab.offsetLeft;
|
|
1403
|
+
const scrollLeft = tabLeft - containerWidth / 2 + tabWidth / 2;
|
|
1404
|
+
scrollRef.current.scrollTo({ left: scrollLeft, behavior: "smooth" });
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
}, [selectedDay]);
|
|
1408
|
+
useEffect(() => {
|
|
1409
|
+
if (!hasUserSelectedDayRef.current) return;
|
|
1410
|
+
if (!detailCardRef.current) return;
|
|
1411
|
+
const raf = window.requestAnimationFrame(() => {
|
|
1412
|
+
const stickyTabsHeight = 76;
|
|
1413
|
+
const safetyGap = 12;
|
|
1414
|
+
const elementPosition = detailCardRef.current?.getBoundingClientRect().top ?? 0;
|
|
1415
|
+
const offsetPosition = elementPosition + window.scrollY - (stickyTabsHeight + safetyGap);
|
|
1416
|
+
window.scrollTo({ top: offsetPosition, behavior: "smooth" });
|
|
1417
|
+
});
|
|
1418
|
+
return () => window.cancelAnimationFrame(raf);
|
|
1419
|
+
}, [selectedDay]);
|
|
1420
|
+
if (!dayItems.length) {
|
|
1421
|
+
return /* @__PURE__ */ jsx("p", { className: "text-secondary-normal-hover text-base mt-4", children: "No itinerary information available." });
|
|
1422
|
+
}
|
|
1423
|
+
const selectedItem = dayItems[selectedDay - 1] ?? dayItems[0];
|
|
1424
|
+
const lowestPrice = startingPrice(Description.Price ?? []);
|
|
1425
|
+
return /* @__PURE__ */ jsxs("div", { className: "mt-4 h-full w-full text-[#5B4D81]", children: [
|
|
1426
|
+
/* @__PURE__ */ jsx("div", { className: "w-full sticky top-0 z-30 mb-6 bg-white py-2 -mx-4 px-4 sm:-mx-6 sm:px-6 border-b border-[#E0E4E8]/60", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
1427
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 flex items-center gap-2 p-1.5 rounded-full overflow-hidden bg-white", children: [
|
|
1428
|
+
/* @__PURE__ */ jsx(
|
|
1429
|
+
"button",
|
|
1430
|
+
{
|
|
1431
|
+
type: "button",
|
|
1432
|
+
onClick: () => scrollRef.current?.scrollBy({ left: -151, behavior: "smooth" }),
|
|
1433
|
+
className: "flex w-8 h-8 rounded-full bg-primary-background items-center justify-center hover:bg-[#65558F15] transition-all shrink-0",
|
|
1434
|
+
children: /* @__PURE__ */ jsx(Icon, { icon: "lucide:arrow-left", className: "w-4 h-4 text-[#65558F]" })
|
|
1435
|
+
}
|
|
1436
|
+
),
|
|
1437
|
+
/* @__PURE__ */ jsx(
|
|
1438
|
+
"div",
|
|
1439
|
+
{
|
|
1440
|
+
ref: scrollRef,
|
|
1441
|
+
className: "flex items-center gap-2 overflow-x-auto hide-scrollbar scroll-smooth px-1 flex-1",
|
|
1442
|
+
children: dayItems.map((_, index) => {
|
|
1443
|
+
const dayNum = index + 1;
|
|
1444
|
+
const isActive = selectedDay === dayNum;
|
|
1445
|
+
return /* @__PURE__ */ jsxs(
|
|
1446
|
+
"button",
|
|
1447
|
+
{
|
|
1448
|
+
type: "button",
|
|
1449
|
+
onClick: () => {
|
|
1450
|
+
hasUserSelectedDayRef.current = true;
|
|
1451
|
+
setSelectedDay(dayNum);
|
|
1452
|
+
},
|
|
1453
|
+
className: `cursor-pointer rounded-full px-4 py-2.5 text-sm font-medium tracking-[-0.02em] transition-all shrink-0 ${isActive ? "bg-secondary text-white shadow-sm" : "bg-primary-background text-primary-normal hover:bg-primary-light-hover"}`,
|
|
1454
|
+
children: [
|
|
1455
|
+
"Day ",
|
|
1456
|
+
dayNum
|
|
1457
|
+
]
|
|
1458
|
+
},
|
|
1459
|
+
index
|
|
1460
|
+
);
|
|
1461
|
+
})
|
|
1462
|
+
}
|
|
1463
|
+
),
|
|
1464
|
+
/* @__PURE__ */ jsx(
|
|
1465
|
+
"button",
|
|
1466
|
+
{
|
|
1467
|
+
type: "button",
|
|
1468
|
+
onClick: () => scrollRef.current?.scrollBy({ left: 151, behavior: "smooth" }),
|
|
1469
|
+
className: "w-10 h-10 rounded-full bg-[#65558F0D] flex items-center justify-center hover:bg-[#65558F15] transition-all shrink-0",
|
|
1470
|
+
children: /* @__PURE__ */ jsx(Icon, { icon: "lucide:arrow-right", className: "w-4 h-4 text-[#65558F]" })
|
|
1471
|
+
}
|
|
1472
|
+
)
|
|
1473
|
+
] }),
|
|
1474
|
+
lowestPrice > 0 && /* @__PURE__ */ jsxs("div", { className: "hidden md:flex items-center gap-3 shrink-0 pl-2", children: [
|
|
1475
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col items-end leading-tight", children: [
|
|
1476
|
+
/* @__PURE__ */ jsx("span", { className: "text-[11px] text-secondary-normal-hover whitespace-nowrap", children: "1x Adult Price" }),
|
|
1477
|
+
/* @__PURE__ */ jsx("span", { className: "text-base font-semibold text-primary-normal whitespace-nowrap", children: formatCurrency(lowestPrice, currency) })
|
|
1478
|
+
] }),
|
|
1479
|
+
/* @__PURE__ */ jsx(
|
|
1480
|
+
"button",
|
|
1481
|
+
{
|
|
1482
|
+
type: "button",
|
|
1483
|
+
onClick: onReserveClick,
|
|
1484
|
+
disabled: !onReserveClick,
|
|
1485
|
+
className: "bg-primary-normal text-white rounded-lg px-4 py-2 font-medium text-sm hover:bg-primary-normal-hover transition-colors whitespace-nowrap disabled:opacity-60 disabled:cursor-not-allowed",
|
|
1486
|
+
children: "Reserve Now"
|
|
1487
|
+
}
|
|
1488
|
+
)
|
|
1489
|
+
] })
|
|
1490
|
+
] }) }),
|
|
1491
|
+
/* @__PURE__ */ jsx("div", { className: "w-full", children: /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 lg:grid-cols-5 gap-6 lg:gap-10 w-full h-full", children: [
|
|
1492
|
+
/* @__PURE__ */ jsx("div", { className: "col-span-1 lg:col-span-3 flex flex-col gap-7", children: /* @__PURE__ */ jsxs("div", { ref: detailCardRef, className: "self-stretch rounded-2xl bg-white border border-[#E0E4E8] p-4 sm:p-5 sm:px-7 shadow-sm gap-4 flex flex-col scroll-mt-[130px]", children: [
|
|
1493
|
+
/* @__PURE__ */ jsxs("div", { className: "self-stretch flex items-start flex-col md:flex-row md:justify-between gap-3 md:gap-5", children: [
|
|
1494
|
+
/* @__PURE__ */ jsx("h3", { className: "w-full md:w-[448px] font-medium tracking-[-0.04em] text-lg md:text-xl leading-snug", children: selectedItem.data.title }),
|
|
1495
|
+
/* @__PURE__ */ jsxs("div", { className: "rounded-2xl bg-[#65558F1A] py-1 px-3 text-center text-[10px] md:text-xs text-[#65558F] font-medium shrink-0", children: [
|
|
1496
|
+
"Duration: ",
|
|
1497
|
+
selectedItem.data.duration
|
|
1498
|
+
] })
|
|
1499
|
+
] }),
|
|
1500
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-col md:flex-row items-start md:items-center md:justify-between gap-6", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4 flex-1 min-w-0", children: [
|
|
1501
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start md:items-center flex-wrap gap-2 text-sm md:text-base text-secondary-normal-hover", children: [
|
|
1502
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 shrink-0", children: [
|
|
1503
|
+
/* @__PURE__ */ jsx(Icon, { icon: "solar:bed-outline", className: "w-4 h-4 md:w-5 md:h-5" }),
|
|
1504
|
+
/* @__PURE__ */ jsx("span", { children: selectedItem.data.accommodation || "Yes" })
|
|
1505
|
+
] }),
|
|
1506
|
+
/* @__PURE__ */ jsx("div", { className: "w-1 h-1 rounded-full bg-[#B2BBC6]" }),
|
|
1507
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 shrink-0", children: [
|
|
1508
|
+
/* @__PURE__ */ jsx(Icon, { icon: "solar:bus-outline", className: "w-4 h-4 md:w-5 md:h-5" }),
|
|
1509
|
+
/* @__PURE__ */ jsx("span", { className: "max-w-[100px] md:max-w-none truncate", children: selectedItem.data.transportation || "Car" })
|
|
1510
|
+
] }),
|
|
1511
|
+
selectedItem.data.food && selectedItem.data.food !== "N/A" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1512
|
+
/* @__PURE__ */ jsx("div", { className: "w-1 h-1 rounded-full bg-[#B2BBC6]" }),
|
|
1513
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 shrink-0", children: [
|
|
1514
|
+
/* @__PURE__ */ jsx(Icon, { icon: "solar:cup-hot-outline", className: "w-4 h-4 md:w-5 md:h-5" }),
|
|
1515
|
+
/* @__PURE__ */ jsx("span", { className: "max-w-[100px] md:max-w-none truncate", children: selectedItem.data.food })
|
|
1516
|
+
] })
|
|
1517
|
+
] })
|
|
1518
|
+
] }),
|
|
1519
|
+
/* @__PURE__ */ jsx(DifficultyProgressBar, { level: selectedItem.data.difficulty })
|
|
1520
|
+
] }) }),
|
|
1521
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4 md:gap-6 mt-2", children: [
|
|
1522
|
+
selectedItem.data.locationsData.length > 0 && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
|
|
1523
|
+
/* @__PURE__ */ jsx("h4", { className: "text-primary-normal font-medium tracking-[-0.04em] text-lg", children: "Stops in a Day" }),
|
|
1524
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-col", children: selectedItem.data.locationsData.map((stop, idx, arr) => {
|
|
1525
|
+
const total = arr.length;
|
|
1526
|
+
const label = idx === 0 ? "Start" : idx === total - 1 ? "End" : `Stop ${idx}`;
|
|
1527
|
+
const stopStyles = getStopStyles(label);
|
|
1528
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex gap-4", children: [
|
|
1529
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-col items-start min-w-[50px] pt-1 shrink-0", children: /* @__PURE__ */ jsx("span", { className: `text-sm font-medium ${stopStyles.labelClass}`, children: label }) }),
|
|
1530
|
+
/* @__PURE__ */ jsxs("div", { className: "relative flex flex-col items-center shrink-0", children: [
|
|
1531
|
+
/* @__PURE__ */ jsx("div", { className: `w-4 h-4 rounded-full border-2 z-10 ${stopStyles.dotClass}` }),
|
|
1532
|
+
idx !== total - 1 && /* @__PURE__ */ jsx("div", { className: `absolute top-4 left-1/2 -ml-[1px] w-0.5 h-full z-0 ${stopStyles.lineClass}` })
|
|
1533
|
+
] }),
|
|
1534
|
+
/* @__PURE__ */ jsx("div", { className: "text-base text-secondary-normal-hover pt-px pb-6 min-w-0 flex-1", children: stop.displayName })
|
|
1535
|
+
] }, idx);
|
|
1536
|
+
}) })
|
|
1537
|
+
] }),
|
|
1538
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3", children: [
|
|
1539
|
+
/* @__PURE__ */ jsx("h4", { className: "text-primary-normal font-medium tracking-[-0.04em] text-lg", children: "Description" }),
|
|
1540
|
+
/* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
1541
|
+
/* @__PURE__ */ jsx(
|
|
1542
|
+
"div",
|
|
1543
|
+
{
|
|
1544
|
+
className: `text-base text-secondary-normal-hover leading-relaxed ${!descriptionExpanded ? "line-clamp-3 md:line-clamp-none" : ""}`,
|
|
1545
|
+
dangerouslySetInnerHTML: {
|
|
1546
|
+
__html: (selectedItem.data.content || "").replace(/ /g, " ")
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
),
|
|
1550
|
+
(selectedItem.data.content || "").length > 150 && /* @__PURE__ */ jsx(
|
|
1551
|
+
"button",
|
|
1552
|
+
{
|
|
1553
|
+
type: "button",
|
|
1554
|
+
onClick: () => setDescriptionExpanded((v) => !v),
|
|
1555
|
+
className: "text-secondary text-sm font-medium mt-2 hover:underline md:hidden",
|
|
1556
|
+
children: descriptionExpanded ? "See less" : "See more"
|
|
1557
|
+
}
|
|
1558
|
+
)
|
|
1559
|
+
] })
|
|
1560
|
+
] }),
|
|
1561
|
+
selectedItem.data.activities?.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-4 mt-2", children: selectedItem.data.activities.map((activity, i) => /* @__PURE__ */ jsx(
|
|
1562
|
+
"div",
|
|
1563
|
+
{
|
|
1564
|
+
className: "bg-[#F0EEF4CC] rounded-2xl py-1 px-3 text-xs text-[#65558F] font-medium",
|
|
1565
|
+
children: activity.activityName
|
|
1566
|
+
},
|
|
1567
|
+
i
|
|
1568
|
+
)) })
|
|
1569
|
+
] })
|
|
1570
|
+
] }) }),
|
|
1571
|
+
/* @__PURE__ */ jsx("div", { className: "col-span-1 lg:col-span-2 flex-shrink-0 rounded-lg lg:flex items-center justify-center min-h-[500px] hidden relative z-0", children: /* @__PURE__ */ jsx(
|
|
1572
|
+
PackageMap,
|
|
1573
|
+
{
|
|
1574
|
+
locations: selectedItem.data.locationsData,
|
|
1575
|
+
dayNumber: selectedDay,
|
|
1576
|
+
routes: routesByDay.get(selectedDay) ?? []
|
|
1577
|
+
}
|
|
1578
|
+
) })
|
|
1579
|
+
] }) })
|
|
1580
|
+
] });
|
|
1581
|
+
};
|
|
1582
|
+
var Itinerary_default = Itinerary;
|
|
1583
|
+
|
|
1584
|
+
// src/components/PackageDetail/_checklistUtils.ts
|
|
1585
|
+
var normalizeChecklist = (data) => {
|
|
1586
|
+
if (!data) return [];
|
|
1587
|
+
if (Array.isArray(data)) {
|
|
1588
|
+
return data.map((group) => ({
|
|
1589
|
+
category: typeof group?.category === "string" ? group.category.trim() : "",
|
|
1590
|
+
items: Array.isArray(group?.items) ? group.items.filter(
|
|
1591
|
+
(item) => typeof item === "string" && item.trim() !== ""
|
|
1592
|
+
) : []
|
|
1593
|
+
})).filter((group) => group.category !== "" && group.items.length > 0);
|
|
1594
|
+
}
|
|
1595
|
+
return Object.entries(data).map(([category, items]) => ({
|
|
1596
|
+
category: category.trim(),
|
|
1597
|
+
items: Array.isArray(items) ? items.filter(
|
|
1598
|
+
(item) => typeof item === "string" && item.trim() !== ""
|
|
1599
|
+
) : []
|
|
1600
|
+
})).filter((group) => group.category !== "" && group.items.length > 0);
|
|
1601
|
+
};
|
|
1602
|
+
var getChecklistPreviewCount = (isMobile) => isMobile ? 1 : 2;
|
|
1603
|
+
var iconMapping = {
|
|
1604
|
+
clothing: "mdi:tshirt-crew-outline",
|
|
1605
|
+
clothes: "mdi:tshirt-crew-outline",
|
|
1606
|
+
footwear: "mdi:shoe-sneaker",
|
|
1607
|
+
shoes: "mdi:shoe-sneaker",
|
|
1608
|
+
"toiletries and personal items": "lucide:spray-can",
|
|
1609
|
+
"gadgets and extras": "lucide:smartphone",
|
|
1610
|
+
"optional but useful": "lucide:badge-help",
|
|
1611
|
+
"what not to carry": "material-symbols:do-not-disturb-on-outline-rounded",
|
|
1612
|
+
general: "material-symbols:check-circle-outline-rounded"
|
|
1613
|
+
};
|
|
1614
|
+
var getIconForCategory = (category) => {
|
|
1615
|
+
const normalizedCategory = category.trim().toLowerCase();
|
|
1616
|
+
return iconMapping[normalizedCategory] || "material-symbols:check-circle-outline-rounded";
|
|
1617
|
+
};
|
|
1618
|
+
var getDisplayCategory = (category) => {
|
|
1619
|
+
const normalizedCategory = category.trim().toLowerCase();
|
|
1620
|
+
if (normalizedCategory === "clothing" || normalizedCategory === "clothes") {
|
|
1621
|
+
return "Clothing and Backpack";
|
|
1622
|
+
}
|
|
1623
|
+
return category;
|
|
1624
|
+
};
|
|
1625
|
+
var PackageExcludedAndToPack = ({
|
|
1626
|
+
thingsExcluded,
|
|
1627
|
+
thingsToPack
|
|
1628
|
+
}) => {
|
|
1629
|
+
const excludedGroups = normalizeChecklist(thingsExcluded);
|
|
1630
|
+
const toPackGroups = normalizeChecklist(thingsToPack);
|
|
1631
|
+
const [isMobile, setIsMobile] = useState(false);
|
|
1632
|
+
const [expandedGroups, setExpandedGroups] = useState({});
|
|
1633
|
+
const hasExcluded = excludedGroups.length > 0;
|
|
1634
|
+
const hasToPack = toPackGroups.length > 0;
|
|
1635
|
+
useEffect(() => {
|
|
1636
|
+
const checkScreenSize = () => setIsMobile(window.innerWidth < 768);
|
|
1637
|
+
checkScreenSize();
|
|
1638
|
+
window.addEventListener("resize", checkScreenSize);
|
|
1639
|
+
return () => window.removeEventListener("resize", checkScreenSize);
|
|
1640
|
+
}, []);
|
|
1641
|
+
if (!hasExcluded && !hasToPack) return null;
|
|
1642
|
+
const toggleGroup = (groupKey) => setExpandedGroups((current) => ({ ...current, [groupKey]: !current[groupKey] }));
|
|
1643
|
+
const previewCount = getChecklistPreviewCount(isMobile);
|
|
1644
|
+
const renderGroups = (data, sectionKey) => /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-x-4 md:gap-x-6 lg:gap-x-12 gap-y-4 md:gap-y-6 lg:gap-y-10 mt-2 md:mt-3 lg:mt-4", children: data.map((group, groupIndex) => /* @__PURE__ */ jsxs(
|
|
1645
|
+
"div",
|
|
1646
|
+
{
|
|
1647
|
+
className: "flex flex-col gap-2 md:gap-2.5 lg:gap-3",
|
|
1648
|
+
children: [
|
|
1649
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 md:gap-2.5 lg:gap-3", children: [
|
|
1650
|
+
/* @__PURE__ */ jsx("div", { className: "p-2 rounded-lg text-primary-normal", children: /* @__PURE__ */ jsx(Icon, { icon: getIconForCategory(group.category), className: "w-6 h-6" }) }),
|
|
1651
|
+
/* @__PURE__ */ jsx("h3", { className: "text-primary-normal text-lg font-semibold tracking-tight", children: getDisplayCategory(group.category) })
|
|
1652
|
+
] }),
|
|
1653
|
+
/* @__PURE__ */ jsxs("ul", { className: "flex flex-col gap-1.5 md:gap-2 pl-11", children: [
|
|
1654
|
+
group.items.slice(
|
|
1655
|
+
0,
|
|
1656
|
+
!expandedGroups[`${sectionKey}-${group.category}-${groupIndex}`] ? previewCount : group.items.length
|
|
1657
|
+
).map((item, index) => /* @__PURE__ */ jsxs(
|
|
1658
|
+
"li",
|
|
1659
|
+
{
|
|
1660
|
+
className: "flex items-start gap-2 text-secondary-normal-hover text-base leading-relaxed relative",
|
|
1661
|
+
children: [
|
|
1662
|
+
/* @__PURE__ */ jsx("span", { className: "absolute -left-4 top-2.5 w-1 h-1 bg-secondary-normal-hover rounded-full" }),
|
|
1663
|
+
item
|
|
1664
|
+
]
|
|
1665
|
+
},
|
|
1666
|
+
`${group.category}-${index}`
|
|
1667
|
+
)),
|
|
1668
|
+
group.items.length > previewCount && /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
|
|
1669
|
+
"button",
|
|
1670
|
+
{
|
|
1671
|
+
type: "button",
|
|
1672
|
+
onClick: () => toggleGroup(`${sectionKey}-${group.category}-${groupIndex}`),
|
|
1673
|
+
"aria-expanded": Boolean(
|
|
1674
|
+
expandedGroups[`${sectionKey}-${group.category}-${groupIndex}`]
|
|
1675
|
+
),
|
|
1676
|
+
className: "text-secondary text-sm font-medium hover:underline",
|
|
1677
|
+
children: expandedGroups[`${sectionKey}-${group.category}-${groupIndex}`] ? "See less" : "See more"
|
|
1678
|
+
}
|
|
1679
|
+
) })
|
|
1680
|
+
] })
|
|
1681
|
+
]
|
|
1682
|
+
},
|
|
1683
|
+
`${group.category}-${groupIndex}`
|
|
1684
|
+
)) });
|
|
1685
|
+
return /* @__PURE__ */ jsxs("div", { className: "w-full py-2", children: [
|
|
1686
|
+
hasExcluded && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 md:gap-3 lg:gap-4", children: [
|
|
1687
|
+
/* @__PURE__ */ jsx("h2", { className: "text-primary-normal text-xl md:text-2xl font-medium tracking-[-0.04em]", children: "Things Excluded" }),
|
|
1688
|
+
renderGroups(excludedGroups, "excluded")
|
|
1689
|
+
] }),
|
|
1690
|
+
hasToPack && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 md:gap-3 lg:gap-4 mt-6 md:mt-8 lg:mt-10", children: [
|
|
1691
|
+
/* @__PURE__ */ jsx("h2", { className: "text-primary-normal text-xl md:text-2xl font-medium tracking-[-0.04em]", children: "Things To Pack" }),
|
|
1692
|
+
renderGroups(toPackGroups, "to-pack")
|
|
1693
|
+
] })
|
|
1694
|
+
] });
|
|
1695
|
+
};
|
|
1696
|
+
var PackageExcludedAndToPack_default = PackageExcludedAndToPack;
|
|
1697
|
+
var iconMapping2 = {
|
|
1698
|
+
"Support Team": "lucide:headset",
|
|
1699
|
+
Permits: "lucide:file-text",
|
|
1700
|
+
"Pickup and Drop Service": "lucide:plane",
|
|
1701
|
+
"Airport Pickup & Drop Service": "lucide:plane",
|
|
1702
|
+
"Pickup and Drop": "lucide:plane",
|
|
1703
|
+
"Travel Insurance": "lucide:shield-check",
|
|
1704
|
+
Accommodation: "lucide:hotel",
|
|
1705
|
+
Transport: "lucide:bus",
|
|
1706
|
+
Food: "lucide:utensils-crossed",
|
|
1707
|
+
"Additional Perks": "lucide:plus-circle",
|
|
1708
|
+
"First Aid Services": "material-symbols:medical-services-outline"
|
|
1709
|
+
};
|
|
1710
|
+
var PackageIncluded = ({ thingsToDo }) => {
|
|
1711
|
+
const [isMobile, setIsMobile] = useState(false);
|
|
1712
|
+
const [expandedGroups, setExpandedGroups] = useState({});
|
|
1713
|
+
const normalizedThingsToDo = normalizeChecklist(thingsToDo);
|
|
1714
|
+
useEffect(() => {
|
|
1715
|
+
const checkScreenSize = () => setIsMobile(window.innerWidth < 768);
|
|
1716
|
+
checkScreenSize();
|
|
1717
|
+
window.addEventListener("resize", checkScreenSize);
|
|
1718
|
+
return () => window.removeEventListener("resize", checkScreenSize);
|
|
1719
|
+
}, []);
|
|
1720
|
+
if (normalizedThingsToDo.length === 0) return null;
|
|
1721
|
+
const toggleGroup = (groupKey) => setExpandedGroups((current) => ({ ...current, [groupKey]: !current[groupKey] }));
|
|
1722
|
+
const previewCount = getChecklistPreviewCount(isMobile);
|
|
1723
|
+
return /* @__PURE__ */ jsx("div", { className: "w-full py-2", children: /* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-x-4 md:gap-x-6 lg:gap-x-12 gap-y-4 md:gap-y-6 lg:gap-y-10", children: normalizedThingsToDo.map((category, index) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 md:gap-2.5 lg:gap-3", children: [
|
|
1724
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 md:gap-2.5 lg:gap-3", children: [
|
|
1725
|
+
/* @__PURE__ */ jsx("div", { className: "p-2 rounded-lg text-primary-normal", children: /* @__PURE__ */ jsx(
|
|
1726
|
+
Icon,
|
|
1727
|
+
{
|
|
1728
|
+
icon: iconMapping2[category.category] || "material-symbols:check-circle-outline-rounded",
|
|
1729
|
+
className: "w-6 h-6"
|
|
1730
|
+
}
|
|
1731
|
+
) }),
|
|
1732
|
+
/* @__PURE__ */ jsx("h3", { className: "text-primary-normal text-lg font-semibold tracking-tight", children: category.category })
|
|
1733
|
+
] }),
|
|
1734
|
+
/* @__PURE__ */ jsxs("ul", { className: "flex flex-col gap-1.5 md:gap-2 pl-11", children: [
|
|
1735
|
+
category.items.slice(
|
|
1736
|
+
0,
|
|
1737
|
+
!expandedGroups[`${category.category}-${index}`] ? previewCount : category.items.length
|
|
1738
|
+
).map((item, itemIndex) => /* @__PURE__ */ jsxs(
|
|
1739
|
+
"li",
|
|
1740
|
+
{
|
|
1741
|
+
className: "flex items-start gap-2 text-secondary-normal-hover text-base leading-relaxed relative",
|
|
1742
|
+
children: [
|
|
1743
|
+
/* @__PURE__ */ jsx("span", { className: "absolute -left-4 top-2.5 w-1 h-1 bg-secondary-normal-hover rounded-full" }),
|
|
1744
|
+
item
|
|
1745
|
+
]
|
|
1746
|
+
},
|
|
1747
|
+
itemIndex
|
|
1748
|
+
)),
|
|
1749
|
+
category.items.length > previewCount && /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
|
|
1750
|
+
"button",
|
|
1751
|
+
{
|
|
1752
|
+
type: "button",
|
|
1753
|
+
onClick: () => toggleGroup(`${category.category}-${index}`),
|
|
1754
|
+
"aria-expanded": Boolean(expandedGroups[`${category.category}-${index}`]),
|
|
1755
|
+
className: "text-secondary text-sm font-medium hover:underline",
|
|
1756
|
+
children: expandedGroups[`${category.category}-${index}`] ? "See less" : "See more"
|
|
1757
|
+
}
|
|
1758
|
+
) })
|
|
1759
|
+
] })
|
|
1760
|
+
] }, index)) }) });
|
|
1761
|
+
};
|
|
1762
|
+
var PackageIncluded_default = PackageIncluded;
|
|
1763
|
+
var PackageOverview = ({
|
|
1764
|
+
description,
|
|
1765
|
+
isMobile,
|
|
1766
|
+
topData
|
|
1767
|
+
}) => {
|
|
1768
|
+
const [expanded, setExpanded] = useState(false);
|
|
1769
|
+
const isLong = (description || "").length > 400;
|
|
1770
|
+
return /* @__PURE__ */ jsxs("div", { className: "w-full pb-4", children: [
|
|
1771
|
+
/* @__PURE__ */ jsx("h2", { className: "text-primary-normal font-medium text-xl md:text-2xl tracking-[-0.04em]", children: "Overview" }),
|
|
1772
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-2 text-secondary-normal-hover lg:text-base text-sm font-light leading-relaxed", children: [
|
|
1773
|
+
/* @__PURE__ */ jsx(
|
|
1774
|
+
"div",
|
|
1775
|
+
{
|
|
1776
|
+
className: `w-full break-words whitespace-pre-wrap overflow-hidden transition-[max-height] duration-300
|
|
1777
|
+
[&_ul]:list-disc [&_ul]:pl-5 [&_li]:mb-1
|
|
1778
|
+
[&_ol]:list-decimal [&_ol]:pl-5 [&_ol]:mb-1
|
|
1779
|
+
[&_p]:mb-2 [&_img]:max-w-full [&_img]:h-auto
|
|
1780
|
+
[&_table]:w-full [&_table]:overflow-x-auto
|
|
1781
|
+
[&_strong]:font-bold [&_underline]:underline
|
|
1782
|
+
${isMobile && !expanded ? "max-h-40" : "max-h-none"}`,
|
|
1783
|
+
dangerouslySetInnerHTML: {
|
|
1784
|
+
__html: (description || "").replace(/ /g, " ") || "No description available."
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
),
|
|
1788
|
+
isLong && /* @__PURE__ */ jsx(
|
|
1789
|
+
"button",
|
|
1790
|
+
{
|
|
1791
|
+
type: "button",
|
|
1792
|
+
onClick: () => setExpanded((p) => !p),
|
|
1793
|
+
"aria-expanded": expanded,
|
|
1794
|
+
className: "text-secondary text-sm font-medium mt-1 hover:underline md:hidden",
|
|
1795
|
+
children: expanded ? "See less" : "See more"
|
|
1796
|
+
}
|
|
1797
|
+
)
|
|
1798
|
+
] }),
|
|
1799
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-5 mb-4 pt-5 border-t border-gray-100", children: [
|
|
1800
|
+
/* @__PURE__ */ jsx("h2", { className: "text-[#65558F] font-medium text-xl tracking-[-0.04em] mb-3", children: "Key Facts" }),
|
|
1801
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
1802
|
+
/* @__PURE__ */ jsx(
|
|
1803
|
+
KeyFact,
|
|
1804
|
+
{
|
|
1805
|
+
icon: "solar:suitcase-tag-outline",
|
|
1806
|
+
label: "Trip Style",
|
|
1807
|
+
value: topData?.PackageType || "Sightseeing"
|
|
1808
|
+
}
|
|
1809
|
+
),
|
|
1810
|
+
/* @__PURE__ */ jsx(
|
|
1811
|
+
KeyFact,
|
|
1812
|
+
{
|
|
1813
|
+
icon: "icon-park-outline:level",
|
|
1814
|
+
label: "Difficulty",
|
|
1815
|
+
value: topData?.Difficulty || "Easy"
|
|
1816
|
+
}
|
|
1817
|
+
),
|
|
1818
|
+
/* @__PURE__ */ jsx(
|
|
1819
|
+
KeyFact,
|
|
1820
|
+
{
|
|
1821
|
+
icon: "iconoir:calendar",
|
|
1822
|
+
label: "Number Of Days",
|
|
1823
|
+
value: topData?.TotalDays ? `${topData.TotalDays} Days & ${Math.max(0, topData.TotalDays - 1)} Nights` : "Duration"
|
|
1824
|
+
}
|
|
1825
|
+
),
|
|
1826
|
+
/* @__PURE__ */ jsx(
|
|
1827
|
+
KeyFact,
|
|
1828
|
+
{
|
|
1829
|
+
icon: "iconoir:packages",
|
|
1830
|
+
label: "Package Category",
|
|
1831
|
+
value: topData?.PackageCategory || "Luxury"
|
|
1832
|
+
}
|
|
1833
|
+
)
|
|
1834
|
+
] })
|
|
1835
|
+
] })
|
|
1836
|
+
] });
|
|
1837
|
+
};
|
|
1838
|
+
var KeyFact = ({
|
|
1839
|
+
icon,
|
|
1840
|
+
label,
|
|
1841
|
+
value
|
|
1842
|
+
}) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [
|
|
1843
|
+
/* @__PURE__ */ jsx(Icon, { icon, className: "w-6 h-6 text-[#47586E] flex-shrink-0" }),
|
|
1844
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 grid grid-cols-[110px_1fr] sm:grid-cols-[200px_1fr] gap-4 items-center", children: [
|
|
1845
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm sm:text-base text-[#47586E]", children: label }),
|
|
1846
|
+
/* @__PURE__ */ jsx("span", { className: "text-base text-[#65558F] font-medium", children: value })
|
|
1847
|
+
] })
|
|
1848
|
+
] });
|
|
1849
|
+
var PackageOverview_default = PackageOverview;
|
|
1850
|
+
var DescriptionSection = ({
|
|
1851
|
+
DescriptionData,
|
|
1852
|
+
topData,
|
|
1853
|
+
currency,
|
|
1854
|
+
onReserve,
|
|
1855
|
+
pkg,
|
|
1856
|
+
routes
|
|
1857
|
+
}) => {
|
|
1858
|
+
const [isMobile, setIsMobile] = useState(false);
|
|
1859
|
+
const bookingRef = useRef(null);
|
|
1860
|
+
useEffect(() => {
|
|
1861
|
+
const checkScreenSize = () => setIsMobile(window.innerWidth < 768);
|
|
1862
|
+
checkScreenSize();
|
|
1863
|
+
window.addEventListener("resize", checkScreenSize);
|
|
1864
|
+
return () => window.removeEventListener("resize", checkScreenSize);
|
|
1865
|
+
}, []);
|
|
1866
|
+
const scrollToBooking = () => {
|
|
1867
|
+
if (!bookingRef.current) return;
|
|
1868
|
+
const rect = bookingRef.current.getBoundingClientRect();
|
|
1869
|
+
const offset = 90;
|
|
1870
|
+
window.scrollTo({
|
|
1871
|
+
top: window.scrollY + rect.top - offset,
|
|
1872
|
+
behavior: "smooth"
|
|
1873
|
+
});
|
|
1874
|
+
};
|
|
1875
|
+
return /* @__PURE__ */ jsx("div", { className: "w-full flex flex-col items-center", children: /* @__PURE__ */ jsxs("div", { className: "w-full relative border-gray-200 flex flex-col gap-8", children: [
|
|
1876
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 lg:grid-cols-12 gap-6 w-full justify-between", children: [
|
|
1877
|
+
/* @__PURE__ */ jsx("div", { className: "lg:col-span-7 w-full flex flex-col gap-3 border-b-2 pb-6", children: /* @__PURE__ */ jsx(
|
|
1878
|
+
PackageOverview_default,
|
|
1879
|
+
{
|
|
1880
|
+
description: DescriptionData.Description,
|
|
1881
|
+
isMobile,
|
|
1882
|
+
topData
|
|
1883
|
+
}
|
|
1884
|
+
) }),
|
|
1885
|
+
/* @__PURE__ */ jsxs(
|
|
1886
|
+
"div",
|
|
1887
|
+
{
|
|
1888
|
+
ref: bookingRef,
|
|
1889
|
+
className: "lg:col-span-5 flex justify-start items-end flex-col gap-2 scroll-mt-[100px]",
|
|
1890
|
+
children: [
|
|
1891
|
+
/* @__PURE__ */ jsx("p", { className: "md:hidden text-primary-normal text-start w-full flex justify-start text-xl md:text-2xl font-medium tracking-[-0.04em]", children: "Book Now" }),
|
|
1892
|
+
/* @__PURE__ */ jsx("div", { className: "w-full", children: /* @__PURE__ */ jsx(
|
|
1893
|
+
BookingCardLite_default,
|
|
1894
|
+
{
|
|
1895
|
+
DescriptionData,
|
|
1896
|
+
currency,
|
|
1897
|
+
onReserve: onReserve ? () => onReserve(pkg) : void 0
|
|
1898
|
+
}
|
|
1899
|
+
) })
|
|
1900
|
+
]
|
|
1901
|
+
}
|
|
1902
|
+
)
|
|
1903
|
+
] }),
|
|
1904
|
+
/* @__PURE__ */ jsxs("div", { className: "relative pb-7 border-b-2 border-gray-200", children: [
|
|
1905
|
+
/* @__PURE__ */ jsx("h2", { className: "text-primary-normal text-xl md:text-2xl font-medium tracking-[-0.04em]", children: "Itinerary" }),
|
|
1906
|
+
/* @__PURE__ */ jsx(
|
|
1907
|
+
Itinerary_default,
|
|
1908
|
+
{
|
|
1909
|
+
Description: DescriptionData,
|
|
1910
|
+
routes,
|
|
1911
|
+
currency,
|
|
1912
|
+
onReserveClick: scrollToBooking
|
|
1913
|
+
}
|
|
1914
|
+
)
|
|
1915
|
+
] }),
|
|
1916
|
+
DescriptionData?.thingsToDo && DescriptionData.thingsToDo.length > 0 && /* @__PURE__ */ jsxs("div", { className: "relative pt-5 pb-7 border-b-2 border-gray-200", children: [
|
|
1917
|
+
/* @__PURE__ */ jsx("h2", { className: "text-primary-normal text-xl md:text-2xl font-medium tracking-[-0.04em] mb-6", children: "Things Included" }),
|
|
1918
|
+
/* @__PURE__ */ jsx(PackageIncluded_default, { thingsToDo: DescriptionData.thingsToDo })
|
|
1919
|
+
] }),
|
|
1920
|
+
(DescriptionData?.thingsExcluded && DescriptionData.thingsExcluded.length > 0 || DescriptionData?.thingsToPack && DescriptionData.thingsToPack.length > 0) && /* @__PURE__ */ jsx("div", { className: "relative pb-7", children: /* @__PURE__ */ jsx(
|
|
1921
|
+
PackageExcludedAndToPack_default,
|
|
1922
|
+
{
|
|
1923
|
+
thingsExcluded: DescriptionData?.thingsExcluded || [],
|
|
1924
|
+
thingsToPack: DescriptionData?.thingsToPack || []
|
|
1925
|
+
}
|
|
1926
|
+
) })
|
|
1927
|
+
] }) });
|
|
1928
|
+
};
|
|
1929
|
+
var DescriptionSection_default = DescriptionSection;
|
|
1930
|
+
var HostAbout = ({
|
|
1931
|
+
hostName,
|
|
1932
|
+
description,
|
|
1933
|
+
className = ""
|
|
1934
|
+
}) => /* @__PURE__ */ jsxs("div", { className: `flex flex-col gap-4 ${className}`, children: [
|
|
1935
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-col gap-1", children: /* @__PURE__ */ jsxs("h3", { className: "text-xl md:text-2xl font-medium tracking-[-0.04em] text-primary-normal", children: [
|
|
1936
|
+
"About ",
|
|
1937
|
+
hostName
|
|
1938
|
+
] }) }),
|
|
1939
|
+
/* @__PURE__ */ jsx("p", { className: "text-secondary-normal-hover text-base md:text-lg leading-relaxed line-clamp-4", children: description || "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Praesentium, deleniti! Obcaecati atque, ex itaque voluptatem vel perferendis." })
|
|
1940
|
+
] });
|
|
1941
|
+
var HostAbout_default = HostAbout;
|
|
1942
|
+
var getUserInitials = (name) => name?.charAt(0)?.toUpperCase() || "H";
|
|
1943
|
+
var getMonthsHosting = (createdDate) => {
|
|
1944
|
+
if (!createdDate) return 1;
|
|
1945
|
+
const now = /* @__PURE__ */ new Date();
|
|
1946
|
+
const created = new Date(createdDate);
|
|
1947
|
+
const monthsDiff = Math.floor(
|
|
1948
|
+
(now.getTime() - created.getTime()) / (1e3 * 60 * 60 * 24 * 30)
|
|
1949
|
+
);
|
|
1950
|
+
return Math.max(1, monthsDiff);
|
|
1951
|
+
};
|
|
1952
|
+
var HostCard = ({ hostData, className = "" }) => {
|
|
1953
|
+
const logoUrl = pickImageUrl(hostData?.logo);
|
|
1954
|
+
return /* @__PURE__ */ jsxs(
|
|
1955
|
+
"div",
|
|
1956
|
+
{
|
|
1957
|
+
className: `w-full sm:min-w-[320px] rounded-2xl border border-[#E0E4E8] bg-white flex flex-col gap-6 p-6 lg:p-8 shadow-md ${className}`,
|
|
1958
|
+
children: [
|
|
1959
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [
|
|
1960
|
+
/* @__PURE__ */ jsx("div", { className: "relative rounded-full h-20 w-20 bg-gray-200 flex items-center justify-center text-xl text-primary-normal overflow-hidden", children: logoUrl ? /* @__PURE__ */ jsx(
|
|
1961
|
+
"img",
|
|
1962
|
+
{
|
|
1963
|
+
src: logoUrl,
|
|
1964
|
+
alt: `${hostData?.username}'s logo`,
|
|
1965
|
+
className: "object-cover w-full h-full"
|
|
1966
|
+
}
|
|
1967
|
+
) : /* @__PURE__ */ jsx("span", { children: getUserInitials(hostData?.username) }) }),
|
|
1968
|
+
/* @__PURE__ */ jsx("span", { className: "text-[18px] tracking-[-0.02em] font-medium text-primary-normal whitespace-normal break-words", children: hostData?.username })
|
|
1969
|
+
] }),
|
|
1970
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-between text-center mt-2", children: [
|
|
1971
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1972
|
+
/* @__PURE__ */ jsx("p", { className: "text-base md:text-lg text-primary-normal tracking-[-0.02em] font-medium", children: hostData?.review ?? 0 }),
|
|
1973
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm md:text-base text-secondary-normal-hover tracking-[-0.02em]", children: "Reviews" })
|
|
1974
|
+
] }),
|
|
1975
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1976
|
+
/* @__PURE__ */ jsx("p", { className: "text-base md:text-lg text-primary-normal tracking-[-0.02em] font-medium", children: (hostData?.rating ?? 0).toFixed(2) }),
|
|
1977
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm md:text-base text-secondary-normal-hover tracking-[-0.02em]", children: "Rating" })
|
|
1978
|
+
] }),
|
|
1979
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
1980
|
+
/* @__PURE__ */ jsxs("p", { className: "text-base md:text-lg text-primary-normal tracking-[-0.02em] font-medium", children: [
|
|
1981
|
+
getMonthsHosting(hostData?.agencyCreatedDate),
|
|
1982
|
+
" mth"
|
|
1983
|
+
] }),
|
|
1984
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm md:text-base text-secondary-normal-hover tracking-[-0.02em]", children: "Hosting" })
|
|
1985
|
+
] })
|
|
1986
|
+
] })
|
|
1987
|
+
]
|
|
1988
|
+
}
|
|
1989
|
+
);
|
|
1990
|
+
};
|
|
1991
|
+
var HostCard_default = HostCard;
|
|
1992
|
+
var GalleryImg = ({
|
|
1993
|
+
src,
|
|
1994
|
+
alt,
|
|
1995
|
+
onClick,
|
|
1996
|
+
className
|
|
1997
|
+
}) => src ? /* @__PURE__ */ jsx(
|
|
1998
|
+
"img",
|
|
1999
|
+
{
|
|
2000
|
+
src,
|
|
2001
|
+
alt,
|
|
2002
|
+
onClick,
|
|
2003
|
+
className,
|
|
2004
|
+
loading: "lazy"
|
|
2005
|
+
}
|
|
2006
|
+
) : /* @__PURE__ */ jsx(
|
|
2007
|
+
"div",
|
|
2008
|
+
{
|
|
2009
|
+
className: `${className ?? ""} bg-[#F0EEF4] flex items-center justify-center text-primary-normal/40 text-sm`,
|
|
2010
|
+
children: "No image"
|
|
2011
|
+
}
|
|
2012
|
+
);
|
|
2013
|
+
var GalleryCell = ({
|
|
2014
|
+
src,
|
|
2015
|
+
alt,
|
|
2016
|
+
onClick,
|
|
2017
|
+
colSpan = ""
|
|
2018
|
+
}) => /* @__PURE__ */ jsx(
|
|
2019
|
+
"div",
|
|
2020
|
+
{
|
|
2021
|
+
onClick,
|
|
2022
|
+
className: `relative h-full w-full overflow-hidden rounded-lg ${colSpan}`.trim(),
|
|
2023
|
+
children: /* @__PURE__ */ jsx(
|
|
2024
|
+
GalleryImg,
|
|
2025
|
+
{
|
|
2026
|
+
src,
|
|
2027
|
+
alt,
|
|
2028
|
+
className: "absolute inset-0 w-full h-full object-cover cursor-pointer transition duration-200"
|
|
2029
|
+
}
|
|
2030
|
+
)
|
|
2031
|
+
}
|
|
2032
|
+
);
|
|
2033
|
+
var GallerySection = ({ media, packageTitle }) => {
|
|
2034
|
+
const [overlayIndex, setOverlayIndex] = useState(null);
|
|
2035
|
+
const images = media.map((m) => m.url);
|
|
2036
|
+
const imageAlts = media.map((m) => m.alt || packageTitle);
|
|
2037
|
+
const open = (i) => setOverlayIndex(i);
|
|
2038
|
+
const close = () => setOverlayIndex(null);
|
|
2039
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2040
|
+
/* @__PURE__ */ jsxs("div", { className: "md:grid lg:grid md:grid-cols-5 grid-cols-1 gap-2 mt-4 h-[546px] cursor-pointer relative hidden overflow-hidden", children: [
|
|
2041
|
+
/* @__PURE__ */ jsxs(
|
|
2042
|
+
"div",
|
|
2043
|
+
{
|
|
2044
|
+
onClick: () => open(0),
|
|
2045
|
+
className: "px-2 py-1 bg-white cursor-pointer flex justify-center items-center text-primary-normal absolute bottom-2 right-2 rounded-lg text-sm md:text-base lg:text-lg z-10",
|
|
2046
|
+
children: [
|
|
2047
|
+
/* @__PURE__ */ jsx(
|
|
2048
|
+
Icon,
|
|
2049
|
+
{
|
|
2050
|
+
icon: "material-symbols-light:space-dashboard-outline",
|
|
2051
|
+
className: "text-2xl text-gray-500"
|
|
2052
|
+
}
|
|
2053
|
+
),
|
|
2054
|
+
" ",
|
|
2055
|
+
"See all photos"
|
|
2056
|
+
]
|
|
2057
|
+
}
|
|
2058
|
+
),
|
|
2059
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-12 col-span-3 gap-4 h-[546px]", children: [
|
|
2060
|
+
/* @__PURE__ */ jsx(GalleryCell, { src: images[0], alt: imageAlts[0] || packageTitle, onClick: () => open(0), colSpan: "col-span-6" }),
|
|
2061
|
+
/* @__PURE__ */ jsx(GalleryCell, { src: images[1], alt: imageAlts[1] || packageTitle, onClick: () => open(1), colSpan: "col-span-6" })
|
|
2062
|
+
] }),
|
|
2063
|
+
/* @__PURE__ */ jsxs("div", { className: "col-span-2 h-[546px] lg:grid md:grid hidden grid-rows-2 gap-2", children: [
|
|
2064
|
+
/* @__PURE__ */ jsx(GalleryCell, { src: images[2], alt: imageAlts[2] || packageTitle, onClick: () => open(2) }),
|
|
2065
|
+
/* @__PURE__ */ jsxs("div", { className: "hidden lg:grid md:grid grid-cols-2 gap-2 h-full", children: [
|
|
2066
|
+
/* @__PURE__ */ jsx(GalleryCell, { src: images[3], alt: imageAlts[3] || packageTitle, onClick: () => open(3) }),
|
|
2067
|
+
/* @__PURE__ */ jsxs("div", { className: "relative hidden lg:grid md:grid grid-rows-2 gap-2 h-full", children: [
|
|
2068
|
+
/* @__PURE__ */ jsx(GalleryCell, { src: images[4], alt: imageAlts[4] || packageTitle, onClick: () => open(4) }),
|
|
2069
|
+
/* @__PURE__ */ jsx(GalleryCell, { src: images[5], alt: imageAlts[5] || packageTitle, onClick: () => open(5) })
|
|
2070
|
+
] })
|
|
2071
|
+
] })
|
|
2072
|
+
] })
|
|
2073
|
+
] }),
|
|
2074
|
+
/* @__PURE__ */ jsx("div", { className: "grid cursor-pointer gap-3 pt-2 md:pt-5 lg:hidden md:hidden", children: /* @__PURE__ */ jsxs("div", { className: "col-span-2 relative h-[320px] md:h-[350px]", children: [
|
|
2075
|
+
/* @__PURE__ */ jsx(
|
|
2076
|
+
GalleryImg,
|
|
2077
|
+
{
|
|
2078
|
+
src: images[0],
|
|
2079
|
+
alt: imageAlts[0] || packageTitle,
|
|
2080
|
+
className: "w-full h-full rounded-lg object-cover",
|
|
2081
|
+
onClick: () => open(0)
|
|
2082
|
+
}
|
|
2083
|
+
),
|
|
2084
|
+
/* @__PURE__ */ jsx(
|
|
2085
|
+
"button",
|
|
2086
|
+
{
|
|
2087
|
+
className: "absolute bottom-2 right-2 bg-white text-primary-normal px-3 py-2 rounded-lg z-10 text-sm md:text-base font-medium",
|
|
2088
|
+
onClick: () => open(0),
|
|
2089
|
+
children: "See All Photos"
|
|
2090
|
+
}
|
|
2091
|
+
)
|
|
2092
|
+
] }) }),
|
|
2093
|
+
overlayIndex !== null && /* @__PURE__ */ jsx(
|
|
2094
|
+
LightboxOverlay,
|
|
2095
|
+
{
|
|
2096
|
+
images,
|
|
2097
|
+
alts: imageAlts,
|
|
2098
|
+
startIndex: overlayIndex,
|
|
2099
|
+
onClose: close
|
|
2100
|
+
}
|
|
2101
|
+
)
|
|
2102
|
+
] });
|
|
2103
|
+
};
|
|
2104
|
+
function LightboxOverlay({
|
|
2105
|
+
images,
|
|
2106
|
+
alts,
|
|
2107
|
+
startIndex,
|
|
2108
|
+
onClose
|
|
2109
|
+
}) {
|
|
2110
|
+
const [i, setI] = useState(startIndex);
|
|
2111
|
+
const prev = () => setI((v) => (v - 1 + images.length) % images.length);
|
|
2112
|
+
const next = () => setI((v) => (v + 1) % images.length);
|
|
2113
|
+
return /* @__PURE__ */ jsxs(
|
|
2114
|
+
"div",
|
|
2115
|
+
{
|
|
2116
|
+
className: "fixed inset-0 z-[1000] bg-black/90 flex items-center justify-center",
|
|
2117
|
+
onClick: onClose,
|
|
2118
|
+
children: [
|
|
2119
|
+
/* @__PURE__ */ jsx(
|
|
2120
|
+
"button",
|
|
2121
|
+
{
|
|
2122
|
+
type: "button",
|
|
2123
|
+
onClick: (e) => {
|
|
2124
|
+
e.stopPropagation();
|
|
2125
|
+
onClose();
|
|
2126
|
+
},
|
|
2127
|
+
className: "absolute top-4 right-4 text-white p-2 rounded-full hover:bg-white/10",
|
|
2128
|
+
"aria-label": "Close gallery",
|
|
2129
|
+
children: /* @__PURE__ */ jsx(Icon, { icon: "lucide:x", className: "w-6 h-6" })
|
|
2130
|
+
}
|
|
2131
|
+
),
|
|
2132
|
+
/* @__PURE__ */ jsx(
|
|
2133
|
+
"button",
|
|
2134
|
+
{
|
|
2135
|
+
type: "button",
|
|
2136
|
+
onClick: (e) => {
|
|
2137
|
+
e.stopPropagation();
|
|
2138
|
+
prev();
|
|
2139
|
+
},
|
|
2140
|
+
className: "absolute left-4 text-white p-3 rounded-full hover:bg-white/10",
|
|
2141
|
+
"aria-label": "Previous",
|
|
2142
|
+
children: /* @__PURE__ */ jsx(Icon, { icon: "lucide:chevron-left", className: "w-7 h-7" })
|
|
2143
|
+
}
|
|
2144
|
+
),
|
|
2145
|
+
/* @__PURE__ */ jsx(
|
|
2146
|
+
"img",
|
|
2147
|
+
{
|
|
2148
|
+
src: images[i],
|
|
2149
|
+
alt: alts[i] || "",
|
|
2150
|
+
className: "max-w-[92vw] max-h-[88vh] object-contain rounded-lg",
|
|
2151
|
+
onClick: (e) => e.stopPropagation()
|
|
2152
|
+
}
|
|
2153
|
+
),
|
|
2154
|
+
/* @__PURE__ */ jsx(
|
|
2155
|
+
"button",
|
|
2156
|
+
{
|
|
2157
|
+
type: "button",
|
|
2158
|
+
onClick: (e) => {
|
|
2159
|
+
e.stopPropagation();
|
|
2160
|
+
next();
|
|
2161
|
+
},
|
|
2162
|
+
className: "absolute right-4 text-white p-3 rounded-full hover:bg-white/10",
|
|
2163
|
+
"aria-label": "Next",
|
|
2164
|
+
children: /* @__PURE__ */ jsx(Icon, { icon: "lucide:chevron-right", className: "w-7 h-7" })
|
|
2165
|
+
}
|
|
2166
|
+
),
|
|
2167
|
+
/* @__PURE__ */ jsxs("div", { className: "absolute bottom-4 left-1/2 -translate-x-1/2 text-white text-sm bg-black/40 px-3 py-1 rounded-full", children: [
|
|
2168
|
+
i + 1,
|
|
2169
|
+
" / ",
|
|
2170
|
+
images.length
|
|
2171
|
+
] })
|
|
2172
|
+
]
|
|
2173
|
+
}
|
|
2174
|
+
);
|
|
2175
|
+
}
|
|
2176
|
+
var GallerySection_default = GallerySection;
|
|
2177
|
+
var renderStars = (rating) => {
|
|
2178
|
+
const fullStars = Math.floor(rating);
|
|
2179
|
+
const hasHalfStar = rating % 1 >= 0.5;
|
|
2180
|
+
return /* @__PURE__ */ jsx("div", { className: "flex items-center", children: [1, 2, 3, 4, 5].map((star) => {
|
|
2181
|
+
let icon = "mdi-light:star";
|
|
2182
|
+
if (star <= fullStars) {
|
|
2183
|
+
icon = "material-symbols:star";
|
|
2184
|
+
} else if (star === fullStars + 1 && hasHalfStar) {
|
|
2185
|
+
icon = "material-symbols:star-half";
|
|
2186
|
+
}
|
|
2187
|
+
return /* @__PURE__ */ jsx(Icon, { icon, className: "w-4 h-4 text-secondary" }, star);
|
|
2188
|
+
}) });
|
|
2189
|
+
};
|
|
2190
|
+
var TopSection = ({ topData }) => {
|
|
2191
|
+
const galleryMedia = topData.AgencyPackageMedias.map((item) => ({
|
|
2192
|
+
url: item.url,
|
|
2193
|
+
previewUrl: item.previewUrl,
|
|
2194
|
+
alt: item.alt || topData.PackageTitle
|
|
2195
|
+
}));
|
|
2196
|
+
return /* @__PURE__ */ jsxs("div", { className: "lg:px-4 px-0 py-2 md:py-4 lg:py-6", children: [
|
|
2197
|
+
/* @__PURE__ */ jsxs("nav", { "aria-label": "Breadcrumb", className: "flex items-center gap-2 text-sm", children: [
|
|
2198
|
+
/* @__PURE__ */ jsx("a", { href: "/", className: "text-primary-normal hover:underline", children: "Home" }),
|
|
2199
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "text-primary-normal", children: "\u203A" }),
|
|
2200
|
+
/* @__PURE__ */ jsx("span", { className: "text-primary-normal underline truncate w-[70vw] md:w-full", children: topData.PackageTitle })
|
|
2201
|
+
] }),
|
|
2202
|
+
/* @__PURE__ */ jsx("div", { className: "w-full flex justify-between items-center mt-2 py-0 md:py-4", children: /* @__PURE__ */ jsx("h1", { className: "text-xl md:text-2xl lg:text-3xl font-semibold tracking-[-0.04em] text-primary-normal leading-tight", children: topData.PackageTitle }) }),
|
|
2203
|
+
/* @__PURE__ */ jsx("div", { className: "text-sm py-0 md:py-2", children: topData.Rating ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
2204
|
+
/* @__PURE__ */ jsx("p", { className: "text-primary-normal font-medium", children: topData.Rating.toFixed(1) }),
|
|
2205
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
2206
|
+
renderStars(topData.Rating),
|
|
2207
|
+
/* @__PURE__ */ jsx("p", { className: "text-secondary-normal-hover underline text-xs", children: topData.ReviewCount ? `(${topData.ReviewCount})` : null })
|
|
2208
|
+
] })
|
|
2209
|
+
] }) : /* @__PURE__ */ jsx("p", { className: "text-secondary-normal-hover", children: "Not yet rated" }) }),
|
|
2210
|
+
/* @__PURE__ */ jsx(GallerySection_default, { media: galleryMedia, packageTitle: topData.PackageTitle })
|
|
2211
|
+
] });
|
|
2212
|
+
};
|
|
2213
|
+
var TopSection_default = TopSection;
|
|
2214
|
+
|
|
2215
|
+
// src/components/PackageDetail/_projectToLegacy.ts
|
|
2216
|
+
var mediaFromImage = (img, fallbackAlt) => {
|
|
2217
|
+
const u = pickImageUrl(img);
|
|
2218
|
+
if (!u) return null;
|
|
2219
|
+
return {
|
|
2220
|
+
url: u,
|
|
2221
|
+
previewUrl: img?.preview ?? "",
|
|
2222
|
+
alt: img?.alt || fallbackAlt
|
|
2223
|
+
};
|
|
2224
|
+
};
|
|
2225
|
+
var buildGalleryMedia = (pkg) => {
|
|
2226
|
+
const list = (pkg.media ?? []).map((m) => mediaFromImage(m, pkg.title)).filter((m) => Boolean(m));
|
|
2227
|
+
if (list.length > 0) return list;
|
|
2228
|
+
const thumb = mediaFromImage(pkg.thumbnail, pkg.title);
|
|
2229
|
+
return thumb ? [thumb] : [];
|
|
2230
|
+
};
|
|
2231
|
+
var buildTopData = (pkg, host) => ({
|
|
2232
|
+
id: pkg.id,
|
|
2233
|
+
slug: pkg.slug,
|
|
2234
|
+
PackageTitle: pkg.title,
|
|
2235
|
+
AgencyName: host?.username ?? "",
|
|
2236
|
+
AgencySlug: host?.slug ?? "",
|
|
2237
|
+
AgencyPackageMedias: buildGalleryMedia(pkg),
|
|
2238
|
+
Rating: pkg.rating ?? 0,
|
|
2239
|
+
ReviewCount: host?.review ?? 0,
|
|
2240
|
+
PackageType: pkg.packageType || "N/A",
|
|
2241
|
+
TotalDays: pkg.totalDays || 0,
|
|
2242
|
+
MaxAllowedPeople: pkg.maxAllowedPeople ?? 10,
|
|
2243
|
+
thingsToDo: pkg.thingsIncluded,
|
|
2244
|
+
thingsExcluded: pkg.thingsExcluded,
|
|
2245
|
+
thingsToPack: pkg.thingsToPack,
|
|
2246
|
+
PackageCategory: pkg.packageCategory || "Standard",
|
|
2247
|
+
Difficulty: pkg.difficulty || "Easy"
|
|
2248
|
+
});
|
|
2249
|
+
var buildDescriptionData = (pkg) => ({
|
|
2250
|
+
id: pkg.id,
|
|
2251
|
+
Description: pkg.description ?? "",
|
|
2252
|
+
Price: pkg.prices ?? [],
|
|
2253
|
+
Discounts: pkg.discounts ?? [],
|
|
2254
|
+
TotalDays: pkg.totalDays ?? 0,
|
|
2255
|
+
MaxAllowedPeople: pkg.maxAllowedPeople ?? 10,
|
|
2256
|
+
MinAllowedPeople: pkg.minAllowedPeople ?? 1,
|
|
2257
|
+
Days: (pkg.days ?? []).map((d) => ({
|
|
2258
|
+
id: d.id,
|
|
2259
|
+
dayNumber: d.dayNumber,
|
|
2260
|
+
description: d.description,
|
|
2261
|
+
activities: d.activities,
|
|
2262
|
+
locations: d.locations,
|
|
2263
|
+
travelDuration: d.travelDuration,
|
|
2264
|
+
difficulty: d.difficulty,
|
|
2265
|
+
foodService: d.foodService,
|
|
2266
|
+
transportation: d.transportation,
|
|
2267
|
+
accommodation: d.accommodation
|
|
2268
|
+
})),
|
|
2269
|
+
thingsToDo: pkg.thingsIncluded,
|
|
2270
|
+
thingsExcluded: pkg.thingsExcluded,
|
|
2271
|
+
thingsToPack: pkg.thingsToPack
|
|
2272
|
+
});
|
|
2273
|
+
function PackageDetailView({
|
|
2274
|
+
client,
|
|
2275
|
+
slug,
|
|
2276
|
+
initialBundle,
|
|
2277
|
+
currency = "USD",
|
|
2278
|
+
onReserve,
|
|
2279
|
+
className,
|
|
2280
|
+
renderLoading,
|
|
2281
|
+
renderNotFound
|
|
2282
|
+
}) {
|
|
2283
|
+
if (!initialBundle && (!client || !slug)) {
|
|
2284
|
+
throw new Error(
|
|
2285
|
+
"[PackageDetailView] Provide either `initialBundle` or both `client` and `slug`."
|
|
2286
|
+
);
|
|
2287
|
+
}
|
|
2288
|
+
const remote = usePackageBundle(client, initialBundle ? null : slug);
|
|
2289
|
+
const bundle = initialBundle ?? remote.data;
|
|
2290
|
+
const loading = !initialBundle && remote.loading;
|
|
2291
|
+
const error = !initialBundle && remote.error;
|
|
2292
|
+
if (loading) {
|
|
2293
|
+
return /* @__PURE__ */ jsx("div", { className: `tvr-package-detail ${className ?? ""}`.trim(), children: /* @__PURE__ */ jsx("div", { className: "mx-auto max-w-[1440px]", children: renderLoading ? renderLoading() : /* @__PURE__ */ jsx(DefaultLoading, {}) }) });
|
|
2294
|
+
}
|
|
2295
|
+
if (error) {
|
|
2296
|
+
return /* @__PURE__ */ jsx("div", { className: `tvr-package-detail ${className ?? ""}`.trim(), children: /* @__PURE__ */ jsxs("div", { className: "max-w-screen-xl mx-auto px-6 py-16 text-center text-red-600 text-sm", children: [
|
|
2297
|
+
"Failed to load package: ",
|
|
2298
|
+
error.message
|
|
2299
|
+
] }) });
|
|
2300
|
+
}
|
|
2301
|
+
if (!bundle?.pkg) {
|
|
2302
|
+
return /* @__PURE__ */ jsx("div", { className: `tvr-package-detail ${className ?? ""}`.trim(), children: renderNotFound ? renderNotFound() : /* @__PURE__ */ jsx("div", { className: "max-w-screen-xl mx-auto px-6 py-16 text-center text-secondary-normal-hover text-sm", children: "Package not found." }) });
|
|
2303
|
+
}
|
|
2304
|
+
return /* @__PURE__ */ jsx(
|
|
2305
|
+
DetailContent,
|
|
2306
|
+
{
|
|
2307
|
+
pkg: bundle.pkg,
|
|
2308
|
+
host: bundle.host,
|
|
2309
|
+
routes: bundle.routes ?? [],
|
|
2310
|
+
currency,
|
|
2311
|
+
onReserve,
|
|
2312
|
+
className
|
|
2313
|
+
}
|
|
2314
|
+
);
|
|
2315
|
+
}
|
|
2316
|
+
function DetailContent({
|
|
2317
|
+
pkg,
|
|
2318
|
+
host,
|
|
2319
|
+
routes,
|
|
2320
|
+
currency,
|
|
2321
|
+
onReserve,
|
|
2322
|
+
className
|
|
2323
|
+
}) {
|
|
2324
|
+
const topData = buildTopData(pkg, host);
|
|
2325
|
+
const descriptionData = buildDescriptionData(pkg);
|
|
2326
|
+
return /* @__PURE__ */ jsx("div", { className: `tvr-package-detail ${className ?? ""}`.trim(), children: /* @__PURE__ */ jsxs("article", { className: "mx-auto max-w-[1440px] flex flex-col gap-5 md:px-12 px-6 lg:py-12 mt-4", children: [
|
|
2327
|
+
/* @__PURE__ */ jsx(TopSection_default, { topData }),
|
|
2328
|
+
/* @__PURE__ */ jsx(
|
|
2329
|
+
DescriptionSection_default,
|
|
2330
|
+
{
|
|
2331
|
+
DescriptionData: descriptionData,
|
|
2332
|
+
topData,
|
|
2333
|
+
currency,
|
|
2334
|
+
onReserve,
|
|
2335
|
+
pkg,
|
|
2336
|
+
routes
|
|
2337
|
+
}
|
|
2338
|
+
),
|
|
2339
|
+
host && /* @__PURE__ */ jsxs("div", { className: "py-6 sm:py-8 border-b-2 border-gray-200 flex flex-col gap-6", children: [
|
|
2340
|
+
/* @__PURE__ */ jsx("h2", { className: "text-primary-normal text-xl md:text-2xl font-medium tracking-[-0.04em]", children: "Host Details" }),
|
|
2341
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-col lg:flex-row gap-8 lg:gap-10 h-full items-start lg:items-center", children: [
|
|
2342
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-col gap-6 sm:gap-8 w-full sm:w-[320px]", children: /* @__PURE__ */ jsx(HostCard_default, { hostData: host, className: "w-full" }) }),
|
|
2343
|
+
/* @__PURE__ */ jsx("div", { className: "w-full lg:w-4/5 h-full flex flex-col justify-center", children: /* @__PURE__ */ jsx(
|
|
2344
|
+
HostAbout_default,
|
|
2345
|
+
{
|
|
2346
|
+
hostName: host.username,
|
|
2347
|
+
description: host.description,
|
|
2348
|
+
className: "pr-0 lg:pr-10"
|
|
2349
|
+
}
|
|
2350
|
+
) })
|
|
2351
|
+
] })
|
|
2352
|
+
] })
|
|
2353
|
+
] }) });
|
|
2354
|
+
}
|
|
2355
|
+
function DefaultLoading() {
|
|
2356
|
+
return /* @__PURE__ */ jsxs("div", { className: "max-w-screen-xl mx-auto px-6 py-8 animate-pulse", children: [
|
|
2357
|
+
/* @__PURE__ */ jsx("div", { className: "h-8 w-1/2 bg-gray-200 rounded mb-4" }),
|
|
2358
|
+
/* @__PURE__ */ jsx("div", { className: "h-[546px] bg-gray-100 rounded-lg mb-6" }),
|
|
2359
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 lg:grid-cols-12 gap-6", children: [
|
|
2360
|
+
/* @__PURE__ */ jsxs("div", { className: "lg:col-span-7 space-y-3", children: [
|
|
2361
|
+
/* @__PURE__ */ jsx("div", { className: "h-5 w-1/3 bg-gray-200 rounded" }),
|
|
2362
|
+
/* @__PURE__ */ jsx("div", { className: "h-4 w-full bg-gray-100 rounded" }),
|
|
2363
|
+
/* @__PURE__ */ jsx("div", { className: "h-4 w-5/6 bg-gray-100 rounded" }),
|
|
2364
|
+
/* @__PURE__ */ jsx("div", { className: "h-4 w-4/6 bg-gray-100 rounded" })
|
|
2365
|
+
] }),
|
|
2366
|
+
/* @__PURE__ */ jsx("div", { className: "lg:col-span-5 h-64 bg-gray-100 rounded-2xl" })
|
|
2367
|
+
] })
|
|
2368
|
+
] });
|
|
2369
|
+
}
|
|
2370
|
+
|
|
2371
|
+
export { BookingCardLite_default as BookingCardLite, DescriptionSection_default as DescriptionSection, GallerySection_default as GallerySection, HomeSections, HomeSectionsProvider, HostAbout_default as HostAbout, HostCard_default as HostCard, Itinerary_default as Itinerary, PackageCard_default as PackageCard, PackageDetailView, PackageExcludedAndToPack_default as PackageExcludedAndToPack, PackageIncluded_default as PackageIncluded, PackageMap, PackageOverview_default as PackageOverview, PackagesCarousel_default as PackagesCarousel, PackagesResource, PackagesSection_default as PackagesSection, TopSection_default as TopSection, TravoriesApiError, TravoriesClient, buildDescriptionData, buildGalleryMedia, buildTopData, createTravoriesClient, formatCurrency, formatRating, pickImageUrl, startingPrice, useHomeSections, useHomeSectionsContext, usePackageBundle, usePackageBySlug };
|
|
2372
|
+
//# sourceMappingURL=index.mjs.map
|
|
2373
|
+
//# sourceMappingURL=index.mjs.map
|