@burdenoff/microfe-tours 2026.830.1 → 2026.918.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/tours/ToursAdminRoot.js +28 -27
- package/dist/tours/ToursAdminRoot.js.map +1 -1
- package/dist/tours/ToursAdminRoutes.js +56 -55
- package/dist/tours/ToursAdminRoutes.js.map +1 -1
- package/dist/tours/ToursUserRoot.js +34 -29
- package/dist/tours/ToursUserRoot.js.map +1 -1
- package/dist/tours/ToursUserRoutes.js +33 -33
- package/dist/tours/ToursUserRoutes.js.map +1 -1
- package/dist/tours/components/MarkdownEditor.js +85 -80
- package/dist/tours/components/MarkdownEditor.js.map +1 -1
- package/dist/tours/components/RouteHelpers.js +47 -42
- package/dist/tours/components/RouteHelpers.js.map +1 -1
- package/dist/tours/components/ToursAdminNavRegistrar.js +32 -27
- package/dist/tours/components/ToursAdminNavRegistrar.js.map +1 -1
- package/dist/tours/hooks/useStepSequencer.js +45 -36
- package/dist/tours/hooks/useStepSequencer.js.map +1 -1
- package/dist/tours/hooks/useTourBuilder.js +106 -93
- package/dist/tours/hooks/useTourBuilder.js.map +1 -1
- package/dist/tours/hooks/useTourPreview.js +51 -41
- package/dist/tours/hooks/useTourPreview.js.map +1 -1
- package/dist/tours/hooks/useToursList.js +28 -27
- package/dist/tours/hooks/useToursList.js.map +1 -1
- package/dist/tours/i18n.js +187 -5
- package/dist/tours/i18n.js.map +1 -1
- package/dist/tours/pages/AnalyticsDashboardPage.js +108 -109
- package/dist/tours/pages/AnalyticsDashboardPage.js.map +1 -1
- package/dist/tours/pages/MediaLibraryPage.js +58 -61
- package/dist/tours/pages/MediaLibraryPage.js.map +1 -1
- package/dist/tours/pages/MyToursPage.js +5 -5
- package/dist/tours/pages/MyToursProgressPage.js +5 -5
- package/dist/tours/pages/SchedulerPage.js +78 -81
- package/dist/tours/pages/SchedulerPage.js.map +1 -1
- package/dist/tours/pages/StepEditorPage.js +32 -32
- package/dist/tours/pages/StepEditorPage.js.map +1 -1
- package/dist/tours/pages/SystemAnalyticsPage.js +75 -79
- package/dist/tours/pages/SystemAnalyticsPage.js.map +1 -1
- package/dist/tours/pages/TemplateLibraryPage.js +8 -8
- package/dist/tours/pages/TourBuilderPage.js +295 -279
- package/dist/tours/pages/TourBuilderPage.js.map +1 -1
- package/dist/tours/pages/TourPreviewPage.js +6 -6
- package/dist/tours/pages/ToursListPage.js +155 -155
- package/dist/tours/pages/ToursListPage.js.map +1 -1
- package/dist/tours/store/toursStore.js +11 -7
- package/dist/tours/store/toursStore.js.map +1 -1
- package/dist/tours/utils/index.js +32 -19
- package/dist/tours/utils/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,53 +1,50 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
1
|
+
import { useToursI18n as e } from "../i18n.js";
|
|
2
|
+
import { useToursContext as t } from "../providers/ToursProvider.js";
|
|
3
|
+
import { useCreateTourScheduleMutation as n, useGetTourSchedulesQuery as r } from "../../generated/global-operations.js";
|
|
4
|
+
import { useToursPermissionAccess as i } from "../hooks/useToursPermissionAccess.js";
|
|
5
|
+
import { TOUR_PERMISSIONS as a } from "../../constants/permissions.js";
|
|
6
|
+
import { emitToursApiFailed as o } from "../telemetry.js";
|
|
7
|
+
import { useState as s } from "react";
|
|
7
8
|
import { PermissionGate as c } from "@burdenoff/fe-libs/shared/components";
|
|
8
9
|
import { ArrowLeft as l, Calendar as u, Clock as d, Save as f } from "lucide-react";
|
|
9
10
|
import { jsx as p, jsxs as m } from "react/jsx-runtime";
|
|
10
11
|
import { useNavigate as h, useParams as g } from "react-router";
|
|
11
12
|
import { Button as _, EmphasisPanel as v, GlassCard as y, PagePurpose as b } from "@burdenoff/fe-libs/ui";
|
|
12
|
-
import {
|
|
13
|
-
import { useEventBus as S } from "@burdenoff/fe-libs/shared/events";
|
|
13
|
+
import { useEventBus as x } from "@burdenoff/fe-libs/shared/events";
|
|
14
14
|
//#region src/tours/pages/SchedulerPage.tsx
|
|
15
|
-
function
|
|
16
|
-
let {
|
|
17
|
-
let n = C(e);
|
|
18
|
-
return n === e ? t : n;
|
|
19
|
-
}, [C]), { tourId: T } = g(), E = h(), D = e(), { basePath: O = "/" } = D, k = S(), { hasPermission: A } = r(), j = A(i.TOUR_SCHEDULE_CREATE), M = A(i.TOUR_SCHEDULE_LIST), [N, P] = s({
|
|
15
|
+
function S() {
|
|
16
|
+
let { tr: S } = e(), { tourId: C } = g(), w = h(), T = t(), { basePath: E = "/" } = T, D = x(), { hasPermission: O } = i(), k = O(a.TOUR_SCHEDULE_CREATE), A = O(a.TOUR_SCHEDULE_LIST), [j, M] = s({
|
|
20
17
|
startDate: "",
|
|
21
18
|
endDate: "",
|
|
22
19
|
recurrencePattern: "DAILY",
|
|
23
20
|
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
|
|
24
21
|
isActive: !0
|
|
25
|
-
}), [
|
|
26
|
-
variables: { tourId:
|
|
27
|
-
skip: !
|
|
28
|
-
}),
|
|
29
|
-
if (e.preventDefault(),
|
|
30
|
-
|
|
22
|
+
}), [N, P] = s(!1), [F] = n(), { data: I } = r({
|
|
23
|
+
variables: { tourId: C },
|
|
24
|
+
skip: !C || !A
|
|
25
|
+
}), L = I?.tourSchedules ?? [], R = async (e) => {
|
|
26
|
+
if (e.preventDefault(), C) {
|
|
27
|
+
P(!0);
|
|
31
28
|
try {
|
|
32
|
-
let e =
|
|
33
|
-
await
|
|
29
|
+
let e = j.recurrencePattern === "DAILY" ? "FREQ=DAILY" : j.recurrencePattern === "WEEKLY" ? "FREQ=WEEKLY" : j.recurrencePattern === "MONTHLY" ? "FREQ=MONTHLY" : void 0;
|
|
30
|
+
await F({
|
|
34
31
|
variables: { input: {
|
|
35
|
-
tourId:
|
|
36
|
-
startDate: new Date(
|
|
37
|
-
endDate:
|
|
32
|
+
tourId: C,
|
|
33
|
+
startDate: new Date(j.startDate).toISOString(),
|
|
34
|
+
endDate: j.endDate ? new Date(j.endDate).toISOString() : void 0,
|
|
38
35
|
recurrenceRule: e,
|
|
39
|
-
timezone:
|
|
36
|
+
timezone: j.timezone
|
|
40
37
|
} },
|
|
41
38
|
refetchQueries: ["GetTourSchedules"]
|
|
42
|
-
}),
|
|
39
|
+
}), w(`${E}/${C}/edit`);
|
|
43
40
|
} catch {
|
|
44
|
-
|
|
41
|
+
o(D, "schedule.create", T);
|
|
45
42
|
} finally {
|
|
46
|
-
|
|
43
|
+
P(!1);
|
|
47
44
|
}
|
|
48
45
|
}
|
|
49
|
-
},
|
|
50
|
-
|
|
46
|
+
}, z = () => {
|
|
47
|
+
w(`${E}/${C}/edit`);
|
|
51
48
|
};
|
|
52
49
|
return /* @__PURE__ */ m("div", {
|
|
53
50
|
className: "space-y-4 sm:space-y-6 p-3 sm:p-6 max-w-3xl mx-auto",
|
|
@@ -56,44 +53,44 @@ function C() {
|
|
|
56
53
|
className: "flex items-center gap-3 sm:gap-4",
|
|
57
54
|
children: [/* @__PURE__ */ p("button", {
|
|
58
55
|
type: "button",
|
|
59
|
-
onClick:
|
|
56
|
+
onClick: z,
|
|
60
57
|
className: "p-2 min-h-[44px] min-w-[44px] hover:bg-accent rounded-md",
|
|
61
|
-
"aria-label":
|
|
58
|
+
"aria-label": S("tourScheduler.goBack", "Go back"),
|
|
62
59
|
children: /* @__PURE__ */ p(l, { size: 20 })
|
|
63
60
|
}), /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("h1", {
|
|
64
61
|
className: "text-xl sm:text-2xl font-bold",
|
|
65
|
-
children:
|
|
62
|
+
children: S("tourScheduler.title", "Schedule Tour")
|
|
66
63
|
}), /* @__PURE__ */ p("p", {
|
|
67
64
|
className: "text-sm text-text-secondary",
|
|
68
|
-
children:
|
|
65
|
+
children: S("tourScheduler.subtitle", "Configure when this tour should be activated")
|
|
69
66
|
})] })]
|
|
70
67
|
}),
|
|
71
|
-
/* @__PURE__ */ p(b, { children:
|
|
72
|
-
|
|
68
|
+
/* @__PURE__ */ p(b, { children: S("tourScheduler.purpose", "Decide exactly when this tour goes live and how often it repeats — for example, run an onboarding tour every Monday, or activate a feature-announcement tour for a single launch window. Set a start (and optional end) date plus a recurrence pattern, and the tour will surface to users automatically on schedule.") }),
|
|
69
|
+
L.length > 0 && /* @__PURE__ */ m(v, {
|
|
73
70
|
className: "p-4 sm:p-6",
|
|
74
71
|
children: [/* @__PURE__ */ p("h2", {
|
|
75
72
|
className: "text-lg font-semibold mb-4",
|
|
76
|
-
children:
|
|
73
|
+
children: S("tourScheduler.existingSchedules", "Existing Schedules")
|
|
77
74
|
}), /* @__PURE__ */ p("div", {
|
|
78
75
|
className: "space-y-2",
|
|
79
|
-
children:
|
|
76
|
+
children: L.map((e) => /* @__PURE__ */ m("div", {
|
|
80
77
|
className: "flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between p-3 border border-border-seam rounded-md",
|
|
81
78
|
children: [/* @__PURE__ */ m("div", { children: [/* @__PURE__ */ m("div", {
|
|
82
79
|
className: "text-sm font-medium",
|
|
83
|
-
children: [new Date(e.startDate).toLocaleDateString(), e.endDate ? ` — ${new Date(e.endDate).toLocaleDateString()}` : ` — ${
|
|
80
|
+
children: [new Date(e.startDate).toLocaleDateString(), e.endDate ? ` — ${new Date(e.endDate).toLocaleDateString()}` : ` — ${S("tourScheduler.noEndDate", "No end date")}`]
|
|
84
81
|
}), /* @__PURE__ */ m("div", {
|
|
85
82
|
className: "text-xs text-text-secondary",
|
|
86
83
|
children: [
|
|
87
|
-
e.recurrenceRule ||
|
|
84
|
+
e.recurrenceRule || S("tourScheduler.oneTime", "One-time"),
|
|
88
85
|
" ·",
|
|
89
86
|
" ",
|
|
90
87
|
e.timezone,
|
|
91
|
-
e.isActive ? ` · ${
|
|
88
|
+
e.isActive ? ` · ${S("tourScheduler.active", "Active")}` : ` · ${S("tourScheduler.inactive", "Inactive")}`
|
|
92
89
|
]
|
|
93
90
|
})] }), /* @__PURE__ */ m("div", {
|
|
94
91
|
className: "text-xs text-text-secondary tabular-nums",
|
|
95
92
|
children: [
|
|
96
|
-
|
|
93
|
+
S("tourScheduler.runs", "Runs"),
|
|
97
94
|
": ",
|
|
98
95
|
e.executionCount
|
|
99
96
|
]
|
|
@@ -102,7 +99,7 @@ function C() {
|
|
|
102
99
|
})]
|
|
103
100
|
}),
|
|
104
101
|
/* @__PURE__ */ m("form", {
|
|
105
|
-
onSubmit:
|
|
102
|
+
onSubmit: R,
|
|
106
103
|
className: "space-y-6",
|
|
107
104
|
children: [
|
|
108
105
|
/* @__PURE__ */ m(y, {
|
|
@@ -110,38 +107,38 @@ function C() {
|
|
|
110
107
|
className: "p-4 sm:p-6 border-border-seam",
|
|
111
108
|
children: [/* @__PURE__ */ m("h2", {
|
|
112
109
|
className: "text-lg font-semibold mb-4 flex items-center gap-2",
|
|
113
|
-
children: [/* @__PURE__ */ p(u, { size: 20 }),
|
|
110
|
+
children: [/* @__PURE__ */ p(u, { size: 20 }), S("tourScheduler.sectionDateRange", "Date Range")]
|
|
114
111
|
}), /* @__PURE__ */ m("div", {
|
|
115
112
|
className: "space-y-4",
|
|
116
113
|
children: [/* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
|
|
117
114
|
htmlFor: "scheduler-start-date",
|
|
118
115
|
className: "block text-sm font-medium mb-2",
|
|
119
|
-
children:
|
|
116
|
+
children: S("tourScheduler.labelStartDate", "Start Date *")
|
|
120
117
|
}), /* @__PURE__ */ p("input", {
|
|
121
118
|
id: "scheduler-start-date",
|
|
122
119
|
type: "datetime-local",
|
|
123
|
-
value:
|
|
124
|
-
onChange: (e) =>
|
|
125
|
-
...
|
|
120
|
+
value: j.startDate,
|
|
121
|
+
onChange: (e) => M({
|
|
122
|
+
...j,
|
|
126
123
|
startDate: e.target.value
|
|
127
124
|
}),
|
|
128
125
|
className: "w-full px-3 py-2 min-h-[44px] border border-border-subtle rounded-md bg-bg-surface",
|
|
129
126
|
required: !0,
|
|
130
|
-
"aria-label":
|
|
127
|
+
"aria-label": S("tourScheduler.labelStartDate", "Start Date *")
|
|
131
128
|
})] }), /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
|
|
132
129
|
htmlFor: "scheduler-end-date",
|
|
133
130
|
className: "block text-sm font-medium mb-2",
|
|
134
|
-
children:
|
|
131
|
+
children: S("tourScheduler.labelEndDate", "End Date (Optional)")
|
|
135
132
|
}), /* @__PURE__ */ p("input", {
|
|
136
133
|
id: "scheduler-end-date",
|
|
137
134
|
type: "datetime-local",
|
|
138
|
-
value:
|
|
139
|
-
onChange: (e) =>
|
|
140
|
-
...
|
|
135
|
+
value: j.endDate,
|
|
136
|
+
onChange: (e) => M({
|
|
137
|
+
...j,
|
|
141
138
|
endDate: e.target.value
|
|
142
139
|
}),
|
|
143
140
|
className: "w-full px-3 py-2 min-h-[44px] border border-border-subtle rounded-md bg-bg-surface",
|
|
144
|
-
"aria-label":
|
|
141
|
+
"aria-label": S("tourScheduler.labelEndDate", "End Date (Optional)")
|
|
145
142
|
})] })]
|
|
146
143
|
})]
|
|
147
144
|
}),
|
|
@@ -150,55 +147,55 @@ function C() {
|
|
|
150
147
|
className: "p-4 sm:p-6 border-border-seam",
|
|
151
148
|
children: [/* @__PURE__ */ m("h2", {
|
|
152
149
|
className: "text-lg font-semibold mb-4 flex items-center gap-2",
|
|
153
|
-
children: [/* @__PURE__ */ p(d, { size: 20 }),
|
|
150
|
+
children: [/* @__PURE__ */ p(d, { size: 20 }), S("tourScheduler.sectionRecurrence", "Recurrence")]
|
|
154
151
|
}), /* @__PURE__ */ m("div", {
|
|
155
152
|
className: "space-y-4",
|
|
156
153
|
children: [/* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
|
|
157
154
|
htmlFor: "scheduler-recurrence",
|
|
158
155
|
className: "block text-sm font-medium mb-2",
|
|
159
|
-
children:
|
|
156
|
+
children: S("tourScheduler.labelRecurrencePattern", "Recurrence Pattern")
|
|
160
157
|
}), /* @__PURE__ */ m("select", {
|
|
161
158
|
id: "scheduler-recurrence",
|
|
162
|
-
value:
|
|
163
|
-
onChange: (e) =>
|
|
164
|
-
...
|
|
159
|
+
value: j.recurrencePattern,
|
|
160
|
+
onChange: (e) => M({
|
|
161
|
+
...j,
|
|
165
162
|
recurrencePattern: e.target.value
|
|
166
163
|
}),
|
|
167
164
|
className: "w-full px-3 py-2 min-h-[44px] border border-border-subtle rounded-md bg-bg-surface",
|
|
168
|
-
"aria-label":
|
|
165
|
+
"aria-label": S("tourScheduler.labelRecurrencePattern", "Recurrence Pattern"),
|
|
169
166
|
children: [
|
|
170
167
|
/* @__PURE__ */ p("option", {
|
|
171
168
|
value: "DAILY",
|
|
172
|
-
children:
|
|
169
|
+
children: S("tourScheduler.recurrenceDaily", "Daily")
|
|
173
170
|
}),
|
|
174
171
|
/* @__PURE__ */ p("option", {
|
|
175
172
|
value: "WEEKLY",
|
|
176
|
-
children:
|
|
173
|
+
children: S("tourScheduler.recurrenceWeekly", "Weekly")
|
|
177
174
|
}),
|
|
178
175
|
/* @__PURE__ */ p("option", {
|
|
179
176
|
value: "MONTHLY",
|
|
180
|
-
children:
|
|
177
|
+
children: S("tourScheduler.recurrenceMonthly", "Monthly")
|
|
181
178
|
}),
|
|
182
179
|
/* @__PURE__ */ p("option", {
|
|
183
180
|
value: "CUSTOM",
|
|
184
|
-
children:
|
|
181
|
+
children: S("tourScheduler.recurrenceCustom", "Custom")
|
|
185
182
|
})
|
|
186
183
|
]
|
|
187
184
|
})] }), /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
|
|
188
185
|
htmlFor: "scheduler-timezone",
|
|
189
186
|
className: "block text-sm font-medium mb-2",
|
|
190
|
-
children:
|
|
187
|
+
children: S("tourScheduler.labelTimezone", "Timezone")
|
|
191
188
|
}), /* @__PURE__ */ p("input", {
|
|
192
189
|
id: "scheduler-timezone",
|
|
193
190
|
type: "text",
|
|
194
|
-
value:
|
|
195
|
-
onChange: (e) =>
|
|
196
|
-
...
|
|
191
|
+
value: j.timezone,
|
|
192
|
+
onChange: (e) => M({
|
|
193
|
+
...j,
|
|
197
194
|
timezone: e.target.value
|
|
198
195
|
}),
|
|
199
196
|
className: "w-full px-3 py-2 min-h-[44px] border border-border-subtle rounded-md bg-bg-surface",
|
|
200
197
|
readOnly: !0,
|
|
201
|
-
"aria-label":
|
|
198
|
+
"aria-label": S("tourScheduler.labelTimezone", "Timezone")
|
|
202
199
|
})] })]
|
|
203
200
|
})]
|
|
204
201
|
}),
|
|
@@ -209,19 +206,19 @@ function C() {
|
|
|
209
206
|
className: "flex items-center gap-3 cursor-pointer",
|
|
210
207
|
children: [/* @__PURE__ */ p("input", {
|
|
211
208
|
type: "checkbox",
|
|
212
|
-
checked:
|
|
213
|
-
onChange: (e) =>
|
|
214
|
-
...
|
|
209
|
+
checked: j.isActive,
|
|
210
|
+
onChange: (e) => M({
|
|
211
|
+
...j,
|
|
215
212
|
isActive: e.target.checked
|
|
216
213
|
}),
|
|
217
214
|
className: "size-5 rounded",
|
|
218
|
-
"aria-label":
|
|
215
|
+
"aria-label": S("tourScheduler.statusActiveLabel", "Active")
|
|
219
216
|
}), /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("div", {
|
|
220
217
|
className: "font-medium",
|
|
221
|
-
children:
|
|
218
|
+
children: S("tourScheduler.statusActiveLabel", "Active")
|
|
222
219
|
}), /* @__PURE__ */ p("div", {
|
|
223
220
|
className: "text-sm text-text-secondary",
|
|
224
|
-
children:
|
|
221
|
+
children: S("tourScheduler.statusActiveDescription", "Enable this schedule immediately after creation")
|
|
225
222
|
})] })]
|
|
226
223
|
})
|
|
227
224
|
}),
|
|
@@ -230,16 +227,16 @@ function C() {
|
|
|
230
227
|
children: [/* @__PURE__ */ p(_, {
|
|
231
228
|
type: "button",
|
|
232
229
|
variant: "outline",
|
|
233
|
-
onClick:
|
|
230
|
+
onClick: z,
|
|
234
231
|
className: "flex-1 min-h-[44px]",
|
|
235
|
-
children:
|
|
232
|
+
children: S("tourScheduler.cancel", "Cancel")
|
|
236
233
|
}), /* @__PURE__ */ p(c, {
|
|
237
|
-
permission:
|
|
234
|
+
permission: a.TOUR_SCHEDULE_CREATE,
|
|
238
235
|
children: /* @__PURE__ */ m(_, {
|
|
239
236
|
type: "submit",
|
|
240
|
-
disabled:
|
|
237
|
+
disabled: N || !k,
|
|
241
238
|
className: "flex-1 min-h-[44px]",
|
|
242
|
-
children: [/* @__PURE__ */ p(f, { size: 20 }),
|
|
239
|
+
children: [/* @__PURE__ */ p(f, { size: 20 }), N ? S("tourScheduler.creating", "Creating...") : S("tourScheduler.createSchedule", "Create Schedule")]
|
|
243
240
|
})
|
|
244
241
|
})]
|
|
245
242
|
})
|
|
@@ -249,6 +246,6 @@ function C() {
|
|
|
249
246
|
});
|
|
250
247
|
}
|
|
251
248
|
//#endregion
|
|
252
|
-
export {
|
|
249
|
+
export { S as SchedulerPage };
|
|
253
250
|
|
|
254
251
|
//# sourceMappingURL=SchedulerPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SchedulerPage.js","names":[],"sources":["../../../src/tours/pages/SchedulerPage.tsx"],"sourcesContent":["import { useCallback, useState } from 'react';\nimport { useParams, useNavigate } from 'react-router';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { Calendar, Clock, ArrowLeft, Save } from 'lucide-react';\nimport { PermissionGate } from '@burdenoff/fe-libs/shared/components';\nimport { Button, EmphasisPanel, GlassCard, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { useEventBus } from '@burdenoff/fe-libs/shared/events';\nimport { useToursPermissionAccess } from '../hooks/useToursPermissionAccess';\nimport { useToursContext } from '../providers/ToursProvider';\nimport { TOUR_PERMISSIONS } from '../../constants/permissions';\nimport type { RecurrencePattern } from '../types';\nimport { emitToursApiFailed } from '../telemetry';\nimport { useCreateTourScheduleMutation } from '../../generated/global-operations';\nimport { useGetTourSchedulesQuery } from '../../generated/global-operations';\n\n/**\n * Tour scheduler page\n * Configure tour scheduling and recurrence\n */\nexport function SchedulerPage() {\n const { t } = useI18n();\n const tr = useCallback(\n (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n },\n [t]\n );\n const { tourId } = useParams();\n const navigate = useNavigate();\n const toursContext = useToursContext();\n const { basePath = '/' } = toursContext;\n const eventBus = useEventBus();\n const { hasPermission } = useToursPermissionAccess();\n const canCreateSchedule = hasPermission(TOUR_PERMISSIONS.TOUR_SCHEDULE_CREATE);\n const canListSchedules = hasPermission(TOUR_PERMISSIONS.TOUR_SCHEDULE_LIST);\n\n const [scheduleData, setScheduleData] = useState({\n startDate: '',\n endDate: '',\n recurrencePattern: 'DAILY' as RecurrencePattern,\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n isActive: true,\n });\n\n const [saving, setSaving] = useState(false);\n\n const [createSchedule] = useCreateTourScheduleMutation();\n const { data: schedulesData } = useGetTourSchedulesQuery({\n variables: { tourId: tourId! },\n skip: !tourId || !canListSchedules,\n });\n const existingSchedules = schedulesData?.tourSchedules ?? [];\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n if (!tourId) return;\n setSaving(true);\n try {\n // Convert recurrence pattern to RFC 5545 recurrence rule\n const recurrenceRule =\n scheduleData.recurrencePattern === 'DAILY'\n ? 'FREQ=DAILY'\n : scheduleData.recurrencePattern === 'WEEKLY'\n ? 'FREQ=WEEKLY'\n : scheduleData.recurrencePattern === 'MONTHLY'\n ? 'FREQ=MONTHLY'\n : undefined;\n\n await createSchedule({\n variables: {\n input: {\n tourId,\n startDate: new Date(scheduleData.startDate).toISOString(),\n endDate: scheduleData.endDate\n ? new Date(scheduleData.endDate).toISOString()\n : undefined,\n recurrenceRule,\n timezone: scheduleData.timezone,\n },\n },\n refetchQueries: ['GetTourSchedules'],\n });\n navigate(`${basePath}/${tourId}/edit`);\n } catch {\n emitToursApiFailed(eventBus, 'schedule.create', toursContext);\n } finally {\n setSaving(false);\n }\n };\n\n const handleBack = () => {\n navigate(`${basePath}/${tourId}/edit`);\n };\n\n return (\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6 max-w-3xl mx-auto\">\n {/* Header */}\n <div className=\"flex items-center gap-3 sm:gap-4\">\n <button\n type=\"button\"\n onClick={handleBack}\n className=\"p-2 min-h-[44px] min-w-[44px] hover:bg-accent rounded-md\"\n aria-label={tr('tourScheduler.goBack', 'Go back')}\n >\n <ArrowLeft size={20} />\n </button>\n <div>\n <h1 className=\"text-xl sm:text-2xl font-bold\">\n {tr('tourScheduler.title', 'Schedule Tour')}\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {tr('tourScheduler.subtitle', 'Configure when this tour should be activated')}\n </p>\n </div>\n </div>\n\n <PagePurpose>\n {tr(\n 'tourScheduler.purpose',\n 'Decide exactly when this tour goes live and how often it repeats — for example, run an onboarding tour every Monday, or activate a feature-announcement tour for a single launch window. Set a start (and optional end) date plus a recurrence pattern, and the tour will surface to users automatically on schedule.'\n )}\n </PagePurpose>\n\n {/* Existing Schedules — emphasised so an editor sees current activation at a glance. */}\n {existingSchedules.length > 0 && (\n <EmphasisPanel className=\"p-4 sm:p-6\">\n <h2 className=\"text-lg font-semibold mb-4\">\n {tr('tourScheduler.existingSchedules', 'Existing Schedules')}\n </h2>\n <div className=\"space-y-2\">\n {existingSchedules.map((schedule: (typeof existingSchedules)[number]) => (\n <div\n key={schedule.id}\n className=\"flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between p-3 border border-border-seam rounded-md\"\n >\n <div>\n <div className=\"text-sm font-medium\">\n {new Date(schedule.startDate).toLocaleDateString()}\n {schedule.endDate\n ? ` — ${new Date(schedule.endDate).toLocaleDateString()}`\n : ` — ${tr('tourScheduler.noEndDate', 'No end date')}`}\n </div>\n <div className=\"text-xs text-text-secondary\">\n {schedule.recurrenceRule || tr('tourScheduler.oneTime', 'One-time')} ·{' '}\n {schedule.timezone}\n {schedule.isActive\n ? ` · ${tr('tourScheduler.active', 'Active')}`\n : ` · ${tr('tourScheduler.inactive', 'Inactive')}`}\n </div>\n </div>\n <div className=\"text-xs text-text-secondary tabular-nums\">\n {tr('tourScheduler.runs', 'Runs')}: {schedule.executionCount}\n </div>\n </div>\n ))}\n </div>\n </EmphasisPanel>\n )}\n\n {/* Form */}\n <form onSubmit={handleSubmit} className=\"space-y-6\">\n {/* Date Range */}\n <GlassCard treatment=\"plain\" className=\"p-4 sm:p-6 border-border-seam\">\n <h2 className=\"text-lg font-semibold mb-4 flex items-center gap-2\">\n <Calendar size={20} />\n {tr('tourScheduler.sectionDateRange', 'Date Range')}\n </h2>\n\n <div className=\"space-y-4\">\n <div>\n <label htmlFor=\"scheduler-start-date\" className=\"block text-sm font-medium mb-2\">\n {tr('tourScheduler.labelStartDate', 'Start Date *')}\n </label>\n <input\n id=\"scheduler-start-date\"\n type=\"datetime-local\"\n value={scheduleData.startDate}\n onChange={(e) => setScheduleData({ ...scheduleData, startDate: e.target.value })}\n className=\"w-full px-3 py-2 min-h-[44px] border border-border-subtle rounded-md bg-bg-surface\"\n required\n aria-label={tr('tourScheduler.labelStartDate', 'Start Date *')}\n />\n </div>\n\n <div>\n <label htmlFor=\"scheduler-end-date\" className=\"block text-sm font-medium mb-2\">\n {tr('tourScheduler.labelEndDate', 'End Date (Optional)')}\n </label>\n <input\n id=\"scheduler-end-date\"\n type=\"datetime-local\"\n value={scheduleData.endDate}\n onChange={(e) => setScheduleData({ ...scheduleData, endDate: e.target.value })}\n className=\"w-full px-3 py-2 min-h-[44px] border border-border-subtle rounded-md bg-bg-surface\"\n aria-label={tr('tourScheduler.labelEndDate', 'End Date (Optional)')}\n />\n </div>\n </div>\n </GlassCard>\n\n {/* Recurrence */}\n <GlassCard treatment=\"plain\" className=\"p-4 sm:p-6 border-border-seam\">\n <h2 className=\"text-lg font-semibold mb-4 flex items-center gap-2\">\n <Clock size={20} />\n {tr('tourScheduler.sectionRecurrence', 'Recurrence')}\n </h2>\n\n <div className=\"space-y-4\">\n <div>\n <label htmlFor=\"scheduler-recurrence\" className=\"block text-sm font-medium mb-2\">\n {tr('tourScheduler.labelRecurrencePattern', 'Recurrence Pattern')}\n </label>\n <select\n id=\"scheduler-recurrence\"\n value={scheduleData.recurrencePattern}\n onChange={(e) =>\n setScheduleData({\n ...scheduleData,\n recurrencePattern: e.target.value as RecurrencePattern,\n })\n }\n className=\"w-full px-3 py-2 min-h-[44px] border border-border-subtle rounded-md bg-bg-surface\"\n aria-label={tr('tourScheduler.labelRecurrencePattern', 'Recurrence Pattern')}\n >\n <option value=\"DAILY\">{tr('tourScheduler.recurrenceDaily', 'Daily')}</option>\n <option value=\"WEEKLY\">{tr('tourScheduler.recurrenceWeekly', 'Weekly')}</option>\n <option value=\"MONTHLY\">{tr('tourScheduler.recurrenceMonthly', 'Monthly')}</option>\n <option value=\"CUSTOM\">{tr('tourScheduler.recurrenceCustom', 'Custom')}</option>\n </select>\n </div>\n\n <div>\n <label htmlFor=\"scheduler-timezone\" className=\"block text-sm font-medium mb-2\">\n {tr('tourScheduler.labelTimezone', 'Timezone')}\n </label>\n <input\n id=\"scheduler-timezone\"\n type=\"text\"\n value={scheduleData.timezone}\n onChange={(e) => setScheduleData({ ...scheduleData, timezone: e.target.value })}\n className=\"w-full px-3 py-2 min-h-[44px] border border-border-subtle rounded-md bg-bg-surface\"\n readOnly\n aria-label={tr('tourScheduler.labelTimezone', 'Timezone')}\n />\n </div>\n </div>\n </GlassCard>\n\n {/* Status */}\n <GlassCard treatment=\"plain\" className=\"p-4 sm:p-6 border-border-seam\">\n <label className=\"flex items-center gap-3 cursor-pointer\">\n <input\n type=\"checkbox\"\n checked={scheduleData.isActive}\n onChange={(e) => setScheduleData({ ...scheduleData, isActive: e.target.checked })}\n className=\"size-5 rounded\"\n aria-label={tr('tourScheduler.statusActiveLabel', 'Active')}\n />\n <div>\n <div className=\"font-medium\">{tr('tourScheduler.statusActiveLabel', 'Active')}</div>\n <div className=\"text-sm text-text-secondary\">\n {tr(\n 'tourScheduler.statusActiveDescription',\n 'Enable this schedule immediately after creation'\n )}\n </div>\n </div>\n </label>\n </GlassCard>\n\n {/* Actions */}\n <div className=\"flex flex-col sm:flex-row gap-3\">\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={handleBack}\n className=\"flex-1 min-h-[44px]\"\n >\n {tr('tourScheduler.cancel', 'Cancel')}\n </Button>\n <PermissionGate permission={TOUR_PERMISSIONS.TOUR_SCHEDULE_CREATE}>\n <Button\n type=\"submit\"\n disabled={saving || !canCreateSchedule}\n className=\"flex-1 min-h-[44px]\"\n >\n <Save size={20} />\n {saving\n ? tr('tourScheduler.creating', 'Creating...')\n : tr('tourScheduler.createSchedule', 'Create Schedule')}\n </Button>\n </PermissionGate>\n </div>\n </form>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SchedulerPage.js","names":[],"sources":["../../../src/tours/pages/SchedulerPage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { useParams, useNavigate } from 'react-router';\nimport { Calendar, Clock, ArrowLeft, Save } from 'lucide-react';\nimport { PermissionGate } from '@burdenoff/fe-libs/shared/components';\nimport { Button, EmphasisPanel, GlassCard, PagePurpose } from '@burdenoff/fe-libs/ui';\nimport { useEventBus } from '@burdenoff/fe-libs/shared/events';\nimport { useToursPermissionAccess } from '../hooks/useToursPermissionAccess';\nimport { useToursContext } from '../providers/ToursProvider';\nimport { useToursI18n } from '../i18n';\nimport { TOUR_PERMISSIONS } from '../../constants/permissions';\nimport type { RecurrencePattern } from '../types';\nimport { emitToursApiFailed } from '../telemetry';\nimport { useCreateTourScheduleMutation } from '../../generated/global-operations';\nimport { useGetTourSchedulesQuery } from '../../generated/global-operations';\n\n/**\n * Tour scheduler page\n * Configure tour scheduling and recurrence\n */\nexport function SchedulerPage() {\n const { tr } = useToursI18n();\n const { tourId } = useParams();\n const navigate = useNavigate();\n const toursContext = useToursContext();\n const { basePath = '/' } = toursContext;\n const eventBus = useEventBus();\n const { hasPermission } = useToursPermissionAccess();\n const canCreateSchedule = hasPermission(TOUR_PERMISSIONS.TOUR_SCHEDULE_CREATE);\n const canListSchedules = hasPermission(TOUR_PERMISSIONS.TOUR_SCHEDULE_LIST);\n\n const [scheduleData, setScheduleData] = useState({\n startDate: '',\n endDate: '',\n recurrencePattern: 'DAILY' as RecurrencePattern,\n timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,\n isActive: true,\n });\n\n const [saving, setSaving] = useState(false);\n\n const [createSchedule] = useCreateTourScheduleMutation();\n const { data: schedulesData } = useGetTourSchedulesQuery({\n variables: { tourId: tourId! },\n skip: !tourId || !canListSchedules,\n });\n const existingSchedules = schedulesData?.tourSchedules ?? [];\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault();\n if (!tourId) return;\n setSaving(true);\n try {\n // Convert recurrence pattern to RFC 5545 recurrence rule\n const recurrenceRule =\n scheduleData.recurrencePattern === 'DAILY'\n ? 'FREQ=DAILY'\n : scheduleData.recurrencePattern === 'WEEKLY'\n ? 'FREQ=WEEKLY'\n : scheduleData.recurrencePattern === 'MONTHLY'\n ? 'FREQ=MONTHLY'\n : undefined;\n\n await createSchedule({\n variables: {\n input: {\n tourId,\n startDate: new Date(scheduleData.startDate).toISOString(),\n endDate: scheduleData.endDate\n ? new Date(scheduleData.endDate).toISOString()\n : undefined,\n recurrenceRule,\n timezone: scheduleData.timezone,\n },\n },\n refetchQueries: ['GetTourSchedules'],\n });\n navigate(`${basePath}/${tourId}/edit`);\n } catch {\n emitToursApiFailed(eventBus, 'schedule.create', toursContext);\n } finally {\n setSaving(false);\n }\n };\n\n const handleBack = () => {\n navigate(`${basePath}/${tourId}/edit`);\n };\n\n return (\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6 max-w-3xl mx-auto\">\n {/* Header */}\n <div className=\"flex items-center gap-3 sm:gap-4\">\n <button\n type=\"button\"\n onClick={handleBack}\n className=\"p-2 min-h-[44px] min-w-[44px] hover:bg-accent rounded-md\"\n aria-label={tr('tourScheduler.goBack', 'Go back')}\n >\n <ArrowLeft size={20} />\n </button>\n <div>\n <h1 className=\"text-xl sm:text-2xl font-bold\">\n {tr('tourScheduler.title', 'Schedule Tour')}\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {tr('tourScheduler.subtitle', 'Configure when this tour should be activated')}\n </p>\n </div>\n </div>\n\n <PagePurpose>\n {tr(\n 'tourScheduler.purpose',\n 'Decide exactly when this tour goes live and how often it repeats — for example, run an onboarding tour every Monday, or activate a feature-announcement tour for a single launch window. Set a start (and optional end) date plus a recurrence pattern, and the tour will surface to users automatically on schedule.'\n )}\n </PagePurpose>\n\n {/* Existing Schedules — emphasised so an editor sees current activation at a glance. */}\n {existingSchedules.length > 0 && (\n <EmphasisPanel className=\"p-4 sm:p-6\">\n <h2 className=\"text-lg font-semibold mb-4\">\n {tr('tourScheduler.existingSchedules', 'Existing Schedules')}\n </h2>\n <div className=\"space-y-2\">\n {existingSchedules.map((schedule: (typeof existingSchedules)[number]) => (\n <div\n key={schedule.id}\n className=\"flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between p-3 border border-border-seam rounded-md\"\n >\n <div>\n <div className=\"text-sm font-medium\">\n {new Date(schedule.startDate).toLocaleDateString()}\n {schedule.endDate\n ? ` — ${new Date(schedule.endDate).toLocaleDateString()}`\n : ` — ${tr('tourScheduler.noEndDate', 'No end date')}`}\n </div>\n <div className=\"text-xs text-text-secondary\">\n {schedule.recurrenceRule || tr('tourScheduler.oneTime', 'One-time')} ·{' '}\n {schedule.timezone}\n {schedule.isActive\n ? ` · ${tr('tourScheduler.active', 'Active')}`\n : ` · ${tr('tourScheduler.inactive', 'Inactive')}`}\n </div>\n </div>\n <div className=\"text-xs text-text-secondary tabular-nums\">\n {tr('tourScheduler.runs', 'Runs')}: {schedule.executionCount}\n </div>\n </div>\n ))}\n </div>\n </EmphasisPanel>\n )}\n\n {/* Form */}\n <form onSubmit={handleSubmit} className=\"space-y-6\">\n {/* Date Range */}\n <GlassCard treatment=\"plain\" className=\"p-4 sm:p-6 border-border-seam\">\n <h2 className=\"text-lg font-semibold mb-4 flex items-center gap-2\">\n <Calendar size={20} />\n {tr('tourScheduler.sectionDateRange', 'Date Range')}\n </h2>\n\n <div className=\"space-y-4\">\n <div>\n <label htmlFor=\"scheduler-start-date\" className=\"block text-sm font-medium mb-2\">\n {tr('tourScheduler.labelStartDate', 'Start Date *')}\n </label>\n <input\n id=\"scheduler-start-date\"\n type=\"datetime-local\"\n value={scheduleData.startDate}\n onChange={(e) => setScheduleData({ ...scheduleData, startDate: e.target.value })}\n className=\"w-full px-3 py-2 min-h-[44px] border border-border-subtle rounded-md bg-bg-surface\"\n required\n aria-label={tr('tourScheduler.labelStartDate', 'Start Date *')}\n />\n </div>\n\n <div>\n <label htmlFor=\"scheduler-end-date\" className=\"block text-sm font-medium mb-2\">\n {tr('tourScheduler.labelEndDate', 'End Date (Optional)')}\n </label>\n <input\n id=\"scheduler-end-date\"\n type=\"datetime-local\"\n value={scheduleData.endDate}\n onChange={(e) => setScheduleData({ ...scheduleData, endDate: e.target.value })}\n className=\"w-full px-3 py-2 min-h-[44px] border border-border-subtle rounded-md bg-bg-surface\"\n aria-label={tr('tourScheduler.labelEndDate', 'End Date (Optional)')}\n />\n </div>\n </div>\n </GlassCard>\n\n {/* Recurrence */}\n <GlassCard treatment=\"plain\" className=\"p-4 sm:p-6 border-border-seam\">\n <h2 className=\"text-lg font-semibold mb-4 flex items-center gap-2\">\n <Clock size={20} />\n {tr('tourScheduler.sectionRecurrence', 'Recurrence')}\n </h2>\n\n <div className=\"space-y-4\">\n <div>\n <label htmlFor=\"scheduler-recurrence\" className=\"block text-sm font-medium mb-2\">\n {tr('tourScheduler.labelRecurrencePattern', 'Recurrence Pattern')}\n </label>\n <select\n id=\"scheduler-recurrence\"\n value={scheduleData.recurrencePattern}\n onChange={(e) =>\n setScheduleData({\n ...scheduleData,\n recurrencePattern: e.target.value as RecurrencePattern,\n })\n }\n className=\"w-full px-3 py-2 min-h-[44px] border border-border-subtle rounded-md bg-bg-surface\"\n aria-label={tr('tourScheduler.labelRecurrencePattern', 'Recurrence Pattern')}\n >\n <option value=\"DAILY\">{tr('tourScheduler.recurrenceDaily', 'Daily')}</option>\n <option value=\"WEEKLY\">{tr('tourScheduler.recurrenceWeekly', 'Weekly')}</option>\n <option value=\"MONTHLY\">{tr('tourScheduler.recurrenceMonthly', 'Monthly')}</option>\n <option value=\"CUSTOM\">{tr('tourScheduler.recurrenceCustom', 'Custom')}</option>\n </select>\n </div>\n\n <div>\n <label htmlFor=\"scheduler-timezone\" className=\"block text-sm font-medium mb-2\">\n {tr('tourScheduler.labelTimezone', 'Timezone')}\n </label>\n <input\n id=\"scheduler-timezone\"\n type=\"text\"\n value={scheduleData.timezone}\n onChange={(e) => setScheduleData({ ...scheduleData, timezone: e.target.value })}\n className=\"w-full px-3 py-2 min-h-[44px] border border-border-subtle rounded-md bg-bg-surface\"\n readOnly\n aria-label={tr('tourScheduler.labelTimezone', 'Timezone')}\n />\n </div>\n </div>\n </GlassCard>\n\n {/* Status */}\n <GlassCard treatment=\"plain\" className=\"p-4 sm:p-6 border-border-seam\">\n <label className=\"flex items-center gap-3 cursor-pointer\">\n <input\n type=\"checkbox\"\n checked={scheduleData.isActive}\n onChange={(e) => setScheduleData({ ...scheduleData, isActive: e.target.checked })}\n className=\"size-5 rounded\"\n aria-label={tr('tourScheduler.statusActiveLabel', 'Active')}\n />\n <div>\n <div className=\"font-medium\">{tr('tourScheduler.statusActiveLabel', 'Active')}</div>\n <div className=\"text-sm text-text-secondary\">\n {tr(\n 'tourScheduler.statusActiveDescription',\n 'Enable this schedule immediately after creation'\n )}\n </div>\n </div>\n </label>\n </GlassCard>\n\n {/* Actions */}\n <div className=\"flex flex-col sm:flex-row gap-3\">\n <Button\n type=\"button\"\n variant=\"outline\"\n onClick={handleBack}\n className=\"flex-1 min-h-[44px]\"\n >\n {tr('tourScheduler.cancel', 'Cancel')}\n </Button>\n <PermissionGate permission={TOUR_PERMISSIONS.TOUR_SCHEDULE_CREATE}>\n <Button\n type=\"submit\"\n disabled={saving || !canCreateSchedule}\n className=\"flex-1 min-h-[44px]\"\n >\n <Save size={20} />\n {saving\n ? tr('tourScheduler.creating', 'Creating...')\n : tr('tourScheduler.createSchedule', 'Create Schedule')}\n </Button>\n </PermissionGate>\n </div>\n </form>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { useToursI18n as e } from "../i18n.js";
|
|
2
|
+
import { useToursContext as t } from "../providers/ToursProvider.js";
|
|
3
|
+
import { useToursPermissionAccess as ee } from "../hooks/useToursPermissionAccess.js";
|
|
3
4
|
import { TOUR_PERMISSIONS as n } from "../../constants/permissions.js";
|
|
4
|
-
import { useToursI18n as ee } from "../i18n.js";
|
|
5
5
|
import { emitToursApiFailed as r } from "../telemetry.js";
|
|
6
6
|
import { useMediaLibrary as te } from "../hooks/useMediaLibrary.js";
|
|
7
7
|
import { MarkdownEditor as i } from "../components/MarkdownEditor.js";
|
|
@@ -16,7 +16,7 @@ import { Button as x, GlassCard as oe, IllustratedEmptyState as se, OptionalFiel
|
|
|
16
16
|
import { useEventBus as ue } from "@burdenoff/fe-libs/shared/events";
|
|
17
17
|
//#region src/tours/pages/StepEditorPage.tsx
|
|
18
18
|
function S() {
|
|
19
|
-
let { tr: S } =
|
|
19
|
+
let { tr: S } = e(), { tourId: C, stepId: w } = ae(), T = ie(), E = t(), { basePath: D = "/" } = E, O = ue(), { hasPermission: k } = ee(), A = k(n.TOUR_STEP_CREATE), j = k(n.TOUR_STEP_UPDATE), M = k(n.TOUR_STEP_DELETE), { tour: N, loadTour: P, syncSteps: F, saving: I } = ne(C), { steps: L, selectedStep: R, selectStep: z, createStep: de, deleteStep: B, updateStepField: V, moveStep: H } = a(), { media: fe, fetchMedia: U, attachToStep: pe, loading: me } = te(), [W, G] = s(!1), [K, he] = s(() => typeof window < "u" && window.innerWidth < 768), [q, J] = s(""), [Y, ge] = s("body"), [_e, X] = s(!1);
|
|
20
20
|
o(() => {
|
|
21
21
|
let e = () => {
|
|
22
22
|
let e = window.innerWidth < 768;
|
|
@@ -52,7 +52,7 @@ function S() {
|
|
|
52
52
|
}, ye = async () => {
|
|
53
53
|
if (!(!R || !C || !q)) {
|
|
54
54
|
if (R.id.startsWith("temp_")) {
|
|
55
|
-
window.alert("Save your steps before attaching media to them.");
|
|
55
|
+
window.alert(S("stepEditor.saveBeforeAttach", "Save your steps before attaching media to them."));
|
|
56
56
|
return;
|
|
57
57
|
}
|
|
58
58
|
try {
|
|
@@ -222,8 +222,8 @@ function S() {
|
|
|
222
222
|
e.stopPropagation(), t > 0 && H(t, t - 1);
|
|
223
223
|
},
|
|
224
224
|
className: "p-1.5 sm:p-2 min-h-[32px] min-w-[32px] sm:min-h-[40px] sm:min-w-[40px] hover:bg-accent rounded",
|
|
225
|
-
title: "Move step up",
|
|
226
|
-
"aria-label": "Move step up",
|
|
225
|
+
title: S("stepEditor.moveStepUp", "Move step up"),
|
|
226
|
+
"aria-label": S("stepEditor.moveStepUp", "Move step up"),
|
|
227
227
|
disabled: t === 0,
|
|
228
228
|
children: /* @__PURE__ */ y(f, { size: 14 })
|
|
229
229
|
}),
|
|
@@ -233,8 +233,8 @@ function S() {
|
|
|
233
233
|
e.stopPropagation(), t < L.length - 1 && H(t, t + 1);
|
|
234
234
|
},
|
|
235
235
|
className: "p-1.5 sm:p-2 min-h-[32px] min-w-[32px] sm:min-h-[40px] sm:min-w-[40px] hover:bg-accent rounded",
|
|
236
|
-
title: "Move step down",
|
|
237
|
-
"aria-label": "Move step down",
|
|
236
|
+
title: S("stepEditor.moveStepDown", "Move step down"),
|
|
237
|
+
"aria-label": S("stepEditor.moveStepDown", "Move step down"),
|
|
238
238
|
disabled: t === L.length - 1,
|
|
239
239
|
children: /* @__PURE__ */ y(l, { size: 14 })
|
|
240
240
|
}),
|
|
@@ -348,7 +348,7 @@ function S() {
|
|
|
348
348
|
onChange: (e) => V(R.id, "targetSelector", e.target.value),
|
|
349
349
|
placeholder: S("stepEditor.cssSelectorPlaceholder"),
|
|
350
350
|
className: "w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface font-mono text-sm",
|
|
351
|
-
"aria-label":
|
|
351
|
+
"aria-label": `${S("stepEditor.cssSelector")} * (${S("stepEditor.targetElement")})`
|
|
352
352
|
}),
|
|
353
353
|
/* @__PURE__ */ y("p", {
|
|
354
354
|
className: "text-xs text-text-secondary mt-1",
|
|
@@ -455,18 +455,18 @@ function S() {
|
|
|
455
455
|
checked: R.requireInteraction ?? !1,
|
|
456
456
|
onChange: (e) => V(R.id, "requireInteraction", e.target.checked),
|
|
457
457
|
className: "size-4 rounded",
|
|
458
|
-
"aria-label": "Require interaction"
|
|
458
|
+
"aria-label": S("stepEditor.requireInteraction", "Require interaction")
|
|
459
459
|
}), /* @__PURE__ */ b("div", { children: [/* @__PURE__ */ y("div", {
|
|
460
460
|
className: "text-sm font-medium",
|
|
461
|
-
children: "Require interaction"
|
|
461
|
+
children: S("stepEditor.requireInteraction", "Require interaction")
|
|
462
462
|
}), /* @__PURE__ */ y("div", {
|
|
463
463
|
className: "text-xs text-text-secondary",
|
|
464
|
-
children: "User must interact with the highlighted element before advancing."
|
|
464
|
+
children: S("stepEditor.requireInteractionDescription", "User must interact with the highlighted element before advancing.")
|
|
465
465
|
})] })]
|
|
466
466
|
}), /* @__PURE__ */ b("div", { children: [/* @__PURE__ */ y("label", {
|
|
467
467
|
className: "block text-sm font-medium mb-2",
|
|
468
468
|
htmlFor: "step-editor-wait-timeout",
|
|
469
|
-
children: "Wait for timeout (ms)"
|
|
469
|
+
children: S("stepEditor.waitForTimeout", "Wait for timeout (ms)")
|
|
470
470
|
}), /* @__PURE__ */ y("input", {
|
|
471
471
|
id: "step-editor-wait-timeout",
|
|
472
472
|
type: "number",
|
|
@@ -475,18 +475,18 @@ function S() {
|
|
|
475
475
|
onChange: (e) => V(R.id, "waitForTimeout", e.target.value ? Number(e.target.value) : void 0),
|
|
476
476
|
className: "w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-sm",
|
|
477
477
|
placeholder: "1500",
|
|
478
|
-
"aria-label": "Wait for timeout (ms)"
|
|
478
|
+
"aria-label": S("stepEditor.waitForTimeout", "Wait for timeout (ms)")
|
|
479
479
|
})] })]
|
|
480
480
|
}),
|
|
481
481
|
/* @__PURE__ */ b(ce, {
|
|
482
|
-
title: "Advanced settings",
|
|
483
|
-
description: "Step-timing waits, skip conditions, and analytics tags.",
|
|
482
|
+
title: S("stepEditor.advancedSettings", "Advanced settings"),
|
|
483
|
+
description: S("stepEditor.advancedSettingsDescription", "Step-timing waits, skip conditions, and analytics tags."),
|
|
484
484
|
children: [/* @__PURE__ */ b("div", {
|
|
485
485
|
className: "grid grid-cols-1 gap-4 sm:grid-cols-2",
|
|
486
486
|
children: [/* @__PURE__ */ b("div", { children: [/* @__PURE__ */ y("label", {
|
|
487
487
|
className: "block text-sm font-medium mb-2",
|
|
488
488
|
htmlFor: "step-editor-wait-selector",
|
|
489
|
-
children: "Wait for selector"
|
|
489
|
+
children: S("stepEditor.waitForSelector", "Wait for selector")
|
|
490
490
|
}), /* @__PURE__ */ y("input", {
|
|
491
491
|
id: "step-editor-wait-selector",
|
|
492
492
|
type: "text",
|
|
@@ -494,11 +494,11 @@ function S() {
|
|
|
494
494
|
onChange: (e) => V(R.id, "waitForSelector", e.target.value || void 0),
|
|
495
495
|
className: "w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-sm font-mono",
|
|
496
496
|
placeholder: "[data-tour='agents-tabs']",
|
|
497
|
-
"aria-label": "Wait for selector"
|
|
497
|
+
"aria-label": S("stepEditor.waitForSelector", "Wait for selector")
|
|
498
498
|
})] }), /* @__PURE__ */ b("div", { children: [/* @__PURE__ */ y("label", {
|
|
499
499
|
className: "block text-sm font-medium mb-2",
|
|
500
500
|
htmlFor: "step-editor-wait-event",
|
|
501
|
-
children: "Wait for event"
|
|
501
|
+
children: S("stepEditor.waitForEvent", "Wait for event")
|
|
502
502
|
}), /* @__PURE__ */ y("input", {
|
|
503
503
|
id: "step-editor-wait-event",
|
|
504
504
|
type: "text",
|
|
@@ -506,14 +506,14 @@ function S() {
|
|
|
506
506
|
onChange: (e) => V(R.id, "waitForEvent", e.target.value || void 0),
|
|
507
507
|
className: "w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-sm",
|
|
508
508
|
placeholder: "tour:ready",
|
|
509
|
-
"aria-label": "Wait for event"
|
|
509
|
+
"aria-label": S("stepEditor.waitForEvent", "Wait for event")
|
|
510
510
|
})] })]
|
|
511
511
|
}), /* @__PURE__ */ b("div", {
|
|
512
512
|
className: "mt-4",
|
|
513
513
|
children: [/* @__PURE__ */ y("label", {
|
|
514
514
|
className: "block text-sm font-medium mb-2",
|
|
515
515
|
htmlFor: "step-editor-skip-conditions",
|
|
516
|
-
children: "Skip conditions (JSON)"
|
|
516
|
+
children: S("stepEditor.skipConditions", "Skip conditions (JSON)")
|
|
517
517
|
}), /* @__PURE__ */ y("textarea", {
|
|
518
518
|
id: "step-editor-skip-conditions",
|
|
519
519
|
value: R.skipConditions ? JSON.stringify(R.skipConditions, null, 2) : "",
|
|
@@ -532,7 +532,7 @@ function S() {
|
|
|
532
532
|
rows: 4,
|
|
533
533
|
className: "w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-sm font-mono",
|
|
534
534
|
placeholder: "{\"selectorMissing\":\"[data-tour=\\\"missing\\\"]\"}",
|
|
535
|
-
"aria-label": "Skip conditions (JSON)"
|
|
535
|
+
"aria-label": S("stepEditor.skipConditions", "Skip conditions (JSON)")
|
|
536
536
|
})]
|
|
537
537
|
})]
|
|
538
538
|
}),
|
|
@@ -544,7 +544,7 @@ function S() {
|
|
|
544
544
|
className: "flex items-center gap-2",
|
|
545
545
|
children: [/* @__PURE__ */ y(m, { size: 18 }), /* @__PURE__ */ y("h3", {
|
|
546
546
|
className: "text-sm font-semibold",
|
|
547
|
-
children: "Step media"
|
|
547
|
+
children: S("stepEditor.stepMedia", "Step media")
|
|
548
548
|
})]
|
|
549
549
|
}),
|
|
550
550
|
R.media && R.media.length > 0 ? /* @__PURE__ */ y("div", {
|
|
@@ -564,7 +564,7 @@ function S() {
|
|
|
564
564
|
}, e.id))
|
|
565
565
|
}) : /* @__PURE__ */ y("p", {
|
|
566
566
|
className: "text-sm text-text-secondary",
|
|
567
|
-
children: "No media attached to this step yet."
|
|
567
|
+
children: S("stepEditor.noMediaAttached", "No media attached to this step yet.")
|
|
568
568
|
}),
|
|
569
569
|
/* @__PURE__ */ b("div", {
|
|
570
570
|
className: "grid grid-cols-1 gap-3 sm:grid-cols-[minmax(0,1fr)_160px_auto]",
|
|
@@ -573,10 +573,10 @@ function S() {
|
|
|
573
573
|
value: q,
|
|
574
574
|
onChange: (e) => J(e.target.value),
|
|
575
575
|
className: "w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-sm",
|
|
576
|
-
"aria-label": "Select media
|
|
576
|
+
"aria-label": S("stepEditor.selectUploadedMedia", "Select uploaded media"),
|
|
577
577
|
children: [/* @__PURE__ */ y("option", {
|
|
578
578
|
value: "",
|
|
579
|
-
children: me ? "Loading media..." : "Select uploaded media"
|
|
579
|
+
children: me ? S("stepEditor.loadingMedia", "Loading media...") : S("stepEditor.selectUploadedMedia", "Select uploaded media")
|
|
580
580
|
}), fe.map((e) => /* @__PURE__ */ b("option", {
|
|
581
581
|
value: e.id,
|
|
582
582
|
children: [
|
|
@@ -591,19 +591,19 @@ function S() {
|
|
|
591
591
|
value: Y,
|
|
592
592
|
onChange: (e) => ge(e.target.value),
|
|
593
593
|
className: "w-full px-3 py-2 border border-border-subtle rounded-md bg-bg-surface text-sm",
|
|
594
|
-
"aria-label": "
|
|
594
|
+
"aria-label": S("stepEditor.positionHeader", "Header"),
|
|
595
595
|
children: [
|
|
596
596
|
/* @__PURE__ */ y("option", {
|
|
597
597
|
value: "header",
|
|
598
|
-
children: "Header"
|
|
598
|
+
children: S("stepEditor.positionHeader", "Header")
|
|
599
599
|
}),
|
|
600
600
|
/* @__PURE__ */ y("option", {
|
|
601
601
|
value: "body",
|
|
602
|
-
children: "Body"
|
|
602
|
+
children: S("stepEditor.positionBody", "Body")
|
|
603
603
|
}),
|
|
604
604
|
/* @__PURE__ */ y("option", {
|
|
605
605
|
value: "footer",
|
|
606
|
-
children: "Footer"
|
|
606
|
+
children: S("stepEditor.positionFooter", "Footer")
|
|
607
607
|
})
|
|
608
608
|
]
|
|
609
609
|
}),
|
|
@@ -612,7 +612,7 @@ function S() {
|
|
|
612
612
|
onClick: ye,
|
|
613
613
|
disabled: _e || !q,
|
|
614
614
|
className: "px-4 py-2 min-h-[44px] border border-border-subtle rounded-md hover:bg-accent disabled:opacity-50",
|
|
615
|
-
children: "Attach"
|
|
615
|
+
children: S("stepEditor.attach", "Attach")
|
|
616
616
|
})
|
|
617
617
|
]
|
|
618
618
|
})
|