@burdenoff/microfe-tours 2026.828.1 → 2026.830.2

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.
Files changed (47) hide show
  1. package/dist/tours/ToursAdminRoot.js +28 -27
  2. package/dist/tours/ToursAdminRoot.js.map +1 -1
  3. package/dist/tours/ToursAdminRoutes.js +56 -55
  4. package/dist/tours/ToursAdminRoutes.js.map +1 -1
  5. package/dist/tours/ToursUserRoot.js +34 -29
  6. package/dist/tours/ToursUserRoot.js.map +1 -1
  7. package/dist/tours/ToursUserRoutes.js +33 -33
  8. package/dist/tours/ToursUserRoutes.js.map +1 -1
  9. package/dist/tours/components/MarkdownEditor.js +85 -80
  10. package/dist/tours/components/MarkdownEditor.js.map +1 -1
  11. package/dist/tours/components/RouteHelpers.js +47 -42
  12. package/dist/tours/components/RouteHelpers.js.map +1 -1
  13. package/dist/tours/components/ToursAdminNavRegistrar.js +32 -27
  14. package/dist/tours/components/ToursAdminNavRegistrar.js.map +1 -1
  15. package/dist/tours/hooks/useStepSequencer.js +45 -36
  16. package/dist/tours/hooks/useStepSequencer.js.map +1 -1
  17. package/dist/tours/hooks/useTourBuilder.js +106 -93
  18. package/dist/tours/hooks/useTourBuilder.js.map +1 -1
  19. package/dist/tours/hooks/useTourPreview.js +51 -41
  20. package/dist/tours/hooks/useTourPreview.js.map +1 -1
  21. package/dist/tours/hooks/useToursList.js +28 -27
  22. package/dist/tours/hooks/useToursList.js.map +1 -1
  23. package/dist/tours/i18n.js +187 -5
  24. package/dist/tours/i18n.js.map +1 -1
  25. package/dist/tours/pages/AnalyticsDashboardPage.js +108 -109
  26. package/dist/tours/pages/AnalyticsDashboardPage.js.map +1 -1
  27. package/dist/tours/pages/MediaLibraryPage.js +58 -61
  28. package/dist/tours/pages/MediaLibraryPage.js.map +1 -1
  29. package/dist/tours/pages/MyToursPage.js +5 -5
  30. package/dist/tours/pages/MyToursProgressPage.js +5 -5
  31. package/dist/tours/pages/SchedulerPage.js +78 -81
  32. package/dist/tours/pages/SchedulerPage.js.map +1 -1
  33. package/dist/tours/pages/StepEditorPage.js +32 -32
  34. package/dist/tours/pages/StepEditorPage.js.map +1 -1
  35. package/dist/tours/pages/SystemAnalyticsPage.js +75 -79
  36. package/dist/tours/pages/SystemAnalyticsPage.js.map +1 -1
  37. package/dist/tours/pages/TemplateLibraryPage.js +8 -8
  38. package/dist/tours/pages/TourBuilderPage.js +279 -279
  39. package/dist/tours/pages/TourBuilderPage.js.map +1 -1
  40. package/dist/tours/pages/TourPreviewPage.js +6 -6
  41. package/dist/tours/pages/ToursListPage.js +155 -155
  42. package/dist/tours/pages/ToursListPage.js.map +1 -1
  43. package/dist/tours/store/toursStore.js +11 -7
  44. package/dist/tours/store/toursStore.js.map +1 -1
  45. package/dist/tours/utils/index.js +32 -19
  46. package/dist/tours/utils/index.js.map +1 -1
  47. package/package.json +1 -1
@@ -1,53 +1,50 @@
1
- import { useToursContext as e } from "../providers/ToursProvider.js";
2
- import { useCreateTourScheduleMutation as t, useGetTourSchedulesQuery as n } from "../../generated/global-operations.js";
3
- import { useToursPermissionAccess as r } from "../hooks/useToursPermissionAccess.js";
4
- import { TOUR_PERMISSIONS as i } from "../../constants/permissions.js";
5
- import { emitToursApiFailed as a } from "../telemetry.js";
6
- import { useCallback as o, useState as s } from "react";
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 { useI18n as x } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
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 C() {
16
- let { t: C } = x(), w = o((e, t) => {
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
- }), [F, I] = s(!1), [L] = t(), { data: R } = n({
26
- variables: { tourId: T },
27
- skip: !T || !M
28
- }), z = R?.tourSchedules ?? [], B = async (e) => {
29
- if (e.preventDefault(), T) {
30
- I(!0);
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 = N.recurrencePattern === "DAILY" ? "FREQ=DAILY" : N.recurrencePattern === "WEEKLY" ? "FREQ=WEEKLY" : N.recurrencePattern === "MONTHLY" ? "FREQ=MONTHLY" : void 0;
33
- await L({
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: T,
36
- startDate: new Date(N.startDate).toISOString(),
37
- endDate: N.endDate ? new Date(N.endDate).toISOString() : void 0,
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: N.timezone
36
+ timezone: j.timezone
40
37
  } },
41
38
  refetchQueries: ["GetTourSchedules"]
42
- }), E(`${O}/${T}/edit`);
39
+ }), w(`${E}/${C}/edit`);
43
40
  } catch {
44
- a(k, "schedule.create", D);
41
+ o(D, "schedule.create", T);
45
42
  } finally {
46
- I(!1);
43
+ P(!1);
47
44
  }
48
45
  }
49
- }, V = () => {
50
- E(`${O}/${T}/edit`);
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: V,
56
+ onClick: z,
60
57
  className: "p-2 min-h-[44px] min-w-[44px] hover:bg-accent rounded-md",
61
- "aria-label": w("tourScheduler.goBack", "Go back"),
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: w("tourScheduler.title", "Schedule Tour")
62
+ children: S("tourScheduler.title", "Schedule Tour")
66
63
  }), /* @__PURE__ */ p("p", {
67
64
  className: "text-sm text-text-secondary",
68
- children: w("tourScheduler.subtitle", "Configure when this tour should be activated")
65
+ children: S("tourScheduler.subtitle", "Configure when this tour should be activated")
69
66
  })] })]
70
67
  }),
71
- /* @__PURE__ */ p(b, { children: w("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.") }),
72
- z.length > 0 && /* @__PURE__ */ m(v, {
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: w("tourScheduler.existingSchedules", "Existing Schedules")
73
+ children: S("tourScheduler.existingSchedules", "Existing Schedules")
77
74
  }), /* @__PURE__ */ p("div", {
78
75
  className: "space-y-2",
79
- children: z.map((e) => /* @__PURE__ */ m("div", {
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()}` : ` — ${w("tourScheduler.noEndDate", "No end date")}`]
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 || w("tourScheduler.oneTime", "One-time"),
84
+ e.recurrenceRule || S("tourScheduler.oneTime", "One-time"),
88
85
  " ·",
89
86
  " ",
90
87
  e.timezone,
91
- e.isActive ? ` · ${w("tourScheduler.active", "Active")}` : ` · ${w("tourScheduler.inactive", "Inactive")}`
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
- w("tourScheduler.runs", "Runs"),
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: B,
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 }), w("tourScheduler.sectionDateRange", "Date Range")]
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: w("tourScheduler.labelStartDate", "Start Date *")
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: N.startDate,
124
- onChange: (e) => P({
125
- ...N,
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": w("tourScheduler.labelStartDate", "Start Date *")
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: w("tourScheduler.labelEndDate", "End Date (Optional)")
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: N.endDate,
139
- onChange: (e) => P({
140
- ...N,
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": w("tourScheduler.labelEndDate", "End Date (Optional)")
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 }), w("tourScheduler.sectionRecurrence", "Recurrence")]
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: w("tourScheduler.labelRecurrencePattern", "Recurrence Pattern")
156
+ children: S("tourScheduler.labelRecurrencePattern", "Recurrence Pattern")
160
157
  }), /* @__PURE__ */ m("select", {
161
158
  id: "scheduler-recurrence",
162
- value: N.recurrencePattern,
163
- onChange: (e) => P({
164
- ...N,
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": w("tourScheduler.labelRecurrencePattern", "Recurrence Pattern"),
165
+ "aria-label": S("tourScheduler.labelRecurrencePattern", "Recurrence Pattern"),
169
166
  children: [
170
167
  /* @__PURE__ */ p("option", {
171
168
  value: "DAILY",
172
- children: w("tourScheduler.recurrenceDaily", "Daily")
169
+ children: S("tourScheduler.recurrenceDaily", "Daily")
173
170
  }),
174
171
  /* @__PURE__ */ p("option", {
175
172
  value: "WEEKLY",
176
- children: w("tourScheduler.recurrenceWeekly", "Weekly")
173
+ children: S("tourScheduler.recurrenceWeekly", "Weekly")
177
174
  }),
178
175
  /* @__PURE__ */ p("option", {
179
176
  value: "MONTHLY",
180
- children: w("tourScheduler.recurrenceMonthly", "Monthly")
177
+ children: S("tourScheduler.recurrenceMonthly", "Monthly")
181
178
  }),
182
179
  /* @__PURE__ */ p("option", {
183
180
  value: "CUSTOM",
184
- children: w("tourScheduler.recurrenceCustom", "Custom")
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: w("tourScheduler.labelTimezone", "Timezone")
187
+ children: S("tourScheduler.labelTimezone", "Timezone")
191
188
  }), /* @__PURE__ */ p("input", {
192
189
  id: "scheduler-timezone",
193
190
  type: "text",
194
- value: N.timezone,
195
- onChange: (e) => P({
196
- ...N,
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": w("tourScheduler.labelTimezone", "Timezone")
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: N.isActive,
213
- onChange: (e) => P({
214
- ...N,
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": w("tourScheduler.statusActiveLabel", "Active")
215
+ "aria-label": S("tourScheduler.statusActiveLabel", "Active")
219
216
  }), /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("div", {
220
217
  className: "font-medium",
221
- children: w("tourScheduler.statusActiveLabel", "Active")
218
+ children: S("tourScheduler.statusActiveLabel", "Active")
222
219
  }), /* @__PURE__ */ p("div", {
223
220
  className: "text-sm text-text-secondary",
224
- children: w("tourScheduler.statusActiveDescription", "Enable this schedule immediately after creation")
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: V,
230
+ onClick: z,
234
231
  className: "flex-1 min-h-[44px]",
235
- children: w("tourScheduler.cancel", "Cancel")
232
+ children: S("tourScheduler.cancel", "Cancel")
236
233
  }), /* @__PURE__ */ p(c, {
237
- permission: i.TOUR_SCHEDULE_CREATE,
234
+ permission: a.TOUR_SCHEDULE_CREATE,
238
235
  children: /* @__PURE__ */ m(_, {
239
236
  type: "submit",
240
- disabled: F || !j,
237
+ disabled: N || !k,
241
238
  className: "flex-1 min-h-[44px]",
242
- children: [/* @__PURE__ */ p(f, { size: 20 }), F ? w("tourScheduler.creating", "Creating...") : w("tourScheduler.createSchedule", "Create Schedule")]
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 { C as SchedulerPage };
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 { useToursContext as e } from "../providers/ToursProvider.js";
2
- import { useToursPermissionAccess as t } from "../hooks/useToursPermissionAccess.js";
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 } = ee(), { tourId: C, stepId: w } = ae(), T = ie(), E = e(), { basePath: D = "/" } = E, O = ue(), { hasPermission: k } = t(), 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);
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": "{tr('stepEditor.cssSelector')} *\n \n ({tr('stepEditor.targetElement')})"
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 to attach",
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": "Attachment position",
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
  })