@burdenoff/microfe-tours 2026.514.1 → 2026.516.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.
@@ -1,287 +1,288 @@
1
1
  import { useToursContext as e } from "../providers/ToursProvider.js";
2
2
  import { useDeleteTourMutation as t } from "../../generated/global-operations.js";
3
3
  import { useToursList as n } from "../hooks/useToursList.js";
4
- import { useTourLifecycle as r } from "../hooks/useTourLifecycle.js";
5
- import { useToursPermissionAccess as i } from "../hooks/useToursPermissionAccess.js";
6
- import { useToursStore as a } from "../store/toursStore.js";
4
+ import { useToursStore as r } from "../store/toursStore.js";
5
+ import { useTourLifecycle as i } from "../hooks/useTourLifecycle.js";
6
+ import { useToursPermissionAccess as a } from "../hooks/useToursPermissionAccess.js";
7
7
  import { TOUR_PERMISSIONS as o } from "../../constants/permissions.js";
8
8
  import { useToursI18n as s } from "../i18n.js";
9
9
  import { isNative as c, nativeConfirm as l, nativeImpact as u, nativeNotify as d, nativeToast as f } from "../../utils/nativeBridge.js";
10
10
  import { useEffect as p, useRef as m, useState as h } from "react";
11
11
  import { PermissionGate as g } from "@burdenoff/fe-libs/shared/components";
12
- import { Eye as _, Grid as ee, List as v, ListOrdered as y, MoreVertical as b, Plus as te, Search as ne, Settings as re } from "lucide-react";
13
- import { jsx as x, jsxs as S } from "react/jsx-runtime";
14
- import { useNavigate as C } from "react-router-dom";
12
+ import { Eye as _, Grid as v, List as y, ListOrdered as ee, MoreVertical as te, Plus as ne, Search as b, Settings as x } from "lucide-react";
13
+ import { jsx as S, jsxs as C } from "react/jsx-runtime";
14
+ import { useNavigate as w } from "react-router-dom";
15
15
  //#region src/tours/pages/ToursListPage.tsx
16
- function w() {
17
- let { tr: w } = s(), T = C(), { basePath: E = "/" } = e(), { viewMode: D, tourFilter: O, setViewMode: k, updateTourFilter: A } = a(), { hasPermission: j, isLoading: M } = i(), N = j(o.TOUR_UPDATE), P = j(o.TOUR_STEP_UPDATE), F = j(o.TOUR_DELETE), I = j(o.TOUR_PUBLISH), L = j(o.TOUR_ARCHIVE), R = j(o.TOUR_DUPLICATE), { tours: z, loading: B, fetchTours: V } = n(), { publishTour: H, unpublishTour: U, archiveTour: W, duplicateTour: G } = r(), [K] = t(), [ie, q] = h(""), [J, Y] = h("all"), [X, Z] = h(null), Q = m(null);
16
+ function T() {
17
+ let { tr: T } = s(), E = w(), { basePath: D = "/" } = e(), { viewMode: O, tourFilter: k, toursListRevision: A, setViewMode: j, updateTourFilter: M, notifyToursListChanged: N } = r(), { hasPermission: P, isLoading: F } = a(), I = P(o.TOUR_UPDATE), L = P(o.TOUR_STEP_UPDATE), R = P(o.TOUR_DELETE), z = P(o.TOUR_PUBLISH), B = P(o.TOUR_ARCHIVE), V = P(o.TOUR_DUPLICATE), { tours: H, loading: U, fetchTours: W } = n(), { publishTour: G, unpublishTour: K, archiveTour: re, duplicateTour: q } = i(), [J] = t(), [Y, X] = h(""), [ie, ae] = h("all"), [Z, Q] = h(null), $ = m(null);
18
18
  p(() => {
19
- M || V({ ...O });
19
+ F || W({ ...k });
20
20
  }, [
21
- V,
22
- O,
23
- M
21
+ W,
22
+ k,
23
+ A,
24
+ F
24
25
  ]);
25
- let ae = (e) => {
26
- q(e), A({ search: e });
27
- }, oe = (e) => {
28
- Y(e), A({ status: e });
29
- }, se = () => {
30
- T(E.endsWith("/") ? `${E}new` : `${E}/new`);
31
- }, $ = () => V({ ...O }), ce = (e) => {
26
+ let oe = (e) => {
27
+ X(e), M({ search: e });
28
+ }, se = (e) => {
29
+ ae(e), M({ status: e });
30
+ }, ce = () => {
31
+ E(D.endsWith("/") ? `${D}new` : `${D}/new`);
32
+ }, le = (e) => {
32
33
  switch (e) {
33
- case "DRAFT": return w("toursList.statusDraft");
34
- case "PUBLISHED": return w("toursList.statusPublished");
35
- case "ARCHIVED": return w("toursList.statusArchived");
36
- case "SCHEDULED": return w("toursList.statusScheduled");
37
- }
38
- }, le = async (e) => {
39
- Z(null), u("light");
40
- try {
41
- await H(e), d("success"), $();
42
- } catch {
43
- d("error"), c() ? f(w("toursList.errorPublish")) : alert(w("toursList.errorPublish"));
34
+ case "DRAFT": return T("toursList.statusDraft");
35
+ case "PUBLISHED": return T("toursList.statusPublished");
36
+ case "ARCHIVED": return T("toursList.statusArchived");
37
+ case "SCHEDULED": return T("toursList.statusScheduled");
44
38
  }
45
39
  }, ue = async (e) => {
46
- Z(null), u("light");
40
+ Q(null), u("light");
47
41
  try {
48
- await U(e), d("success"), $();
42
+ await G(e), d("success");
49
43
  } catch {
50
- d("error"), c() ? f(w("toursList.errorUnpublish")) : alert(w("toursList.errorUnpublish"));
44
+ d("error"), c() ? f(T("toursList.errorPublish")) : alert(T("toursList.errorPublish"));
51
45
  }
52
46
  }, de = async (e) => {
53
- Z(null), u("light");
47
+ Q(null), u("light");
54
48
  try {
55
- await W(e), d("success"), $();
49
+ await K(e), d("success");
56
50
  } catch {
57
- d("error"), c() ? f(w("toursList.errorArchive")) : alert(w("toursList.errorArchive"));
51
+ d("error"), c() ? f(T("toursList.errorUnpublish")) : alert(T("toursList.errorUnpublish"));
58
52
  }
59
53
  }, fe = async (e) => {
60
- Z(null), u("light");
54
+ Q(null), u("light");
61
55
  try {
62
- await G(e), d("success"), $();
56
+ await re(e), d("success");
63
57
  } catch {
64
- d("error"), c() ? f(w("toursList.errorDuplicate")) : alert(w("toursList.errorDuplicate"));
58
+ d("error"), c() ? f(T("toursList.errorArchive")) : alert(T("toursList.errorArchive"));
65
59
  }
66
60
  }, pe = async (e) => {
67
- Z(null), u("medium");
68
- let t = w("toursList.confirmDelete"), n = await l({
69
- title: w("toursList.actionDelete"),
61
+ Q(null), u("light");
62
+ try {
63
+ await q(e), d("success");
64
+ } catch {
65
+ d("error"), c() ? f(T("toursList.errorDuplicate")) : alert(T("toursList.errorDuplicate"));
66
+ }
67
+ }, me = async (e) => {
68
+ Q(null), u("medium");
69
+ let t = T("toursList.confirmDelete"), n = await l({
70
+ title: T("toursList.actionDelete"),
70
71
  message: t,
71
- okButtonTitle: w("toursList.actionDelete")
72
+ okButtonTitle: T("toursList.actionDelete")
72
73
  });
73
74
  if (n === null ? window.confirm(t) : n) try {
74
- await K({ variables: { id: e } }), d("success"), $();
75
+ await J({ variables: { id: e } }), N(), d("success");
75
76
  } catch {
76
- d("error"), c() ? f(w("toursList.errorDelete")) : alert(w("toursList.errorDelete"));
77
+ d("error"), c() ? f(T("toursList.errorDelete")) : alert(T("toursList.errorDelete"));
77
78
  }
78
79
  };
79
80
  return p(() => {
80
81
  let e = (e) => {
81
- Q.current && !Q.current.contains(e.target) && Z(null);
82
+ $.current && !$.current.contains(e.target) && Q(null);
82
83
  };
83
- return X && document.addEventListener("mousedown", e), () => document.removeEventListener("mousedown", e);
84
- }, [X]), /* @__PURE__ */ S("div", {
84
+ return Z && document.addEventListener("mousedown", e), () => document.removeEventListener("mousedown", e);
85
+ }, [Z]), /* @__PURE__ */ C("div", {
85
86
  className: "space-y-4 sm:space-y-6 p-3 sm:p-6",
86
87
  children: [
87
- /* @__PURE__ */ S("div", {
88
+ /* @__PURE__ */ C("div", {
88
89
  className: "flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3",
89
- children: [/* @__PURE__ */ S("div", {
90
+ children: [/* @__PURE__ */ C("div", {
90
91
  className: "flex-1 min-w-0",
91
- children: [/* @__PURE__ */ x("h1", {
92
+ children: [/* @__PURE__ */ S("h1", {
92
93
  className: "text-xl sm:text-2xl font-bold text-foreground",
93
- children: w("toursList.title")
94
- }), /* @__PURE__ */ x("p", {
94
+ children: T("toursList.title")
95
+ }), /* @__PURE__ */ S("p", {
95
96
  className: "text-sm sm:text-base text-muted-foreground",
96
- children: w("toursList.subtitle")
97
+ children: T("toursList.subtitle")
97
98
  })]
98
- }), /* @__PURE__ */ x(g, {
99
+ }), /* @__PURE__ */ S(g, {
99
100
  permission: o.TOUR_CREATE,
100
- children: /* @__PURE__ */ S("button", {
101
- onClick: se,
101
+ children: /* @__PURE__ */ C("button", {
102
+ onClick: ce,
102
103
  className: "w-full sm:w-auto px-4 py-2 min-h-[44px] bg-primary text-primary-foreground rounded-md hover:bg-primary/90 active:bg-primary/80 flex items-center justify-center gap-2",
103
- children: [/* @__PURE__ */ x(te, { size: 20 }), w("toursList.createTour")]
104
+ children: [/* @__PURE__ */ S(ne, { size: 20 }), T("toursList.createTour")]
104
105
  })
105
106
  })]
106
107
  }),
107
- /* @__PURE__ */ S("div", {
108
+ /* @__PURE__ */ C("div", {
108
109
  className: "flex flex-col sm:flex-row gap-3 sm:gap-4",
109
- children: [/* @__PURE__ */ S("div", {
110
+ children: [/* @__PURE__ */ C("div", {
110
111
  className: "flex-1 relative",
111
- children: [/* @__PURE__ */ x(ne, {
112
+ children: [/* @__PURE__ */ S(b, {
112
113
  className: "absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground",
113
114
  size: 20
114
- }), /* @__PURE__ */ x("input", {
115
+ }), /* @__PURE__ */ S("input", {
115
116
  type: "text",
116
- placeholder: w("toursList.searchPlaceholder"),
117
- value: ie,
118
- onChange: (e) => ae(e.target.value),
117
+ placeholder: T("toursList.searchPlaceholder"),
118
+ value: Y,
119
+ onChange: (e) => oe(e.target.value),
119
120
  className: "w-full pl-[3.25rem] pr-4 py-2 min-h-[44px] border border-border rounded-md bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring"
120
121
  })]
121
- }), /* @__PURE__ */ S("div", {
122
+ }), /* @__PURE__ */ C("div", {
122
123
  className: "flex gap-2",
123
- children: [/* @__PURE__ */ S("select", {
124
- value: J,
125
- onChange: (e) => oe(e.target.value),
124
+ children: [/* @__PURE__ */ C("select", {
125
+ value: ie,
126
+ onChange: (e) => se(e.target.value),
126
127
  className: "px-4 py-2 min-h-[44px] border border-border rounded-md bg-background text-foreground",
127
128
  children: [
128
- /* @__PURE__ */ x("option", {
129
+ /* @__PURE__ */ S("option", {
129
130
  value: "all",
130
- children: w("toursList.statusAll")
131
+ children: T("toursList.statusAll")
131
132
  }),
132
- /* @__PURE__ */ x("option", {
133
+ /* @__PURE__ */ S("option", {
133
134
  value: "DRAFT",
134
- children: w("toursList.statusDraft")
135
+ children: T("toursList.statusDraft")
135
136
  }),
136
- /* @__PURE__ */ x("option", {
137
+ /* @__PURE__ */ S("option", {
137
138
  value: "PUBLISHED",
138
- children: w("toursList.statusPublished")
139
+ children: T("toursList.statusPublished")
139
140
  }),
140
- /* @__PURE__ */ x("option", {
141
+ /* @__PURE__ */ S("option", {
141
142
  value: "ARCHIVED",
142
- children: w("toursList.statusArchived")
143
+ children: T("toursList.statusArchived")
143
144
  }),
144
- /* @__PURE__ */ x("option", {
145
+ /* @__PURE__ */ S("option", {
145
146
  value: "SCHEDULED",
146
- children: w("toursList.statusScheduled")
147
+ children: T("toursList.statusScheduled")
147
148
  })
148
149
  ]
149
- }), /* @__PURE__ */ S("div", {
150
+ }), /* @__PURE__ */ C("div", {
150
151
  className: "flex border border-border rounded-md overflow-hidden",
151
- children: [/* @__PURE__ */ x("button", {
152
- onClick: () => k("grid"),
153
- className: `px-3 py-2 min-h-[44px] ${D === "grid" ? "bg-primary text-primary-foreground" : "bg-background text-foreground hover:bg-accent"}`,
154
- "aria-label": w("toursList.gridView"),
155
- title: w("toursList.gridView"),
156
- children: /* @__PURE__ */ x(ee, { size: 20 })
157
- }), /* @__PURE__ */ x("button", {
158
- onClick: () => k("list"),
159
- className: `px-3 py-2 min-h-[44px] ${D === "list" ? "bg-primary text-primary-foreground" : "bg-background text-foreground hover:bg-accent"}`,
160
- "aria-label": w("toursList.listView"),
161
- title: w("toursList.listView"),
162
- children: /* @__PURE__ */ x(v, { size: 20 })
152
+ children: [/* @__PURE__ */ S("button", {
153
+ onClick: () => j("grid"),
154
+ className: `px-3 py-2 min-h-[44px] ${O === "grid" ? "bg-primary text-primary-foreground" : "bg-background text-foreground hover:bg-accent"}`,
155
+ "aria-label": T("toursList.gridView"),
156
+ title: T("toursList.gridView"),
157
+ children: /* @__PURE__ */ S(v, { size: 20 })
158
+ }), /* @__PURE__ */ S("button", {
159
+ onClick: () => j("list"),
160
+ className: `px-3 py-2 min-h-[44px] ${O === "list" ? "bg-primary text-primary-foreground" : "bg-background text-foreground hover:bg-accent"}`,
161
+ "aria-label": T("toursList.listView"),
162
+ title: T("toursList.listView"),
163
+ children: /* @__PURE__ */ S(y, { size: 20 })
163
164
  })]
164
165
  })]
165
166
  })]
166
167
  }),
167
- B ? /* @__PURE__ */ x("div", {
168
+ U ? /* @__PURE__ */ S("div", {
168
169
  className: "text-center py-12",
169
- children: /* @__PURE__ */ x("p", {
170
+ children: /* @__PURE__ */ S("p", {
170
171
  className: "text-sm sm:text-base text-muted-foreground",
171
- children: w("toursList.loading")
172
+ children: T("toursList.loading")
172
173
  })
173
- }) : z.length > 0 ? /* @__PURE__ */ x("div", {
174
- className: D === "grid" ? "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3 sm:gap-4" : "space-y-3",
175
- children: z.map((e) => {
176
- let t = e.status, n = t === "DRAFT" && I, r = t === "PUBLISHED" && I, i = t !== "ARCHIVED" && L, a = n || r || R || i || F;
177
- return /* @__PURE__ */ S("div", {
174
+ }) : H.length > 0 ? /* @__PURE__ */ S("div", {
175
+ className: O === "grid" ? "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3 sm:gap-4" : "space-y-3",
176
+ children: H.map((e) => {
177
+ let t = e.status, n = t === "DRAFT" && z, r = t === "PUBLISHED" && z, i = t !== "ARCHIVED" && B, a = n || r || V || i || R;
178
+ return /* @__PURE__ */ C("div", {
178
179
  className: "border border-border rounded-md p-4 bg-card hover:shadow-md transition-shadow",
179
180
  children: [
180
- /* @__PURE__ */ S("div", {
181
+ /* @__PURE__ */ C("div", {
181
182
  className: "flex items-start justify-between mb-2",
182
- children: [/* @__PURE__ */ x("h3", {
183
- className: `font-semibold text-foreground ${N ? "cursor-pointer hover:text-primary" : ""}`,
183
+ children: [/* @__PURE__ */ S("h3", {
184
+ className: `font-semibold text-foreground ${I ? "cursor-pointer hover:text-primary" : ""}`,
184
185
  onClick: () => {
185
- N && T(`${E}/${e.id}/edit`);
186
+ I && E(`${D}/${e.id}/edit`);
186
187
  },
187
188
  children: e.name
188
- }), /* @__PURE__ */ x("span", {
189
+ }), /* @__PURE__ */ S("span", {
189
190
  className: `px-2 py-1 text-xs rounded-full ${e.status === "PUBLISHED" ? "bg-success/10 text-success" : e.status === "DRAFT" ? "bg-muted text-muted-foreground" : "bg-warning/10 text-warning"}`,
190
- children: ce(t)
191
+ children: le(t)
191
192
  })]
192
193
  }),
193
- e.description && /* @__PURE__ */ x("p", {
194
+ e.description && /* @__PURE__ */ S("p", {
194
195
  className: "text-sm text-muted-foreground mb-3",
195
196
  children: e.description
196
197
  }),
197
- /* @__PURE__ */ S("div", {
198
+ /* @__PURE__ */ C("div", {
198
199
  className: "flex items-center justify-between text-xs text-muted-foreground mb-3",
199
- children: [/* @__PURE__ */ S("span", { children: [
200
+ children: [/* @__PURE__ */ C("span", { children: [
200
201
  e.steps?.length || 0,
201
202
  " ",
202
- w("toursList.steps")
203
- ] }), /* @__PURE__ */ S("span", { children: [
203
+ T("toursList.steps")
204
+ ] }), /* @__PURE__ */ C("span", { children: [
204
205
  e.viewCount,
205
206
  " ",
206
- w("toursList.views")
207
+ T("toursList.views")
207
208
  ] })]
208
209
  }),
209
- /* @__PURE__ */ S("div", {
210
+ /* @__PURE__ */ C("div", {
210
211
  className: "flex items-center gap-2 pt-3 border-t border-border",
211
212
  children: [
212
- P && /* @__PURE__ */ S("button", {
213
+ L && /* @__PURE__ */ C("button", {
213
214
  onClick: (t) => {
214
- t.stopPropagation(), T(`${E}/${e.id}/steps`);
215
+ t.stopPropagation(), E(`${D}/${e.id}/steps`);
215
216
  },
216
217
  className: "flex-1 px-3 py-1.5 text-xs bg-secondary text-secondary-foreground rounded hover:bg-secondary/80 flex items-center justify-center gap-1.5",
217
- title: w("toursList.editStepsTitle"),
218
- children: [/* @__PURE__ */ x(y, { size: 14 }), w("toursList.stepsButton")]
218
+ title: T("toursList.editStepsTitle"),
219
+ children: [/* @__PURE__ */ S(ee, { size: 14 }), T("toursList.stepsButton")]
219
220
  }),
220
- /* @__PURE__ */ S("button", {
221
+ /* @__PURE__ */ C("button", {
221
222
  onClick: (t) => {
222
- t.stopPropagation(), T(`${E}/${e.id}/preview`);
223
+ t.stopPropagation(), E(`${D}/${e.id}/preview`);
223
224
  },
224
225
  className: "flex-1 px-3 py-1.5 text-xs border border-border rounded hover:bg-accent flex items-center justify-center gap-1.5",
225
- title: w("toursList.previewTitle"),
226
- children: [/* @__PURE__ */ x(_, { size: 14 }), w("toursList.previewButton")]
226
+ title: T("toursList.previewTitle"),
227
+ children: [/* @__PURE__ */ S(_, { size: 14 }), T("toursList.previewButton")]
227
228
  }),
228
- N && /* @__PURE__ */ x("button", {
229
+ I && /* @__PURE__ */ S("button", {
229
230
  onClick: (t) => {
230
- t.stopPropagation(), T(`${E}/${e.id}/edit`);
231
+ t.stopPropagation(), E(`${D}/${e.id}/edit`);
231
232
  },
232
233
  className: "px-2 py-1.5 text-xs border border-border rounded hover:bg-accent",
233
- title: w("toursList.settingsTitle"),
234
- "aria-label": w("toursList.settingsTitle"),
235
- children: /* @__PURE__ */ x(re, { size: 14 })
234
+ title: T("toursList.settingsTitle"),
235
+ "aria-label": T("toursList.settingsTitle"),
236
+ children: /* @__PURE__ */ S(x, { size: 14 })
236
237
  }),
237
- a && /* @__PURE__ */ S("div", {
238
+ a && /* @__PURE__ */ C("div", {
238
239
  className: "relative",
239
- ref: X === e.id ? Q : void 0,
240
- children: [/* @__PURE__ */ x("button", {
240
+ ref: Z === e.id ? $ : void 0,
241
+ children: [/* @__PURE__ */ S("button", {
241
242
  onClick: (t) => {
242
- t.stopPropagation(), Z(X === e.id ? null : e.id);
243
+ t.stopPropagation(), Q(Z === e.id ? null : e.id);
243
244
  },
244
245
  className: "px-2 py-1.5 text-xs border border-border rounded hover:bg-accent",
245
- title: w("toursList.moreActionsTitle"),
246
- "aria-label": w("toursList.moreActionsTitle"),
247
- children: /* @__PURE__ */ x(b, { size: 14 })
248
- }), X === e.id && /* @__PURE__ */ S("div", {
246
+ title: T("toursList.moreActionsTitle"),
247
+ "aria-label": T("toursList.moreActionsTitle"),
248
+ children: /* @__PURE__ */ S(te, { size: 14 })
249
+ }), Z === e.id && /* @__PURE__ */ C("div", {
249
250
  className: "absolute right-0 top-full mt-1 w-40 bg-card border border-border rounded-md shadow-lg z-10",
250
251
  children: [
251
- n && /* @__PURE__ */ x("button", {
252
+ n && /* @__PURE__ */ S("button", {
252
253
  onClick: (t) => {
253
- t.stopPropagation(), le(e.id);
254
+ t.stopPropagation(), ue(e.id);
254
255
  },
255
256
  className: "w-full text-left px-3 py-2 text-xs hover:bg-accent",
256
- children: w("toursList.actionPublish")
257
+ children: T("toursList.actionPublish")
257
258
  }),
258
- r && /* @__PURE__ */ x("button", {
259
+ r && /* @__PURE__ */ S("button", {
259
260
  onClick: (t) => {
260
- t.stopPropagation(), ue(e.id);
261
+ t.stopPropagation(), de(e.id);
261
262
  },
262
263
  className: "w-full text-left px-3 py-2 text-xs hover:bg-accent",
263
- children: w("toursList.actionUnpublish")
264
+ children: T("toursList.actionUnpublish")
264
265
  }),
265
- R && /* @__PURE__ */ x("button", {
266
+ V && /* @__PURE__ */ S("button", {
266
267
  onClick: (t) => {
267
- t.stopPropagation(), fe(e.id);
268
+ t.stopPropagation(), pe(e.id);
268
269
  },
269
270
  className: "w-full text-left px-3 py-2 text-xs hover:bg-accent",
270
- children: w("toursList.actionDuplicate")
271
+ children: T("toursList.actionDuplicate")
271
272
  }),
272
- i && /* @__PURE__ */ x("button", {
273
+ i && /* @__PURE__ */ S("button", {
273
274
  onClick: (t) => {
274
- t.stopPropagation(), de(e.id);
275
+ t.stopPropagation(), fe(e.id);
275
276
  },
276
277
  className: "w-full text-left px-3 py-2 text-xs hover:bg-accent",
277
- children: w("toursList.actionArchive")
278
+ children: T("toursList.actionArchive")
278
279
  }),
279
- F && /* @__PURE__ */ x("button", {
280
+ R && /* @__PURE__ */ S("button", {
280
281
  onClick: (t) => {
281
- t.stopPropagation(), pe(e.id);
282
+ t.stopPropagation(), me(e.id);
282
283
  },
283
284
  className: "w-full text-left px-3 py-2 text-xs text-destructive hover:bg-destructive/10",
284
- children: w("toursList.actionDelete")
285
+ children: T("toursList.actionDelete")
285
286
  })
286
287
  ]
287
288
  })]
@@ -291,20 +292,20 @@ function w() {
291
292
  ]
292
293
  }, e.id);
293
294
  })
294
- }) : /* @__PURE__ */ S("div", {
295
+ }) : /* @__PURE__ */ C("div", {
295
296
  className: "text-center py-12 border border-dashed border-border rounded-md",
296
- children: [/* @__PURE__ */ x("p", {
297
+ children: [/* @__PURE__ */ S("p", {
297
298
  className: "text-sm sm:text-base text-muted-foreground mb-2",
298
- children: w("toursList.emptyTitle")
299
- }), /* @__PURE__ */ x("p", {
299
+ children: T("toursList.emptyTitle")
300
+ }), /* @__PURE__ */ S("p", {
300
301
  className: "text-xs sm:text-sm text-muted-foreground",
301
- children: w("toursList.emptySubtitle")
302
+ children: T("toursList.emptySubtitle")
302
303
  })]
303
304
  })
304
305
  ]
305
306
  });
306
307
  }
307
308
  //#endregion
308
- export { w as ToursListPage };
309
+ export { T as ToursListPage };
309
310
 
310
311
  //# sourceMappingURL=ToursListPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ToursListPage.js","names":[],"sources":["../../../src/tours/pages/ToursListPage.tsx"],"sourcesContent":["import { useEffect, useRef, useState } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport {\n Plus,\n Search,\n Grid,\n List as ListIcon,\n ListOrdered,\n Eye,\n Settings,\n MoreVertical,\n} from 'lucide-react';\nimport { PermissionGate } from '@burdenoff/fe-libs/shared/components';\nimport { useToursList } from '../hooks/useToursList';\nimport { useTourLifecycle } from '../hooks/useTourLifecycle';\nimport { useToursPermissionAccess } from '../hooks/useToursPermissionAccess';\nimport { useToursStore } from '../store/toursStore';\nimport { useToursContext } from '../providers/ToursProvider';\nimport { useDeleteTourMutation } from '../../generated/global-operations';\nimport { TOUR_PERMISSIONS } from '../../constants/permissions';\nimport type { TourStatus } from '../types';\nimport type { Tour } from '../../generated/global-types';\nimport { useToursI18n } from '../i18n';\nimport {\n isNative,\n nativeConfirm,\n nativeImpact,\n nativeNotify,\n nativeToast,\n} from '../../utils/nativeBridge';\n\n/**\n * Tours list page\n * Displays all tours with filtering and search\n */\nexport function ToursListPage() {\n const { tr } = useToursI18n();\n const navigate = useNavigate();\n const { basePath = '/' } = useToursContext();\n const { viewMode, tourFilter, setViewMode, updateTourFilter } = useToursStore();\n const { hasPermission, isLoading: permissionsLoading } = useToursPermissionAccess();\n\n const canUpdateTour = hasPermission(TOUR_PERMISSIONS.TOUR_UPDATE);\n const canUpdateTourSteps = hasPermission(TOUR_PERMISSIONS.TOUR_STEP_UPDATE);\n const canDeleteTour = hasPermission(TOUR_PERMISSIONS.TOUR_DELETE);\n const canPublishTour = hasPermission(TOUR_PERMISSIONS.TOUR_PUBLISH);\n const canArchiveTour = hasPermission(TOUR_PERMISSIONS.TOUR_ARCHIVE);\n const canDuplicateTour = hasPermission(TOUR_PERMISSIONS.TOUR_DUPLICATE);\n\n // Fetch tours (includes both workspace-specific and global tours)\n const { tours, loading, fetchTours } = useToursList();\n\n const { publishTour, unpublishTour, archiveTour, duplicateTour } = useTourLifecycle();\n const [deleteTourMutation] = useDeleteTourMutation();\n\n const [searchQuery, setSearchQuery] = useState('');\n const [statusFilter, setStatusFilter] = useState<TourStatus | 'all'>('all');\n const [openMenuId, setOpenMenuId] = useState<string | null>(null);\n const menuRef = useRef<HTMLDivElement>(null);\n\n // Fetch tours when filter changes. The list page is also the user-facing\n // tour browser, so backend permissions remain authoritative for actions while\n // normal users can still see published tours returned by the API.\n useEffect(() => {\n if (permissionsLoading) return;\n fetchTours({ ...tourFilter });\n }, [fetchTours, tourFilter, permissionsLoading]);\n\n const handleSearch = (query: string) => {\n setSearchQuery(query);\n updateTourFilter({ search: query });\n };\n\n const handleStatusFilter = (status: TourStatus | 'all') => {\n setStatusFilter(status);\n updateTourFilter({ status });\n };\n\n const handleCreateTour = () => {\n const newPath = basePath.endsWith('/') ? `${basePath}new` : `${basePath}/new`;\n navigate(newPath);\n };\n\n const refreshTours = () => fetchTours({ ...tourFilter });\n\n const getStatusLabel = (status: TourStatus) => {\n switch (status) {\n case 'DRAFT':\n return tr('toursList.statusDraft');\n case 'PUBLISHED':\n return tr('toursList.statusPublished');\n case 'ARCHIVED':\n return tr('toursList.statusArchived');\n case 'SCHEDULED':\n return tr('toursList.statusScheduled');\n }\n };\n\n const handlePublish = async (id: string) => {\n setOpenMenuId(null);\n nativeImpact('light');\n try {\n await publishTour(id);\n void nativeNotify('success');\n refreshTours();\n } catch {\n void nativeNotify('error');\n if (isNative()) void nativeToast(tr('toursList.errorPublish'));\n else alert(tr('toursList.errorPublish'));\n }\n };\n\n const handleUnpublish = async (id: string) => {\n setOpenMenuId(null);\n nativeImpact('light');\n try {\n await unpublishTour(id);\n void nativeNotify('success');\n refreshTours();\n } catch {\n void nativeNotify('error');\n if (isNative()) void nativeToast(tr('toursList.errorUnpublish'));\n else alert(tr('toursList.errorUnpublish'));\n }\n };\n\n const handleArchive = async (id: string) => {\n setOpenMenuId(null);\n nativeImpact('light');\n try {\n await archiveTour(id);\n void nativeNotify('success');\n refreshTours();\n } catch {\n void nativeNotify('error');\n if (isNative()) void nativeToast(tr('toursList.errorArchive'));\n else alert(tr('toursList.errorArchive'));\n }\n };\n\n const handleDuplicate = async (id: string) => {\n setOpenMenuId(null);\n nativeImpact('light');\n try {\n await duplicateTour(id);\n void nativeNotify('success');\n refreshTours();\n } catch {\n void nativeNotify('error');\n if (isNative()) void nativeToast(tr('toursList.errorDuplicate'));\n else alert(tr('toursList.errorDuplicate'));\n }\n };\n\n const handleDelete = async (id: string) => {\n setOpenMenuId(null);\n nativeImpact('medium');\n const confirmMsg = tr('toursList.confirmDelete');\n const nativeResult = await nativeConfirm({\n title: tr('toursList.actionDelete'),\n message: confirmMsg,\n okButtonTitle: tr('toursList.actionDelete'),\n });\n const confirmed = nativeResult === null ? window.confirm(confirmMsg) : nativeResult;\n if (!confirmed) return;\n try {\n await deleteTourMutation({ variables: { id } });\n void nativeNotify('success');\n refreshTours();\n } catch {\n void nativeNotify('error');\n if (isNative()) void nativeToast(tr('toursList.errorDelete'));\n else alert(tr('toursList.errorDelete'));\n }\n };\n\n // Close dropdown when clicking outside\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (menuRef.current && !menuRef.current.contains(event.target as Node)) {\n setOpenMenuId(null);\n }\n };\n if (openMenuId) {\n document.addEventListener('mousedown', handleClickOutside);\n }\n return () => document.removeEventListener('mousedown', handleClickOutside);\n }, [openMenuId]);\n\n return (\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6\">\n {/* Header */}\n <div className=\"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3\">\n <div className=\"flex-1 min-w-0\">\n <h1 className=\"text-xl sm:text-2xl font-bold text-foreground\">{tr('toursList.title')}</h1>\n <p className=\"text-sm sm:text-base text-muted-foreground\">{tr('toursList.subtitle')}</p>\n </div>\n <PermissionGate permission={TOUR_PERMISSIONS.TOUR_CREATE}>\n <button\n onClick={handleCreateTour}\n className=\"w-full sm:w-auto px-4 py-2 min-h-[44px] bg-primary text-primary-foreground rounded-md hover:bg-primary/90 active:bg-primary/80 flex items-center justify-center gap-2\"\n >\n <Plus size={20} />\n {tr('toursList.createTour')}\n </button>\n </PermissionGate>\n </div>\n\n {/* Filters and Search */}\n <div className=\"flex flex-col sm:flex-row gap-3 sm:gap-4\">\n {/* Search */}\n <div className=\"flex-1 relative\">\n <Search\n className=\"absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground\"\n size={20}\n />\n <input\n type=\"text\"\n placeholder={tr('toursList.searchPlaceholder')}\n value={searchQuery}\n onChange={(e) => handleSearch(e.target.value)}\n className=\"w-full pl-[3.25rem] pr-4 py-2 min-h-[44px] border border-border rounded-md bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring\"\n />\n </div>\n\n {/* Status Filter */}\n <div className=\"flex gap-2\">\n <select\n value={statusFilter}\n onChange={(e) => handleStatusFilter(e.target.value as TourStatus | 'all')}\n className=\"px-4 py-2 min-h-[44px] border border-border rounded-md bg-background text-foreground\"\n >\n <option value=\"all\">{tr('toursList.statusAll')}</option>\n <option value=\"DRAFT\">{tr('toursList.statusDraft')}</option>\n <option value=\"PUBLISHED\">{tr('toursList.statusPublished')}</option>\n <option value=\"ARCHIVED\">{tr('toursList.statusArchived')}</option>\n <option value=\"SCHEDULED\">{tr('toursList.statusScheduled')}</option>\n </select>\n\n {/* View Mode Toggle */}\n <div className=\"flex border border-border rounded-md overflow-hidden\">\n <button\n onClick={() => setViewMode('grid')}\n className={`px-3 py-2 min-h-[44px] ${\n viewMode === 'grid'\n ? 'bg-primary text-primary-foreground'\n : 'bg-background text-foreground hover:bg-accent'\n }`}\n aria-label={tr('toursList.gridView')}\n title={tr('toursList.gridView')}\n >\n <Grid size={20} />\n </button>\n <button\n onClick={() => setViewMode('list')}\n className={`px-3 py-2 min-h-[44px] ${\n viewMode === 'list'\n ? 'bg-primary text-primary-foreground'\n : 'bg-background text-foreground hover:bg-accent'\n }`}\n aria-label={tr('toursList.listView')}\n title={tr('toursList.listView')}\n >\n <ListIcon size={20} />\n </button>\n </div>\n </div>\n </div>\n\n {/* Tours List */}\n {loading ? (\n <div className=\"text-center py-12\">\n <p className=\"text-sm sm:text-base text-muted-foreground\">{tr('toursList.loading')}</p>\n </div>\n ) : tours.length > 0 ? (\n <div\n className={\n viewMode === 'grid'\n ? 'grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3 sm:gap-4'\n : 'space-y-3'\n }\n >\n {tours.map((tour: Tour) => {\n const status = tour.status as TourStatus;\n const canShowPublishAction = status === 'DRAFT' && canPublishTour;\n const canShowUnpublishAction = status === 'PUBLISHED' && canPublishTour;\n const canShowArchiveAction = status !== 'ARCHIVED' && canArchiveTour;\n const hasMoreActions =\n canShowPublishAction ||\n canShowUnpublishAction ||\n canDuplicateTour ||\n canShowArchiveAction ||\n canDeleteTour;\n\n return (\n <div\n key={tour.id}\n className=\"border border-border rounded-md p-4 bg-card hover:shadow-md transition-shadow\"\n >\n <div className=\"flex items-start justify-between mb-2\">\n <h3\n className={`font-semibold text-foreground ${\n canUpdateTour ? 'cursor-pointer hover:text-primary' : ''\n }`}\n onClick={() => {\n if (canUpdateTour) navigate(`${basePath}/${tour.id}/edit`);\n }}\n >\n {tour.name}\n </h3>\n <span\n className={`px-2 py-1 text-xs rounded-full ${\n tour.status === 'PUBLISHED'\n ? 'bg-success/10 text-success'\n : tour.status === 'DRAFT'\n ? 'bg-muted text-muted-foreground'\n : 'bg-warning/10 text-warning'\n }`}\n >\n {getStatusLabel(status)}\n </span>\n </div>\n {tour.description && (\n <p className=\"text-sm text-muted-foreground mb-3\">{tour.description}</p>\n )}\n <div className=\"flex items-center justify-between text-xs text-muted-foreground mb-3\">\n <span>\n {tour.steps?.length || 0} {tr('toursList.steps')}\n </span>\n <span>\n {tour.viewCount} {tr('toursList.views')}\n </span>\n </div>\n {/* Quick Actions */}\n <div className=\"flex items-center gap-2 pt-3 border-t border-border\">\n {canUpdateTourSteps && (\n <button\n onClick={(e) => {\n e.stopPropagation();\n navigate(`${basePath}/${tour.id}/steps`);\n }}\n className=\"flex-1 px-3 py-1.5 text-xs bg-secondary text-secondary-foreground rounded hover:bg-secondary/80 flex items-center justify-center gap-1.5\"\n title={tr('toursList.editStepsTitle')}\n >\n <ListOrdered size={14} />\n {tr('toursList.stepsButton')}\n </button>\n )}\n <button\n onClick={(e) => {\n e.stopPropagation();\n navigate(`${basePath}/${tour.id}/preview`);\n }}\n className=\"flex-1 px-3 py-1.5 text-xs border border-border rounded hover:bg-accent flex items-center justify-center gap-1.5\"\n title={tr('toursList.previewTitle')}\n >\n <Eye size={14} />\n {tr('toursList.previewButton')}\n </button>\n {canUpdateTour && (\n <button\n onClick={(e) => {\n e.stopPropagation();\n navigate(`${basePath}/${tour.id}/edit`);\n }}\n className=\"px-2 py-1.5 text-xs border border-border rounded hover:bg-accent\"\n title={tr('toursList.settingsTitle')}\n aria-label={tr('toursList.settingsTitle')}\n >\n <Settings size={14} />\n </button>\n )}\n {/* More Actions Dropdown */}\n {hasMoreActions && (\n <div className=\"relative\" ref={openMenuId === tour.id ? menuRef : undefined}>\n <button\n onClick={(e) => {\n e.stopPropagation();\n setOpenMenuId(openMenuId === tour.id ? null : tour.id);\n }}\n className=\"px-2 py-1.5 text-xs border border-border rounded hover:bg-accent\"\n title={tr('toursList.moreActionsTitle')}\n aria-label={tr('toursList.moreActionsTitle')}\n >\n <MoreVertical size={14} />\n </button>\n {openMenuId === tour.id && (\n <div className=\"absolute right-0 top-full mt-1 w-40 bg-card border border-border rounded-md shadow-lg z-10\">\n {canShowPublishAction && (\n <button\n onClick={(e) => {\n e.stopPropagation();\n handlePublish(tour.id);\n }}\n className=\"w-full text-left px-3 py-2 text-xs hover:bg-accent\"\n >\n {tr('toursList.actionPublish')}\n </button>\n )}\n {canShowUnpublishAction && (\n <button\n onClick={(e) => {\n e.stopPropagation();\n handleUnpublish(tour.id);\n }}\n className=\"w-full text-left px-3 py-2 text-xs hover:bg-accent\"\n >\n {tr('toursList.actionUnpublish')}\n </button>\n )}\n {canDuplicateTour && (\n <button\n onClick={(e) => {\n e.stopPropagation();\n handleDuplicate(tour.id);\n }}\n className=\"w-full text-left px-3 py-2 text-xs hover:bg-accent\"\n >\n {tr('toursList.actionDuplicate')}\n </button>\n )}\n {canShowArchiveAction && (\n <button\n onClick={(e) => {\n e.stopPropagation();\n handleArchive(tour.id);\n }}\n className=\"w-full text-left px-3 py-2 text-xs hover:bg-accent\"\n >\n {tr('toursList.actionArchive')}\n </button>\n )}\n {canDeleteTour && (\n <button\n onClick={(e) => {\n e.stopPropagation();\n handleDelete(tour.id);\n }}\n className=\"w-full text-left px-3 py-2 text-xs text-destructive hover:bg-destructive/10\"\n >\n {tr('toursList.actionDelete')}\n </button>\n )}\n </div>\n )}\n </div>\n )}\n </div>\n </div>\n );\n })}\n </div>\n ) : (\n <div className=\"text-center py-12 border border-dashed border-border rounded-md\">\n <p className=\"text-sm sm:text-base text-muted-foreground mb-2\">\n {tr('toursList.emptyTitle')}\n </p>\n <p className=\"text-xs sm:text-sm text-muted-foreground\">\n {tr('toursList.emptySubtitle')}\n </p>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ToursListPage.js","names":[],"sources":["../../../src/tours/pages/ToursListPage.tsx"],"sourcesContent":["import { useEffect, useRef, useState } from 'react';\nimport { useNavigate } from 'react-router-dom';\nimport {\n Plus,\n Search,\n Grid,\n List as ListIcon,\n ListOrdered,\n Eye,\n Settings,\n MoreVertical,\n} from 'lucide-react';\nimport { PermissionGate } from '@burdenoff/fe-libs/shared/components';\nimport { useToursList } from '../hooks/useToursList';\nimport { useTourLifecycle } from '../hooks/useTourLifecycle';\nimport { useToursPermissionAccess } from '../hooks/useToursPermissionAccess';\nimport { useToursStore } from '../store/toursStore';\nimport { useToursContext } from '../providers/ToursProvider';\nimport { useDeleteTourMutation } from '../../generated/global-operations';\nimport { TOUR_PERMISSIONS } from '../../constants/permissions';\nimport type { TourStatus } from '../types';\nimport type { Tour } from '../../generated/global-types';\nimport { useToursI18n } from '../i18n';\nimport {\n isNative,\n nativeConfirm,\n nativeImpact,\n nativeNotify,\n nativeToast,\n} from '../../utils/nativeBridge';\n\n/**\n * Tours list page\n * Displays all tours with filtering and search\n */\nexport function ToursListPage() {\n const { tr } = useToursI18n();\n const navigate = useNavigate();\n const { basePath = '/' } = useToursContext();\n const {\n viewMode,\n tourFilter,\n toursListRevision,\n setViewMode,\n updateTourFilter,\n notifyToursListChanged,\n } = useToursStore();\n const { hasPermission, isLoading: permissionsLoading } = useToursPermissionAccess();\n\n const canUpdateTour = hasPermission(TOUR_PERMISSIONS.TOUR_UPDATE);\n const canUpdateTourSteps = hasPermission(TOUR_PERMISSIONS.TOUR_STEP_UPDATE);\n const canDeleteTour = hasPermission(TOUR_PERMISSIONS.TOUR_DELETE);\n const canPublishTour = hasPermission(TOUR_PERMISSIONS.TOUR_PUBLISH);\n const canArchiveTour = hasPermission(TOUR_PERMISSIONS.TOUR_ARCHIVE);\n const canDuplicateTour = hasPermission(TOUR_PERMISSIONS.TOUR_DUPLICATE);\n\n // Fetch tours (includes both workspace-specific and global tours)\n const { tours, loading, fetchTours } = useToursList();\n\n const { publishTour, unpublishTour, archiveTour, duplicateTour } = useTourLifecycle();\n const [deleteTourMutation] = useDeleteTourMutation();\n\n const [searchQuery, setSearchQuery] = useState('');\n const [statusFilter, setStatusFilter] = useState<TourStatus | 'all'>('all');\n const [openMenuId, setOpenMenuId] = useState<string | null>(null);\n const menuRef = useRef<HTMLDivElement>(null);\n\n // Fetch tours when filter changes. The list page is also the user-facing\n // tour browser, so backend permissions remain authoritative for actions while\n // normal users can still see published tours returned by the API.\n useEffect(() => {\n if (permissionsLoading) return;\n fetchTours({ ...tourFilter });\n }, [fetchTours, tourFilter, toursListRevision, permissionsLoading]);\n\n const handleSearch = (query: string) => {\n setSearchQuery(query);\n updateTourFilter({ search: query });\n };\n\n const handleStatusFilter = (status: TourStatus | 'all') => {\n setStatusFilter(status);\n updateTourFilter({ status });\n };\n\n const handleCreateTour = () => {\n const newPath = basePath.endsWith('/') ? `${basePath}new` : `${basePath}/new`;\n navigate(newPath);\n };\n\n const getStatusLabel = (status: TourStatus) => {\n switch (status) {\n case 'DRAFT':\n return tr('toursList.statusDraft');\n case 'PUBLISHED':\n return tr('toursList.statusPublished');\n case 'ARCHIVED':\n return tr('toursList.statusArchived');\n case 'SCHEDULED':\n return tr('toursList.statusScheduled');\n }\n };\n\n const handlePublish = async (id: string) => {\n setOpenMenuId(null);\n nativeImpact('light');\n try {\n await publishTour(id);\n void nativeNotify('success');\n } catch {\n void nativeNotify('error');\n if (isNative()) void nativeToast(tr('toursList.errorPublish'));\n else alert(tr('toursList.errorPublish'));\n }\n };\n\n const handleUnpublish = async (id: string) => {\n setOpenMenuId(null);\n nativeImpact('light');\n try {\n await unpublishTour(id);\n void nativeNotify('success');\n } catch {\n void nativeNotify('error');\n if (isNative()) void nativeToast(tr('toursList.errorUnpublish'));\n else alert(tr('toursList.errorUnpublish'));\n }\n };\n\n const handleArchive = async (id: string) => {\n setOpenMenuId(null);\n nativeImpact('light');\n try {\n await archiveTour(id);\n void nativeNotify('success');\n } catch {\n void nativeNotify('error');\n if (isNative()) void nativeToast(tr('toursList.errorArchive'));\n else alert(tr('toursList.errorArchive'));\n }\n };\n\n const handleDuplicate = async (id: string) => {\n setOpenMenuId(null);\n nativeImpact('light');\n try {\n await duplicateTour(id);\n void nativeNotify('success');\n } catch {\n void nativeNotify('error');\n if (isNative()) void nativeToast(tr('toursList.errorDuplicate'));\n else alert(tr('toursList.errorDuplicate'));\n }\n };\n\n const handleDelete = async (id: string) => {\n setOpenMenuId(null);\n nativeImpact('medium');\n const confirmMsg = tr('toursList.confirmDelete');\n const nativeResult = await nativeConfirm({\n title: tr('toursList.actionDelete'),\n message: confirmMsg,\n okButtonTitle: tr('toursList.actionDelete'),\n });\n const confirmed = nativeResult === null ? window.confirm(confirmMsg) : nativeResult;\n if (!confirmed) return;\n try {\n await deleteTourMutation({ variables: { id } });\n notifyToursListChanged();\n void nativeNotify('success');\n } catch {\n void nativeNotify('error');\n if (isNative()) void nativeToast(tr('toursList.errorDelete'));\n else alert(tr('toursList.errorDelete'));\n }\n };\n\n // Close dropdown when clicking outside\n useEffect(() => {\n const handleClickOutside = (event: MouseEvent) => {\n if (menuRef.current && !menuRef.current.contains(event.target as Node)) {\n setOpenMenuId(null);\n }\n };\n if (openMenuId) {\n document.addEventListener('mousedown', handleClickOutside);\n }\n return () => document.removeEventListener('mousedown', handleClickOutside);\n }, [openMenuId]);\n\n return (\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6\">\n {/* Header */}\n <div className=\"flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3\">\n <div className=\"flex-1 min-w-0\">\n <h1 className=\"text-xl sm:text-2xl font-bold text-foreground\">{tr('toursList.title')}</h1>\n <p className=\"text-sm sm:text-base text-muted-foreground\">{tr('toursList.subtitle')}</p>\n </div>\n <PermissionGate permission={TOUR_PERMISSIONS.TOUR_CREATE}>\n <button\n onClick={handleCreateTour}\n className=\"w-full sm:w-auto px-4 py-2 min-h-[44px] bg-primary text-primary-foreground rounded-md hover:bg-primary/90 active:bg-primary/80 flex items-center justify-center gap-2\"\n >\n <Plus size={20} />\n {tr('toursList.createTour')}\n </button>\n </PermissionGate>\n </div>\n\n {/* Filters and Search */}\n <div className=\"flex flex-col sm:flex-row gap-3 sm:gap-4\">\n {/* Search */}\n <div className=\"flex-1 relative\">\n <Search\n className=\"absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground\"\n size={20}\n />\n <input\n type=\"text\"\n placeholder={tr('toursList.searchPlaceholder')}\n value={searchQuery}\n onChange={(e) => handleSearch(e.target.value)}\n className=\"w-full pl-[3.25rem] pr-4 py-2 min-h-[44px] border border-border rounded-md bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring\"\n />\n </div>\n\n {/* Status Filter */}\n <div className=\"flex gap-2\">\n <select\n value={statusFilter}\n onChange={(e) => handleStatusFilter(e.target.value as TourStatus | 'all')}\n className=\"px-4 py-2 min-h-[44px] border border-border rounded-md bg-background text-foreground\"\n >\n <option value=\"all\">{tr('toursList.statusAll')}</option>\n <option value=\"DRAFT\">{tr('toursList.statusDraft')}</option>\n <option value=\"PUBLISHED\">{tr('toursList.statusPublished')}</option>\n <option value=\"ARCHIVED\">{tr('toursList.statusArchived')}</option>\n <option value=\"SCHEDULED\">{tr('toursList.statusScheduled')}</option>\n </select>\n\n {/* View Mode Toggle */}\n <div className=\"flex border border-border rounded-md overflow-hidden\">\n <button\n onClick={() => setViewMode('grid')}\n className={`px-3 py-2 min-h-[44px] ${\n viewMode === 'grid'\n ? 'bg-primary text-primary-foreground'\n : 'bg-background text-foreground hover:bg-accent'\n }`}\n aria-label={tr('toursList.gridView')}\n title={tr('toursList.gridView')}\n >\n <Grid size={20} />\n </button>\n <button\n onClick={() => setViewMode('list')}\n className={`px-3 py-2 min-h-[44px] ${\n viewMode === 'list'\n ? 'bg-primary text-primary-foreground'\n : 'bg-background text-foreground hover:bg-accent'\n }`}\n aria-label={tr('toursList.listView')}\n title={tr('toursList.listView')}\n >\n <ListIcon size={20} />\n </button>\n </div>\n </div>\n </div>\n\n {/* Tours List */}\n {loading ? (\n <div className=\"text-center py-12\">\n <p className=\"text-sm sm:text-base text-muted-foreground\">{tr('toursList.loading')}</p>\n </div>\n ) : tours.length > 0 ? (\n <div\n className={\n viewMode === 'grid'\n ? 'grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3 sm:gap-4'\n : 'space-y-3'\n }\n >\n {tours.map((tour: Tour) => {\n const status = tour.status as TourStatus;\n const canShowPublishAction = status === 'DRAFT' && canPublishTour;\n const canShowUnpublishAction = status === 'PUBLISHED' && canPublishTour;\n const canShowArchiveAction = status !== 'ARCHIVED' && canArchiveTour;\n const hasMoreActions =\n canShowPublishAction ||\n canShowUnpublishAction ||\n canDuplicateTour ||\n canShowArchiveAction ||\n canDeleteTour;\n\n return (\n <div\n key={tour.id}\n className=\"border border-border rounded-md p-4 bg-card hover:shadow-md transition-shadow\"\n >\n <div className=\"flex items-start justify-between mb-2\">\n <h3\n className={`font-semibold text-foreground ${\n canUpdateTour ? 'cursor-pointer hover:text-primary' : ''\n }`}\n onClick={() => {\n if (canUpdateTour) navigate(`${basePath}/${tour.id}/edit`);\n }}\n >\n {tour.name}\n </h3>\n <span\n className={`px-2 py-1 text-xs rounded-full ${\n tour.status === 'PUBLISHED'\n ? 'bg-success/10 text-success'\n : tour.status === 'DRAFT'\n ? 'bg-muted text-muted-foreground'\n : 'bg-warning/10 text-warning'\n }`}\n >\n {getStatusLabel(status)}\n </span>\n </div>\n {tour.description && (\n <p className=\"text-sm text-muted-foreground mb-3\">{tour.description}</p>\n )}\n <div className=\"flex items-center justify-between text-xs text-muted-foreground mb-3\">\n <span>\n {tour.steps?.length || 0} {tr('toursList.steps')}\n </span>\n <span>\n {tour.viewCount} {tr('toursList.views')}\n </span>\n </div>\n {/* Quick Actions */}\n <div className=\"flex items-center gap-2 pt-3 border-t border-border\">\n {canUpdateTourSteps && (\n <button\n onClick={(e) => {\n e.stopPropagation();\n navigate(`${basePath}/${tour.id}/steps`);\n }}\n className=\"flex-1 px-3 py-1.5 text-xs bg-secondary text-secondary-foreground rounded hover:bg-secondary/80 flex items-center justify-center gap-1.5\"\n title={tr('toursList.editStepsTitle')}\n >\n <ListOrdered size={14} />\n {tr('toursList.stepsButton')}\n </button>\n )}\n <button\n onClick={(e) => {\n e.stopPropagation();\n navigate(`${basePath}/${tour.id}/preview`);\n }}\n className=\"flex-1 px-3 py-1.5 text-xs border border-border rounded hover:bg-accent flex items-center justify-center gap-1.5\"\n title={tr('toursList.previewTitle')}\n >\n <Eye size={14} />\n {tr('toursList.previewButton')}\n </button>\n {canUpdateTour && (\n <button\n onClick={(e) => {\n e.stopPropagation();\n navigate(`${basePath}/${tour.id}/edit`);\n }}\n className=\"px-2 py-1.5 text-xs border border-border rounded hover:bg-accent\"\n title={tr('toursList.settingsTitle')}\n aria-label={tr('toursList.settingsTitle')}\n >\n <Settings size={14} />\n </button>\n )}\n {/* More Actions Dropdown */}\n {hasMoreActions && (\n <div className=\"relative\" ref={openMenuId === tour.id ? menuRef : undefined}>\n <button\n onClick={(e) => {\n e.stopPropagation();\n setOpenMenuId(openMenuId === tour.id ? null : tour.id);\n }}\n className=\"px-2 py-1.5 text-xs border border-border rounded hover:bg-accent\"\n title={tr('toursList.moreActionsTitle')}\n aria-label={tr('toursList.moreActionsTitle')}\n >\n <MoreVertical size={14} />\n </button>\n {openMenuId === tour.id && (\n <div className=\"absolute right-0 top-full mt-1 w-40 bg-card border border-border rounded-md shadow-lg z-10\">\n {canShowPublishAction && (\n <button\n onClick={(e) => {\n e.stopPropagation();\n handlePublish(tour.id);\n }}\n className=\"w-full text-left px-3 py-2 text-xs hover:bg-accent\"\n >\n {tr('toursList.actionPublish')}\n </button>\n )}\n {canShowUnpublishAction && (\n <button\n onClick={(e) => {\n e.stopPropagation();\n handleUnpublish(tour.id);\n }}\n className=\"w-full text-left px-3 py-2 text-xs hover:bg-accent\"\n >\n {tr('toursList.actionUnpublish')}\n </button>\n )}\n {canDuplicateTour && (\n <button\n onClick={(e) => {\n e.stopPropagation();\n handleDuplicate(tour.id);\n }}\n className=\"w-full text-left px-3 py-2 text-xs hover:bg-accent\"\n >\n {tr('toursList.actionDuplicate')}\n </button>\n )}\n {canShowArchiveAction && (\n <button\n onClick={(e) => {\n e.stopPropagation();\n handleArchive(tour.id);\n }}\n className=\"w-full text-left px-3 py-2 text-xs hover:bg-accent\"\n >\n {tr('toursList.actionArchive')}\n </button>\n )}\n {canDeleteTour && (\n <button\n onClick={(e) => {\n e.stopPropagation();\n handleDelete(tour.id);\n }}\n className=\"w-full text-left px-3 py-2 text-xs text-destructive hover:bg-destructive/10\"\n >\n {tr('toursList.actionDelete')}\n </button>\n )}\n </div>\n )}\n </div>\n )}\n </div>\n </div>\n );\n })}\n </div>\n ) : (\n <div className=\"text-center py-12 border border-dashed border-border rounded-md\">\n <p className=\"text-sm sm:text-base text-muted-foreground mb-2\">\n {tr('toursList.emptyTitle')}\n </p>\n <p className=\"text-xs sm:text-sm text-muted-foreground\">\n {tr('toursList.emptySubtitle')}\n </p>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -6,6 +6,7 @@ interface ToursState {
6
6
  viewMode: ToursViewMode;
7
7
  devicePreview: DeviceType;
8
8
  tourFilter: TourFilters;
9
+ toursListRevision: number;
9
10
  stepSequence: TourStep[];
10
11
  isDirty: boolean;
11
12
  validationErrors: ValidationError[];
@@ -20,6 +21,7 @@ interface ToursState {
20
21
  setSidebarOpen: (open: boolean) => void;
21
22
  updateTourFilter: (filter: Partial<TourFilters>) => void;
22
23
  resetTourFilter: () => void;
24
+ notifyToursListChanged: () => void;
23
25
  updateStepSequence: (steps: TourStep[]) => void;
24
26
  reorderSteps: (startIndex: number, endIndex: number) => void;
25
27
  addStep: (step: Partial<TourStep>) => void;