@burdenoff/microfe-ggnomad 2026.626.1 → 2026.704.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.
@@ -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
- `, r = e`
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
- `, i = e`
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
- `, a = e`
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
- `, o = e`
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
- `, s = e`
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
- `, c = e`
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
- `, l = e`
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
- `, u = e`
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
- `, d = e`
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
- `, f = e`
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
- `, p = e`
298
+ `, h = e`
255
299
  query GGNomadFavorites {
256
300
  ggnomadFavorites
257
301
  }
258
- `, m = e`
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
- `, h = e`
317
+ `, _ = e`
274
318
  mutation ToggleGGNomadFavorite($input: ToggleGgnomadFavoriteInput!) {
275
319
  toggleGgnomadFavorite(input: $input)
276
320
  }
277
- `, g = e`
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 { g as CANCEL_GGNOMAD_BOOKING_MUTATION, m as CREATE_GGNOMAD_BOOKING_MUTATION, l as GGNOMAD_ACTIVITIES_QUERY, u as GGNOMAD_ACTIVITY_QUERY, f as GGNOMAD_BOOKINGS_QUERY, s as GGNOMAD_EVENTS_QUERY, c as GGNOMAD_EVENT_QUERY, p as GGNOMAD_FAVORITES_QUERY, t as GGNOMAD_OVERVIEW_QUERY, r as GGNOMAD_PROPERTIES_QUERY, i as GGNOMAD_PROPERTY_QUERY, n as GGNOMAD_PROVIDERS_QUERY, a as GGNOMAD_TOURS_QUERY, o as GGNOMAD_TOUR_QUERY, d as GGNOMAD_TRIPS_QUERY, h as TOGGLE_GGNOMAD_FAVORITE_MUTATION };
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 monthlyTrends\n topDestinations\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;;;;;;;;;;;;;;;;;;GAoB5B,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"}
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,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 { BarChart3 as i, CalendarCheck as a, DollarSign as o, Percent as s, Users as c } from "lucide-react";
7
- import { useI18n as l } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
8
- import { useQuery as u } from "@apollo/client/react";
9
- import { EmphasisPanel as d, PagePurpose as f, ShowMore as p } from "@burdenoff/fe-libs/ui";
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
- function g() {
13
- let { t: g } = l(), { data: _ } = u(e), v = _?.ggnomadAnalytics;
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: g("ggnomad.analytics.title", "Analytics"),
16
- titleAccent: g("ggnomad.analytics.title.accent", "Analytics"),
17
- subtitle: g("ggnomad.analytics.subtitle", "GGNomad performance and growth analytics"),
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(f, {
20
- icon: /* @__PURE__ */ m(i, { className: "size-4" }),
21
- children: g("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.")
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(d, { children: [/* @__PURE__ */ m("p", {
37
+ /* @__PURE__ */ h(f, { children: [/* @__PURE__ */ m("p", {
24
38
  className: "text-sm text-text-secondary",
25
- children: g("ggnomad.analytics.totalRevenue", "Total revenue")
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(v?.totalRevenue ?? 0).toLocaleString()]
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: g("ggnomad.analytics.conversion", "Conversion"),
36
- value: `${Number(v?.conversionRate ?? 0).toFixed(2)}%`,
37
- icon: /* @__PURE__ */ m(s, {})
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: g("ggnomad.analytics.revenue", "Revenue"),
41
- value: `$${Number(v?.totalRevenue ?? 0).toLocaleString()}`,
42
- icon: /* @__PURE__ */ m(o, {})
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: g("ggnomad.analytics.bookings", "Bookings"),
46
- value: Number(v?.totalBookings ?? 0).toLocaleString(),
47
- icon: /* @__PURE__ */ m(a, {})
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: g("ggnomad.analytics.users", "Users"),
51
- value: Number(v?.totalUsers ?? 0).toLocaleString(),
52
- icon: /* @__PURE__ */ m(c, {})
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: g("ggnomad.analytics.revenueByCategory", "Revenue by category"),
58
- children: /* @__PURE__ */ m(p, {
59
- collapsedHeight: 140,
60
- label: g("ggnomad.analytics.rawData", "Raw breakdown"),
61
- children: /* @__PURE__ */ m("pre", {
62
- className: "overflow-auto rounded-lg border border-border-seam bg-bg-sunken/40 p-4 text-xs text-text-secondary",
63
- children: JSON.stringify(v?.revenueByCategory ?? {}, null, 2)
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 { g as AnalyticsPage };
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, ShowMore } from '@burdenoff/fe-libs/ui';\n\nimport { GGNOMAD_OVERVIEW_QUERY } from '../graphql';\nimport { PageShell } from './PageShell';\nimport { SectionCard, StatCard } from '../components';\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 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 <ShowMore collapsedHeight={140} label={t('ggnomad.analytics.rawData', 'Raw breakdown')}>\n <pre className=\"overflow-auto rounded-lg border border-border-seam bg-bg-sunken/40 p-4 text-xs text-text-secondary\">\n {JSON.stringify(analytics?.revenueByCategory ?? {}, null, 2)}\n </pre>\n </ShowMore>\n </SectionCard>\n </PageShell>\n );\n}\n"],"mappings":";;;;;;;;;;;AAUA,SAAgB,IAAgB;CAC9B,IAAM,EAAE,SAAM,GAAS,EACjB,EAAE,YAAS,EACf,EAYD,EACK,IAAY,GAAM;AAExB,QACE,kBAAC,GAAD;EACE,OAAO,EAAE,2BAA2B,YAAY;EAChD,aAAa,EAAE,kCAAkC,YAAY;EAC7D,UAAU,EAAE,8BAA8B,2CAA2C;YAHvF;GAKE,kBAAC,GAAD;IAAa,MAAM,kBAAC,GAAD,EAAW,WAAU,UAAW,CAAA;cAChD,EACC,6BACA,kNACD;IACW,CAAA;GAEd,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,KAAD;IAAG,WAAU;cACV,EAAE,kCAAkC,gBAAgB;IACnD,CAAA,EACJ,kBAAC,KAAD;IAAG,WAAU;cAAb,CAA0E,KACtE,OAAO,GAAW,gBAAgB,EAAE,CAAC,gBAAgB,CACrD;MACU,EAAA,CAAA;GAEhB,kBAAC,WAAD;IAAS,WAAU;cAAnB;KACE,kBAAC,GAAD;MACE,MAAA;MACA,OAAO,EAAE,gCAAgC,aAAa;MACtD,OAAO,GAAG,OAAO,GAAW,kBAAkB,EAAE,CAAC,QAAQ,EAAE,CAAC;MAC5D,MAAM,kBAAC,GAAD,EAAW,CAAA;MACjB,CAAA;KACF,kBAAC,GAAD;MACE,OAAO,EAAE,6BAA6B,UAAU;MAChD,OAAO,IAAI,OAAO,GAAW,gBAAgB,EAAE,CAAC,gBAAgB;MAChE,MAAM,kBAAC,GAAD,EAAc,CAAA;MACpB,CAAA;KACF,kBAAC,GAAD;MACE,OAAO,EAAE,8BAA8B,WAAW;MAClD,OAAO,OAAO,GAAW,iBAAiB,EAAE,CAAC,gBAAgB;MAC7D,MAAM,kBAAC,GAAD,EAAiB,CAAA;MACvB,CAAA;KACF,kBAAC,GAAD;MACE,OAAO,EAAE,2BAA2B,QAAQ;MAC5C,OAAO,OAAO,GAAW,cAAc,EAAE,CAAC,gBAAgB;MAC1D,MAAM,kBAAC,GAAD,EAAS,CAAA;MACf,CAAA;KACM;;GAEV,kBAAC,GAAD;IAAa,OAAO,EAAE,uCAAuC,sBAAsB;cACjF,kBAAC,GAAD;KAAU,iBAAiB;KAAK,OAAO,EAAE,6BAA6B,gBAAgB;eACpF,kBAAC,OAAD;MAAK,WAAU;gBACZ,KAAK,UAAU,GAAW,qBAAqB,EAAE,EAAE,MAAM,EAAE;MACxD,CAAA;KACG,CAAA;IACC,CAAA;GACJ"}
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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"}
@@ -1,119 +1,193 @@
1
- import { CANCEL_GGNOMAD_BOOKING_MUTATION as e, CREATE_GGNOMAD_BOOKING_MUTATION as t, GGNOMAD_BOOKINGS_QUERY as n } from "../graphql.js";
2
- import { PageShell as r } from "./PageShell.js";
3
- import { StatusBadge as i } from "../components/StatusBadge.js";
4
- import { EntityCard as a } from "../components/EntityCard.js";
1
+ import { CANCEL_GGNOMAD_BOOKING_MUTATION as e, CREATE_GGNOMAD_BOOKING_MUTATION as t, GGNOMAD_BOOKABLE_SAMPLE_QUERY as n, GGNOMAD_BOOKINGS_QUERY as r } from "../graphql.js";
2
+ import { PageShell as i } from "./PageShell.js";
3
+ import { StatusBadge as a } from "../components/StatusBadge.js";
4
+ import { EntityCard as o } from "../components/EntityCard.js";
5
5
  import "../components/index.js";
6
- import { CalendarCheck as o, Compass as s, Hotel as c, Plus as l, Ticket as u, XCircle as d } from "lucide-react";
7
- import { useI18n as f } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
8
- import { useNavigate as p } from "react-router-dom";
9
- import { useMutation as m, useQuery as h } from "@apollo/client/react";
10
- import { CTAOverflowMenu as g, GlassCard as _, IllustratedEmptyState as v, NextSteps as y, PagePurpose as b } from "@burdenoff/fe-libs/ui";
11
- import { jsx as x, jsxs as S } from "react/jsx-runtime";
6
+ import { notify as s } from "../utils/notify.js";
7
+ import { useState as c } from "react";
8
+ import { CalendarCheck as l, Compass as u, Hotel as d, Plus as f, Ticket as p, XCircle as m } from "lucide-react";
9
+ import { useI18n as h } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
10
+ import { useNavigate as g } from "react-router-dom";
11
+ import { useMutation as _, useQuery as v } from "@apollo/client/react";
12
+ import { AlertDialog as y, AlertDialogAction as b, AlertDialogCancel as x, AlertDialogContent as S, AlertDialogDescription as C, AlertDialogFooter as w, AlertDialogHeader as T, AlertDialogTitle as E, CTAOverflowMenu as D, GlassCard as O, IllustratedEmptyState as k, NextSteps as A, PagePurpose as j } from "@burdenoff/fe-libs/ui";
13
+ import { jsx as M, jsxs as N } from "react/jsx-runtime";
12
14
  //#region src/ggnomad/pages/BookingsPage.tsx
13
- function C() {
14
- let { t: C } = f(), w = p(), { data: T, refetch: E } = h(n, { variables: {
15
+ function P() {
16
+ let { t: P } = h(), F = g(), [I, L] = c(null), { data: R, refetch: z } = v(r, { variables: {
15
17
  first: 20,
16
18
  skip: 0
17
- } }), [D, { loading: O }] = m(t, { onCompleted: () => void E() }), [k] = m(e, { onCompleted: () => void E() }), A = T?.ggnomadBookings ?? [], j = () => {
18
- D({ variables: { input: {
19
- bookingType: "PROPERTY",
20
- itemId: "demo-property",
21
- itemName: "Demo Property",
22
- guestName: "Demo Guest",
23
- guestEmail: "demo@burdenoff.com",
24
- guestPhone: "+910000000000",
25
- guests: 2,
26
- totalPrice: 299,
27
- currency: "USD",
28
- paymentMethod: "card"
29
- } } });
30
- }, M = (e) => {
31
- window.confirm(C("ggnomad.bookings.actions.cancelConfirm", "Cancel this booking? This cannot be undone.")) && k({ variables: {
32
- id: e,
33
- reason: C("ggnomad.bookings.actions.cancelReason", "Cancelled via UI")
34
- } });
19
+ } }), { data: B } = v(n), [V, { loading: H }] = _(t), [U, { loading: W }] = _(e), G = R?.ggnomadBookings ?? [], K = () => {
20
+ let e = B?.ggnomadProperties?.[0];
21
+ if (e) return {
22
+ type: "PROPERTY",
23
+ listing: {
24
+ id: e.id,
25
+ name: e.name,
26
+ price: e.pricePerNight,
27
+ currency: e.currency
28
+ }
29
+ };
30
+ let t = B?.ggnomadTours?.[0];
31
+ if (t) return {
32
+ type: "TOUR",
33
+ listing: t
34
+ };
35
+ let n = B?.ggnomadEvents?.[0];
36
+ if (n) return {
37
+ type: "EVENT",
38
+ listing: n
39
+ };
40
+ let r = B?.ggnomadActivities?.[0];
41
+ return r ? {
42
+ type: "ACTIVITY",
43
+ listing: r
44
+ } : null;
45
+ }, q = async () => {
46
+ let e = K();
47
+ if (!e) {
48
+ s.warning(P("ggnomad.bookings.toast.noListingsTitle", "No listings available to book yet"), P("ggnomad.bookings.toast.noListingsBody", "Add a property, tour, event or activity first, then create a booking against it."));
49
+ return;
50
+ }
51
+ let { type: t, listing: n } = e;
52
+ try {
53
+ let e = await V({ variables: { input: {
54
+ bookingType: t,
55
+ itemId: n.id,
56
+ itemName: n.name,
57
+ guestName: P("ggnomad.bookings.demo.guestName", "Demo Guest"),
58
+ guestEmail: "demo@burdenoff.com",
59
+ guests: 2,
60
+ totalPrice: Number(n.price ?? 0),
61
+ currency: n.currency ?? "USD",
62
+ paymentMethod: "card"
63
+ } } });
64
+ if (e.error) {
65
+ s.error(P("ggnomad.bookings.toast.createErrorTitle", "Could not create booking"), e.error.message);
66
+ return;
67
+ }
68
+ let r = e.data?.createGgnomadBooking;
69
+ s.success(P("ggnomad.bookings.toast.createSuccessTitle", "Booking confirmed"), r ? P("ggnomad.bookings.toast.createSuccessBody", "{{ref}} · {{item}}").replace("{{ref}}", r.referenceCode).replace("{{item}}", r.itemName) : n.name), await z();
70
+ } catch (e) {
71
+ s.error(P("ggnomad.bookings.toast.createErrorTitle", "Could not create booking"), e instanceof Error ? e.message : String(e));
72
+ }
73
+ }, J = async () => {
74
+ if (I) try {
75
+ let e = await U({ variables: {
76
+ id: I.id,
77
+ reason: P("ggnomad.bookings.actions.cancelReason", "Cancelled via UI")
78
+ } });
79
+ if (e.error) {
80
+ s.error(P("ggnomad.bookings.toast.cancelErrorTitle", "Could not cancel booking"), e.error.message);
81
+ return;
82
+ }
83
+ s.success(P("ggnomad.bookings.toast.cancelSuccessTitle", "Booking cancelled"), I.referenceCode), await z();
84
+ } catch (e) {
85
+ s.error(P("ggnomad.bookings.toast.cancelErrorTitle", "Could not cancel booking"), e instanceof Error ? e.message : String(e));
86
+ } finally {
87
+ L(null);
88
+ }
35
89
  };
36
- return /* @__PURE__ */ S(r, {
37
- title: C("ggnomad.bookings.title", "Bookings"),
38
- titleAccent: C("ggnomad.bookings.title.accent", "Bookings"),
39
- subtitle: C("ggnomad.bookings.subtitle", "End-to-end booking operations with real API mutations"),
40
- actions: /* @__PURE__ */ x(g, {
90
+ return /* @__PURE__ */ N(i, {
91
+ title: P("ggnomad.bookings.title", "Bookings"),
92
+ titleAccent: P("ggnomad.bookings.title.accent", "Bookings"),
93
+ subtitle: P("ggnomad.bookings.subtitle", "End-to-end booking operations with real API mutations"),
94
+ actions: /* @__PURE__ */ M(D, {
41
95
  primary: {
42
- label: C("ggnomad.bookings.actions.createDemoBooking", "Create demo booking"),
43
- onSelect: j,
44
- disabled: O,
45
- icon: /* @__PURE__ */ x(l, {})
96
+ label: P("ggnomad.bookings.actions.createDemoBooking", "Create demo booking"),
97
+ onSelect: () => void q(),
98
+ disabled: H,
99
+ icon: /* @__PURE__ */ M(f, {})
46
100
  },
47
101
  actions: []
48
102
  }),
49
103
  children: [
50
- /* @__PURE__ */ x(b, {
51
- icon: /* @__PURE__ */ x(o, { className: "size-4" }),
52
- children: C("ggnomad.bookings.purpose", "Your single place to manage every reservation across travel, stays, tours and events. Track each booking’s status and reference code in real time, and cancel a reservation when plans change — all backed by live API mutations.")
104
+ /* @__PURE__ */ M(j, {
105
+ icon: /* @__PURE__ */ M(l, { className: "size-4" }),
106
+ children: P("ggnomad.bookings.purpose", "Your single place to manage every reservation across travel, stays, tours and events. Track each booking’s status and reference code in real time, and cancel a reservation when plans change — all backed by live API mutations.")
53
107
  }),
54
- /* @__PURE__ */ x(y, {
108
+ /* @__PURE__ */ M(A, {
55
109
  storageKey: "ggnomad-bookings",
56
110
  steps: [
57
111
  {
58
112
  id: "book-stay",
59
- label: C("ggnomad.bookings.next.stay", "Book a place to stay"),
60
- description: C("ggnomad.bookings.next.stayDesc", "Browse hospitality listings and reserve a property."),
61
- icon: /* @__PURE__ */ x(c, { className: "size-4" }),
62
- onClick: () => w("../hospitality")
113
+ label: P("ggnomad.bookings.next.stay", "Book a place to stay"),
114
+ description: P("ggnomad.bookings.next.stayDesc", "Browse hospitality listings and reserve a property."),
115
+ icon: /* @__PURE__ */ M(d, { className: "size-4" }),
116
+ onClick: () => F("../hospitality")
63
117
  },
64
118
  {
65
119
  id: "book-tour",
66
- label: C("ggnomad.bookings.next.tour", "Add a tour or activity"),
67
- description: C("ggnomad.bookings.next.tourDesc", "Pick a curated experience from Tourism."),
68
- icon: /* @__PURE__ */ x(s, { className: "size-4" }),
69
- onClick: () => w("../tourism")
120
+ label: P("ggnomad.bookings.next.tour", "Add a tour or activity"),
121
+ description: P("ggnomad.bookings.next.tourDesc", "Pick a curated experience from Tourism."),
122
+ icon: /* @__PURE__ */ M(u, { className: "size-4" }),
123
+ onClick: () => F("../tourism")
70
124
  },
71
125
  {
72
126
  id: "book-event",
73
- label: C("ggnomad.bookings.next.event", "Grab event tickets"),
74
- description: C("ggnomad.bookings.next.eventDesc", "Find live events to round out the itinerary."),
75
- icon: /* @__PURE__ */ x(u, { className: "size-4" }),
76
- onClick: () => w("../entertainment")
127
+ label: P("ggnomad.bookings.next.event", "Grab event tickets"),
128
+ description: P("ggnomad.bookings.next.eventDesc", "Find live events to round out the itinerary."),
129
+ icon: /* @__PURE__ */ M(p, { className: "size-4" }),
130
+ onClick: () => F("../entertainment")
77
131
  }
78
132
  ]
79
133
  }),
80
- A.length === 0 ? /* @__PURE__ */ x(v, {
134
+ G.length === 0 ? /* @__PURE__ */ M(k, {
81
135
  illustration: "empty-data",
82
- title: C("ggnomad.bookings.emptyTitle", "No bookings yet"),
83
- description: C("ggnomad.bookings.emptyDescription", "Create your first booking to see it tracked here in real time.")
84
- }) : /* @__PURE__ */ S(_, {
136
+ title: P("ggnomad.bookings.emptyTitle", "No bookings yet"),
137
+ description: P("ggnomad.bookings.emptyDescription", "Create your first booking to see it tracked here in real time.")
138
+ }) : /* @__PURE__ */ N(O, {
85
139
  treatment: "glass",
86
140
  glow: !0,
87
141
  className: "border-border-seam p-5",
88
- children: [/* @__PURE__ */ x("h2", {
142
+ children: [/* @__PURE__ */ M("h2", {
89
143
  className: "mb-4 text-lg font-medium tracking-tight text-text-primary",
90
- children: C("ggnomad.bookings.activeBookings", "Your bookings")
91
- }), /* @__PURE__ */ x("div", {
144
+ children: P("ggnomad.bookings.activeBookings", "Your bookings")
145
+ }), /* @__PURE__ */ M("div", {
92
146
  className: "grid gap-3 sm:grid-cols-2",
93
- children: A.map((e) => /* @__PURE__ */ x(a, {
147
+ children: G.map((e) => /* @__PURE__ */ M(o, {
94
148
  title: e.referenceCode,
95
149
  meta: e.itemName,
96
150
  price: `${e.currency} ${Number(e.totalPrice).toFixed(2)}`,
97
- badge: /* @__PURE__ */ x(i, {
151
+ badge: /* @__PURE__ */ M(a, {
98
152
  status: e.status,
99
153
  live: e.status !== "CANCELLED"
100
154
  }),
101
- footer: e.status === "CANCELLED" ? null : /* @__PURE__ */ x(g, {
155
+ footer: e.status === "CANCELLED" ? null : /* @__PURE__ */ M(D, {
102
156
  align: "end",
103
157
  actions: [{
104
- label: C("ggnomad.bookings.actions.cancel", "Cancel booking"),
105
- onSelect: () => M(e.id),
158
+ label: P("ggnomad.bookings.actions.cancel", "Cancel booking"),
159
+ onSelect: () => L({
160
+ id: e.id,
161
+ referenceCode: e.referenceCode
162
+ }),
106
163
  intent: "destructive",
107
- icon: /* @__PURE__ */ x(d, {})
164
+ icon: /* @__PURE__ */ M(m, {})
108
165
  }]
109
166
  })
110
167
  }, e.id))
111
168
  })]
169
+ }),
170
+ /* @__PURE__ */ M(y, {
171
+ open: I !== null,
172
+ onOpenChange: (e) => {
173
+ e || L(null);
174
+ },
175
+ children: /* @__PURE__ */ N(S, { children: [/* @__PURE__ */ N(T, { children: [/* @__PURE__ */ M(E, { children: P("ggnomad.bookings.cancelDialog.title", "Cancel this booking?") }), /* @__PURE__ */ M(C, { children: P("ggnomad.bookings.cancelDialog.body", "This releases reservation {{ref}} and cannot be undone.").replace("{{ref}}", I?.referenceCode ?? "") })] }), /* @__PURE__ */ N(w, { children: [/* @__PURE__ */ M(x, {
176
+ disabled: W,
177
+ children: P("ggnomad.bookings.cancelDialog.keep", "Keep booking")
178
+ }), /* @__PURE__ */ M(b, {
179
+ className: "bg-action-danger-bg text-action-danger-text hover:bg-action-danger-bgHover",
180
+ disabled: W,
181
+ onClick: (e) => {
182
+ e.preventDefault(), J();
183
+ },
184
+ children: W ? P("ggnomad.bookings.cancelDialog.cancelling", "Cancelling…") : P("ggnomad.bookings.cancelDialog.confirm", "Cancel booking")
185
+ })] })] })
112
186
  })
113
187
  ]
114
188
  });
115
189
  }
116
190
  //#endregion
117
- export { C as BookingsPage };
191
+ export { P as BookingsPage };
118
192
 
119
193
  //# sourceMappingURL=BookingsPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"BookingsPage.js","names":[],"sources":["../../../src/ggnomad/pages/BookingsPage.tsx"],"sourcesContent":["import { useMutation, useQuery } from '@apollo/client/react';\nimport type { TypedDocumentNode } from '@apollo/client';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { CalendarCheck, Compass, Hotel, Plus, Ticket, XCircle } from 'lucide-react';\nimport { useNavigate } from 'react-router-dom';\nimport {\n CTAOverflowMenu,\n GlassCard,\n IllustratedEmptyState,\n NextSteps,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\n\nimport {\n CANCEL_GGNOMAD_BOOKING_MUTATION,\n CREATE_GGNOMAD_BOOKING_MUTATION,\n GGNOMAD_BOOKINGS_QUERY,\n} from '../graphql';\nimport { PageShell } from './PageShell';\nimport { EntityCard, StatusBadge } from '../components';\n\nexport function BookingsPage() {\n const { t } = useI18n();\n const navigate = useNavigate();\n const { data, refetch } = useQuery(\n GGNOMAD_BOOKINGS_QUERY as TypedDocumentNode<\n {\n ggnomadBookings: Array<{\n id: string;\n referenceCode: string;\n itemName: string;\n status: string;\n totalPrice: number;\n currency: string;\n }>;\n },\n { first: number; skip: number }\n >,\n { variables: { first: 20, skip: 0 } }\n );\n const [createBooking, { loading: creating }] = useMutation(CREATE_GGNOMAD_BOOKING_MUTATION, {\n onCompleted: () => void refetch(),\n });\n const [cancelBooking] = useMutation(CANCEL_GGNOMAD_BOOKING_MUTATION, {\n onCompleted: () => void refetch(),\n });\n\n const bookings = data?.ggnomadBookings ?? [];\n\n const handleCreateDemoBooking = () => {\n void createBooking({\n variables: {\n input: {\n bookingType: 'PROPERTY',\n itemId: 'demo-property',\n itemName: 'Demo Property',\n guestName: 'Demo Guest',\n guestEmail: 'demo@burdenoff.com',\n guestPhone: '+910000000000',\n guests: 2,\n totalPrice: 299,\n currency: 'USD',\n paymentMethod: 'card',\n },\n },\n });\n };\n\n const handleCancel = (id: string) => {\n if (\n !window.confirm(\n t('ggnomad.bookings.actions.cancelConfirm', 'Cancel this booking? This cannot be undone.')\n )\n ) {\n return;\n }\n void cancelBooking({\n variables: {\n id,\n reason: t('ggnomad.bookings.actions.cancelReason', 'Cancelled via UI'),\n },\n });\n };\n\n return (\n <PageShell\n title={t('ggnomad.bookings.title', 'Bookings')}\n titleAccent={t('ggnomad.bookings.title.accent', 'Bookings')}\n subtitle={t(\n 'ggnomad.bookings.subtitle',\n 'End-to-end booking operations with real API mutations'\n )}\n actions={\n <CTAOverflowMenu\n primary={{\n label: t('ggnomad.bookings.actions.createDemoBooking', 'Create demo booking'),\n onSelect: handleCreateDemoBooking,\n disabled: creating,\n icon: <Plus />,\n }}\n actions={[]}\n />\n }\n >\n <PagePurpose icon={<CalendarCheck className=\"size-4\" />}>\n {t(\n 'ggnomad.bookings.purpose',\n 'Your single place to manage every reservation across travel, stays, tours and events. Track each booking’s status and reference code in real time, and cancel a reservation when plans change — all backed by live API mutations.'\n )}\n </PagePurpose>\n\n <NextSteps\n storageKey=\"ggnomad-bookings\"\n steps={[\n {\n id: 'book-stay',\n label: t('ggnomad.bookings.next.stay', 'Book a place to stay'),\n description: t(\n 'ggnomad.bookings.next.stayDesc',\n 'Browse hospitality listings and reserve a property.'\n ),\n icon: <Hotel className=\"size-4\" />,\n onClick: () => navigate('../hospitality'),\n },\n {\n id: 'book-tour',\n label: t('ggnomad.bookings.next.tour', 'Add a tour or activity'),\n description: t(\n 'ggnomad.bookings.next.tourDesc',\n 'Pick a curated experience from Tourism.'\n ),\n icon: <Compass className=\"size-4\" />,\n onClick: () => navigate('../tourism'),\n },\n {\n id: 'book-event',\n label: t('ggnomad.bookings.next.event', 'Grab event tickets'),\n description: t(\n 'ggnomad.bookings.next.eventDesc',\n 'Find live events to round out the itinerary.'\n ),\n icon: <Ticket className=\"size-4\" />,\n onClick: () => navigate('../entertainment'),\n },\n ]}\n />\n\n {bookings.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={t('ggnomad.bookings.emptyTitle', 'No bookings yet')}\n description={t(\n 'ggnomad.bookings.emptyDescription',\n 'Create your first booking to see it tracked here in real time.'\n )}\n />\n ) : (\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.bookings.activeBookings', 'Your bookings')}\n </h2>\n <div className=\"grid gap-3 sm:grid-cols-2\">\n {bookings.map((booking) => (\n <EntityCard\n key={booking.id}\n title={booking.referenceCode}\n meta={booking.itemName}\n price={`${booking.currency} ${Number(booking.totalPrice).toFixed(2)}`}\n badge={\n <StatusBadge status={booking.status} live={booking.status !== 'CANCELLED'} />\n }\n footer={\n booking.status !== 'CANCELLED' ? (\n <CTAOverflowMenu\n align=\"end\"\n actions={[\n {\n label: t('ggnomad.bookings.actions.cancel', 'Cancel booking'),\n onSelect: () => handleCancel(booking.id),\n intent: 'destructive',\n icon: <XCircle />,\n },\n ]}\n />\n ) : null\n }\n />\n ))}\n </div>\n </GlassCard>\n )}\n </PageShell>\n );\n}\n"],"mappings":";;;;;;;;;;;;AAqBA,SAAgB,IAAe;CAC7B,IAAM,EAAE,SAAM,GAAS,EACjB,IAAW,GAAa,EACxB,EAAE,SAAM,eAAY,EACxB,GAaA,EAAE,WAAW;EAAE,OAAO;EAAI,MAAM;EAAG,EAAE,CACtC,EACK,CAAC,GAAe,EAAE,SAAS,OAAc,EAAY,GAAiC,EAC1F,mBAAmB,KAAK,GAAS,EAClC,CAAC,EACI,CAAC,KAAiB,EAAY,GAAiC,EACnE,mBAAmB,KAAK,GAAS,EAClC,CAAC,EAEI,IAAW,GAAM,mBAAmB,EAAE,EAEtC,UAAgC;AAC/B,IAAc,EACjB,WAAW,EACT,OAAO;GACL,aAAa;GACb,QAAQ;GACR,UAAU;GACV,WAAW;GACX,YAAY;GACZ,YAAY;GACZ,QAAQ;GACR,YAAY;GACZ,UAAU;GACV,eAAe;GAChB,EACF,EACF,CAAC;IAGE,KAAgB,MAAe;AAEhC,SAAO,QACN,EAAE,0CAA0C,8CAA8C,CAC3F,IAIE,EAAc,EACjB,WAAW;GACT;GACA,QAAQ,EAAE,yCAAyC,mBAAmB;GACvE,EACF,CAAC;;AAGJ,QACE,kBAAC,GAAD;EACE,OAAO,EAAE,0BAA0B,WAAW;EAC9C,aAAa,EAAE,iCAAiC,WAAW;EAC3D,UAAU,EACR,6BACA,wDACD;EACD,SACE,kBAAC,GAAD;GACE,SAAS;IACP,OAAO,EAAE,8CAA8C,sBAAsB;IAC7E,UAAU;IACV,UAAU;IACV,MAAM,kBAAC,GAAD,EAAQ,CAAA;IACf;GACD,SAAS,EAAE;GACX,CAAA;YAhBN;GAmBE,kBAAC,GAAD;IAAa,MAAM,kBAAC,GAAD,EAAe,WAAU,UAAW,CAAA;cACpD,EACC,4BACA,oOACD;IACW,CAAA;GAEd,kBAAC,GAAD;IACE,YAAW;IACX,OAAO;KACL;MACE,IAAI;MACJ,OAAO,EAAE,8BAA8B,uBAAuB;MAC9D,aAAa,EACX,kCACA,sDACD;MACD,MAAM,kBAAC,GAAD,EAAO,WAAU,UAAW,CAAA;MAClC,eAAe,EAAS,iBAAiB;MAC1C;KACD;MACE,IAAI;MACJ,OAAO,EAAE,8BAA8B,yBAAyB;MAChE,aAAa,EACX,kCACA,0CACD;MACD,MAAM,kBAAC,GAAD,EAAS,WAAU,UAAW,CAAA;MACpC,eAAe,EAAS,aAAa;MACtC;KACD;MACE,IAAI;MACJ,OAAO,EAAE,+BAA+B,qBAAqB;MAC7D,aAAa,EACX,mCACA,+CACD;MACD,MAAM,kBAAC,GAAD,EAAQ,WAAU,UAAW,CAAA;MACnC,eAAe,EAAS,mBAAmB;MAC5C;KACF;IACD,CAAA;GAED,EAAS,WAAW,IACnB,kBAAC,GAAD;IACE,cAAa;IACb,OAAO,EAAE,+BAA+B,kBAAkB;IAC1D,aAAa,EACX,qCACA,iEACD;IACD,CAAA,GAEF,kBAAC,GAAD;IAAW,WAAU;IAAQ,MAAA;IAAK,WAAU;cAA5C,CACE,kBAAC,MAAD;KAAI,WAAU;eACX,EAAE,mCAAmC,gBAAgB;KACnD,CAAA,EACL,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAS,KAAK,MACb,kBAAC,GAAD;MAEE,OAAO,EAAQ;MACf,MAAM,EAAQ;MACd,OAAO,GAAG,EAAQ,SAAS,GAAG,OAAO,EAAQ,WAAW,CAAC,QAAQ,EAAE;MACnE,OACE,kBAAC,GAAD;OAAa,QAAQ,EAAQ;OAAQ,MAAM,EAAQ,WAAW;OAAe,CAAA;MAE/E,QACE,EAAQ,WAAW,cAYf,OAXF,kBAAC,GAAD;OACE,OAAM;OACN,SAAS,CACP;QACE,OAAO,EAAE,mCAAmC,iBAAiB;QAC7D,gBAAgB,EAAa,EAAQ,GAAG;QACxC,QAAQ;QACR,MAAM,kBAAC,GAAD,EAAW,CAAA;QAClB,CACF;OACD,CAAA;MAGN,EAtBK,EAAQ,GAsBb,CACF;KACE,CAAA,CACI;;GAEJ"}
1
+ {"version":3,"file":"BookingsPage.js","names":[],"sources":["../../../src/ggnomad/pages/BookingsPage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { useMutation, useQuery } from '@apollo/client/react';\nimport type { TypedDocumentNode } from '@apollo/client';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { CalendarCheck, Compass, Hotel, Plus, Ticket, XCircle } from 'lucide-react';\nimport { useNavigate } from 'react-router-dom';\nimport {\n AlertDialog,\n AlertDialogAction,\n AlertDialogCancel,\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialogTitle,\n CTAOverflowMenu,\n GlassCard,\n IllustratedEmptyState,\n NextSteps,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\n\nimport {\n CANCEL_GGNOMAD_BOOKING_MUTATION,\n CREATE_GGNOMAD_BOOKING_MUTATION,\n GGNOMAD_BOOKABLE_SAMPLE_QUERY,\n GGNOMAD_BOOKINGS_QUERY,\n} from '../graphql';\nimport { PageShell } from './PageShell';\nimport { EntityCard, StatusBadge } from '../components';\nimport { notify } from '../utils/notify';\n\ntype BookingType = 'PROPERTY' | 'TOUR' | 'EVENT' | 'ACTIVITY';\n\ntype PricedListing = { id: string; name: string; price: number; currency: string };\n\ntype CancelTarget = { id: string; referenceCode: string };\n\nexport function BookingsPage() {\n const { t } = useI18n();\n const navigate = useNavigate();\n const [cancelTarget, setCancelTarget] = useState<CancelTarget | null>(null);\n\n const { data, refetch } = useQuery(\n GGNOMAD_BOOKINGS_QUERY as TypedDocumentNode<\n {\n ggnomadBookings: Array<{\n id: string;\n referenceCode: string;\n itemName: string;\n status: string;\n totalPrice: number;\n currency: string;\n }>;\n },\n { first: number; skip: number }\n >,\n { variables: { first: 20, skip: 0 } }\n );\n\n // One real listing of each type so the demo booking targets an id that\n // actually exists in the workspace (the backend rejects synthetic ids).\n const { data: sampleData } = useQuery(\n GGNOMAD_BOOKABLE_SAMPLE_QUERY as TypedDocumentNode<\n {\n ggnomadProperties: Array<{\n id: string;\n name: string;\n pricePerNight: number;\n currency: string;\n }>;\n ggnomadTours: Array<{ id: string; name: string; price: number; currency: string }>;\n ggnomadEvents: Array<{ id: string; name: string; price: number; currency: string }>;\n ggnomadActivities: Array<{ id: string; name: string; price: number; currency: string }>;\n },\n Record<string, never>\n >\n );\n\n const [createBooking, { loading: creating }] = useMutation(\n CREATE_GGNOMAD_BOOKING_MUTATION as TypedDocumentNode<\n { createGgnomadBooking: { id: string; referenceCode: string; itemName: string } },\n {\n input: {\n bookingType: BookingType;\n itemId: string;\n itemName: string;\n guestName: string;\n guestEmail: string;\n guests: number;\n totalPrice: number;\n currency: string;\n paymentMethod: string;\n };\n }\n >\n );\n\n const [cancelBooking, { loading: cancelling }] = useMutation(\n CANCEL_GGNOMAD_BOOKING_MUTATION as TypedDocumentNode<\n { cancelGgnomadBooking: { id: string; status: string } },\n { id: string; reason: string }\n >\n );\n\n const bookings = data?.ggnomadBookings ?? [];\n\n /** First available real listing, in priority order, mapped to a booking type. */\n const resolveDemoTarget = (): { type: BookingType; listing: PricedListing } | null => {\n const property = sampleData?.ggnomadProperties?.[0];\n if (property) {\n return {\n type: 'PROPERTY',\n listing: {\n id: property.id,\n name: property.name,\n price: property.pricePerNight,\n currency: property.currency,\n },\n };\n }\n const tour = sampleData?.ggnomadTours?.[0];\n if (tour) return { type: 'TOUR', listing: tour };\n const event = sampleData?.ggnomadEvents?.[0];\n if (event) return { type: 'EVENT', listing: event };\n const activity = sampleData?.ggnomadActivities?.[0];\n if (activity) return { type: 'ACTIVITY', listing: activity };\n return null;\n };\n\n const handleCreateDemoBooking = async () => {\n const target = resolveDemoTarget();\n if (!target) {\n notify.warning(\n t('ggnomad.bookings.toast.noListingsTitle', 'No listings available to book yet'),\n t(\n 'ggnomad.bookings.toast.noListingsBody',\n 'Add a property, tour, event or activity first, then create a booking against it.'\n )\n );\n return;\n }\n const { type, listing } = target;\n try {\n const result = await createBooking({\n variables: {\n input: {\n bookingType: type,\n itemId: listing.id,\n itemName: listing.name,\n guestName: t('ggnomad.bookings.demo.guestName', 'Demo Guest'),\n guestEmail: 'demo@burdenoff.com',\n guests: 2,\n totalPrice: Number(listing.price ?? 0),\n currency: listing.currency ?? 'USD',\n paymentMethod: 'card',\n },\n },\n });\n if (result.error) {\n notify.error(\n t('ggnomad.bookings.toast.createErrorTitle', 'Could not create booking'),\n result.error.message\n );\n return;\n }\n const created = result.data?.createGgnomadBooking;\n notify.success(\n t('ggnomad.bookings.toast.createSuccessTitle', 'Booking confirmed'),\n created\n ? t('ggnomad.bookings.toast.createSuccessBody', '{{ref}} · {{item}}')\n .replace('{{ref}}', created.referenceCode)\n .replace('{{item}}', created.itemName)\n : listing.name\n );\n await refetch();\n } catch (err) {\n notify.error(\n t('ggnomad.bookings.toast.createErrorTitle', 'Could not create booking'),\n err instanceof Error ? err.message : String(err)\n );\n }\n };\n\n const confirmCancel = async () => {\n if (!cancelTarget) return;\n try {\n const result = await cancelBooking({\n variables: {\n id: cancelTarget.id,\n reason: t('ggnomad.bookings.actions.cancelReason', 'Cancelled via UI'),\n },\n });\n if (result.error) {\n notify.error(\n t('ggnomad.bookings.toast.cancelErrorTitle', 'Could not cancel booking'),\n result.error.message\n );\n return;\n }\n notify.success(\n t('ggnomad.bookings.toast.cancelSuccessTitle', 'Booking cancelled'),\n cancelTarget.referenceCode\n );\n await refetch();\n } catch (err) {\n notify.error(\n t('ggnomad.bookings.toast.cancelErrorTitle', 'Could not cancel booking'),\n err instanceof Error ? err.message : String(err)\n );\n } finally {\n setCancelTarget(null);\n }\n };\n\n return (\n <PageShell\n title={t('ggnomad.bookings.title', 'Bookings')}\n titleAccent={t('ggnomad.bookings.title.accent', 'Bookings')}\n subtitle={t(\n 'ggnomad.bookings.subtitle',\n 'End-to-end booking operations with real API mutations'\n )}\n actions={\n <CTAOverflowMenu\n primary={{\n label: t('ggnomad.bookings.actions.createDemoBooking', 'Create demo booking'),\n onSelect: () => void handleCreateDemoBooking(),\n disabled: creating,\n icon: <Plus />,\n }}\n actions={[]}\n />\n }\n >\n <PagePurpose icon={<CalendarCheck className=\"size-4\" />}>\n {t(\n 'ggnomad.bookings.purpose',\n 'Your single place to manage every reservation across travel, stays, tours and events. Track each booking’s status and reference code in real time, and cancel a reservation when plans change — all backed by live API mutations.'\n )}\n </PagePurpose>\n\n <NextSteps\n storageKey=\"ggnomad-bookings\"\n steps={[\n {\n id: 'book-stay',\n label: t('ggnomad.bookings.next.stay', 'Book a place to stay'),\n description: t(\n 'ggnomad.bookings.next.stayDesc',\n 'Browse hospitality listings and reserve a property.'\n ),\n icon: <Hotel className=\"size-4\" />,\n onClick: () => navigate('../hospitality'),\n },\n {\n id: 'book-tour',\n label: t('ggnomad.bookings.next.tour', 'Add a tour or activity'),\n description: t(\n 'ggnomad.bookings.next.tourDesc',\n 'Pick a curated experience from Tourism.'\n ),\n icon: <Compass className=\"size-4\" />,\n onClick: () => navigate('../tourism'),\n },\n {\n id: 'book-event',\n label: t('ggnomad.bookings.next.event', 'Grab event tickets'),\n description: t(\n 'ggnomad.bookings.next.eventDesc',\n 'Find live events to round out the itinerary.'\n ),\n icon: <Ticket className=\"size-4\" />,\n onClick: () => navigate('../entertainment'),\n },\n ]}\n />\n\n {bookings.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={t('ggnomad.bookings.emptyTitle', 'No bookings yet')}\n description={t(\n 'ggnomad.bookings.emptyDescription',\n 'Create your first booking to see it tracked here in real time.'\n )}\n />\n ) : (\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.bookings.activeBookings', 'Your bookings')}\n </h2>\n <div className=\"grid gap-3 sm:grid-cols-2\">\n {bookings.map((booking) => (\n <EntityCard\n key={booking.id}\n title={booking.referenceCode}\n meta={booking.itemName}\n price={`${booking.currency} ${Number(booking.totalPrice).toFixed(2)}`}\n badge={\n <StatusBadge status={booking.status} live={booking.status !== 'CANCELLED'} />\n }\n footer={\n booking.status !== 'CANCELLED' ? (\n <CTAOverflowMenu\n align=\"end\"\n actions={[\n {\n label: t('ggnomad.bookings.actions.cancel', 'Cancel booking'),\n onSelect: () =>\n setCancelTarget({\n id: booking.id,\n referenceCode: booking.referenceCode,\n }),\n intent: 'destructive',\n icon: <XCircle />,\n },\n ]}\n />\n ) : null\n }\n />\n ))}\n </div>\n </GlassCard>\n )}\n\n <AlertDialog\n open={cancelTarget !== null}\n onOpenChange={(open) => {\n if (!open) setCancelTarget(null);\n }}\n >\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>\n {t('ggnomad.bookings.cancelDialog.title', 'Cancel this booking?')}\n </AlertDialogTitle>\n <AlertDialogDescription>\n {t(\n 'ggnomad.bookings.cancelDialog.body',\n 'This releases reservation {{ref}} and cannot be undone.'\n ).replace('{{ref}}', cancelTarget?.referenceCode ?? '')}\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel disabled={cancelling}>\n {t('ggnomad.bookings.cancelDialog.keep', 'Keep booking')}\n </AlertDialogCancel>\n <AlertDialogAction\n className=\"bg-action-danger-bg text-action-danger-text hover:bg-action-danger-bgHover\"\n disabled={cancelling}\n onClick={(event) => {\n event.preventDefault();\n void confirmCancel();\n }}\n >\n {cancelling\n ? t('ggnomad.bookings.cancelDialog.cancelling', 'Cancelling…')\n : t('ggnomad.bookings.cancelDialog.confirm', 'Cancel booking')}\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n </PageShell>\n );\n}\n"],"mappings":"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"}
@@ -1,57 +1,117 @@
1
- import { GGNOMAD_FAVORITES_QUERY as e, TOGGLE_GGNOMAD_FAVORITE_MUTATION as t } from "../graphql.js";
2
- import { PageShell as n } from "./PageShell.js";
3
- import { EntityCard as r } from "../components/EntityCard.js";
1
+ import { GGNOMAD_FAVORITES_QUERY as e, GGNOMAD_FAVORITE_TARGETS_QUERY as t, TOGGLE_GGNOMAD_FAVORITE_MUTATION as n } from "../graphql.js";
2
+ import { PageShell as r } from "./PageShell.js";
3
+ import { EntityCard as i } from "../components/EntityCard.js";
4
4
  import "../components/index.js";
5
- import { Heart as i } from "lucide-react";
6
- import { useI18n as a } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
7
- import { useMutation as o, useQuery as s } from "@apollo/client/react";
8
- import { CTAOverflowMenu as c, GlassCard as l, IllustratedEmptyState as u, PagePurpose as d } from "@burdenoff/fe-libs/ui";
9
- import { jsx as f, jsxs as p } from "react/jsx-runtime";
5
+ import { notify as a } from "../utils/notify.js";
6
+ import { useMemo as o } from "react";
7
+ import { Heart as s } from "lucide-react";
8
+ import { useI18n as c } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
9
+ import { useMutation as l, useQuery as u } from "@apollo/client/react";
10
+ import { CTAOverflowMenu as d, GlassCard as f, IllustratedEmptyState as p, PagePurpose as m } from "@burdenoff/fe-libs/ui";
11
+ import { jsx as h, jsxs as g } from "react/jsx-runtime";
10
12
  //#region src/ggnomad/pages/FavoritesPage.tsx
11
- function m() {
12
- let { t: m } = a(), { data: h, refetch: g } = s(e), [_] = o(t, { onCompleted: () => void g() }), v = h?.ggnomadFavorites ?? [];
13
- return /* @__PURE__ */ p(n, {
14
- title: m("ggnomad.favorites.title", "Favorites"),
15
- titleAccent: m("ggnomad.favorites.title.accent", "Favorites"),
16
- subtitle: m("ggnomad.favorites.subtitle", "Social ecosystem: saved entities across GGNomad domains"),
17
- actions: /* @__PURE__ */ f(c, {
13
+ function _() {
14
+ let { t: _ } = c(), v = (e) => {
15
+ switch (e.toUpperCase()) {
16
+ case "PROPERTY": return _("ggnomad.favorites.type.property", "Property");
17
+ case "TOUR": return _("ggnomad.favorites.type.tour", "Tour");
18
+ case "EVENT": return _("ggnomad.favorites.type.event", "Event");
19
+ case "ACTIVITY": return _("ggnomad.favorites.type.activity", "Activity");
20
+ case "TRIP": return _("ggnomad.favorites.type.trip", "Trip");
21
+ default: return _("ggnomad.favorites.type.saved", "Saved item");
22
+ }
23
+ }, { data: y, refetch: b } = u(e), { data: x } = u(t), [S] = l(n), C = o(() => {
24
+ let e = /* @__PURE__ */ new Map();
25
+ for (let t of [
26
+ ...x?.ggnomadProperties ?? [],
27
+ ...x?.ggnomadTours ?? [],
28
+ ...x?.ggnomadEvents ?? [],
29
+ ...x?.ggnomadActivities ?? []
30
+ ]) e.set(t.id, t.name);
31
+ return e;
32
+ }, [x]), w = y?.ggnomadFavorites ?? [], T = () => {
33
+ let e = x?.ggnomadProperties?.[0];
34
+ if (e) return {
35
+ type: "PROPERTY",
36
+ listing: e
37
+ };
38
+ let t = x?.ggnomadTours?.[0];
39
+ if (t) return {
40
+ type: "TOUR",
41
+ listing: t
42
+ };
43
+ let n = x?.ggnomadEvents?.[0];
44
+ if (n) return {
45
+ type: "EVENT",
46
+ listing: n
47
+ };
48
+ let r = x?.ggnomadActivities?.[0];
49
+ return r ? {
50
+ type: "ACTIVITY",
51
+ listing: r
52
+ } : null;
53
+ }, E = async () => {
54
+ let e = T();
55
+ if (!e) {
56
+ a.warning(_("ggnomad.favorites.toast.noListingsTitle", "Nothing to favorite yet"), _("ggnomad.favorites.toast.noListingsBody", "Add a property, tour, event or activity first, then save it to your favorites."));
57
+ return;
58
+ }
59
+ let { type: t, listing: n } = e;
60
+ try {
61
+ let e = await S({ variables: { input: {
62
+ targetType: t,
63
+ targetId: n.id
64
+ } } });
65
+ if (e.error) {
66
+ a.error(_("ggnomad.favorites.toast.errorTitle", "Could not update favorite"), e.error.message);
67
+ return;
68
+ }
69
+ let r = e.data?.toggleGgnomadFavorite === !0;
70
+ a.success(r ? _("ggnomad.favorites.toast.added", "Added to favorites") : _("ggnomad.favorites.toast.removed", "Removed from favorites"), n.name), await b();
71
+ } catch (e) {
72
+ a.error(_("ggnomad.favorites.toast.errorTitle", "Could not update favorite"), e instanceof Error ? e.message : String(e));
73
+ }
74
+ };
75
+ return /* @__PURE__ */ g(r, {
76
+ title: _("ggnomad.favorites.title", "Favorites"),
77
+ titleAccent: _("ggnomad.favorites.title.accent", "Favorites"),
78
+ subtitle: _("ggnomad.favorites.subtitle", "Social ecosystem: saved entities across GGNomad domains"),
79
+ actions: /* @__PURE__ */ h(d, {
18
80
  primary: {
19
- label: m("ggnomad.favorites.actions.toggleDemo", "Toggle demo favorite"),
20
- onSelect: () => {
21
- _({ variables: { input: {
22
- targetType: "PROPERTY",
23
- targetId: "demo-property"
24
- } } });
25
- },
26
- icon: /* @__PURE__ */ f(i, {})
81
+ label: _("ggnomad.favorites.actions.toggleDemo", "Toggle demo favorite"),
82
+ onSelect: () => void E(),
83
+ icon: /* @__PURE__ */ h(s, {})
27
84
  },
28
85
  actions: []
29
86
  }),
30
- children: [/* @__PURE__ */ f(d, {
31
- icon: /* @__PURE__ */ f(i, { className: "size-4" }),
32
- children: m("ggnomad.favorites.purpose", "A personal shortlist that spans every GGNomad domain — save properties, tours, events and trips you’re considering so you can compare and come back to them before you book.")
33
- }), v.length === 0 ? /* @__PURE__ */ f(u, {
87
+ children: [/* @__PURE__ */ h(m, {
88
+ icon: /* @__PURE__ */ h(s, { className: "size-4" }),
89
+ children: _("ggnomad.favorites.purpose", "A personal shortlist that spans every GGNomad domain — save properties, tours, events and trips you’re considering so you can compare and come back to them before you book.")
90
+ }), w.length === 0 ? /* @__PURE__ */ h(p, {
34
91
  illustration: "empty-notifications",
35
- title: m("ggnomad.favorites.emptyTitle", "No favorites saved"),
36
- description: m("ggnomad.favorites.emptyDescription", "Items you save across travel, hospitality and tourism will collect here.")
37
- }) : /* @__PURE__ */ p(l, {
92
+ title: _("ggnomad.favorites.emptyTitle", "No favorites saved"),
93
+ description: _("ggnomad.favorites.emptyDescription", "Items you save across travel, hospitality and tourism will collect here.")
94
+ }) : /* @__PURE__ */ g(f, {
38
95
  treatment: "glass",
39
96
  glow: !0,
40
97
  className: "border-border-seam p-5",
41
- children: [/* @__PURE__ */ f("h2", {
98
+ children: [/* @__PURE__ */ h("h2", {
42
99
  className: "mb-4 text-lg font-medium tracking-tight text-text-primary",
43
- children: m("ggnomad.favorites.savedItems", "Saved items")
44
- }), /* @__PURE__ */ f("div", {
100
+ children: _("ggnomad.favorites.savedItems", "Saved items")
101
+ }), /* @__PURE__ */ h("div", {
45
102
  className: "grid gap-3 sm:grid-cols-2 xl:grid-cols-3",
46
- children: v.map((e) => /* @__PURE__ */ f(r, {
47
- title: e.targetType,
48
- meta: e.targetId
49
- }, e.id))
103
+ children: w.map((e) => {
104
+ let t = C.get(e.targetId);
105
+ return /* @__PURE__ */ h(i, {
106
+ title: t ?? v(e.targetType),
107
+ meta: t ? v(e.targetType) : _("ggnomad.favorites.savedListing", "Saved listing")
108
+ }, e.id);
109
+ })
50
110
  })]
51
111
  })]
52
112
  });
53
113
  }
54
114
  //#endregion
55
- export { m as FavoritesPage };
115
+ export { _ as FavoritesPage };
56
116
 
57
117
  //# sourceMappingURL=FavoritesPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"FavoritesPage.js","names":[],"sources":["../../../src/ggnomad/pages/FavoritesPage.tsx"],"sourcesContent":["import { useMutation, useQuery } from '@apollo/client/react';\nimport type { TypedDocumentNode } from '@apollo/client';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { Heart } from 'lucide-react';\nimport {\n CTAOverflowMenu,\n GlassCard,\n IllustratedEmptyState,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\n\nimport { GGNOMAD_FAVORITES_QUERY, TOGGLE_GGNOMAD_FAVORITE_MUTATION } from '../graphql';\nimport { PageShell } from './PageShell';\nimport { EntityCard } from '../components';\n\nexport function FavoritesPage() {\n const { t } = useI18n();\n const { data, refetch } = useQuery(\n GGNOMAD_FAVORITES_QUERY as TypedDocumentNode<\n { ggnomadFavorites: Array<{ id: string; targetType: string; targetId: string }> },\n Record<string, never>\n >\n );\n const [toggleFavorite] = useMutation(TOGGLE_GGNOMAD_FAVORITE_MUTATION, {\n onCompleted: () => void refetch(),\n });\n const favorites = (data?.ggnomadFavorites ?? []) as Array<{\n id: string;\n targetType: string;\n targetId: string;\n }>;\n\n const handleToggleDemo = () => {\n void toggleFavorite({\n variables: { input: { targetType: 'PROPERTY', targetId: 'demo-property' } },\n });\n };\n\n return (\n <PageShell\n title={t('ggnomad.favorites.title', 'Favorites')}\n titleAccent={t('ggnomad.favorites.title.accent', 'Favorites')}\n subtitle={t(\n 'ggnomad.favorites.subtitle',\n 'Social ecosystem: saved entities across GGNomad domains'\n )}\n actions={\n <CTAOverflowMenu\n primary={{\n label: t('ggnomad.favorites.actions.toggleDemo', 'Toggle demo favorite'),\n onSelect: handleToggleDemo,\n icon: <Heart />,\n }}\n actions={[]}\n />\n }\n >\n <PagePurpose icon={<Heart className=\"size-4\" />}>\n {t(\n 'ggnomad.favorites.purpose',\n 'A personal shortlist that spans every GGNomad domain — save properties, tours, events and trips you’re considering so you can compare and come back to them before you book.'\n )}\n </PagePurpose>\n\n {favorites.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-notifications\"\n title={t('ggnomad.favorites.emptyTitle', 'No favorites saved')}\n description={t(\n 'ggnomad.favorites.emptyDescription',\n 'Items you save across travel, hospitality and tourism will collect here.'\n )}\n />\n ) : (\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.favorites.savedItems', 'Saved items')}\n </h2>\n <div className=\"grid gap-3 sm:grid-cols-2 xl:grid-cols-3\">\n {favorites.map((favorite) => (\n <EntityCard key={favorite.id} title={favorite.targetType} meta={favorite.targetId} />\n ))}\n </div>\n </GlassCard>\n )}\n </PageShell>\n );\n}\n"],"mappings":";;;;;;;;;;AAeA,SAAgB,IAAgB;CAC9B,IAAM,EAAE,SAAM,GAAS,EACjB,EAAE,SAAM,eAAY,EACxB,EAID,EACK,CAAC,KAAkB,EAAY,GAAkC,EACrE,mBAAmB,KAAK,GAAS,EAClC,CAAC,EACI,IAAa,GAAM,oBAAoB,EAAE;AAY/C,QACE,kBAAC,GAAD;EACE,OAAO,EAAE,2BAA2B,YAAY;EAChD,aAAa,EAAE,kCAAkC,YAAY;EAC7D,UAAU,EACR,8BACA,0DACD;EACD,SACE,kBAAC,GAAD;GACE,SAAS;IACP,OAAO,EAAE,wCAAwC,uBAAuB;IACxE,gBAlBqB;AACxB,OAAe,EAClB,WAAW,EAAE,OAAO;MAAE,YAAY;MAAY,UAAU;MAAiB,EAAE,EAC5E,CAAC;;IAgBM,MAAM,kBAAC,GAAD,EAAS,CAAA;IAChB;GACD,SAAS,EAAE;GACX,CAAA;YAfN,CAkBE,kBAAC,GAAD;GAAa,MAAM,kBAAC,GAAD,EAAO,WAAU,UAAW,CAAA;aAC5C,EACC,6BACA,+KACD;GACW,CAAA,EAEb,EAAU,WAAW,IACpB,kBAAC,GAAD;GACE,cAAa;GACb,OAAO,EAAE,gCAAgC,qBAAqB;GAC9D,aAAa,EACX,sCACA,2EACD;GACD,CAAA,GAEF,kBAAC,GAAD;GAAW,WAAU;GAAQ,MAAA;GAAK,WAAU;aAA5C,CACE,kBAAC,MAAD;IAAI,WAAU;cACX,EAAE,gCAAgC,cAAc;IAC9C,CAAA,EACL,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAU,KAAK,MACd,kBAAC,GAAD;KAA8B,OAAO,EAAS;KAAY,MAAM,EAAS;KAAY,EAApE,EAAS,GAA2D,CACrF;IACE,CAAA,CACI;KAEJ"}
1
+ {"version":3,"file":"FavoritesPage.js","names":[],"sources":["../../../src/ggnomad/pages/FavoritesPage.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport { useMutation, useQuery } from '@apollo/client/react';\nimport type { TypedDocumentNode } from '@apollo/client';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { Heart } from 'lucide-react';\nimport {\n CTAOverflowMenu,\n GlassCard,\n IllustratedEmptyState,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\n\nimport {\n GGNOMAD_FAVORITES_QUERY,\n GGNOMAD_FAVORITE_TARGETS_QUERY,\n TOGGLE_GGNOMAD_FAVORITE_MUTATION,\n} from '../graphql';\nimport { PageShell } from './PageShell';\nimport { EntityCard } from '../components';\nimport { notify } from '../utils/notify';\n\ntype FavoriteType = 'PROPERTY' | 'TOUR' | 'EVENT' | 'ACTIVITY';\n\ntype Listing = { id: string; name: string };\n\nexport function FavoritesPage() {\n const { t } = useI18n();\n\n const typeLabel = (type: string): string => {\n switch (type.toUpperCase()) {\n case 'PROPERTY':\n return t('ggnomad.favorites.type.property', 'Property');\n case 'TOUR':\n return t('ggnomad.favorites.type.tour', 'Tour');\n case 'EVENT':\n return t('ggnomad.favorites.type.event', 'Event');\n case 'ACTIVITY':\n return t('ggnomad.favorites.type.activity', 'Activity');\n case 'TRIP':\n return t('ggnomad.favorites.type.trip', 'Trip');\n default:\n return t('ggnomad.favorites.type.saved', 'Saved item');\n }\n };\n\n const { data, refetch } = useQuery(\n GGNOMAD_FAVORITES_QUERY as TypedDocumentNode<\n { ggnomadFavorites: Array<{ id: string; targetType: string; targetId: string }> },\n Record<string, never>\n >\n );\n\n // Real listing names so a saved favorite reads as its entity name instead of\n // a raw enum + UUID (the backend only stores targetType + targetId).\n const { data: targetsData } = useQuery(\n GGNOMAD_FAVORITE_TARGETS_QUERY as TypedDocumentNode<\n {\n ggnomadProperties: Listing[];\n ggnomadTours: Listing[];\n ggnomadEvents: Listing[];\n ggnomadActivities: Listing[];\n },\n Record<string, never>\n >\n );\n\n const [toggleFavorite] = useMutation(\n TOGGLE_GGNOMAD_FAVORITE_MUTATION as TypedDocumentNode<\n { toggleGgnomadFavorite: boolean },\n { input: { targetType: FavoriteType; targetId: string } }\n >\n );\n\n const nameById = useMemo(() => {\n const map = new Map<string, string>();\n for (const listing of [\n ...(targetsData?.ggnomadProperties ?? []),\n ...(targetsData?.ggnomadTours ?? []),\n ...(targetsData?.ggnomadEvents ?? []),\n ...(targetsData?.ggnomadActivities ?? []),\n ]) {\n map.set(listing.id, listing.name);\n }\n return map;\n }, [targetsData]);\n\n const favorites = (data?.ggnomadFavorites ?? []) as Array<{\n id: string;\n targetType: string;\n targetId: string;\n }>;\n\n /** First available real listing, in priority order, mapped to a favorite type. */\n const resolveDemoTarget = (): { type: FavoriteType; listing: Listing } | null => {\n const property = targetsData?.ggnomadProperties?.[0];\n if (property) return { type: 'PROPERTY', listing: property };\n const tour = targetsData?.ggnomadTours?.[0];\n if (tour) return { type: 'TOUR', listing: tour };\n const event = targetsData?.ggnomadEvents?.[0];\n if (event) return { type: 'EVENT', listing: event };\n const activity = targetsData?.ggnomadActivities?.[0];\n if (activity) return { type: 'ACTIVITY', listing: activity };\n return null;\n };\n\n const handleToggleDemo = async () => {\n const target = resolveDemoTarget();\n if (!target) {\n notify.warning(\n t('ggnomad.favorites.toast.noListingsTitle', 'Nothing to favorite yet'),\n t(\n 'ggnomad.favorites.toast.noListingsBody',\n 'Add a property, tour, event or activity first, then save it to your favorites.'\n )\n );\n return;\n }\n const { type, listing } = target;\n try {\n const result = await toggleFavorite({\n variables: { input: { targetType: type, targetId: listing.id } },\n });\n if (result.error) {\n notify.error(\n t('ggnomad.favorites.toast.errorTitle', 'Could not update favorite'),\n result.error.message\n );\n return;\n }\n const added = result.data?.toggleGgnomadFavorite === true;\n notify.success(\n added\n ? t('ggnomad.favorites.toast.added', 'Added to favorites')\n : t('ggnomad.favorites.toast.removed', 'Removed from favorites'),\n listing.name\n );\n await refetch();\n } catch (err) {\n notify.error(\n t('ggnomad.favorites.toast.errorTitle', 'Could not update favorite'),\n err instanceof Error ? err.message : String(err)\n );\n }\n };\n\n return (\n <PageShell\n title={t('ggnomad.favorites.title', 'Favorites')}\n titleAccent={t('ggnomad.favorites.title.accent', 'Favorites')}\n subtitle={t(\n 'ggnomad.favorites.subtitle',\n 'Social ecosystem: saved entities across GGNomad domains'\n )}\n actions={\n <CTAOverflowMenu\n primary={{\n label: t('ggnomad.favorites.actions.toggleDemo', 'Toggle demo favorite'),\n onSelect: () => void handleToggleDemo(),\n icon: <Heart />,\n }}\n actions={[]}\n />\n }\n >\n <PagePurpose icon={<Heart className=\"size-4\" />}>\n {t(\n 'ggnomad.favorites.purpose',\n 'A personal shortlist that spans every GGNomad domain — save properties, tours, events and trips you’re considering so you can compare and come back to them before you book.'\n )}\n </PagePurpose>\n\n {favorites.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-notifications\"\n title={t('ggnomad.favorites.emptyTitle', 'No favorites saved')}\n description={t(\n 'ggnomad.favorites.emptyDescription',\n 'Items you save across travel, hospitality and tourism will collect here.'\n )}\n />\n ) : (\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.favorites.savedItems', 'Saved items')}\n </h2>\n <div className=\"grid gap-3 sm:grid-cols-2 xl:grid-cols-3\">\n {favorites.map((favorite) => {\n const resolvedName = nameById.get(favorite.targetId);\n return (\n <EntityCard\n key={favorite.id}\n title={resolvedName ?? typeLabel(favorite.targetType)}\n meta={\n resolvedName\n ? typeLabel(favorite.targetType)\n : t('ggnomad.favorites.savedListing', 'Saved listing')\n }\n />\n );\n })}\n </div>\n </GlassCard>\n )}\n </PageShell>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ import { toast as e } from "sonner";
2
+ //#region src/ggnomad/utils/notify.ts
3
+ var t = {
4
+ success(t, n) {
5
+ e.success(t, {
6
+ description: n,
7
+ duration: 4e3
8
+ });
9
+ },
10
+ info(t, n) {
11
+ e.info(t, {
12
+ description: n,
13
+ duration: 4e3
14
+ });
15
+ },
16
+ warning(t, n) {
17
+ e.warning(t, {
18
+ description: n,
19
+ duration: 8e3
20
+ });
21
+ },
22
+ error(t, n) {
23
+ e.error(t, {
24
+ description: n,
25
+ duration: Infinity,
26
+ closeButton: !0
27
+ });
28
+ },
29
+ dismiss(t) {
30
+ e.dismiss(t);
31
+ }
32
+ };
33
+ //#endregion
34
+ export { t as notify };
35
+
36
+ //# sourceMappingURL=notify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notify.js","names":[],"sources":["../../../src/ggnomad/utils/notify.ts"],"sourcesContent":["import { toast } from 'sonner';\n\n/**\n * Module-wide toast API. sonner is a global singleton; the matching <Toaster>\n * is owned by the HOST — the production app shell mounts a single one via\n * AppShellProvider, and the standalone dev harness mounts its own (see\n * GGNomadRoot's `renderToaster`). Every page/component must emit feedback\n * through these helpers instead of hand-rolling fixed-position toast markup.\n *\n * Durations follow the notification spec: success/info auto-dismiss, errors\n * stay until the user dismisses them (sonner renders a close button and\n * announces via aria-live).\n */\nexport const notify = {\n success(message: string, description?: string): void {\n toast.success(message, { description, duration: 4000 });\n },\n info(message: string, description?: string): void {\n toast.info(message, { description, duration: 4000 });\n },\n warning(message: string, description?: string): void {\n toast.warning(message, { description, duration: 8000 });\n },\n error(message: string, description?: string): void {\n toast.error(message, { description, duration: Infinity, closeButton: true });\n },\n dismiss(id?: string | number): void {\n toast.dismiss(id);\n },\n};\n\nexport { toast };\n"],"mappings":";;AAaA,IAAa,IAAS;CACpB,QAAQ,GAAiB,GAA4B;AACnD,IAAM,QAAQ,GAAS;GAAE;GAAa,UAAU;GAAM,CAAC;;CAEzD,KAAK,GAAiB,GAA4B;AAChD,IAAM,KAAK,GAAS;GAAE;GAAa,UAAU;GAAM,CAAC;;CAEtD,QAAQ,GAAiB,GAA4B;AACnD,IAAM,QAAQ,GAAS;GAAE;GAAa,UAAU;GAAM,CAAC;;CAEzD,MAAM,GAAiB,GAA4B;AACjD,IAAM,MAAM,GAAS;GAAE;GAAa,UAAU;GAAU,aAAa;GAAM,CAAC;;CAE9E,QAAQ,GAA4B;AAClC,IAAM,QAAQ,EAAG;;CAEpB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-ggnomad",
3
- "version": "2026.626.1",
3
+ "version": "2026.704.1",
4
4
  "description": "Burdenoff GGNomad Microfrontend",
5
5
  "type": "module",
6
6
  "files": [
@@ -77,6 +77,7 @@
77
77
  "react-dom": "^19.0.0",
78
78
  "react-router-dom": "^7.1.3",
79
79
  "recharts": "^2.15.0",
80
+ "sonner": "^2.0.7",
80
81
  "tailwind-merge": "^3.4.0",
81
82
  "zustand": "^5.0.3"
82
83
  },