@burdenoff/microfe-tours 2026.527.1 → 2026.527.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/tours/ToursUserRoutes.js +2 -4
- package/dist/tours/ToursUserRoutes.js.map +1 -1
- package/dist/tours/components/RouteHelpers.js +4 -4
- package/dist/tours/components/RouteHelpers.js.map +1 -1
- package/dist/tours/hooks/useTourPreview.js +39 -39
- package/dist/tours/hooks/useTourPreview.js.map +1 -1
- package/dist/tours/hooks/useToursList.js.map +1 -1
- package/dist/tours/i18n.js +4 -0
- package/dist/tours/i18n.js.map +1 -1
- package/dist/tours/pages/MediaLibraryPage.js +11 -8
- package/dist/tours/pages/MediaLibraryPage.js.map +1 -1
- package/dist/tours/pages/MyToursPage.js +59 -57
- package/dist/tours/pages/MyToursPage.js.map +1 -1
- package/dist/tours/pages/MyToursProgressPage.js +65 -61
- package/dist/tours/pages/MyToursProgressPage.js.map +1 -1
- package/dist/tours/pages/SchedulerPage.js +18 -4
- package/dist/tours/pages/SchedulerPage.js.map +1 -1
- package/dist/tours/pages/StepEditorPage.js +330 -276
- package/dist/tours/pages/StepEditorPage.js.map +1 -1
- package/dist/tours/pages/TemplateLibraryPage.js +7 -2
- package/dist/tours/pages/TemplateLibraryPage.js.map +1 -1
- package/dist/tours/pages/TourBuilderPage.js +144 -101
- package/dist/tours/pages/TourBuilderPage.js.map +1 -1
- package/dist/tours/pages/TourPreviewPage.js +12 -4
- package/dist/tours/pages/TourPreviewPage.js.map +1 -1
- package/dist/tours/pages/ToursListPage.js +165 -149
- package/dist/tours/pages/ToursListPage.js.map +1 -1
- package/dist/tours/providers/ToursProvider.js +1 -1
- package/dist/tours/providers/ToursProvider.js.map +1 -1
- package/dist/tours/utils/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -9,280 +9,296 @@ 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 v, List as
|
|
13
|
-
import { jsx as
|
|
14
|
-
import { useNavigate as
|
|
12
|
+
import { Eye as _, Grid as v, List as ee, ListOrdered as te, MoreVertical as ne, Plus as re, Search as y, Settings as b } from "lucide-react";
|
|
13
|
+
import { jsx as x, jsxs as S } from "react/jsx-runtime";
|
|
14
|
+
import { useNavigate as C } from "react-router-dom";
|
|
15
15
|
//#region src/tours/pages/ToursListPage.tsx
|
|
16
|
-
function
|
|
17
|
-
let { tr:
|
|
16
|
+
function w() {
|
|
17
|
+
let { tr: w } = s(), T = C(), { basePath: E = "/" } = e(), { viewMode: D, tourFilter: O, toursListRevision: k, setViewMode: A, updateTourFilter: j, notifyToursListChanged: M } = t(), { hasPermission: N, isLoading: P } = a(), F = N(o.TOUR_UPDATE), I = N(o.TOUR_STEP_UPDATE), L = N(o.TOUR_READ), R = N(o.TOUR_DELETE), z = N(o.TOUR_PUBLISH), B = N(o.TOUR_ARCHIVE), V = N(o.TOUR_DUPLICATE), { tours: H, loading: U, fetchTours: W } = r(), { publishTour: G, unpublishTour: ie, archiveTour: K, duplicateTour: q } = i(), [J] = n(), [Y, ae] = h(""), [oe, se] = h("all"), [X, Z] = h(null), Q = m(null);
|
|
18
18
|
p(() => {
|
|
19
|
-
|
|
19
|
+
P || W({ ...O });
|
|
20
20
|
}, [
|
|
21
|
-
|
|
21
|
+
W,
|
|
22
|
+
O,
|
|
22
23
|
k,
|
|
23
|
-
|
|
24
|
-
F
|
|
24
|
+
P
|
|
25
25
|
]);
|
|
26
|
-
let
|
|
27
|
-
|
|
28
|
-
},
|
|
29
|
-
|
|
30
|
-
},
|
|
31
|
-
E
|
|
32
|
-
},
|
|
26
|
+
let ce = (e) => {
|
|
27
|
+
ae(e), j({ search: e });
|
|
28
|
+
}, le = (e) => {
|
|
29
|
+
se(e), j({ status: e });
|
|
30
|
+
}, ue = () => {
|
|
31
|
+
T(E.endsWith("/") ? `${E}new` : `${E}/new`);
|
|
32
|
+
}, $ = (e) => {
|
|
33
33
|
switch (e) {
|
|
34
|
-
case "DRAFT": return
|
|
35
|
-
case "PUBLISHED": return
|
|
36
|
-
case "ARCHIVED": return
|
|
37
|
-
case "SCHEDULED": return
|
|
34
|
+
case "DRAFT": return w("toursList.statusDraft");
|
|
35
|
+
case "PUBLISHED": return w("toursList.statusPublished");
|
|
36
|
+
case "ARCHIVED": return w("toursList.statusArchived");
|
|
37
|
+
case "SCHEDULED": return w("toursList.statusScheduled");
|
|
38
38
|
}
|
|
39
39
|
}, de = async (e) => {
|
|
40
|
-
|
|
40
|
+
Z(null), u("light");
|
|
41
41
|
try {
|
|
42
|
-
await
|
|
42
|
+
await G(e), d("success");
|
|
43
43
|
} catch {
|
|
44
|
-
d("error"), c() ? f(
|
|
44
|
+
d("error"), c() ? f(w("toursList.errorPublish")) : alert(w("toursList.errorPublish"));
|
|
45
45
|
}
|
|
46
46
|
}, fe = async (e) => {
|
|
47
|
-
|
|
47
|
+
Z(null), u("light");
|
|
48
48
|
try {
|
|
49
|
-
await
|
|
49
|
+
await ie(e), d("success");
|
|
50
50
|
} catch {
|
|
51
|
-
d("error"), c() ? f(
|
|
51
|
+
d("error"), c() ? f(w("toursList.errorUnpublish")) : alert(w("toursList.errorUnpublish"));
|
|
52
52
|
}
|
|
53
53
|
}, pe = async (e) => {
|
|
54
|
-
|
|
54
|
+
Z(null), u("light");
|
|
55
55
|
try {
|
|
56
|
-
await
|
|
56
|
+
await K(e), d("success");
|
|
57
57
|
} catch {
|
|
58
|
-
d("error"), c() ? f(
|
|
58
|
+
d("error"), c() ? f(w("toursList.errorArchive")) : alert(w("toursList.errorArchive"));
|
|
59
59
|
}
|
|
60
60
|
}, me = async (e) => {
|
|
61
|
-
|
|
61
|
+
Z(null), u("light");
|
|
62
62
|
try {
|
|
63
|
-
await
|
|
63
|
+
await q(e), d("success");
|
|
64
64
|
} catch {
|
|
65
|
-
d("error"), c() ? f(
|
|
65
|
+
d("error"), c() ? f(w("toursList.errorDuplicate")) : alert(w("toursList.errorDuplicate"));
|
|
66
66
|
}
|
|
67
67
|
}, he = async (e) => {
|
|
68
|
-
|
|
69
|
-
let t =
|
|
70
|
-
title:
|
|
68
|
+
Z(null), u("medium");
|
|
69
|
+
let t = w("toursList.confirmDelete"), n = await l({
|
|
70
|
+
title: w("toursList.actionDelete"),
|
|
71
71
|
message: t,
|
|
72
|
-
okButtonTitle:
|
|
72
|
+
okButtonTitle: w("toursList.actionDelete")
|
|
73
73
|
});
|
|
74
74
|
if (n === null ? window.confirm(t) : n) try {
|
|
75
|
-
await
|
|
75
|
+
await J({ variables: { id: e } }), M(), d("success");
|
|
76
76
|
} catch {
|
|
77
|
-
d("error"), c() ? f(
|
|
77
|
+
d("error"), c() ? f(w("toursList.errorDelete")) : alert(w("toursList.errorDelete"));
|
|
78
78
|
}
|
|
79
79
|
};
|
|
80
80
|
return p(() => {
|
|
81
81
|
let e = (e) => {
|
|
82
|
-
|
|
82
|
+
Q.current && !Q.current.contains(e.target) && Z(null);
|
|
83
83
|
};
|
|
84
|
-
return
|
|
85
|
-
}, [
|
|
84
|
+
return X && document.addEventListener("mousedown", e), () => document.removeEventListener("mousedown", e);
|
|
85
|
+
}, [X]), /* @__PURE__ */ S("div", {
|
|
86
86
|
className: "space-y-4 sm:space-y-6 p-3 sm:p-6",
|
|
87
87
|
children: [
|
|
88
|
-
/* @__PURE__ */
|
|
88
|
+
/* @__PURE__ */ S("div", {
|
|
89
89
|
className: "flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3",
|
|
90
|
-
children: [/* @__PURE__ */
|
|
90
|
+
children: [/* @__PURE__ */ S("div", {
|
|
91
91
|
className: "flex-1 min-w-0",
|
|
92
|
-
children: [/* @__PURE__ */
|
|
92
|
+
children: [/* @__PURE__ */ x("h1", {
|
|
93
93
|
className: "text-xl sm:text-2xl font-bold text-foreground",
|
|
94
|
-
children:
|
|
95
|
-
}), /* @__PURE__ */
|
|
94
|
+
children: w("toursList.title")
|
|
95
|
+
}), /* @__PURE__ */ x("p", {
|
|
96
96
|
className: "text-sm sm:text-base text-muted-foreground",
|
|
97
|
-
children:
|
|
97
|
+
children: w("toursList.subtitle")
|
|
98
98
|
})]
|
|
99
|
-
}), /* @__PURE__ */
|
|
99
|
+
}), /* @__PURE__ */ x(g, {
|
|
100
100
|
permission: o.TOUR_CREATE,
|
|
101
|
-
children: /* @__PURE__ */
|
|
102
|
-
|
|
101
|
+
children: /* @__PURE__ */ S("button", {
|
|
102
|
+
type: "button",
|
|
103
|
+
onClick: ue,
|
|
103
104
|
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",
|
|
104
|
-
children: [/* @__PURE__ */
|
|
105
|
+
children: [/* @__PURE__ */ x(re, { size: 20 }), w("toursList.createTour")]
|
|
105
106
|
})
|
|
106
107
|
})]
|
|
107
108
|
}),
|
|
108
|
-
/* @__PURE__ */
|
|
109
|
+
/* @__PURE__ */ S("div", {
|
|
109
110
|
className: "flex flex-col sm:flex-row gap-3 sm:gap-4",
|
|
110
|
-
children: [/* @__PURE__ */
|
|
111
|
+
children: [/* @__PURE__ */ S("div", {
|
|
111
112
|
className: "flex-1 relative",
|
|
112
|
-
children: [/* @__PURE__ */
|
|
113
|
+
children: [/* @__PURE__ */ x(y, {
|
|
113
114
|
className: "absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground",
|
|
114
115
|
size: 20
|
|
115
|
-
}), /* @__PURE__ */
|
|
116
|
+
}), /* @__PURE__ */ x("input", {
|
|
116
117
|
type: "text",
|
|
117
|
-
placeholder:
|
|
118
|
-
value:
|
|
119
|
-
onChange: (e) =>
|
|
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"
|
|
118
|
+
placeholder: w("toursList.searchPlaceholder"),
|
|
119
|
+
value: Y,
|
|
120
|
+
onChange: (e) => ce(e.target.value),
|
|
121
|
+
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",
|
|
122
|
+
"aria-label": w("toursList.searchPlaceholder")
|
|
121
123
|
})]
|
|
122
|
-
}), /* @__PURE__ */
|
|
124
|
+
}), /* @__PURE__ */ S("div", {
|
|
123
125
|
className: "flex gap-2",
|
|
124
|
-
children: [/* @__PURE__ */
|
|
125
|
-
value:
|
|
126
|
-
onChange: (e) =>
|
|
126
|
+
children: [/* @__PURE__ */ S("select", {
|
|
127
|
+
value: oe,
|
|
128
|
+
onChange: (e) => le(e.target.value),
|
|
127
129
|
className: "px-4 py-2 min-h-[44px] border border-border rounded-md bg-background text-foreground",
|
|
130
|
+
"aria-label": w("toursList.statusAll"),
|
|
128
131
|
children: [
|
|
129
|
-
/* @__PURE__ */
|
|
132
|
+
/* @__PURE__ */ x("option", {
|
|
130
133
|
value: "all",
|
|
131
|
-
children:
|
|
134
|
+
children: w("toursList.statusAll")
|
|
132
135
|
}),
|
|
133
|
-
/* @__PURE__ */
|
|
136
|
+
/* @__PURE__ */ x("option", {
|
|
134
137
|
value: "DRAFT",
|
|
135
|
-
children:
|
|
138
|
+
children: w("toursList.statusDraft")
|
|
136
139
|
}),
|
|
137
|
-
/* @__PURE__ */
|
|
140
|
+
/* @__PURE__ */ x("option", {
|
|
138
141
|
value: "PUBLISHED",
|
|
139
|
-
children:
|
|
142
|
+
children: w("toursList.statusPublished")
|
|
140
143
|
}),
|
|
141
|
-
/* @__PURE__ */
|
|
144
|
+
/* @__PURE__ */ x("option", {
|
|
142
145
|
value: "ARCHIVED",
|
|
143
|
-
children:
|
|
146
|
+
children: w("toursList.statusArchived")
|
|
144
147
|
}),
|
|
145
|
-
/* @__PURE__ */
|
|
148
|
+
/* @__PURE__ */ x("option", {
|
|
146
149
|
value: "SCHEDULED",
|
|
147
|
-
children:
|
|
150
|
+
children: w("toursList.statusScheduled")
|
|
148
151
|
})
|
|
149
152
|
]
|
|
150
|
-
}), /* @__PURE__ */
|
|
153
|
+
}), /* @__PURE__ */ S("div", {
|
|
151
154
|
className: "flex border border-border rounded-md overflow-hidden",
|
|
152
|
-
children: [/* @__PURE__ */
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
"
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
155
|
+
children: [/* @__PURE__ */ x("button", {
|
|
156
|
+
type: "button",
|
|
157
|
+
onClick: () => A("grid"),
|
|
158
|
+
className: `px-3 py-2 min-h-[44px] ${D === "grid" ? "bg-primary text-primary-foreground" : "bg-background text-foreground hover:bg-accent"}`,
|
|
159
|
+
"aria-label": w("toursList.gridView"),
|
|
160
|
+
title: w("toursList.gridView"),
|
|
161
|
+
children: /* @__PURE__ */ x(v, { size: 20 })
|
|
162
|
+
}), /* @__PURE__ */ x("button", {
|
|
163
|
+
type: "button",
|
|
164
|
+
onClick: () => A("list"),
|
|
165
|
+
className: `px-3 py-2 min-h-[44px] ${D === "list" ? "bg-primary text-primary-foreground" : "bg-background text-foreground hover:bg-accent"}`,
|
|
166
|
+
"aria-label": w("toursList.listView"),
|
|
167
|
+
title: w("toursList.listView"),
|
|
168
|
+
children: /* @__PURE__ */ x(ee, { size: 20 })
|
|
164
169
|
})]
|
|
165
170
|
})]
|
|
166
171
|
})]
|
|
167
172
|
}),
|
|
168
|
-
|
|
173
|
+
U ? /* @__PURE__ */ x("div", {
|
|
169
174
|
className: "text-center py-12",
|
|
170
|
-
children: /* @__PURE__ */
|
|
175
|
+
children: /* @__PURE__ */ x("p", {
|
|
171
176
|
className: "text-sm sm:text-base text-muted-foreground",
|
|
172
|
-
children:
|
|
177
|
+
children: w("toursList.loading")
|
|
173
178
|
})
|
|
174
|
-
}) :
|
|
175
|
-
className:
|
|
176
|
-
children:
|
|
177
|
-
let t = e.status, n = t === "DRAFT" &&
|
|
178
|
-
return /* @__PURE__ */
|
|
179
|
+
}) : H.length > 0 ? /* @__PURE__ */ x("div", {
|
|
180
|
+
className: D === "grid" ? "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3 sm:gap-4" : "space-y-3",
|
|
181
|
+
children: H.map((e) => {
|
|
182
|
+
let t = e.status, n = t === "DRAFT" && z, r = t === "PUBLISHED" && z, i = t !== "ARCHIVED" && B, a = n || r || V || i || R;
|
|
183
|
+
return /* @__PURE__ */ S("div", {
|
|
179
184
|
className: "border border-border rounded-md p-4 bg-card hover:shadow-md transition-shadow",
|
|
180
185
|
children: [
|
|
181
|
-
/* @__PURE__ */
|
|
186
|
+
/* @__PURE__ */ S("div", {
|
|
182
187
|
className: "flex items-start justify-between mb-2",
|
|
183
|
-
children: [/* @__PURE__ */
|
|
184
|
-
className:
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
188
|
+
children: [/* @__PURE__ */ x("h3", {
|
|
189
|
+
className: "font-semibold text-foreground",
|
|
190
|
+
children: F ? /* @__PURE__ */ x("button", {
|
|
191
|
+
type: "button",
|
|
192
|
+
className: "cursor-pointer hover:text-primary bg-transparent border-0 p-0 text-left font-inherit",
|
|
193
|
+
onClick: () => T(`${E}/${e.id}/edit`),
|
|
194
|
+
children: e.name
|
|
195
|
+
}) : e.name
|
|
196
|
+
}), /* @__PURE__ */ x("span", {
|
|
190
197
|
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"}`,
|
|
191
|
-
children:
|
|
198
|
+
children: $(t)
|
|
192
199
|
})]
|
|
193
200
|
}),
|
|
194
|
-
e.description && /* @__PURE__ */
|
|
201
|
+
e.description && /* @__PURE__ */ x("p", {
|
|
195
202
|
className: "text-sm text-muted-foreground mb-3",
|
|
196
203
|
children: e.description
|
|
197
204
|
}),
|
|
198
|
-
/* @__PURE__ */
|
|
205
|
+
/* @__PURE__ */ S("div", {
|
|
199
206
|
className: "flex items-center justify-between text-xs text-muted-foreground mb-3",
|
|
200
|
-
children: [/* @__PURE__ */
|
|
207
|
+
children: [/* @__PURE__ */ S("span", { children: [
|
|
201
208
|
e.steps?.length || 0,
|
|
202
209
|
" ",
|
|
203
|
-
|
|
204
|
-
] }), /* @__PURE__ */
|
|
210
|
+
w("toursList.steps")
|
|
211
|
+
] }), /* @__PURE__ */ S("span", { children: [
|
|
205
212
|
e.viewCount,
|
|
206
213
|
" ",
|
|
207
|
-
|
|
214
|
+
w("toursList.views")
|
|
208
215
|
] })]
|
|
209
216
|
}),
|
|
210
|
-
/* @__PURE__ */
|
|
217
|
+
/* @__PURE__ */ S("div", {
|
|
211
218
|
className: "flex flex-wrap items-center gap-2 pt-3 border-t border-border",
|
|
212
219
|
children: [
|
|
213
|
-
|
|
220
|
+
I && /* @__PURE__ */ S("button", {
|
|
221
|
+
type: "button",
|
|
214
222
|
onClick: (t) => {
|
|
215
|
-
t.stopPropagation(),
|
|
223
|
+
t.stopPropagation(), T(`${E}/${e.id}/steps`);
|
|
216
224
|
},
|
|
217
225
|
className: "flex-1 min-w-[6.5rem] px-3 py-2 min-h-[44px] text-xs bg-secondary text-secondary-foreground rounded hover:bg-secondary/80 flex items-center justify-center gap-1.5",
|
|
218
|
-
title:
|
|
219
|
-
children: [/* @__PURE__ */
|
|
226
|
+
title: w("toursList.editStepsTitle"),
|
|
227
|
+
children: [/* @__PURE__ */ x(te, { size: 14 }), w("toursList.stepsButton")]
|
|
220
228
|
}),
|
|
221
|
-
|
|
229
|
+
L && /* @__PURE__ */ S("button", {
|
|
230
|
+
type: "button",
|
|
222
231
|
onClick: (t) => {
|
|
223
|
-
t.stopPropagation(),
|
|
232
|
+
t.stopPropagation(), T(`${E}/${e.id}/preview`);
|
|
224
233
|
},
|
|
225
234
|
className: "flex-1 min-w-[6.5rem] px-3 py-2 min-h-[44px] text-xs border border-border rounded hover:bg-accent flex items-center justify-center gap-1.5",
|
|
226
|
-
title:
|
|
227
|
-
children: [/* @__PURE__ */
|
|
235
|
+
title: w("toursList.previewTitle"),
|
|
236
|
+
children: [/* @__PURE__ */ x(_, { size: 14 }), w("toursList.previewButton")]
|
|
228
237
|
}),
|
|
229
|
-
|
|
238
|
+
F && /* @__PURE__ */ x("button", {
|
|
239
|
+
type: "button",
|
|
230
240
|
onClick: (t) => {
|
|
231
|
-
t.stopPropagation(),
|
|
241
|
+
t.stopPropagation(), T(`${E}/${e.id}/edit`);
|
|
232
242
|
},
|
|
233
|
-
className: "
|
|
234
|
-
title:
|
|
235
|
-
"aria-label":
|
|
236
|
-
children: /* @__PURE__ */
|
|
243
|
+
className: "p-2 min-h-[44px] min-w-[44px] text-xs border border-border rounded hover:bg-accent",
|
|
244
|
+
title: w("toursList.settingsTitle"),
|
|
245
|
+
"aria-label": w("toursList.settingsTitle"),
|
|
246
|
+
children: /* @__PURE__ */ x(b, { size: 14 })
|
|
237
247
|
}),
|
|
238
|
-
a && /* @__PURE__ */
|
|
248
|
+
a && /* @__PURE__ */ S("div", {
|
|
239
249
|
className: "relative",
|
|
240
|
-
ref:
|
|
241
|
-
children: [/* @__PURE__ */
|
|
250
|
+
ref: X === e.id ? Q : void 0,
|
|
251
|
+
children: [/* @__PURE__ */ x("button", {
|
|
252
|
+
type: "button",
|
|
242
253
|
onClick: (t) => {
|
|
243
|
-
t.stopPropagation(),
|
|
254
|
+
t.stopPropagation(), Z(X === e.id ? null : e.id);
|
|
244
255
|
},
|
|
245
|
-
className: "
|
|
246
|
-
title:
|
|
247
|
-
"aria-label":
|
|
248
|
-
children: /* @__PURE__ */
|
|
249
|
-
}),
|
|
256
|
+
className: "p-2 min-h-[44px] min-w-[44px] text-xs border border-border rounded hover:bg-accent",
|
|
257
|
+
title: w("toursList.moreActionsTitle"),
|
|
258
|
+
"aria-label": w("toursList.moreActionsTitle"),
|
|
259
|
+
children: /* @__PURE__ */ x(ne, { size: 14 })
|
|
260
|
+
}), X === e.id && /* @__PURE__ */ S("div", {
|
|
250
261
|
className: "absolute right-0 top-full mt-1 w-40 bg-card border border-border rounded-md shadow-lg z-10",
|
|
251
262
|
children: [
|
|
252
|
-
n && /* @__PURE__ */
|
|
263
|
+
n && /* @__PURE__ */ x("button", {
|
|
264
|
+
type: "button",
|
|
253
265
|
onClick: (t) => {
|
|
254
266
|
t.stopPropagation(), de(e.id);
|
|
255
267
|
},
|
|
256
268
|
className: "w-full text-left px-3 py-2 min-h-[44px] text-xs hover:bg-accent",
|
|
257
|
-
children:
|
|
269
|
+
children: w("toursList.actionPublish")
|
|
258
270
|
}),
|
|
259
|
-
r && /* @__PURE__ */
|
|
271
|
+
r && /* @__PURE__ */ x("button", {
|
|
272
|
+
type: "button",
|
|
260
273
|
onClick: (t) => {
|
|
261
274
|
t.stopPropagation(), fe(e.id);
|
|
262
275
|
},
|
|
263
276
|
className: "w-full text-left px-3 py-2 min-h-[44px] text-xs hover:bg-accent",
|
|
264
|
-
children:
|
|
277
|
+
children: w("toursList.actionUnpublish")
|
|
265
278
|
}),
|
|
266
|
-
|
|
279
|
+
V && /* @__PURE__ */ x("button", {
|
|
280
|
+
type: "button",
|
|
267
281
|
onClick: (t) => {
|
|
268
282
|
t.stopPropagation(), me(e.id);
|
|
269
283
|
},
|
|
270
284
|
className: "w-full text-left px-3 py-2 min-h-[44px] text-xs hover:bg-accent",
|
|
271
|
-
children:
|
|
285
|
+
children: w("toursList.actionDuplicate")
|
|
272
286
|
}),
|
|
273
|
-
i && /* @__PURE__ */
|
|
287
|
+
i && /* @__PURE__ */ x("button", {
|
|
288
|
+
type: "button",
|
|
274
289
|
onClick: (t) => {
|
|
275
290
|
t.stopPropagation(), pe(e.id);
|
|
276
291
|
},
|
|
277
292
|
className: "w-full text-left px-3 py-2 min-h-[44px] text-xs hover:bg-accent",
|
|
278
|
-
children:
|
|
293
|
+
children: w("toursList.actionArchive")
|
|
279
294
|
}),
|
|
280
|
-
|
|
295
|
+
R && /* @__PURE__ */ x("button", {
|
|
296
|
+
type: "button",
|
|
281
297
|
onClick: (t) => {
|
|
282
298
|
t.stopPropagation(), he(e.id);
|
|
283
299
|
},
|
|
284
300
|
className: "w-full text-left px-3 py-2 min-h-[44px] text-xs text-destructive hover:bg-destructive/10",
|
|
285
|
-
children:
|
|
301
|
+
children: w("toursList.actionDelete")
|
|
286
302
|
})
|
|
287
303
|
]
|
|
288
304
|
})]
|
|
@@ -292,20 +308,20 @@ function T() {
|
|
|
292
308
|
]
|
|
293
309
|
}, e.id);
|
|
294
310
|
})
|
|
295
|
-
}) : /* @__PURE__ */
|
|
311
|
+
}) : /* @__PURE__ */ S("div", {
|
|
296
312
|
className: "text-center py-12 border border-dashed border-border rounded-md",
|
|
297
|
-
children: [/* @__PURE__ */
|
|
313
|
+
children: [/* @__PURE__ */ x("p", {
|
|
298
314
|
className: "text-sm sm:text-base text-muted-foreground mb-2",
|
|
299
|
-
children:
|
|
300
|
-
}), /* @__PURE__ */
|
|
315
|
+
children: w("toursList.emptyTitle")
|
|
316
|
+
}), /* @__PURE__ */ x("p", {
|
|
301
317
|
className: "text-xs sm:text-sm text-muted-foreground",
|
|
302
|
-
children:
|
|
318
|
+
children: w("toursList.emptySubtitle")
|
|
303
319
|
})]
|
|
304
320
|
})
|
|
305
321
|
]
|
|
306
322
|
});
|
|
307
323
|
}
|
|
308
324
|
//#endregion
|
|
309
|
-
export {
|
|
325
|
+
export { w as ToursListPage };
|
|
310
326
|
|
|
311
327
|
//# 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 {\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 canPreviewTour = hasPermission(TOUR_PERMISSIONS.TOUR_READ);\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 flex-wrap 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 min-w-[6.5rem] px-3 py-2 min-h-[44px] 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 {canPreviewTour && (\n <button\n onClick={(e) => {\n e.stopPropagation();\n navigate(`${basePath}/${tour.id}/preview`);\n }}\n className=\"flex-1 min-w-[6.5rem] px-3 py-2 min-h-[44px] 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 )}\n {canUpdateTour && (\n <button\n onClick={(e) => {\n e.stopPropagation();\n navigate(`${basePath}/${tour.id}/edit`);\n }}\n className=\"px-2 py-2 min-h-[44px] min-w-[44px] 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-2 min-h-[44px] min-w-[44px] 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 min-h-[44px] 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 min-h-[44px] 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 min-h-[44px] 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 min-h-[44px] 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 min-h-[44px] 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":";;;;;;;;;;;;;;;AAmCA,SAAgB,IAAgB;CAC9B,IAAM,EAAE,UAAO,GAAc,EACvB,IAAW,GAAa,EACxB,EAAE,cAAW,QAAQ,GAAiB,EACtC,EACJ,aACA,eACA,sBACA,gBACA,qBACA,8BACE,GAAe,EACb,EAAE,kBAAe,WAAW,MAAuB,GAA0B,EAE7E,IAAgB,EAAc,EAAiB,YAAY,EAC3D,IAAqB,EAAc,EAAiB,iBAAiB,EACrE,IAAiB,EAAc,EAAiB,UAAU,EAC1D,IAAgB,EAAc,EAAiB,YAAY,EAC3D,IAAiB,EAAc,EAAiB,aAAa,EAC7D,IAAiB,EAAc,EAAiB,aAAa,EAC7D,IAAmB,EAAc,EAAiB,eAAe,EAGjE,EAAE,UAAO,YAAS,kBAAe,GAAc,EAE/C,EAAE,gBAAa,mBAAe,gBAAa,qBAAkB,GAAkB,EAC/E,CAAC,KAAsB,GAAuB,EAE9C,CAAC,GAAa,MAAkB,EAAS,GAAG,EAC5C,CAAC,IAAc,MAAmB,EAA6B,MAAM,EACrE,CAAC,GAAY,KAAiB,EAAwB,KAAK,EAC3D,IAAU,EAAuB,KAAK;AAK5C,SAAgB;AACV,OACJ,EAAW,EAAE,GAAG,GAAY,CAAC;IAC5B;EAAC;EAAY;EAAY;EAAmB;EAAmB,CAAC;CAEnE,IAAM,MAAgB,MAAkB;AAEtC,EADA,GAAe,EAAM,EACrB,EAAiB,EAAE,QAAQ,GAAO,CAAC;IAG/B,MAAsB,MAA+B;AAEzD,EADA,GAAgB,EAAO,EACvB,EAAiB,EAAE,WAAQ,CAAC;IAGxB,WAAyB;AAE7B,IADgB,EAAS,SAAS,IAAI,GAAG,GAAG,EAAS,OAAO,GAAG,EAAS,MACvD;IAGb,MAAkB,MAAuB;AAC7C,UAAQ,GAAR;GACE,KAAK,QACH,QAAO,EAAG,wBAAwB;GACpC,KAAK,YACH,QAAO,EAAG,4BAA4B;GACxC,KAAK,WACH,QAAO,EAAG,2BAA2B;GACvC,KAAK,YACH,QAAO,EAAG,4BAA4B;;IAItC,KAAgB,OAAO,MAAe;AAE1C,EADA,EAAc,KAAK,EACnB,EAAa,QAAQ;AACrB,MAAI;AAEG,GADL,MAAM,EAAY,EAAG,EAChB,EAAa,UAAU;UACtB;AAEN,GADK,EAAa,QAAQ,EACtB,GAAU,GAAO,EAAY,EAAG,yBAAyB,CAAC,GACzD,MAAM,EAAG,yBAAyB,CAAC;;IAItC,KAAkB,OAAO,MAAe;AAE5C,EADA,EAAc,KAAK,EACnB,EAAa,QAAQ;AACrB,MAAI;AAEG,GADL,MAAM,GAAc,EAAG,EAClB,EAAa,UAAU;UACtB;AAEN,GADK,EAAa,QAAQ,EACtB,GAAU,GAAO,EAAY,EAAG,2BAA2B,CAAC,GAC3D,MAAM,EAAG,2BAA2B,CAAC;;IAIxC,KAAgB,OAAO,MAAe;AAE1C,EADA,EAAc,KAAK,EACnB,EAAa,QAAQ;AACrB,MAAI;AAEG,GADL,MAAM,EAAY,EAAG,EAChB,EAAa,UAAU;UACtB;AAEN,GADK,EAAa,QAAQ,EACtB,GAAU,GAAO,EAAY,EAAG,yBAAyB,CAAC,GACzD,MAAM,EAAG,yBAAyB,CAAC;;IAItC,KAAkB,OAAO,MAAe;AAE5C,EADA,EAAc,KAAK,EACnB,EAAa,QAAQ;AACrB,MAAI;AAEG,GADL,MAAM,EAAc,EAAG,EAClB,EAAa,UAAU;UACtB;AAEN,GADK,EAAa,QAAQ,EACtB,GAAU,GAAO,EAAY,EAAG,2BAA2B,CAAC,GAC3D,MAAM,EAAG,2BAA2B,CAAC;;IAIxC,KAAe,OAAO,MAAe;AAEzC,EADA,EAAc,KAAK,EACnB,EAAa,SAAS;EACtB,IAAM,IAAa,EAAG,0BAA0B,EAC1C,IAAe,MAAM,EAAc;GACvC,OAAO,EAAG,yBAAyB;GACnC,SAAS;GACT,eAAe,EAAG,yBAAyB;GAC5C,CAAC;AACgB,YAAiB,OAAO,OAAO,QAAQ,EAAW,GAAG,EAEvE,KAAI;AAGG,GAFL,MAAM,EAAmB,EAAE,WAAW,EAAE,OAAI,EAAE,CAAC,EAC/C,GAAwB,EACnB,EAAa,UAAU;UACtB;AAEN,GADK,EAAa,QAAQ,EACtB,GAAU,GAAO,EAAY,EAAG,wBAAwB,CAAC,GACxD,MAAM,EAAG,wBAAwB,CAAC;;;AAiB3C,QAZA,QAAgB;EACd,IAAM,KAAsB,MAAsB;AAChD,GAAI,EAAQ,WAAW,CAAC,EAAQ,QAAQ,SAAS,EAAM,OAAe,IACpE,EAAc,KAAK;;AAMvB,SAHI,KACF,SAAS,iBAAiB,aAAa,EAAmB,QAE/C,SAAS,oBAAoB,aAAa,EAAmB;IACzE,CAAC,EAAW,CAAC,EAGd,kBAAC,OAAD;EAAK,WAAU;YAAf;GAEE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,MAAD;MAAI,WAAU;gBAAiD,EAAG,kBAAkB;MAAM,CAAA,EAC1F,kBAAC,KAAD;MAAG,WAAU;gBAA8C,EAAG,qBAAqB;MAAK,CAAA,CACpF;QACN,kBAAC,GAAD;KAAgB,YAAY,EAAiB;eAC3C,kBAAC,UAAD;MACE,SAAS;MACT,WAAU;gBAFZ,CAIE,kBAAC,IAAD,EAAM,MAAM,IAAM,CAAA,EACjB,EAAG,uBAAuB,CACpB;;KACM,CAAA,CACb;;GAGN,kBAAC,OAAD;IAAK,WAAU;cAAf,CAEE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,IAAD;MACE,WAAU;MACV,MAAM;MACN,CAAA,EACF,kBAAC,SAAD;MACE,MAAK;MACL,aAAa,EAAG,8BAA8B;MAC9C,OAAO;MACP,WAAW,MAAM,GAAa,EAAE,OAAO,MAAM;MAC7C,WAAU;MACV,CAAA,CACE;QAGN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,UAAD;MACE,OAAO;MACP,WAAW,MAAM,GAAmB,EAAE,OAAO,MAA4B;MACzE,WAAU;gBAHZ;OAKE,kBAAC,UAAD;QAAQ,OAAM;kBAAO,EAAG,sBAAsB;QAAU,CAAA;OACxD,kBAAC,UAAD;QAAQ,OAAM;kBAAS,EAAG,wBAAwB;QAAU,CAAA;OAC5D,kBAAC,UAAD;QAAQ,OAAM;kBAAa,EAAG,4BAA4B;QAAU,CAAA;OACpE,kBAAC,UAAD;QAAQ,OAAM;kBAAY,EAAG,2BAA2B;QAAU,CAAA;OAClE,kBAAC,UAAD;QAAQ,OAAM;kBAAa,EAAG,4BAA4B;QAAU,CAAA;OAC7D;SAGT,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,UAAD;OACE,eAAe,EAAY,OAAO;OAClC,WAAW,0BACT,MAAa,SACT,uCACA;OAEN,cAAY,EAAG,qBAAqB;OACpC,OAAO,EAAG,qBAAqB;iBAE/B,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA;OACX,CAAA,EACT,kBAAC,UAAD;OACE,eAAe,EAAY,OAAO;OAClC,WAAW,0BACT,MAAa,SACT,uCACA;OAEN,cAAY,EAAG,qBAAqB;OACpC,OAAO,EAAG,qBAAqB;iBAE/B,kBAAC,GAAD,EAAU,MAAM,IAAM,CAAA;OACf,CAAA,CACL;QACF;OACF;;GAGL,IACC,kBAAC,OAAD;IAAK,WAAU;cACb,kBAAC,KAAD;KAAG,WAAU;eAA8C,EAAG,oBAAoB;KAAK,CAAA;IACnF,CAAA,GACJ,EAAM,SAAS,IACjB,kBAAC,OAAD;IACE,WACE,MAAa,SACT,kEACA;cAGL,EAAM,KAAK,MAAe;KACzB,IAAM,IAAS,EAAK,QACd,IAAuB,MAAW,WAAW,GAC7C,IAAyB,MAAW,eAAe,GACnD,IAAuB,MAAW,cAAc,GAChD,IACJ,KACA,KACA,KACA,KACA;AAEF,YACE,kBAAC,OAAD;MAEE,WAAU;gBAFZ;OAIE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,MAAD;SACE,WAAW,iCACT,IAAgB,sCAAsC;SAExD,eAAe;AACb,UAAI,KAAe,EAAS,GAAG,EAAS,GAAG,EAAK,GAAG,OAAO;;mBAG3D,EAAK;SACH,CAAA,EACL,kBAAC,QAAD;SACE,WAAW,kCACT,EAAK,WAAW,cACZ,+BACA,EAAK,WAAW,UACd,mCACA;mBAGP,GAAe,EAAO;SAClB,CAAA,CACH;;OACL,EAAK,eACJ,kBAAC,KAAD;QAAG,WAAU;kBAAsC,EAAK;QAAgB,CAAA;OAE1E,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,QAAD,EAAA,UAAA;SACG,EAAK,OAAO,UAAU;SAAE;SAAE,EAAG,kBAAkB;SAC3C,EAAA,CAAA,EACP,kBAAC,QAAD,EAAA,UAAA;SACG,EAAK;SAAU;SAAE,EAAG,kBAAkB;SAClC,EAAA,CAAA,CACH;;OAEN,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACG,KACC,kBAAC,UAAD;UACE,UAAU,MAAM;AAEd,WADA,EAAE,iBAAiB,EACnB,EAAS,GAAG,EAAS,GAAG,EAAK,GAAG,QAAQ;;UAE1C,WAAU;UACV,OAAO,EAAG,2BAA2B;oBANvC,CAQE,kBAAC,GAAD,EAAa,MAAM,IAAM,CAAA,EACxB,EAAG,wBAAwB,CACrB;;SAEV,KACC,kBAAC,UAAD;UACE,UAAU,MAAM;AAEd,WADA,EAAE,iBAAiB,EACnB,EAAS,GAAG,EAAS,GAAG,EAAK,GAAG,UAAU;;UAE5C,WAAU;UACV,OAAO,EAAG,yBAAyB;oBANrC,CAQE,kBAAC,GAAD,EAAK,MAAM,IAAM,CAAA,EAChB,EAAG,0BAA0B,CACvB;;SAEV,KACC,kBAAC,UAAD;UACE,UAAU,MAAM;AAEd,WADA,EAAE,iBAAiB,EACnB,EAAS,GAAG,EAAS,GAAG,EAAK,GAAG,OAAO;;UAEzC,WAAU;UACV,OAAO,EAAG,0BAA0B;UACpC,cAAY,EAAG,0BAA0B;oBAEzC,kBAAC,GAAD,EAAU,MAAM,IAAM,CAAA;UACf,CAAA;SAGV,KACC,kBAAC,OAAD;UAAK,WAAU;UAAW,KAAK,MAAe,EAAK,KAAK,IAAU,KAAA;oBAAlE,CACE,kBAAC,UAAD;WACE,UAAU,MAAM;AAEd,YADA,EAAE,iBAAiB,EACnB,EAAc,MAAe,EAAK,KAAK,OAAO,EAAK,GAAG;;WAExD,WAAU;WACV,OAAO,EAAG,6BAA6B;WACvC,cAAY,EAAG,6BAA6B;qBAE5C,kBAAC,IAAD,EAAc,MAAM,IAAM,CAAA;WACnB,CAAA,EACR,MAAe,EAAK,MACnB,kBAAC,OAAD;WAAK,WAAU;qBAAf;YACG,KACC,kBAAC,UAAD;aACE,UAAU,MAAM;AAEd,cADA,EAAE,iBAAiB,EACnB,GAAc,EAAK,GAAG;;aAExB,WAAU;uBAET,EAAG,0BAA0B;aACvB,CAAA;YAEV,KACC,kBAAC,UAAD;aACE,UAAU,MAAM;AAEd,cADA,EAAE,iBAAiB,EACnB,GAAgB,EAAK,GAAG;;aAE1B,WAAU;uBAET,EAAG,4BAA4B;aACzB,CAAA;YAEV,KACC,kBAAC,UAAD;aACE,UAAU,MAAM;AAEd,cADA,EAAE,iBAAiB,EACnB,GAAgB,EAAK,GAAG;;aAE1B,WAAU;uBAET,EAAG,4BAA4B;aACzB,CAAA;YAEV,KACC,kBAAC,UAAD;aACE,UAAU,MAAM;AAEd,cADA,EAAE,iBAAiB,EACnB,GAAc,EAAK,GAAG;;aAExB,WAAU;uBAET,EAAG,0BAA0B;aACvB,CAAA;YAEV,KACC,kBAAC,UAAD;aACE,UAAU,MAAM;AAEd,cADA,EAAE,iBAAiB,EACnB,GAAa,EAAK,GAAG;;aAEvB,WAAU;uBAET,EAAG,yBAAyB;aACtB,CAAA;YAEP;aAEJ;;SAEJ;;OACF;QA1JC,EAAK,GA0JN;MAER;IACE,CAAA,GAEN,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,KAAD;KAAG,WAAU;eACV,EAAG,uBAAuB;KACzB,CAAA,EACJ,kBAAC,KAAD;KAAG,WAAU;eACV,EAAG,0BAA0B;KAC5B,CAAA,CACA;;GAEJ"}
|
|
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 canPreviewTour = hasPermission(TOUR_PERMISSIONS.TOUR_READ);\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 type=\"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 aria-label={tr('toursList.searchPlaceholder')}\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 aria-label={tr('toursList.statusAll')}\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 type=\"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 type=\"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 className=\"font-semibold text-foreground\">\n {canUpdateTour ? (\n <button\n type=\"button\"\n className=\"cursor-pointer hover:text-primary bg-transparent border-0 p-0 text-left font-inherit\"\n onClick={() => navigate(`${basePath}/${tour.id}/edit`)}\n >\n {tour.name}\n </button>\n ) : (\n tour.name\n )}\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 flex-wrap items-center gap-2 pt-3 border-t border-border\">\n {canUpdateTourSteps && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n navigate(`${basePath}/${tour.id}/steps`);\n }}\n className=\"flex-1 min-w-[6.5rem] px-3 py-2 min-h-[44px] 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 {canPreviewTour && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n navigate(`${basePath}/${tour.id}/preview`);\n }}\n className=\"flex-1 min-w-[6.5rem] px-3 py-2 min-h-[44px] 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 )}\n {canUpdateTour && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n navigate(`${basePath}/${tour.id}/edit`);\n }}\n className=\"p-2 min-h-[44px] min-w-[44px] 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 type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n setOpenMenuId(openMenuId === tour.id ? null : tour.id);\n }}\n className=\"p-2 min-h-[44px] min-w-[44px] 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 type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n handlePublish(tour.id);\n }}\n className=\"w-full text-left px-3 py-2 min-h-[44px] text-xs hover:bg-accent\"\n >\n {tr('toursList.actionPublish')}\n </button>\n )}\n {canShowUnpublishAction && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n handleUnpublish(tour.id);\n }}\n className=\"w-full text-left px-3 py-2 min-h-[44px] text-xs hover:bg-accent\"\n >\n {tr('toursList.actionUnpublish')}\n </button>\n )}\n {canDuplicateTour && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n handleDuplicate(tour.id);\n }}\n className=\"w-full text-left px-3 py-2 min-h-[44px] text-xs hover:bg-accent\"\n >\n {tr('toursList.actionDuplicate')}\n </button>\n )}\n {canShowArchiveAction && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n handleArchive(tour.id);\n }}\n className=\"w-full text-left px-3 py-2 min-h-[44px] text-xs hover:bg-accent\"\n >\n {tr('toursList.actionArchive')}\n </button>\n )}\n {canDeleteTour && (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation();\n handleDelete(tour.id);\n }}\n className=\"w-full text-left px-3 py-2 min-h-[44px] 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,4 +1,4 @@
|
|
|
1
|
-
import { createContext as e,
|
|
1
|
+
import { createContext as e, use as t } from "react";
|
|
2
2
|
import { jsx as n } from "react/jsx-runtime";
|
|
3
3
|
import { MemoryRouter as r } from "react-router-dom";
|
|
4
4
|
import { PermissionProvider as i } from "@burdenoff/fe-libs/shared/providers/shell";
|