@pablo2410/shared-ui 0.18.2 → 0.19.0

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.
@@ -12,8 +12,9 @@ import {
12
12
  SelectItem,
13
13
  SelectTrigger,
14
14
  SelectValue,
15
+ Switch,
15
16
  Textarea
16
- } from "../chunk-NXE653CX.js";
17
+ } from "../chunk-K6FHU4HN.js";
17
18
  import {
18
19
  cn
19
20
  } from "../chunk-JT3XLKKD.js";
@@ -29,7 +30,7 @@ import {
29
30
  } from "../chunk-BKGUIIN2.js";
30
31
 
31
32
  // src/reporting/ReportingToolbar.tsx
32
- import { useState } from "react";
33
+ import { useRef, useState } from "react";
33
34
  import {
34
35
  ShieldAlert,
35
36
  Leaf,
@@ -38,6 +39,8 @@ import {
38
39
  Lightbulb,
39
40
  ChevronDown,
40
41
  Send,
42
+ ImagePlus,
43
+ X,
41
44
  UserCog
42
45
  } from "lucide-react";
43
46
  import { toast } from "sonner";
@@ -68,7 +71,7 @@ var ALL_REPORT_OPTIONS = [
68
71
  var ALL_FEEDBACK_OPTIONS = [
69
72
  {
70
73
  category: "problem",
71
- label: "Report Problem",
74
+ label: "Report a Problem",
72
75
  description: "Something isn't working",
73
76
  icon: Bug,
74
77
  color: "#EF4444"
@@ -76,17 +79,11 @@ var ALL_FEEDBACK_OPTIONS = [
76
79
  {
77
80
  category: "suggestion",
78
81
  label: "Suggestion",
79
- description: "Idea for improvement",
82
+ description: "An idea for improvement",
80
83
  icon: Lightbulb,
81
84
  color: "#8C34E9"
82
85
  }
83
86
  ];
84
- var SEVERITY_OPTIONS = [
85
- { value: "critical", label: "Critical", color: "#EF4444" },
86
- { value: "high", label: "High", color: "#F59E0B" },
87
- { value: "medium", label: "Medium", color: "#3b82f6" },
88
- { value: "low", label: "Low", color: "#22C55E" }
89
- ];
90
87
  function ReportingToolbar({
91
88
  moduleName = "Oplytics",
92
89
  showIncidentReporting = true,
@@ -95,7 +92,12 @@ function ReportingToolbar({
95
92
  feedbackCategories = ["problem", "suggestion"],
96
93
  onReportSubmit,
97
94
  onFeedbackSubmit,
98
- defaultLocation = "",
95
+ hierarchyContext,
96
+ currentUser,
97
+ services = OPLYTICS_SERVICES,
98
+ defaultServiceSlug,
99
+ enableScreenshot = true,
100
+ reportsComingSoon = true,
99
101
  className
100
102
  }) {
101
103
  const reportOptions = ALL_REPORT_OPTIONS.filter(
@@ -104,91 +106,82 @@ function ReportingToolbar({
104
106
  const feedbackOptions = ALL_FEEDBACK_OPTIONS.filter(
105
107
  (opt) => feedbackCategories.includes(opt.category)
106
108
  );
107
- const [reportDialogOpen, setReportDialogOpen] = useState(false);
108
- const [selectedReportCategory, setSelectedReportCategory] = useState("safety");
109
- const [reportSeverity, setReportSeverity] = useState("medium");
110
- const [reportLocation, setReportLocation] = useState(defaultLocation);
111
- const [reportDescription, setReportDescription] = useState("");
112
- const [reportImmediateAction, setReportImmediateAction] = useState("");
113
- const [feedbackDialogOpen, setFeedbackDialogOpen] = useState(false);
114
- const [selectedFeedbackCategory, setSelectedFeedbackCategory] = useState("problem");
115
- const [feedbackSubject, setFeedbackSubject] = useState("");
116
- const [feedbackDescription, setFeedbackDescription] = useState("");
109
+ const firstServiceSlug = defaultServiceSlug ?? services[0]?.slug ?? "portal";
110
+ const [feedbackOpen, setFeedbackOpen] = useState(false);
111
+ const [category, setCategory] = useState("problem");
112
+ const [serviceSlug, setServiceSlug] = useState(firstServiceSlug);
113
+ const [isBlocking, setIsBlocking] = useState(false);
114
+ const [summary, setSummary] = useState("");
115
+ const [details, setDetails] = useState("");
116
+ const [screenshot, setScreenshot] = useState(null);
117
+ const [screenshotName, setScreenshotName] = useState(null);
117
118
  const [submitting, setSubmitting] = useState(false);
118
- const openReportDialog = (category) => {
119
- setSelectedReportCategory(category);
120
- setReportSeverity(category === "stop-work" ? "critical" : "medium");
121
- setReportLocation(defaultLocation);
122
- setReportDescription("");
123
- setReportImmediateAction("");
124
- setReportDialogOpen(true);
125
- };
126
- const openFeedbackDialog = (category) => {
127
- setSelectedFeedbackCategory(category);
128
- setFeedbackSubject("");
129
- setFeedbackDescription("");
130
- setFeedbackDialogOpen(true);
119
+ const fileInputRef = useRef(null);
120
+ const openFeedback = (c) => {
121
+ setCategory(c);
122
+ setServiceSlug(firstServiceSlug);
123
+ setIsBlocking(false);
124
+ setSummary("");
125
+ setDetails("");
126
+ setScreenshot(null);
127
+ setScreenshotName(null);
128
+ setFeedbackOpen(true);
131
129
  };
132
- const handleReportSubmit = async () => {
133
- if (!reportDescription.trim()) {
134
- toast.error("Please provide a description");
130
+ const handlePickFile = async (e) => {
131
+ const file = e.target.files?.[0];
132
+ e.target.value = "";
133
+ if (!file) return;
134
+ const check = isAcceptableScreenshot(file);
135
+ if (!check.ok) {
136
+ toast.error(check.reason);
135
137
  return;
136
138
  }
137
- setSubmitting(true);
138
139
  try {
139
- const report = {
140
- category: selectedReportCategory,
141
- description: reportDescription,
142
- location: reportLocation || void 0,
143
- severity: reportSeverity,
144
- immediateAction: reportImmediateAction || void 0,
145
- moduleName,
146
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
147
- };
148
- await onReportSubmit(report);
149
- toast.success("Report submitted successfully", {
150
- description: selectedReportCategory === "stop-work" ? "Stop Work authority raised \u2014 management has been notified" : "Your report has been logged and assigned for review"
151
- });
152
- setReportDialogOpen(false);
140
+ const shot = await readFileAsScreenshot(file);
141
+ setScreenshot(shot);
142
+ setScreenshotName(file.name);
153
143
  } catch (err) {
154
- console.error("[ReportingToolbar] report submit failed:", err);
155
- toast.error("Failed to submit report. Please try again.");
156
- } finally {
157
- setSubmitting(false);
144
+ console.error("[ReportingToolbar] screenshot read failed:", err);
145
+ toast.error("Could not read that image.");
158
146
  }
159
147
  };
160
- const handleFeedbackSubmit = async () => {
161
- if (!feedbackDescription.trim()) {
162
- toast.error("Please provide a description");
148
+ const submitFeedback = async () => {
149
+ if (!details.trim()) {
150
+ toast.error("Please describe the problem or idea.");
163
151
  return;
164
152
  }
165
153
  setSubmitting(true);
166
154
  try {
167
155
  const feedback = {
168
- category: selectedFeedbackCategory,
169
- subject: feedbackSubject || void 0,
170
- description: feedbackDescription,
156
+ category,
157
+ serviceSlug,
158
+ summary: summary.trim() || void 0,
159
+ details: details.trim(),
160
+ isBlocking,
171
161
  moduleName,
172
162
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
173
- pageContext: typeof window !== "undefined" ? window.location.pathname : void 0
163
+ pageContext: typeof window !== "undefined" ? window.location.pathname : void 0,
164
+ submittedBy: currentUser,
165
+ screenshot: screenshot ?? void 0
174
166
  };
175
167
  await onFeedbackSubmit(feedback);
176
168
  toast.success(
177
- selectedFeedbackCategory === "problem" ? "Problem reported \u2014 Oplytics team will review" : "Suggestion submitted \u2014 thank you for your feedback"
169
+ category === "problem" ? "Thanks \u2014 we've logged it. You'll get a confirmation email." : "Thanks for the suggestion \u2014 you'll get a confirmation email."
178
170
  );
179
- setFeedbackDialogOpen(false);
171
+ setFeedbackOpen(false);
180
172
  } catch (err) {
181
173
  console.error("[ReportingToolbar] feedback submit failed:", err);
182
- toast.error("Failed to submit feedback. Please try again.");
174
+ toast.error("Couldn't submit that. Please try again.");
183
175
  } finally {
184
176
  setSubmitting(false);
185
177
  }
186
178
  };
187
- const reportConfig = ALL_REPORT_OPTIONS.find(
188
- (r) => r.category === selectedReportCategory
189
- );
179
+ const showReportGroup = showIncidentReporting && reportOptions.length > 0;
180
+ const showFeedbackGroup = showFeedback && feedbackOptions.length > 0;
181
+ if (!showReportGroup && !showFeedbackGroup) return null;
182
+ void hierarchyContext;
190
183
  return /* @__PURE__ */ jsxs(Fragment, { children: [
191
- /* @__PURE__ */ jsx("div", { className: cn("flex items-center gap-2", className), children: showIncidentReporting && reportOptions.length > 0 || showFeedback && feedbackOptions.length > 0 ? /* @__PURE__ */ jsxs(DropdownMenu, { children: [
184
+ /* @__PURE__ */ jsx("div", { className: cn("flex items-center gap-2", className), children: /* @__PURE__ */ jsxs(DropdownMenu, { children: [
192
185
  /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
193
186
  Button,
194
187
  {
@@ -202,13 +195,28 @@ function ReportingToolbar({
202
195
  ]
203
196
  }
204
197
  ) }),
205
- /* @__PURE__ */ jsxs(DropdownMenuContent, { align: "end", className: "w-56", children: [
206
- showIncidentReporting && reportOptions.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
207
- /* @__PURE__ */ jsx(DropdownMenuLabel, { className: "text-[10px] uppercase tracking-wide text-muted-foreground", children: "Report" }),
198
+ /* @__PURE__ */ jsxs(DropdownMenuContent, { align: "end", className: "w-60", children: [
199
+ showReportGroup && /* @__PURE__ */ jsxs(Fragment, { children: [
200
+ /* @__PURE__ */ jsxs(DropdownMenuLabel, { className: "flex items-center gap-2 text-[10px] uppercase tracking-wide text-muted-foreground", children: [
201
+ "Report",
202
+ reportsComingSoon && /* @__PURE__ */ jsx(
203
+ Badge,
204
+ {
205
+ variant: "outline",
206
+ className: "border-[#8C34E9]/40 text-[9px] font-normal text-[#C084FC]",
207
+ children: "Coming soon"
208
+ }
209
+ )
210
+ ] }),
208
211
  reportOptions.map((opt) => /* @__PURE__ */ jsxs(
209
212
  DropdownMenuItem,
210
213
  {
211
- onClick: () => openReportDialog(opt.category),
214
+ disabled: reportsComingSoon,
215
+ onClick: () => {
216
+ if (!reportsComingSoon) {
217
+ void onReportSubmit;
218
+ }
219
+ },
212
220
  className: "gap-3 py-2.5",
213
221
  children: [
214
222
  /* @__PURE__ */ jsx(
@@ -227,13 +235,13 @@ function ReportingToolbar({
227
235
  opt.category
228
236
  ))
229
237
  ] }),
230
- showIncidentReporting && reportOptions.length > 0 && showFeedback && feedbackOptions.length > 0 && /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
231
- showFeedback && feedbackOptions.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
238
+ showReportGroup && showFeedbackGroup && /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
239
+ showFeedbackGroup && /* @__PURE__ */ jsxs(Fragment, { children: [
232
240
  /* @__PURE__ */ jsx(DropdownMenuLabel, { className: "text-[10px] uppercase tracking-wide text-muted-foreground", children: "Feedback" }),
233
241
  feedbackOptions.map((opt) => /* @__PURE__ */ jsxs(
234
242
  DropdownMenuItem,
235
243
  {
236
- onClick: () => openFeedbackDialog(opt.category),
244
+ onClick: () => openFeedback(opt.category),
237
245
  className: "gap-3 py-2.5",
238
246
  children: [
239
247
  /* @__PURE__ */ jsx(
@@ -253,176 +261,117 @@ function ReportingToolbar({
253
261
  ))
254
262
  ] })
255
263
  ] })
256
- ] }) : null }),
257
- /* @__PURE__ */ jsx(Dialog, { open: reportDialogOpen, onOpenChange: setReportDialogOpen, children: /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-[500px]", children: [
264
+ ] }) }),
265
+ /* @__PURE__ */ jsx(Dialog, { open: feedbackOpen, onOpenChange: setFeedbackOpen, children: /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-[520px]", children: [
258
266
  /* @__PURE__ */ jsxs(DialogHeader, { children: [
259
267
  /* @__PURE__ */ jsxs(DialogTitle, { className: "flex items-center gap-2", children: [
260
- reportConfig && /* @__PURE__ */ jsx(
261
- reportConfig.icon,
262
- {
263
- className: "h-5 w-5",
264
- style: { color: reportConfig.color }
265
- }
266
- ),
267
- reportConfig?.label || "Report",
268
- selectedReportCategory === "stop-work" && /* @__PURE__ */ jsx(Badge, { className: "bg-[#F59E0B] text-white text-[9px] border-0", children: "URGENT" })
268
+ category === "problem" ? /* @__PURE__ */ jsx(Bug, { className: "h-5 w-5 text-[#EF4444]" }) : /* @__PURE__ */ jsx(Lightbulb, { className: "h-5 w-5 text-[#8C34E9]" }),
269
+ category === "problem" ? "Report a Problem" : "Share a Suggestion"
269
270
  ] }),
270
- /* @__PURE__ */ jsxs(DialogDescription, { children: [
271
- reportConfig?.description,
272
- ". All reports are logged and tracked."
273
- ] })
271
+ /* @__PURE__ */ jsx(DialogDescription, { children: category === "problem" ? "Tell us what went wrong. We triage within 24 hours." : "We review every suggestion and schedule improvements against the roadmap." })
274
272
  ] }),
275
273
  /* @__PURE__ */ jsxs("div", { className: "space-y-4 py-2", children: [
276
274
  /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
277
- /* @__PURE__ */ jsx(Label, { className: "text-xs font-medium", children: "Severity" }),
278
- /* @__PURE__ */ jsxs(
279
- Select,
280
- {
281
- value: reportSeverity,
282
- onValueChange: (v) => setReportSeverity(
283
- v
284
- ),
285
- children: [
286
- /* @__PURE__ */ jsx(SelectTrigger, { className: "h-9", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
287
- /* @__PURE__ */ jsx(SelectContent, { children: SEVERITY_OPTIONS.map((s) => /* @__PURE__ */ jsx(SelectItem, { value: s.value, children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
288
- /* @__PURE__ */ jsx(
289
- "div",
290
- {
291
- className: "h-2 w-2 rounded-full",
292
- style: { backgroundColor: s.color }
293
- }
294
- ),
295
- s.label
296
- ] }) }, s.value)) })
297
- ]
298
- }
299
- )
275
+ /* @__PURE__ */ jsx(Label, { className: "text-xs font-medium", children: "Which service?" }),
276
+ /* @__PURE__ */ jsxs(Select, { value: serviceSlug, onValueChange: setServiceSlug, children: [
277
+ /* @__PURE__ */ jsx(SelectTrigger, { className: "h-9", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
278
+ /* @__PURE__ */ jsx(SelectContent, { children: services.map((s) => /* @__PURE__ */ jsx(SelectItem, { value: s.slug, children: s.label }, s.slug)) })
279
+ ] })
280
+ ] }),
281
+ category === "problem" && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between rounded-md border border-[#1E2738] px-3 py-2", children: [
282
+ /* @__PURE__ */ jsxs("div", { children: [
283
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: "Is this blocking you?" }),
284
+ /* @__PURE__ */ jsx("p", { className: "text-[10px] text-muted-foreground", children: "Blocking issues are treated as high priority." })
285
+ ] }),
286
+ /* @__PURE__ */ jsx(Switch, { checked: isBlocking, onCheckedChange: setIsBlocking })
300
287
  ] }),
301
288
  /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
302
- /* @__PURE__ */ jsx(Label, { className: "text-xs font-medium", children: "Location / Area" }),
289
+ /* @__PURE__ */ jsx(Label, { className: "text-xs font-medium", children: "Summary" }),
303
290
  /* @__PURE__ */ jsx(
304
291
  Input,
305
292
  {
306
- value: reportLocation,
307
- onChange: (e) => setReportLocation(e.target.value),
308
- placeholder: "e.g., Production Line 3, Warehouse B",
293
+ value: summary,
294
+ onChange: (e) => setSummary(e.target.value),
295
+ placeholder: category === "problem" ? "Brief summary of the issue" : "Brief summary of your idea",
309
296
  className: "h-9"
310
297
  }
311
298
  )
312
299
  ] }),
313
300
  /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
314
- /* @__PURE__ */ jsx(Label, { className: "text-xs font-medium", children: "Description *" }),
301
+ /* @__PURE__ */ jsx(Label, { className: "text-xs font-medium", children: "Details *" }),
315
302
  /* @__PURE__ */ jsx(
316
303
  Textarea,
317
304
  {
318
- value: reportDescription,
319
- onChange: (e) => setReportDescription(e.target.value),
320
- placeholder: "Describe what happened or what you observed...",
321
- rows: 4
305
+ value: details,
306
+ onChange: (e) => setDetails(e.target.value),
307
+ placeholder: category === "problem" ? "What were you trying to do? What happened instead?" : "Describe your suggestion and how it would help\u2026",
308
+ rows: 5
322
309
  }
323
310
  )
324
311
  ] }),
325
- /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
326
- /* @__PURE__ */ jsx(Label, { className: "text-xs font-medium", children: "Immediate Action Taken" }),
312
+ enableScreenshot && /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
313
+ /* @__PURE__ */ jsx(Label, { className: "text-xs font-medium", children: "Screenshot (optional)" }),
327
314
  /* @__PURE__ */ jsx(
328
- Textarea,
315
+ "input",
329
316
  {
330
- value: reportImmediateAction,
331
- onChange: (e) => setReportImmediateAction(e.target.value),
332
- placeholder: "Describe any immediate action taken to make the area safe...",
333
- rows: 2
317
+ ref: fileInputRef,
318
+ type: "file",
319
+ accept: "image/*",
320
+ className: "hidden",
321
+ onChange: handlePickFile
334
322
  }
335
- )
336
- ] })
337
- ] }),
338
- /* @__PURE__ */ jsxs(DialogFooter, { children: [
339
- /* @__PURE__ */ jsx(
340
- Button,
341
- {
342
- variant: "outline",
343
- onClick: () => setReportDialogOpen(false),
344
- children: "Cancel"
345
- }
346
- ),
347
- /* @__PURE__ */ jsxs(
348
- Button,
349
- {
350
- onClick: handleReportSubmit,
351
- disabled: submitting || !reportDescription.trim(),
352
- className: "gap-2",
353
- style: {
354
- backgroundColor: reportConfig?.color,
355
- color: "white"
356
- },
357
- children: [
358
- /* @__PURE__ */ jsx(Send, { className: "h-3.5 w-3.5" }),
359
- submitting ? "Submitting..." : "Submit Report"
360
- ]
361
- }
362
- )
363
- ] })
364
- ] }) }),
365
- /* @__PURE__ */ jsx(Dialog, { open: feedbackDialogOpen, onOpenChange: setFeedbackDialogOpen, children: /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-[500px]", children: [
366
- /* @__PURE__ */ jsxs(DialogHeader, { children: [
367
- /* @__PURE__ */ jsxs(DialogTitle, { className: "flex items-center gap-2", children: [
368
- selectedFeedbackCategory === "problem" ? /* @__PURE__ */ jsx(Bug, { className: "h-5 w-5 text-[#EF4444]" }) : /* @__PURE__ */ jsx(Lightbulb, { className: "h-5 w-5 text-[#8C34E9]" }),
369
- selectedFeedbackCategory === "problem" ? "Report a Problem" : "Submit a Suggestion"
370
- ] }),
371
- /* @__PURE__ */ jsx(DialogDescription, { children: selectedFeedbackCategory === "problem" ? "Help us fix issues \u2014 describe what went wrong." : "We'd love to hear your ideas for improving Oplytics." })
372
- ] }),
373
- /* @__PURE__ */ jsxs("div", { className: "space-y-4 py-2", children: [
374
- /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
375
- /* @__PURE__ */ jsx(Label, { className: "text-xs font-medium", children: "Subject" }),
376
- /* @__PURE__ */ jsx(
377
- Input,
378
- {
379
- value: feedbackSubject,
380
- onChange: (e) => setFeedbackSubject(e.target.value),
381
- placeholder: selectedFeedbackCategory === "problem" ? "Brief summary of the issue" : "Brief summary of your idea",
382
- className: "h-9"
383
- }
384
- )
385
- ] }),
386
- /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
387
- /* @__PURE__ */ jsx(Label, { className: "text-xs font-medium", children: "Description *" }),
388
- /* @__PURE__ */ jsx(
389
- Textarea,
323
+ ),
324
+ screenshot ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 rounded-md border border-[#1E2738] px-3 py-2", children: [
325
+ /* @__PURE__ */ jsx(ImagePlus, { className: "h-4 w-4 text-[#8C34E9]" }),
326
+ /* @__PURE__ */ jsx("span", { className: "flex-1 truncate text-xs text-[#E2E8F0]", children: screenshotName ?? "screenshot" }),
327
+ /* @__PURE__ */ jsx(
328
+ "button",
329
+ {
330
+ type: "button",
331
+ onClick: () => {
332
+ setScreenshot(null);
333
+ setScreenshotName(null);
334
+ },
335
+ className: "text-muted-foreground hover:text-[#E2E8F0]",
336
+ "aria-label": "Remove screenshot",
337
+ children: /* @__PURE__ */ jsx(X, { className: "h-3.5 w-3.5" })
338
+ }
339
+ )
340
+ ] }) : /* @__PURE__ */ jsxs(
341
+ Button,
390
342
  {
391
- value: feedbackDescription,
392
- onChange: (e) => setFeedbackDescription(e.target.value),
393
- placeholder: selectedFeedbackCategory === "problem" ? "What were you trying to do? What happened instead?" : "Describe your suggestion and how it would help...",
394
- rows: 5
343
+ type: "button",
344
+ variant: "outline",
345
+ size: "sm",
346
+ className: "h-9 gap-1.5",
347
+ onClick: () => fileInputRef.current?.click(),
348
+ children: [
349
+ /* @__PURE__ */ jsx(ImagePlus, { className: "h-3.5 w-3.5" }),
350
+ "Attach an image"
351
+ ]
395
352
  }
396
353
  )
397
354
  ] }),
398
- /* @__PURE__ */ jsxs("div", { className: "text-[10px] text-muted-foreground", children: [
399
- "Module: ",
355
+ /* @__PURE__ */ jsxs("p", { className: "text-[10px] text-muted-foreground", children: [
400
356
  moduleName,
401
- " \u2014 Feedback is reviewed by the Oplytics team"
357
+ " \xB7 you'll get a confirmation email once this is logged."
402
358
  ] })
403
359
  ] }),
404
360
  /* @__PURE__ */ jsxs(DialogFooter, { children: [
405
- /* @__PURE__ */ jsx(
406
- Button,
407
- {
408
- variant: "outline",
409
- onClick: () => setFeedbackDialogOpen(false),
410
- children: "Cancel"
411
- }
412
- ),
361
+ /* @__PURE__ */ jsx(Button, { variant: "outline", onClick: () => setFeedbackOpen(false), children: "Cancel" }),
413
362
  /* @__PURE__ */ jsxs(
414
363
  Button,
415
364
  {
416
- onClick: handleFeedbackSubmit,
417
- disabled: submitting || !feedbackDescription.trim(),
365
+ onClick: submitFeedback,
366
+ disabled: submitting || !details.trim(),
418
367
  className: "gap-2",
419
368
  style: {
420
- backgroundColor: selectedFeedbackCategory === "problem" ? "#EF4444" : "#8C34E9",
369
+ backgroundColor: category === "problem" ? "#EF4444" : "#8C34E9",
421
370
  color: "white"
422
371
  },
423
372
  children: [
424
373
  /* @__PURE__ */ jsx(Send, { className: "h-3.5 w-3.5" }),
425
- submitting ? "Submitting..." : "Submit"
374
+ submitting ? "Submitting\u2026" : "Submit"
426
375
  ]
427
376
  }
428
377
  )
@@ -432,6 +381,14 @@ function ReportingToolbar({
432
381
  }
433
382
 
434
383
  // src/reporting/index.ts
384
+ var OPLYTICS_SERVICES = [
385
+ { slug: "sqdcp", label: "SQDCP" },
386
+ { slug: "oee-manager", label: "OEE Manager" },
387
+ { slug: "action-manager", label: "Action Manager" },
388
+ { slug: "policy-deployment", label: "Policy Deployment" },
389
+ { slug: "connect", label: "Connect" },
390
+ { slug: "portal", label: "Portal" }
391
+ ];
435
392
  var REPORT_BUTTONS = [
436
393
  {
437
394
  category: "safety",
@@ -481,10 +438,40 @@ function createReportingConfig(moduleName, handlers) {
481
438
  ...handlers
482
439
  };
483
440
  }
441
+ var FEEDBACK_SCREENSHOT_MAX_BYTES = 300 * 1024;
442
+ function isAcceptableScreenshot(file, maxBytes = FEEDBACK_SCREENSHOT_MAX_BYTES) {
443
+ if (!file.type.startsWith("image/")) {
444
+ return { ok: false, reason: "Please choose an image file." };
445
+ }
446
+ if (file.size > maxBytes) {
447
+ return {
448
+ ok: false,
449
+ reason: `Image is too large (max ${Math.round(maxBytes / 1024)} KB).`
450
+ };
451
+ }
452
+ return { ok: true };
453
+ }
454
+ function readFileAsScreenshot(file) {
455
+ return new Promise((resolve, reject) => {
456
+ const reader = new FileReader();
457
+ reader.onerror = () => reject(new Error("Could not read the file."));
458
+ reader.onload = () => {
459
+ const result = String(reader.result || "");
460
+ const comma = result.indexOf(",");
461
+ const dataBase64 = comma >= 0 ? result.slice(comma + 1) : result;
462
+ resolve({ dataBase64, mime: file.type || "image/png" });
463
+ };
464
+ reader.readAsDataURL(file);
465
+ });
466
+ }
484
467
  export {
485
468
  FEEDBACK_BUTTONS,
469
+ FEEDBACK_SCREENSHOT_MAX_BYTES,
470
+ OPLYTICS_SERVICES,
486
471
  REPORT_BUTTONS,
487
472
  ReportingToolbar,
488
- createReportingConfig
473
+ createReportingConfig,
474
+ isAcceptableScreenshot,
475
+ readFileAsScreenshot
489
476
  };
490
477
  //# sourceMappingURL=index.js.map