@burdenoff/microfe-ggnomad 2026.626.1 → 2026.705.1
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/dist/ggnomad/components/LoadMoreButton.js +19 -0
- package/dist/ggnomad/components/LoadMoreButton.js.map +1 -0
- package/dist/ggnomad/components/index.js +1 -0
- package/dist/ggnomad/graphql.js +61 -17
- package/dist/ggnomad/graphql.js.map +1 -1
- package/dist/ggnomad/pages/ActivityDetailsPage.js +55 -53
- package/dist/ggnomad/pages/ActivityDetailsPage.js.map +1 -1
- package/dist/ggnomad/pages/AnalyticsPage.js +82 -35
- package/dist/ggnomad/pages/AnalyticsPage.js.map +1 -1
- package/dist/ggnomad/pages/BookingsPage.js +143 -69
- package/dist/ggnomad/pages/BookingsPage.js.map +1 -1
- package/dist/ggnomad/pages/EntertainmentPage.js +43 -34
- package/dist/ggnomad/pages/EntertainmentPage.js.map +1 -1
- package/dist/ggnomad/pages/EventDetailsPage.js +55 -53
- package/dist/ggnomad/pages/EventDetailsPage.js.map +1 -1
- package/dist/ggnomad/pages/FavoritesPage.js +98 -38
- package/dist/ggnomad/pages/FavoritesPage.js.map +1 -1
- package/dist/ggnomad/pages/HospitalityPage.js +49 -40
- package/dist/ggnomad/pages/HospitalityPage.js.map +1 -1
- package/dist/ggnomad/pages/PropertyDetailsPage.js +60 -58
- package/dist/ggnomad/pages/PropertyDetailsPage.js.map +1 -1
- package/dist/ggnomad/pages/ProviderPage.js +70 -61
- package/dist/ggnomad/pages/ProviderPage.js.map +1 -1
- package/dist/ggnomad/pages/TourDetailsPage.js +55 -53
- package/dist/ggnomad/pages/TourDetailsPage.js.map +1 -1
- package/dist/ggnomad/pages/TourismPage.js +48 -40
- package/dist/ggnomad/pages/TourismPage.js.map +1 -1
- package/dist/ggnomad/utils/notify.js +36 -0
- package/dist/ggnomad/utils/notify.js.map +1 -0
- package/package.json +2 -1
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { useI18n as e } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
2
|
+
import { jsx as t } from "react/jsx-runtime";
|
|
3
|
+
//#region src/ggnomad/components/LoadMoreButton.tsx
|
|
4
|
+
function n({ visible: n, onLoadMore: r }) {
|
|
5
|
+
let { t: i } = e();
|
|
6
|
+
return n ? /* @__PURE__ */ t("div", {
|
|
7
|
+
className: "mt-6 flex justify-center",
|
|
8
|
+
children: /* @__PURE__ */ t("button", {
|
|
9
|
+
type: "button",
|
|
10
|
+
onClick: r,
|
|
11
|
+
className: "rounded-lg border border-border-seam bg-bg-surface px-5 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-action-primary-bg",
|
|
12
|
+
children: i("ggnomad.common.loadMore", "Load more")
|
|
13
|
+
})
|
|
14
|
+
}) : null;
|
|
15
|
+
}
|
|
16
|
+
//#endregion
|
|
17
|
+
export { n as LoadMoreButton };
|
|
18
|
+
|
|
19
|
+
//# sourceMappingURL=LoadMoreButton.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LoadMoreButton.js","names":[],"sources":["../../../src/ggnomad/components/LoadMoreButton.tsx"],"sourcesContent":["import { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\ninterface LoadMoreButtonProps {\n /** Whether more items may exist (typically items.length >= current limit). */\n visible: boolean;\n /** Increase the page limit to fetch the next batch. */\n onLoadMore: () => void;\n}\n\n/**\n * Offset \"Load more\" control shown under a list grid when the current page is\n * full and more results may exist. Tokenized + responsive; no fixed widths.\n */\nexport function LoadMoreButton({ visible, onLoadMore }: LoadMoreButtonProps) {\n const { t } = useI18n();\n if (!visible) return null;\n return (\n <div className=\"mt-6 flex justify-center\">\n <button\n type=\"button\"\n onClick={onLoadMore}\n className=\"rounded-lg border border-border-seam bg-bg-surface px-5 py-2 text-sm font-medium text-text-primary transition-colors hover:bg-bg-sunken focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-action-primary-bg\"\n >\n {t('ggnomad.common.loadMore', 'Load more')}\n </button>\n </div>\n );\n}\n"],"mappings":";;;AAaA,SAAgB,EAAe,EAAE,YAAS,iBAAmC;CAC3E,IAAM,EAAE,SAAM,GAAS;AAEvB,QADK,IAEH,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,UAAD;GACE,MAAK;GACL,SAAS;GACT,WAAU;aAET,EAAE,2BAA2B,YAAY;GACnC,CAAA;EACL,CAAA,GAVa"}
|
package/dist/ggnomad/graphql.js
CHANGED
|
@@ -14,11 +14,55 @@ var t = e`
|
|
|
14
14
|
totalUsers
|
|
15
15
|
conversionRate
|
|
16
16
|
revenueByCategory
|
|
17
|
-
monthlyTrends
|
|
18
|
-
topDestinations
|
|
19
17
|
}
|
|
20
18
|
}
|
|
21
19
|
`, n = e`
|
|
20
|
+
query GGNomadBookableSample {
|
|
21
|
+
ggnomadProperties(pagination: { first: 1, skip: 0 }) {
|
|
22
|
+
id
|
|
23
|
+
name
|
|
24
|
+
pricePerNight
|
|
25
|
+
currency
|
|
26
|
+
}
|
|
27
|
+
ggnomadTours(pagination: { first: 1, skip: 0 }) {
|
|
28
|
+
id
|
|
29
|
+
name
|
|
30
|
+
price
|
|
31
|
+
currency
|
|
32
|
+
}
|
|
33
|
+
ggnomadEvents(pagination: { first: 1, skip: 0 }) {
|
|
34
|
+
id
|
|
35
|
+
name
|
|
36
|
+
price
|
|
37
|
+
currency
|
|
38
|
+
}
|
|
39
|
+
ggnomadActivities(pagination: { first: 1, skip: 0 }) {
|
|
40
|
+
id
|
|
41
|
+
name
|
|
42
|
+
price
|
|
43
|
+
currency
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
`, r = e`
|
|
47
|
+
query GGNomadFavoriteTargets {
|
|
48
|
+
ggnomadProperties(pagination: { first: 100, skip: 0 }) {
|
|
49
|
+
id
|
|
50
|
+
name
|
|
51
|
+
}
|
|
52
|
+
ggnomadTours(pagination: { first: 100, skip: 0 }) {
|
|
53
|
+
id
|
|
54
|
+
name
|
|
55
|
+
}
|
|
56
|
+
ggnomadEvents(pagination: { first: 100, skip: 0 }) {
|
|
57
|
+
id
|
|
58
|
+
name
|
|
59
|
+
}
|
|
60
|
+
ggnomadActivities(pagination: { first: 100, skip: 0 }) {
|
|
61
|
+
id
|
|
62
|
+
name
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
`, i = e`
|
|
22
66
|
query GGNomadProviders($first: Int, $skip: Int) {
|
|
23
67
|
ggnomadProviders(pagination: { first: $first, skip: $skip }) {
|
|
24
68
|
id
|
|
@@ -33,7 +77,7 @@ var t = e`
|
|
|
33
77
|
website
|
|
34
78
|
}
|
|
35
79
|
}
|
|
36
|
-
`,
|
|
80
|
+
`, a = e`
|
|
37
81
|
query GGNomadProperties($first: Int, $skip: Int) {
|
|
38
82
|
ggnomadProperties(pagination: { first: $first, skip: $skip }) {
|
|
39
83
|
id
|
|
@@ -51,7 +95,7 @@ var t = e`
|
|
|
51
95
|
providerId
|
|
52
96
|
}
|
|
53
97
|
}
|
|
54
|
-
`,
|
|
98
|
+
`, o = e`
|
|
55
99
|
query GGNomadProperty($id: ID!) {
|
|
56
100
|
ggnomadProperty(id: $id) {
|
|
57
101
|
id
|
|
@@ -79,7 +123,7 @@ var t = e`
|
|
|
79
123
|
}
|
|
80
124
|
}
|
|
81
125
|
}
|
|
82
|
-
`,
|
|
126
|
+
`, s = e`
|
|
83
127
|
query GGNomadTours($first: Int, $skip: Int) {
|
|
84
128
|
ggnomadTours(pagination: { first: $first, skip: $skip }) {
|
|
85
129
|
id
|
|
@@ -97,7 +141,7 @@ var t = e`
|
|
|
97
141
|
images
|
|
98
142
|
}
|
|
99
143
|
}
|
|
100
|
-
`,
|
|
144
|
+
`, c = e`
|
|
101
145
|
query GGNomadTour($id: ID!) {
|
|
102
146
|
ggnomadTour(id: $id) {
|
|
103
147
|
id
|
|
@@ -121,7 +165,7 @@ var t = e`
|
|
|
121
165
|
}
|
|
122
166
|
}
|
|
123
167
|
}
|
|
124
|
-
`,
|
|
168
|
+
`, l = e`
|
|
125
169
|
query GGNomadEvents($first: Int, $skip: Int) {
|
|
126
170
|
ggnomadEvents(pagination: { first: $first, skip: $skip }) {
|
|
127
171
|
id
|
|
@@ -138,7 +182,7 @@ var t = e`
|
|
|
138
182
|
images
|
|
139
183
|
}
|
|
140
184
|
}
|
|
141
|
-
`,
|
|
185
|
+
`, u = e`
|
|
142
186
|
query GGNomadEvent($id: ID!) {
|
|
143
187
|
ggnomadEvent(id: $id) {
|
|
144
188
|
id
|
|
@@ -161,7 +205,7 @@ var t = e`
|
|
|
161
205
|
}
|
|
162
206
|
}
|
|
163
207
|
}
|
|
164
|
-
`,
|
|
208
|
+
`, d = e`
|
|
165
209
|
query GGNomadActivities($first: Int, $skip: Int) {
|
|
166
210
|
ggnomadActivities(pagination: { first: $first, skip: $skip }) {
|
|
167
211
|
id
|
|
@@ -181,7 +225,7 @@ var t = e`
|
|
|
181
225
|
images
|
|
182
226
|
}
|
|
183
227
|
}
|
|
184
|
-
`,
|
|
228
|
+
`, f = e`
|
|
185
229
|
query GGNomadActivity($id: ID!) {
|
|
186
230
|
ggnomadActivity(id: $id) {
|
|
187
231
|
id
|
|
@@ -207,7 +251,7 @@ var t = e`
|
|
|
207
251
|
}
|
|
208
252
|
}
|
|
209
253
|
}
|
|
210
|
-
`,
|
|
254
|
+
`, p = e`
|
|
211
255
|
query GGNomadTrips($first: Int, $skip: Int) {
|
|
212
256
|
ggnomadTrips(pagination: { first: $first, skip: $skip }) {
|
|
213
257
|
id
|
|
@@ -227,7 +271,7 @@ var t = e`
|
|
|
227
271
|
ecoFriendly
|
|
228
272
|
}
|
|
229
273
|
}
|
|
230
|
-
`,
|
|
274
|
+
`, m = e`
|
|
231
275
|
query GGNomadBookings($first: Int, $skip: Int) {
|
|
232
276
|
ggnomadBookings(pagination: { first: $first, skip: $skip }) {
|
|
233
277
|
id
|
|
@@ -251,11 +295,11 @@ var t = e`
|
|
|
251
295
|
createdAt
|
|
252
296
|
}
|
|
253
297
|
}
|
|
254
|
-
`,
|
|
298
|
+
`, h = e`
|
|
255
299
|
query GGNomadFavorites {
|
|
256
300
|
ggnomadFavorites
|
|
257
301
|
}
|
|
258
|
-
`,
|
|
302
|
+
`, g = e`
|
|
259
303
|
mutation CreateGGNomadBooking($input: CreateGgnomadBookingInput!) {
|
|
260
304
|
createGgnomadBooking(input: $input) {
|
|
261
305
|
id
|
|
@@ -270,11 +314,11 @@ var t = e`
|
|
|
270
314
|
createdAt
|
|
271
315
|
}
|
|
272
316
|
}
|
|
273
|
-
`,
|
|
317
|
+
`, _ = e`
|
|
274
318
|
mutation ToggleGGNomadFavorite($input: ToggleGgnomadFavoriteInput!) {
|
|
275
319
|
toggleGgnomadFavorite(input: $input)
|
|
276
320
|
}
|
|
277
|
-
`,
|
|
321
|
+
`, v = e`
|
|
278
322
|
mutation CancelGGNomadBooking($id: ID!, $reason: String) {
|
|
279
323
|
cancelGgnomadBooking(id: $id, reason: $reason) {
|
|
280
324
|
id
|
|
@@ -283,6 +327,6 @@ var t = e`
|
|
|
283
327
|
}
|
|
284
328
|
`;
|
|
285
329
|
//#endregion
|
|
286
|
-
export {
|
|
330
|
+
export { v as CANCEL_GGNOMAD_BOOKING_MUTATION, g as CREATE_GGNOMAD_BOOKING_MUTATION, d as GGNOMAD_ACTIVITIES_QUERY, f as GGNOMAD_ACTIVITY_QUERY, n as GGNOMAD_BOOKABLE_SAMPLE_QUERY, m as GGNOMAD_BOOKINGS_QUERY, l as GGNOMAD_EVENTS_QUERY, u as GGNOMAD_EVENT_QUERY, h as GGNOMAD_FAVORITES_QUERY, r as GGNOMAD_FAVORITE_TARGETS_QUERY, t as GGNOMAD_OVERVIEW_QUERY, a as GGNOMAD_PROPERTIES_QUERY, o as GGNOMAD_PROPERTY_QUERY, i as GGNOMAD_PROVIDERS_QUERY, s as GGNOMAD_TOURS_QUERY, c as GGNOMAD_TOUR_QUERY, p as GGNOMAD_TRIPS_QUERY, _ as TOGGLE_GGNOMAD_FAVORITE_MUTATION };
|
|
287
331
|
|
|
288
332
|
//# sourceMappingURL=graphql.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graphql.js","names":[],"sources":["../../src/ggnomad/graphql.ts"],"sourcesContent":["import { gql } from '@apollo/client/core';\n\nexport const GGNOMAD_OVERVIEW_QUERY = gql`\n query GGNomadOverview {\n ggnomadOverviewStats {\n users\n listings\n bookings\n monthlyRevenue\n }\n ggnomadAnalytics {\n totalRevenue\n totalBookings\n totalUsers\n conversionRate\n revenueByCategory\n
|
|
1
|
+
{"version":3,"file":"graphql.js","names":[],"sources":["../../src/ggnomad/graphql.ts"],"sourcesContent":["import { gql } from '@apollo/client/core';\n\nexport const GGNOMAD_OVERVIEW_QUERY = gql`\n query GGNomadOverview {\n ggnomadOverviewStats {\n users\n listings\n bookings\n monthlyRevenue\n }\n ggnomadAnalytics {\n totalRevenue\n totalBookings\n totalUsers\n conversionRate\n revenueByCategory\n }\n }\n`;\n\n/**\n * One bookable item of each type (properties are priced per night, the rest\n * carry a flat price). Used to seed the Bookings \"create demo booking\" action\n * with a REAL listing id so the mutation passes the backend's\n * assertTargetInWorkspace check instead of failing on a synthetic id.\n */\nexport const GGNOMAD_BOOKABLE_SAMPLE_QUERY = gql`\n query GGNomadBookableSample {\n ggnomadProperties(pagination: { first: 1, skip: 0 }) {\n id\n name\n pricePerNight\n currency\n }\n ggnomadTours(pagination: { first: 1, skip: 0 }) {\n id\n name\n price\n currency\n }\n ggnomadEvents(pagination: { first: 1, skip: 0 }) {\n id\n name\n price\n currency\n }\n ggnomadActivities(pagination: { first: 1, skip: 0 }) {\n id\n name\n price\n currency\n }\n }\n`;\n\n/**\n * id + name for every favouritable listing type, used to (a) resolve the real\n * entity name for a saved favorite (which the backend only exposes as\n * targetType + targetId) and (b) seed the \"toggle demo favorite\" action with a\n * real target id.\n */\nexport const GGNOMAD_FAVORITE_TARGETS_QUERY = gql`\n query GGNomadFavoriteTargets {\n ggnomadProperties(pagination: { first: 100, skip: 0 }) {\n id\n name\n }\n ggnomadTours(pagination: { first: 100, skip: 0 }) {\n id\n name\n }\n ggnomadEvents(pagination: { first: 100, skip: 0 }) {\n id\n name\n }\n ggnomadActivities(pagination: { first: 100, skip: 0 }) {\n id\n name\n }\n }\n`;\n\nexport const GGNOMAD_PROVIDERS_QUERY = gql`\n query GGNomadProviders($first: Int, $skip: Int) {\n ggnomadProviders(pagination: { first: $first, skip: $skip }) {\n id\n name\n type\n status\n listingCount\n totalRevenue\n rating\n reviewCount\n contactEmail\n website\n }\n }\n`;\n\nexport const GGNOMAD_PROPERTIES_QUERY = gql`\n query GGNomadProperties($first: Int, $skip: Int) {\n ggnomadProperties(pagination: { first: $first, skip: $skip }) {\n id\n name\n type\n status\n description\n pricePerNight\n currency\n rating\n reviewCount\n maxGuests\n amenities\n images\n providerId\n }\n }\n`;\n\nexport const GGNOMAD_PROPERTY_QUERY = gql`\n query GGNomadProperty($id: ID!) {\n ggnomadProperty(id: $id) {\n id\n name\n type\n status\n description\n address\n pricePerNight\n currency\n rating\n reviewCount\n bedrooms\n bathrooms\n maxGuests\n checkInTime\n checkOutTime\n amenities\n images\n provider {\n id\n name\n type\n rating\n }\n }\n }\n`;\n\nexport const GGNOMAD_TOURS_QUERY = gql`\n query GGNomadTours($first: Int, $skip: Int) {\n ggnomadTours(pagination: { first: $first, skip: $skip }) {\n id\n name\n description\n type\n status\n duration\n difficulty\n price\n currency\n rating\n reviewCount\n highlights\n images\n }\n }\n`;\n\nexport const GGNOMAD_TOUR_QUERY = gql`\n query GGNomadTour($id: ID!) {\n ggnomadTour(id: $id) {\n id\n name\n description\n type\n status\n duration\n difficulty\n price\n currency\n rating\n reviewCount\n highlights\n images\n languages\n guideName\n provider {\n id\n name\n }\n }\n }\n`;\n\nexport const GGNOMAD_EVENTS_QUERY = gql`\n query GGNomadEvents($first: Int, $skip: Int) {\n ggnomadEvents(pagination: { first: $first, skip: $skip }) {\n id\n name\n description\n category\n status\n startsAt\n price\n currency\n rating\n reviewCount\n performers\n images\n }\n }\n`;\n\nexport const GGNOMAD_EVENT_QUERY = gql`\n query GGNomadEvent($id: ID!) {\n ggnomadEvent(id: $id) {\n id\n name\n description\n category\n status\n startsAt\n endsAt\n venue\n price\n currency\n rating\n reviewCount\n performers\n images\n provider {\n id\n name\n }\n }\n }\n`;\n\nexport const GGNOMAD_ACTIVITIES_QUERY = gql`\n query GGNomadActivities($first: Int, $skip: Int) {\n ggnomadActivities(pagination: { first: $first, skip: $skip }) {\n id\n name\n description\n category\n status\n duration\n difficulty\n price\n currency\n rating\n reviewCount\n maxParticipants\n includes\n requirements\n images\n }\n }\n`;\n\nexport const GGNOMAD_ACTIVITY_QUERY = gql`\n query GGNomadActivity($id: ID!) {\n ggnomadActivity(id: $id) {\n id\n name\n description\n category\n status\n location\n duration\n difficulty\n price\n currency\n rating\n reviewCount\n maxParticipants\n includes\n requirements\n safetyInfo\n images\n provider {\n id\n name\n }\n }\n }\n`;\n\nexport const GGNOMAD_TRIPS_QUERY = gql`\n query GGNomadTrips($first: Int, $skip: Int) {\n ggnomadTrips(pagination: { first: $first, skip: $skip }) {\n id\n name\n transportMode\n status\n origin\n destination\n departureAt\n arrivalAt\n isRoundTrip\n carrier\n class\n price\n currency\n passengers\n ecoFriendly\n }\n }\n`;\n\nexport const GGNOMAD_BOOKINGS_QUERY = gql`\n query GGNomadBookings($first: Int, $skip: Int) {\n ggnomadBookings(pagination: { first: $first, skip: $skip }) {\n id\n referenceCode\n bookingType\n status\n itemId\n itemName\n guestName\n guestEmail\n guestPhone\n guests\n checkIn\n checkOut\n eventDate\n totalPrice\n currency\n paymentStatus\n paymentMethod\n specialRequests\n createdAt\n }\n }\n`;\n\nexport const GGNOMAD_FAVORITES_QUERY = gql`\n query GGNomadFavorites {\n ggnomadFavorites\n }\n`;\n\nexport const CREATE_GGNOMAD_BOOKING_MUTATION = gql`\n mutation CreateGGNomadBooking($input: CreateGgnomadBookingInput!) {\n createGgnomadBooking(input: $input) {\n id\n referenceCode\n bookingType\n status\n itemId\n itemName\n guestName\n totalPrice\n currency\n createdAt\n }\n }\n`;\n\nexport const TOGGLE_GGNOMAD_FAVORITE_MUTATION = gql`\n mutation ToggleGGNomadFavorite($input: ToggleGgnomadFavoriteInput!) {\n toggleGgnomadFavorite(input: $input)\n }\n`;\n\nexport const CANCEL_GGNOMAD_BOOKING_MUTATION = gql`\n mutation CancelGGNomadBooking($id: ID!, $reason: String) {\n cancelGgnomadBooking(id: $id, reason: $reason) {\n id\n status\n }\n }\n`;\n"],"mappings":";;AAEA,IAAa,IAAyB,CAAG;;;;;;;;;;;;;;;;GAwB5B,IAAgC,CAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCnC,IAAiC,CAAG;;;;;;;;;;;;;;;;;;;GAqBpC,IAA0B,CAAG;;;;;;;;;;;;;;;GAiB7B,IAA2B,CAAG;;;;;;;;;;;;;;;;;;GAoB9B,IAAyB,CAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8B5B,IAAsB,CAAG;;;;;;;;;;;;;;;;;;GAoBzB,IAAqB,CAAG;;;;;;;;;;;;;;;;;;;;;;;;GA0BxB,IAAuB,CAAG;;;;;;;;;;;;;;;;;GAmB1B,IAAsB,CAAG;;;;;;;;;;;;;;;;;;;;;;;GAyBzB,IAA2B,CAAG;;;;;;;;;;;;;;;;;;;;GAsB9B,IAAyB,CAAG;;;;;;;;;;;;;;;;;;;;;;;;;;GA4B5B,IAAsB,CAAG;;;;;;;;;;;;;;;;;;;;GAsBzB,IAAyB,CAAG;;;;;;;;;;;;;;;;;;;;;;;;GA0B5B,IAA0B,CAAG;;;;GAM7B,IAAkC,CAAG;;;;;;;;;;;;;;;GAiBrC,IAAmC,CAAG;;;;GAMtC,IAAkC,CAAG"}
|
|
@@ -3,91 +3,93 @@ import { PageShell as t } from "./PageShell.js";
|
|
|
3
3
|
import { SectionCard as n } from "../components/SectionCard.js";
|
|
4
4
|
import { DetailRow as r } from "../components/DetailRow.js";
|
|
5
5
|
import "../components/index.js";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
6
|
+
import { useGGNomad as i } from "../providers/GGNomadProvider.js";
|
|
7
|
+
import { joinPath as a } from "../utils/navigation.js";
|
|
8
|
+
import { Activity as o, CalendarCheck as s, Compass as c, ShieldCheck as l } from "lucide-react";
|
|
9
|
+
import { useI18n as u } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
10
|
+
import { useNavigate as d, useParams as f } from "react-router-dom";
|
|
11
|
+
import { useQuery as p } from "@apollo/client/react";
|
|
12
|
+
import { GlassCard as m, IllustratedEmptyState as h, NextSteps as g, PagePurpose as _ } from "@burdenoff/fe-libs/ui";
|
|
13
|
+
import { Fragment as v, jsx as y, jsxs as b } from "react/jsx-runtime";
|
|
12
14
|
//#region src/ggnomad/pages/ActivityDetailsPage.tsx
|
|
13
|
-
function
|
|
14
|
-
let { t:
|
|
15
|
-
variables: { id:
|
|
16
|
-
skip: !
|
|
17
|
-
}),
|
|
18
|
-
return /* @__PURE__ */
|
|
19
|
-
title:
|
|
20
|
-
subtitle:
|
|
21
|
-
children:
|
|
22
|
-
/* @__PURE__ */ _
|
|
23
|
-
icon: /* @__PURE__ */
|
|
24
|
-
children:
|
|
15
|
+
function x() {
|
|
16
|
+
let { t: x } = u(), S = d(), { basePath: C = "/" } = i(), { id: w = "" } = f(), { data: T } = p(e, {
|
|
17
|
+
variables: { id: w },
|
|
18
|
+
skip: !w
|
|
19
|
+
}), E = T?.ggnomadActivity;
|
|
20
|
+
return /* @__PURE__ */ y(t, {
|
|
21
|
+
title: E?.name ?? x("ggnomad.activity.fallbackTitle", "Activity details"),
|
|
22
|
+
subtitle: x("ggnomad.activity.subtitle", "Safety, inclusions and participation details"),
|
|
23
|
+
children: E ? /* @__PURE__ */ b(v, { children: [
|
|
24
|
+
/* @__PURE__ */ y(_, {
|
|
25
|
+
icon: /* @__PURE__ */ y(o, { className: "size-4" }),
|
|
26
|
+
children: x("ggnomad.activity.purpose", "Everything you need to know before joining this activity — its category, duration, group size, price and the safety policy that applies — so you can be confident it fits your group before you book.")
|
|
25
27
|
}),
|
|
26
|
-
/* @__PURE__ */
|
|
28
|
+
/* @__PURE__ */ y(g, {
|
|
27
29
|
storageKey: "ggnomad-activity-detail",
|
|
28
30
|
steps: [{
|
|
29
31
|
id: "book",
|
|
30
|
-
label:
|
|
31
|
-
description:
|
|
32
|
-
icon: /* @__PURE__ */
|
|
33
|
-
onClick: () =>
|
|
32
|
+
label: x("ggnomad.activity.next.book", "Book this activity"),
|
|
33
|
+
description: x("ggnomad.activity.next.bookDesc", "Add it to your itinerary and track it under Bookings."),
|
|
34
|
+
icon: /* @__PURE__ */ y(s, { className: "size-4" }),
|
|
35
|
+
onClick: () => S(a(C, "bookings"))
|
|
34
36
|
}, {
|
|
35
37
|
id: "browse",
|
|
36
|
-
label:
|
|
37
|
-
description:
|
|
38
|
-
icon: /* @__PURE__ */
|
|
39
|
-
onClick: () =>
|
|
38
|
+
label: x("ggnomad.activity.next.browse", "Explore more experiences"),
|
|
39
|
+
description: x("ggnomad.activity.next.browseDesc", "Return to Tourism to compare tours and activities."),
|
|
40
|
+
icon: /* @__PURE__ */ y(c, { className: "size-4" }),
|
|
41
|
+
onClick: () => S(a(C, "tourism"))
|
|
40
42
|
}]
|
|
41
43
|
}),
|
|
42
|
-
/* @__PURE__ */
|
|
44
|
+
/* @__PURE__ */ b("div", {
|
|
43
45
|
className: "grid gap-4 lg:grid-cols-2",
|
|
44
|
-
children: [/* @__PURE__ */
|
|
46
|
+
children: [/* @__PURE__ */ b(m, {
|
|
45
47
|
treatment: "glass",
|
|
46
48
|
glow: !0,
|
|
47
49
|
className: "border-border-seam p-5",
|
|
48
|
-
children: [/* @__PURE__ */
|
|
50
|
+
children: [/* @__PURE__ */ y("h2", {
|
|
49
51
|
className: "mb-4 text-lg font-medium tracking-tight text-text-primary",
|
|
50
|
-
children:
|
|
51
|
-
}), /* @__PURE__ */
|
|
52
|
+
children: x("ggnomad.activity.details", "Details")
|
|
53
|
+
}), /* @__PURE__ */ b("dl", {
|
|
52
54
|
className: "space-y-2 text-sm",
|
|
53
55
|
children: [
|
|
54
|
-
/* @__PURE__ */
|
|
55
|
-
label:
|
|
56
|
-
value:
|
|
56
|
+
/* @__PURE__ */ y(r, {
|
|
57
|
+
label: x("ggnomad.activity.category", "Category"),
|
|
58
|
+
value: E.category
|
|
57
59
|
}),
|
|
58
|
-
/* @__PURE__ */
|
|
59
|
-
label:
|
|
60
|
-
value:
|
|
60
|
+
/* @__PURE__ */ y(r, {
|
|
61
|
+
label: x("ggnomad.activity.duration", "Duration"),
|
|
62
|
+
value: E.duration
|
|
61
63
|
}),
|
|
62
|
-
/* @__PURE__ */
|
|
63
|
-
label:
|
|
64
|
-
value: String(
|
|
64
|
+
/* @__PURE__ */ y(r, {
|
|
65
|
+
label: x("ggnomad.activity.maxParticipants", "Max participants"),
|
|
66
|
+
value: String(E.maxParticipants)
|
|
65
67
|
}),
|
|
66
|
-
/* @__PURE__ */
|
|
67
|
-
label:
|
|
68
|
-
value: `${
|
|
68
|
+
/* @__PURE__ */ y(r, {
|
|
69
|
+
label: x("ggnomad.activity.price", "Price"),
|
|
70
|
+
value: `${E.currency} ${Number(E.price).toFixed(2)}`
|
|
69
71
|
})
|
|
70
72
|
]
|
|
71
73
|
})]
|
|
72
|
-
}), /* @__PURE__ */
|
|
73
|
-
title:
|
|
74
|
-
children: /* @__PURE__ */
|
|
74
|
+
}), /* @__PURE__ */ y(n, {
|
|
75
|
+
title: x("ggnomad.activity.safety", "Safety information"),
|
|
76
|
+
children: /* @__PURE__ */ b("div", {
|
|
75
77
|
className: "flex items-start gap-2 text-sm text-text-primary",
|
|
76
|
-
children: [/* @__PURE__ */
|
|
78
|
+
children: [/* @__PURE__ */ y(l, {
|
|
77
79
|
className: "mt-0.5 size-4 flex-shrink-0 text-status-success-text",
|
|
78
80
|
"aria-hidden": "true"
|
|
79
|
-
}), /* @__PURE__ */
|
|
81
|
+
}), /* @__PURE__ */ y("p", { children: E.safetyInfo ?? x("ggnomad.activity.defaultSafety", "Standard provider safety policy applies.") })]
|
|
80
82
|
})
|
|
81
83
|
})]
|
|
82
84
|
})
|
|
83
|
-
] }) : /* @__PURE__ */
|
|
85
|
+
] }) : /* @__PURE__ */ y(h, {
|
|
84
86
|
illustration: "empty-data",
|
|
85
|
-
title:
|
|
86
|
-
description:
|
|
87
|
+
title: x("ggnomad.activity.notFoundTitle", "No activity found"),
|
|
88
|
+
description: x("ggnomad.activity.notFoundDescription", "This activity is unavailable or may have been removed.")
|
|
87
89
|
})
|
|
88
90
|
});
|
|
89
91
|
}
|
|
90
92
|
//#endregion
|
|
91
|
-
export {
|
|
93
|
+
export { x as ActivityDetailsPage };
|
|
92
94
|
|
|
93
95
|
//# sourceMappingURL=ActivityDetailsPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ActivityDetailsPage.js","names":[],"sources":["../../../src/ggnomad/pages/ActivityDetailsPage.tsx"],"sourcesContent":["import { useQuery } from '@apollo/client/react';\nimport type { TypedDocumentNode } from '@apollo/client';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useNavigate, useParams } from 'react-router-dom';\nimport { Activity, CalendarCheck, Compass, ShieldCheck } from 'lucide-react';\nimport { GlassCard, IllustratedEmptyState, NextSteps, PagePurpose } from '@burdenoff/fe-libs/ui';\n\nimport { GGNOMAD_ACTIVITY_QUERY } from '../graphql';\nimport { PageShell } from './PageShell';\nimport { DetailRow, SectionCard } from '../components';\n\nexport function ActivityDetailsPage() {\n const { t } = useI18n();\n const navigate = useNavigate();\n const { id = '' } = useParams();\n const { data } = useQuery(\n GGNOMAD_ACTIVITY_QUERY as TypedDocumentNode<\n {\n ggnomadActivity: {\n name: string;\n category: string;\n duration: string;\n maxParticipants: number;\n currency: string;\n price: number;\n safetyInfo?: string;\n } | null;\n },\n { id: string }\n >,\n { variables: { id }, skip: !id }\n );\n const activity = data?.ggnomadActivity;\n\n return (\n <PageShell\n title={activity?.name ?? t('ggnomad.activity.fallbackTitle', 'Activity details')}\n subtitle={t('ggnomad.activity.subtitle', 'Safety, inclusions and participation details')}\n >\n {!activity ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={t('ggnomad.activity.notFoundTitle', 'No activity found')}\n description={t(\n 'ggnomad.activity.notFoundDescription',\n 'This activity is unavailable or may have been removed.'\n )}\n />\n ) : (\n <>\n <PagePurpose icon={<Activity className=\"size-4\" />}>\n {t(\n 'ggnomad.activity.purpose',\n 'Everything you need to know before joining this activity — its category, duration, group size, price and the safety policy that applies — so you can be confident it fits your group before you book.'\n )}\n </PagePurpose>\n\n <NextSteps\n storageKey=\"ggnomad-activity-detail\"\n steps={[\n {\n id: 'book',\n label: t('ggnomad.activity.next.book', 'Book this activity'),\n description: t(\n 'ggnomad.activity.next.bookDesc',\n 'Add it to your itinerary and track it under Bookings.'\n ),\n icon: <CalendarCheck className=\"size-4\" />,\n onClick: () => navigate('
|
|
1
|
+
{"version":3,"file":"ActivityDetailsPage.js","names":[],"sources":["../../../src/ggnomad/pages/ActivityDetailsPage.tsx"],"sourcesContent":["import { useQuery } from '@apollo/client/react';\nimport type { TypedDocumentNode } from '@apollo/client';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useNavigate, useParams } from 'react-router-dom';\nimport { Activity, CalendarCheck, Compass, ShieldCheck } from 'lucide-react';\nimport { GlassCard, IllustratedEmptyState, NextSteps, PagePurpose } from '@burdenoff/fe-libs/ui';\n\nimport { GGNOMAD_ACTIVITY_QUERY } from '../graphql';\nimport { useGGNomad } from '../providers/GGNomadProvider';\nimport { joinPath } from '../utils/navigation';\nimport { PageShell } from './PageShell';\nimport { DetailRow, SectionCard } from '../components';\n\nexport function ActivityDetailsPage() {\n const { t } = useI18n();\n const navigate = useNavigate();\n const { basePath = '/' } = useGGNomad();\n const { id = '' } = useParams();\n const { data } = useQuery(\n GGNOMAD_ACTIVITY_QUERY as TypedDocumentNode<\n {\n ggnomadActivity: {\n name: string;\n category: string;\n duration: string;\n maxParticipants: number;\n currency: string;\n price: number;\n safetyInfo?: string;\n } | null;\n },\n { id: string }\n >,\n { variables: { id }, skip: !id }\n );\n const activity = data?.ggnomadActivity;\n\n return (\n <PageShell\n title={activity?.name ?? t('ggnomad.activity.fallbackTitle', 'Activity details')}\n subtitle={t('ggnomad.activity.subtitle', 'Safety, inclusions and participation details')}\n >\n {!activity ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={t('ggnomad.activity.notFoundTitle', 'No activity found')}\n description={t(\n 'ggnomad.activity.notFoundDescription',\n 'This activity is unavailable or may have been removed.'\n )}\n />\n ) : (\n <>\n <PagePurpose icon={<Activity className=\"size-4\" />}>\n {t(\n 'ggnomad.activity.purpose',\n 'Everything you need to know before joining this activity — its category, duration, group size, price and the safety policy that applies — so you can be confident it fits your group before you book.'\n )}\n </PagePurpose>\n\n <NextSteps\n storageKey=\"ggnomad-activity-detail\"\n steps={[\n {\n id: 'book',\n label: t('ggnomad.activity.next.book', 'Book this activity'),\n description: t(\n 'ggnomad.activity.next.bookDesc',\n 'Add it to your itinerary and track it under Bookings.'\n ),\n icon: <CalendarCheck className=\"size-4\" />,\n onClick: () => navigate(joinPath(basePath, 'bookings')),\n },\n {\n id: 'browse',\n label: t('ggnomad.activity.next.browse', 'Explore more experiences'),\n description: t(\n 'ggnomad.activity.next.browseDesc',\n 'Return to Tourism to compare tours and activities.'\n ),\n icon: <Compass className=\"size-4\" />,\n onClick: () => navigate(joinPath(basePath, 'tourism')),\n },\n ]}\n />\n\n <div className=\"grid gap-4 lg:grid-cols-2\">\n <GlassCard treatment=\"glass\" glow className=\"border-border-seam p-5\">\n <h2 className=\"mb-4 text-lg font-medium tracking-tight text-text-primary\">\n {t('ggnomad.activity.details', 'Details')}\n </h2>\n <dl className=\"space-y-2 text-sm\">\n <DetailRow\n label={t('ggnomad.activity.category', 'Category')}\n value={activity.category}\n />\n <DetailRow\n label={t('ggnomad.activity.duration', 'Duration')}\n value={activity.duration}\n />\n <DetailRow\n label={t('ggnomad.activity.maxParticipants', 'Max participants')}\n value={String(activity.maxParticipants)}\n />\n <DetailRow\n label={t('ggnomad.activity.price', 'Price')}\n value={`${activity.currency} ${Number(activity.price).toFixed(2)}`}\n />\n </dl>\n </GlassCard>\n <SectionCard title={t('ggnomad.activity.safety', 'Safety information')}>\n <div className=\"flex items-start gap-2 text-sm text-text-primary\">\n <ShieldCheck\n className=\"mt-0.5 size-4 flex-shrink-0 text-status-success-text\"\n aria-hidden=\"true\"\n />\n <p>\n {activity.safetyInfo ??\n t('ggnomad.activity.defaultSafety', 'Standard provider safety policy applies.')}\n </p>\n </div>\n </SectionCard>\n </div>\n </>\n )}\n </PageShell>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;AAaA,SAAgB,IAAsB;CACpC,IAAM,EAAE,SAAM,GAAS,EACjB,IAAW,GAAa,EACxB,EAAE,cAAW,QAAQ,GAAY,EACjC,EAAE,QAAK,OAAO,GAAW,EACzB,EAAE,YAAS,EACf,GAcA;EAAE,WAAW,EAAE,OAAI;EAAE,MAAM,CAAC;EAAI,CACjC,EACK,IAAW,GAAM;AAEvB,QACE,kBAAC,GAAD;EACE,OAAO,GAAU,QAAQ,EAAE,kCAAkC,mBAAmB;EAChF,UAAU,EAAE,6BAA6B,+CAA+C;YAEtF,IAUA,kBAAA,GAAA,EAAA,UAAA;GACE,kBAAC,GAAD;IAAa,MAAM,kBAAC,GAAD,EAAU,WAAU,UAAW,CAAA;cAC/C,EACC,4BACA,wMACD;IACW,CAAA;GAEd,kBAAC,GAAD;IACE,YAAW;IACX,OAAO,CACL;KACE,IAAI;KACJ,OAAO,EAAE,8BAA8B,qBAAqB;KAC5D,aAAa,EACX,kCACA,wDACD;KACD,MAAM,kBAAC,GAAD,EAAe,WAAU,UAAW,CAAA;KAC1C,eAAe,EAAS,EAAS,GAAU,WAAW,CAAC;KACxD,EACD;KACE,IAAI;KACJ,OAAO,EAAE,gCAAgC,2BAA2B;KACpE,aAAa,EACX,oCACA,qDACD;KACD,MAAM,kBAAC,GAAD,EAAS,WAAU,UAAW,CAAA;KACpC,eAAe,EAAS,EAAS,GAAU,UAAU,CAAC;KACvD,CACF;IACD,CAAA;GAEF,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD;KAAW,WAAU;KAAQ,MAAA;KAAK,WAAU;eAA5C,CACE,kBAAC,MAAD;MAAI,WAAU;gBACX,EAAE,4BAA4B,UAAU;MACtC,CAAA,EACL,kBAAC,MAAD;MAAI,WAAU;gBAAd;OACE,kBAAC,GAAD;QACE,OAAO,EAAE,6BAA6B,WAAW;QACjD,OAAO,EAAS;QAChB,CAAA;OACF,kBAAC,GAAD;QACE,OAAO,EAAE,6BAA6B,WAAW;QACjD,OAAO,EAAS;QAChB,CAAA;OACF,kBAAC,GAAD;QACE,OAAO,EAAE,oCAAoC,mBAAmB;QAChE,OAAO,OAAO,EAAS,gBAAgB;QACvC,CAAA;OACF,kBAAC,GAAD;QACE,OAAO,EAAE,0BAA0B,QAAQ;QAC3C,OAAO,GAAG,EAAS,SAAS,GAAG,OAAO,EAAS,MAAM,CAAC,QAAQ,EAAE;QAChE,CAAA;OACC;QACK;QACZ,kBAAC,GAAD;KAAa,OAAO,EAAE,2BAA2B,qBAAqB;eACpE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD;OACE,WAAU;OACV,eAAY;OACZ,CAAA,EACF,kBAAC,KAAD,EAAA,UACG,EAAS,cACR,EAAE,kCAAkC,2CAA2C,EAC/E,CAAA,CACA;;KACM,CAAA,CACV;;GACL,EAAA,CAAA,GAhFH,kBAAC,GAAD;GACE,cAAa;GACb,OAAO,EAAE,kCAAkC,oBAAoB;GAC/D,aAAa,EACX,wCACA,yDACD;GACD,CAAA;EA2EM,CAAA"}
|
|
@@ -3,64 +3,111 @@ import { PageShell as t } from "./PageShell.js";
|
|
|
3
3
|
import { StatCard as n } from "../components/StatCard.js";
|
|
4
4
|
import { SectionCard as r } from "../components/SectionCard.js";
|
|
5
5
|
import "../components/index.js";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
6
|
+
import { useMemo as i } from "react";
|
|
7
|
+
import { BarChart3 as a, CalendarCheck as o, DollarSign as s, Percent as c, Users as l } from "lucide-react";
|
|
8
|
+
import { useI18n as u } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
9
|
+
import { useQuery as d } from "@apollo/client/react";
|
|
10
|
+
import { EmphasisPanel as f, PagePurpose as p } from "@burdenoff/fe-libs/ui";
|
|
10
11
|
import { jsx as m, jsxs as h } from "react/jsx-runtime";
|
|
11
12
|
//#region src/ggnomad/pages/AnalyticsPage.tsx
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
var g = [
|
|
14
|
+
"var(--color-chart-1)",
|
|
15
|
+
"var(--color-chart-2)",
|
|
16
|
+
"var(--color-chart-3)",
|
|
17
|
+
"var(--color-chart-4)",
|
|
18
|
+
"var(--color-chart-5)"
|
|
19
|
+
], _ = (e) => e.replace(/[_-]+/g, " ").trim().replace(/\b\w/g, (e) => e.toUpperCase()) || e;
|
|
20
|
+
function v() {
|
|
21
|
+
let { t: v } = u(), { data: y } = d(e), b = y?.ggnomadAnalytics, x = i(() => {
|
|
22
|
+
let e = b?.revenueByCategory ?? {};
|
|
23
|
+
return Object.entries(e).map(([e, t]) => ({
|
|
24
|
+
label: _(e),
|
|
25
|
+
value: Number(t)
|
|
26
|
+
})).filter((e) => Number.isFinite(e.value) && e.value > 0).sort((e, t) => t.value - e.value);
|
|
27
|
+
}, [b?.revenueByCategory]), S = x.length > 0 ? x[0].value : 0;
|
|
14
28
|
return /* @__PURE__ */ h(t, {
|
|
15
|
-
title:
|
|
16
|
-
titleAccent:
|
|
17
|
-
subtitle:
|
|
29
|
+
title: v("ggnomad.analytics.title", "Analytics"),
|
|
30
|
+
titleAccent: v("ggnomad.analytics.title.accent", "Analytics"),
|
|
31
|
+
subtitle: v("ggnomad.analytics.subtitle", "GGNomad performance and growth analytics"),
|
|
18
32
|
children: [
|
|
19
|
-
/* @__PURE__ */ m(
|
|
20
|
-
icon: /* @__PURE__ */ m(
|
|
21
|
-
children:
|
|
33
|
+
/* @__PURE__ */ m(p, {
|
|
34
|
+
icon: /* @__PURE__ */ m(a, { className: "size-4" }),
|
|
35
|
+
children: v("ggnomad.analytics.purpose", "Understand how the marketplace is performing — total revenue, bookings, active users and conversion rate at a glance, with a category breakdown to see which domains (stays, tours, events) are driving growth.")
|
|
22
36
|
}),
|
|
23
|
-
/* @__PURE__ */ h(
|
|
37
|
+
/* @__PURE__ */ h(f, { children: [/* @__PURE__ */ m("p", {
|
|
24
38
|
className: "text-sm text-text-secondary",
|
|
25
|
-
children:
|
|
39
|
+
children: v("ggnomad.analytics.totalRevenue", "Total revenue")
|
|
26
40
|
}), /* @__PURE__ */ h("p", {
|
|
27
41
|
className: "mt-1 text-3xl font-semibold tabular-nums text-text-primary",
|
|
28
|
-
children: ["$", Number(
|
|
42
|
+
children: ["$", Number(b?.totalRevenue ?? 0).toLocaleString()]
|
|
29
43
|
})] }),
|
|
30
44
|
/* @__PURE__ */ h("section", {
|
|
31
45
|
className: "grid gap-4 sm:grid-cols-2 xl:grid-cols-4",
|
|
32
46
|
children: [
|
|
33
47
|
/* @__PURE__ */ m(n, {
|
|
34
48
|
hero: !0,
|
|
35
|
-
label:
|
|
36
|
-
value: `${Number(
|
|
37
|
-
icon: /* @__PURE__ */ m(
|
|
49
|
+
label: v("ggnomad.analytics.conversion", "Conversion"),
|
|
50
|
+
value: `${Number(b?.conversionRate ?? 0).toFixed(2)}%`,
|
|
51
|
+
icon: /* @__PURE__ */ m(c, {})
|
|
38
52
|
}),
|
|
39
53
|
/* @__PURE__ */ m(n, {
|
|
40
|
-
label:
|
|
41
|
-
value: `$${Number(
|
|
42
|
-
icon: /* @__PURE__ */ m(
|
|
54
|
+
label: v("ggnomad.analytics.revenue", "Revenue"),
|
|
55
|
+
value: `$${Number(b?.totalRevenue ?? 0).toLocaleString()}`,
|
|
56
|
+
icon: /* @__PURE__ */ m(s, {})
|
|
43
57
|
}),
|
|
44
58
|
/* @__PURE__ */ m(n, {
|
|
45
|
-
label:
|
|
46
|
-
value: Number(
|
|
47
|
-
icon: /* @__PURE__ */ m(
|
|
59
|
+
label: v("ggnomad.analytics.bookings", "Bookings"),
|
|
60
|
+
value: Number(b?.totalBookings ?? 0).toLocaleString(),
|
|
61
|
+
icon: /* @__PURE__ */ m(o, {})
|
|
48
62
|
}),
|
|
49
63
|
/* @__PURE__ */ m(n, {
|
|
50
|
-
label:
|
|
51
|
-
value: Number(
|
|
52
|
-
icon: /* @__PURE__ */ m(
|
|
64
|
+
label: v("ggnomad.analytics.users", "Users"),
|
|
65
|
+
value: Number(b?.totalUsers ?? 0).toLocaleString(),
|
|
66
|
+
icon: /* @__PURE__ */ m(l, {})
|
|
53
67
|
})
|
|
54
68
|
]
|
|
55
69
|
}),
|
|
56
70
|
/* @__PURE__ */ m(r, {
|
|
57
|
-
title:
|
|
58
|
-
children: /* @__PURE__ */
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
71
|
+
title: v("ggnomad.analytics.revenueByCategory", "Revenue by category"),
|
|
72
|
+
children: x.length === 0 ? /* @__PURE__ */ h("div", {
|
|
73
|
+
className: "flex flex-col items-center gap-2 py-8 text-center",
|
|
74
|
+
children: [
|
|
75
|
+
/* @__PURE__ */ m("span", {
|
|
76
|
+
className: "flex size-10 items-center justify-center rounded-full bg-[var(--color-accent-soft)] text-text-secondary",
|
|
77
|
+
children: /* @__PURE__ */ m(a, { className: "size-5" })
|
|
78
|
+
}),
|
|
79
|
+
/* @__PURE__ */ m("p", {
|
|
80
|
+
className: "text-sm font-medium text-text-primary",
|
|
81
|
+
children: v("ggnomad.analytics.empty.title", "No analytics yet")
|
|
82
|
+
}),
|
|
83
|
+
/* @__PURE__ */ m("p", {
|
|
84
|
+
className: "max-w-sm text-sm text-text-secondary",
|
|
85
|
+
children: v("ggnomad.analytics.empty.description", "Revenue by category appears here as bookings come in across stays, tours and events.")
|
|
86
|
+
})
|
|
87
|
+
]
|
|
88
|
+
}) : /* @__PURE__ */ m("ul", {
|
|
89
|
+
className: "flex flex-col gap-4",
|
|
90
|
+
children: x.map((e, t) => {
|
|
91
|
+
let n = S > 0 ? Math.max(e.value / S * 100, 2) : 0;
|
|
92
|
+
return /* @__PURE__ */ h("li", { children: [/* @__PURE__ */ h("div", {
|
|
93
|
+
className: "flex items-baseline justify-between gap-3",
|
|
94
|
+
children: [/* @__PURE__ */ m("span", {
|
|
95
|
+
className: "truncate text-sm font-medium text-text-primary",
|
|
96
|
+
children: e.label
|
|
97
|
+
}), /* @__PURE__ */ h("span", {
|
|
98
|
+
className: "flex-shrink-0 text-sm tabular-nums text-text-secondary",
|
|
99
|
+
children: ["$", e.value.toLocaleString()]
|
|
100
|
+
})]
|
|
101
|
+
}), /* @__PURE__ */ m("div", {
|
|
102
|
+
className: "mt-1.5 h-2.5 w-full overflow-hidden rounded-full bg-bg-sunken",
|
|
103
|
+
children: /* @__PURE__ */ m("div", {
|
|
104
|
+
className: "h-full rounded-full transition-[width] duration-500 ease-out",
|
|
105
|
+
style: {
|
|
106
|
+
width: `${n}%`,
|
|
107
|
+
backgroundColor: g[t % g.length]
|
|
108
|
+
}
|
|
109
|
+
})
|
|
110
|
+
})] }, e.label);
|
|
64
111
|
})
|
|
65
112
|
})
|
|
66
113
|
})
|
|
@@ -68,6 +115,6 @@ function g() {
|
|
|
68
115
|
});
|
|
69
116
|
}
|
|
70
117
|
//#endregion
|
|
71
|
-
export {
|
|
118
|
+
export { v as AnalyticsPage };
|
|
72
119
|
|
|
73
120
|
//# sourceMappingURL=AnalyticsPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AnalyticsPage.js","names":[],"sources":["../../../src/ggnomad/pages/AnalyticsPage.tsx"],"sourcesContent":["import { useQuery } from '@apollo/client/react';\nimport type { TypedDocumentNode } from '@apollo/client';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { BarChart3, CalendarCheck, DollarSign, Percent, Users } from 'lucide-react';\nimport { EmphasisPanel, PagePurpose
|
|
1
|
+
{"version":3,"file":"AnalyticsPage.js","names":[],"sources":["../../../src/ggnomad/pages/AnalyticsPage.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport { useQuery } from '@apollo/client/react';\nimport type { TypedDocumentNode } from '@apollo/client';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { BarChart3, CalendarCheck, DollarSign, Percent, Users } from 'lucide-react';\nimport { EmphasisPanel, PagePurpose } from '@burdenoff/fe-libs/ui';\n\nimport { GGNOMAD_OVERVIEW_QUERY } from '../graphql';\nimport { PageShell } from './PageShell';\nimport { SectionCard, StatCard } from '../components';\n\nconst CHART_COLORS = [\n 'var(--color-chart-1)',\n 'var(--color-chart-2)',\n 'var(--color-chart-3)',\n 'var(--color-chart-4)',\n 'var(--color-chart-5)',\n];\n\nconst humanizeCategory = (key: string): string =>\n key\n .replace(/[_-]+/g, ' ')\n .trim()\n .replace(/\\b\\w/g, (char) => char.toUpperCase()) || key;\n\nexport function AnalyticsPage() {\n const { t } = useI18n();\n const { data } = useQuery(\n GGNOMAD_OVERVIEW_QUERY as TypedDocumentNode<\n {\n ggnomadAnalytics: {\n totalRevenue: number;\n totalBookings: number;\n totalUsers: number;\n conversionRate: number;\n revenueByCategory: Record<string, unknown>;\n };\n },\n Record<string, never>\n >\n );\n const analytics = data?.ggnomadAnalytics;\n\n const categories = useMemo(() => {\n const raw = analytics?.revenueByCategory ?? {};\n return Object.entries(raw)\n .map(([key, value]) => ({ label: humanizeCategory(key), value: Number(value) }))\n .filter((entry) => Number.isFinite(entry.value) && entry.value > 0)\n .sort((a, b) => b.value - a.value);\n }, [analytics?.revenueByCategory]);\n\n const maxCategory = categories.length > 0 ? categories[0].value : 0;\n\n return (\n <PageShell\n title={t('ggnomad.analytics.title', 'Analytics')}\n titleAccent={t('ggnomad.analytics.title.accent', 'Analytics')}\n subtitle={t('ggnomad.analytics.subtitle', 'GGNomad performance and growth analytics')}\n >\n <PagePurpose icon={<BarChart3 className=\"size-4\" />}>\n {t(\n 'ggnomad.analytics.purpose',\n 'Understand how the marketplace is performing — total revenue, bookings, active users and conversion rate at a glance, with a category breakdown to see which domains (stays, tours, events) are driving growth.'\n )}\n </PagePurpose>\n\n <EmphasisPanel>\n <p className=\"text-sm text-text-secondary\">\n {t('ggnomad.analytics.totalRevenue', 'Total revenue')}\n </p>\n <p className=\"mt-1 text-3xl font-semibold tabular-nums text-text-primary\">\n ${Number(analytics?.totalRevenue ?? 0).toLocaleString()}\n </p>\n </EmphasisPanel>\n\n <section className=\"grid gap-4 sm:grid-cols-2 xl:grid-cols-4\">\n <StatCard\n hero\n label={t('ggnomad.analytics.conversion', 'Conversion')}\n value={`${Number(analytics?.conversionRate ?? 0).toFixed(2)}%`}\n icon={<Percent />}\n />\n <StatCard\n label={t('ggnomad.analytics.revenue', 'Revenue')}\n value={`$${Number(analytics?.totalRevenue ?? 0).toLocaleString()}`}\n icon={<DollarSign />}\n />\n <StatCard\n label={t('ggnomad.analytics.bookings', 'Bookings')}\n value={Number(analytics?.totalBookings ?? 0).toLocaleString()}\n icon={<CalendarCheck />}\n />\n <StatCard\n label={t('ggnomad.analytics.users', 'Users')}\n value={Number(analytics?.totalUsers ?? 0).toLocaleString()}\n icon={<Users />}\n />\n </section>\n\n <SectionCard title={t('ggnomad.analytics.revenueByCategory', 'Revenue by category')}>\n {categories.length === 0 ? (\n <div className=\"flex flex-col items-center gap-2 py-8 text-center\">\n <span className=\"flex size-10 items-center justify-center rounded-full bg-[var(--color-accent-soft)] text-text-secondary\">\n <BarChart3 className=\"size-5\" />\n </span>\n <p className=\"text-sm font-medium text-text-primary\">\n {t('ggnomad.analytics.empty.title', 'No analytics yet')}\n </p>\n <p className=\"max-w-sm text-sm text-text-secondary\">\n {t(\n 'ggnomad.analytics.empty.description',\n 'Revenue by category appears here as bookings come in across stays, tours and events.'\n )}\n </p>\n </div>\n ) : (\n <ul className=\"flex flex-col gap-4\">\n {categories.map((category, index) => {\n const pct = maxCategory > 0 ? Math.max((category.value / maxCategory) * 100, 2) : 0;\n return (\n <li key={category.label}>\n <div className=\"flex items-baseline justify-between gap-3\">\n <span className=\"truncate text-sm font-medium text-text-primary\">\n {category.label}\n </span>\n <span className=\"flex-shrink-0 text-sm tabular-nums text-text-secondary\">\n ${category.value.toLocaleString()}\n </span>\n </div>\n <div className=\"mt-1.5 h-2.5 w-full overflow-hidden rounded-full bg-bg-sunken\">\n <div\n className=\"h-full rounded-full transition-[width] duration-500 ease-out\"\n style={{\n width: `${pct}%`,\n backgroundColor: CHART_COLORS[index % CHART_COLORS.length],\n }}\n />\n </div>\n </li>\n );\n })}\n </ul>\n )}\n </SectionCard>\n </PageShell>\n );\n}\n"],"mappings":"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"}
|