@pablo2410/shared-ui 0.18.1 → 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.
Files changed (44) hide show
  1. package/README.md +18 -15
  2. package/dist/{HealthFooter-C5K9s38i.d.ts → HealthFooter-BoOozbWG.d.ts} +4 -4
  3. package/dist/{chunk-G7XJLTNY.js → chunk-5RY3AJKI.js} +8 -9
  4. package/dist/{chunk-G7XJLTNY.js.map → chunk-5RY3AJKI.js.map} +1 -1
  5. package/dist/{chunk-3G43UG63.js → chunk-A6UTXAPS.js} +11 -3
  6. package/dist/chunk-A6UTXAPS.js.map +1 -0
  7. package/dist/{chunk-R4NOLCSI.js → chunk-BKGUIIN2.js} +1 -1
  8. package/dist/{chunk-R4NOLCSI.js.map → chunk-BKGUIIN2.js.map} +1 -1
  9. package/dist/{chunk-IEBZGU6X.js → chunk-DMUQ5WNO.js} +2 -4
  10. package/dist/chunk-DMUQ5WNO.js.map +1 -0
  11. package/dist/{chunk-PLCWXXUE.js → chunk-K6FHU4HN.js} +9 -15
  12. package/dist/chunk-K6FHU4HN.js.map +1 -0
  13. package/dist/{chunk-KXUNOSMZ.js → chunk-NLQ45OYS.js} +193 -91
  14. package/dist/chunk-NLQ45OYS.js.map +1 -0
  15. package/dist/{chunk-WTR6SNOE.js → chunk-VJEXXX2P.js} +158 -40
  16. package/dist/chunk-VJEXXX2P.js.map +1 -0
  17. package/dist/components/index.d.ts +7 -8
  18. package/dist/components/index.js +85 -63
  19. package/dist/components/index.js.map +1 -1
  20. package/dist/hierarchy/core.js +1 -1
  21. package/dist/hierarchy/index.d.ts +3 -3
  22. package/dist/hierarchy/index.js +5 -5
  23. package/dist/insights/index.d.ts +8 -8
  24. package/dist/insights/index.js +150 -61
  25. package/dist/insights/index.js.map +1 -1
  26. package/dist/layout/index.d.ts +8 -8
  27. package/dist/layout/index.js +6 -6
  28. package/dist/primitives/index.d.ts +288 -289
  29. package/dist/primitives/index.js +2 -2
  30. package/dist/rbac/index.js.map +1 -1
  31. package/dist/reporting/index.d.ts +101 -43
  32. package/dist/reporting/index.js +208 -187
  33. package/dist/reporting/index.js.map +1 -1
  34. package/dist/services/index.js +2 -2
  35. package/dist/shell/index.d.ts +2 -3
  36. package/dist/shell/index.js +7 -7
  37. package/dist/shell/oplytics-shell.css +2 -2
  38. package/dist/theme/oplytics-theme.css +4 -4
  39. package/package.json +48 -46
  40. package/dist/chunk-3G43UG63.js.map +0 -1
  41. package/dist/chunk-IEBZGU6X.js.map +0 -1
  42. package/dist/chunk-KXUNOSMZ.js.map +0 -1
  43. package/dist/chunk-PLCWXXUE.js.map +0 -1
  44. package/dist/chunk-WTR6SNOE.js.map +0 -1
@@ -12,8 +12,9 @@ import {
12
12
  SelectItem,
13
13
  SelectTrigger,
14
14
  SelectValue,
15
+ Switch,
15
16
  Textarea
16
- } from "../chunk-PLCWXXUE.js";
17
+ } from "../chunk-K6FHU4HN.js";
17
18
  import {
18
19
  cn
19
20
  } from "../chunk-JT3XLKKD.js";
@@ -26,10 +27,10 @@ import {
26
27
  DialogHeader,
27
28
  DialogTitle,
28
29
  Label
29
- } from "../chunk-R4NOLCSI.js";
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,16 +195,37 @@ 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
- /* @__PURE__ */ jsx(opt.icon, { className: "h-4 w-4", style: { color: opt.color } }),
222
+ /* @__PURE__ */ jsx(
223
+ opt.icon,
224
+ {
225
+ className: "h-4 w-4",
226
+ style: { color: opt.color }
227
+ }
228
+ ),
215
229
  /* @__PURE__ */ jsxs("div", { children: [
216
230
  /* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: opt.label }),
217
231
  /* @__PURE__ */ jsx("p", { className: "text-[10px] text-muted-foreground", children: opt.description })
@@ -221,16 +235,22 @@ function ReportingToolbar({
221
235
  opt.category
222
236
  ))
223
237
  ] }),
224
- showIncidentReporting && reportOptions.length > 0 && showFeedback && feedbackOptions.length > 0 && /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
225
- showFeedback && feedbackOptions.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
238
+ showReportGroup && showFeedbackGroup && /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
239
+ showFeedbackGroup && /* @__PURE__ */ jsxs(Fragment, { children: [
226
240
  /* @__PURE__ */ jsx(DropdownMenuLabel, { className: "text-[10px] uppercase tracking-wide text-muted-foreground", children: "Feedback" }),
227
241
  feedbackOptions.map((opt) => /* @__PURE__ */ jsxs(
228
242
  DropdownMenuItem,
229
243
  {
230
- onClick: () => openFeedbackDialog(opt.category),
244
+ onClick: () => openFeedback(opt.category),
231
245
  className: "gap-3 py-2.5",
232
246
  children: [
233
- /* @__PURE__ */ jsx(opt.icon, { className: "h-4 w-4", style: { color: opt.color } }),
247
+ /* @__PURE__ */ jsx(
248
+ opt.icon,
249
+ {
250
+ className: "h-4 w-4",
251
+ style: { color: opt.color }
252
+ }
253
+ ),
234
254
  /* @__PURE__ */ jsxs("div", { children: [
235
255
  /* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: opt.label }),
236
256
  /* @__PURE__ */ jsx("p", { className: "text-[10px] text-muted-foreground", children: opt.description })
@@ -241,154 +261,117 @@ function ReportingToolbar({
241
261
  ))
242
262
  ] })
243
263
  ] })
244
- ] }) : null }),
245
- /* @__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: [
246
266
  /* @__PURE__ */ jsxs(DialogHeader, { children: [
247
267
  /* @__PURE__ */ jsxs(DialogTitle, { className: "flex items-center gap-2", children: [
248
- reportConfig && /* @__PURE__ */ jsx(reportConfig.icon, { className: "h-5 w-5", style: { color: reportConfig.color } }),
249
- reportConfig?.label || "Report",
250
- 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"
251
270
  ] }),
252
- /* @__PURE__ */ jsxs(DialogDescription, { children: [
253
- reportConfig?.description,
254
- ". All reports are logged and tracked."
255
- ] })
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." })
256
272
  ] }),
257
273
  /* @__PURE__ */ jsxs("div", { className: "space-y-4 py-2", children: [
258
274
  /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
259
- /* @__PURE__ */ jsx(Label, { className: "text-xs font-medium", children: "Severity" }),
260
- /* @__PURE__ */ jsxs(
261
- Select,
262
- {
263
- value: reportSeverity,
264
- onValueChange: (v) => setReportSeverity(v),
265
- children: [
266
- /* @__PURE__ */ jsx(SelectTrigger, { className: "h-9", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
267
- /* @__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: [
268
- /* @__PURE__ */ jsx(
269
- "div",
270
- {
271
- className: "h-2 w-2 rounded-full",
272
- style: { backgroundColor: s.color }
273
- }
274
- ),
275
- s.label
276
- ] }) }, s.value)) })
277
- ]
278
- }
279
- )
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 })
280
287
  ] }),
281
288
  /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
282
- /* @__PURE__ */ jsx(Label, { className: "text-xs font-medium", children: "Location / Area" }),
289
+ /* @__PURE__ */ jsx(Label, { className: "text-xs font-medium", children: "Summary" }),
283
290
  /* @__PURE__ */ jsx(
284
291
  Input,
285
292
  {
286
- value: reportLocation,
287
- onChange: (e) => setReportLocation(e.target.value),
288
- 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",
289
296
  className: "h-9"
290
297
  }
291
298
  )
292
299
  ] }),
293
300
  /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
294
- /* @__PURE__ */ jsx(Label, { className: "text-xs font-medium", children: "Description *" }),
301
+ /* @__PURE__ */ jsx(Label, { className: "text-xs font-medium", children: "Details *" }),
295
302
  /* @__PURE__ */ jsx(
296
303
  Textarea,
297
304
  {
298
- value: reportDescription,
299
- onChange: (e) => setReportDescription(e.target.value),
300
- placeholder: "Describe what happened or what you observed...",
301
- 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
302
309
  }
303
310
  )
304
311
  ] }),
305
- /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
306
- /* @__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)" }),
307
314
  /* @__PURE__ */ jsx(
308
- Textarea,
315
+ "input",
309
316
  {
310
- value: reportImmediateAction,
311
- onChange: (e) => setReportImmediateAction(e.target.value),
312
- placeholder: "Describe any immediate action taken to make the area safe...",
313
- rows: 2
317
+ ref: fileInputRef,
318
+ type: "file",
319
+ accept: "image/*",
320
+ className: "hidden",
321
+ onChange: handlePickFile
314
322
  }
315
- )
316
- ] })
317
- ] }),
318
- /* @__PURE__ */ jsxs(DialogFooter, { children: [
319
- /* @__PURE__ */ jsx(Button, { variant: "outline", onClick: () => setReportDialogOpen(false), children: "Cancel" }),
320
- /* @__PURE__ */ jsxs(
321
- Button,
322
- {
323
- onClick: handleReportSubmit,
324
- disabled: submitting || !reportDescription.trim(),
325
- className: "gap-2",
326
- style: {
327
- backgroundColor: reportConfig?.color,
328
- color: "white"
329
- },
330
- children: [
331
- /* @__PURE__ */ jsx(Send, { className: "h-3.5 w-3.5" }),
332
- submitting ? "Submitting..." : "Submit Report"
333
- ]
334
- }
335
- )
336
- ] })
337
- ] }) }),
338
- /* @__PURE__ */ jsx(Dialog, { open: feedbackDialogOpen, onOpenChange: setFeedbackDialogOpen, children: /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-[500px]", children: [
339
- /* @__PURE__ */ jsxs(DialogHeader, { children: [
340
- /* @__PURE__ */ jsxs(DialogTitle, { className: "flex items-center gap-2", children: [
341
- selectedFeedbackCategory === "problem" ? /* @__PURE__ */ jsx(Bug, { className: "h-5 w-5 text-[#EF4444]" }) : /* @__PURE__ */ jsx(Lightbulb, { className: "h-5 w-5 text-[#8C34E9]" }),
342
- selectedFeedbackCategory === "problem" ? "Report a Problem" : "Submit a Suggestion"
343
- ] }),
344
- /* @__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." })
345
- ] }),
346
- /* @__PURE__ */ jsxs("div", { className: "space-y-4 py-2", children: [
347
- /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
348
- /* @__PURE__ */ jsx(Label, { className: "text-xs font-medium", children: "Subject" }),
349
- /* @__PURE__ */ jsx(
350
- Input,
351
- {
352
- value: feedbackSubject,
353
- onChange: (e) => setFeedbackSubject(e.target.value),
354
- placeholder: selectedFeedbackCategory === "problem" ? "Brief summary of the issue" : "Brief summary of your idea",
355
- className: "h-9"
356
- }
357
- )
358
- ] }),
359
- /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
360
- /* @__PURE__ */ jsx(Label, { className: "text-xs font-medium", children: "Description *" }),
361
- /* @__PURE__ */ jsx(
362
- 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,
363
342
  {
364
- value: feedbackDescription,
365
- onChange: (e) => setFeedbackDescription(e.target.value),
366
- placeholder: selectedFeedbackCategory === "problem" ? "What were you trying to do? What happened instead?" : "Describe your suggestion and how it would help...",
367
- 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
+ ]
368
352
  }
369
353
  )
370
354
  ] }),
371
- /* @__PURE__ */ jsxs("div", { className: "text-[10px] text-muted-foreground", children: [
372
- "Module: ",
355
+ /* @__PURE__ */ jsxs("p", { className: "text-[10px] text-muted-foreground", children: [
373
356
  moduleName,
374
- " \u2014 Feedback is reviewed by the Oplytics team"
357
+ " \xB7 you'll get a confirmation email once this is logged."
375
358
  ] })
376
359
  ] }),
377
360
  /* @__PURE__ */ jsxs(DialogFooter, { children: [
378
- /* @__PURE__ */ jsx(Button, { variant: "outline", onClick: () => setFeedbackDialogOpen(false), children: "Cancel" }),
361
+ /* @__PURE__ */ jsx(Button, { variant: "outline", onClick: () => setFeedbackOpen(false), children: "Cancel" }),
379
362
  /* @__PURE__ */ jsxs(
380
363
  Button,
381
364
  {
382
- onClick: handleFeedbackSubmit,
383
- disabled: submitting || !feedbackDescription.trim(),
365
+ onClick: submitFeedback,
366
+ disabled: submitting || !details.trim(),
384
367
  className: "gap-2",
385
368
  style: {
386
- backgroundColor: selectedFeedbackCategory === "problem" ? "#EF4444" : "#8C34E9",
369
+ backgroundColor: category === "problem" ? "#EF4444" : "#8C34E9",
387
370
  color: "white"
388
371
  },
389
372
  children: [
390
373
  /* @__PURE__ */ jsx(Send, { className: "h-3.5 w-3.5" }),
391
- submitting ? "Submitting..." : "Submit"
374
+ submitting ? "Submitting\u2026" : "Submit"
392
375
  ]
393
376
  }
394
377
  )
@@ -398,6 +381,14 @@ function ReportingToolbar({
398
381
  }
399
382
 
400
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
+ ];
401
392
  var REPORT_BUTTONS = [
402
393
  {
403
394
  category: "safety",
@@ -447,10 +438,40 @@ function createReportingConfig(moduleName, handlers) {
447
438
  ...handlers
448
439
  };
449
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
+ }
450
467
  export {
451
468
  FEEDBACK_BUTTONS,
469
+ FEEDBACK_SCREENSHOT_MAX_BYTES,
470
+ OPLYTICS_SERVICES,
452
471
  REPORT_BUTTONS,
453
472
  ReportingToolbar,
454
- createReportingConfig
473
+ createReportingConfig,
474
+ isAcceptableScreenshot,
475
+ readFileAsScreenshot
455
476
  };
456
477
  //# sourceMappingURL=index.js.map