@mhosaic/feedback 0.7.2 → 0.8.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.
- package/dist/{chunk-2TLTE2VZ.mjs → chunk-XHK6VAU2.mjs} +871 -224
- package/dist/chunk-XHK6VAU2.mjs.map +1 -0
- package/dist/embed.min.js +257 -5
- package/dist/embed.min.js.map +1 -1
- package/dist/error-tracking.d.ts +1 -1
- package/dist/{index-CasX-2Bm.d.ts → index-DK9sbiTf.d.ts} +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.mjs +1 -1
- package/dist/react.d.ts +2 -2
- package/dist/react.mjs +1 -1
- package/dist/replay.d.ts +1 -1
- package/dist/{types-CNwNoYUE.d.ts → types-ZCdu32FU.d.ts} +7 -0
- package/dist/webvitals.d.ts +1 -1
- package/package.json +1 -1
- package/dist/chunk-2TLTE2VZ.mjs.map +0 -1
|
@@ -30,6 +30,9 @@ function createApiClient(options) {
|
|
|
30
30
|
if (payload.user?.id) {
|
|
31
31
|
form.append("user", JSON.stringify(payload.user));
|
|
32
32
|
}
|
|
33
|
+
if (payload.widget_version) {
|
|
34
|
+
form.append("widget_version", payload.widget_version);
|
|
35
|
+
}
|
|
33
36
|
const response = await fetcher(`${endpoint}/api/feedback/v1/reports/`, {
|
|
34
37
|
method: "POST",
|
|
35
38
|
headers: { Authorization: `Bearer ${options.apiKey}` },
|
|
@@ -59,6 +62,18 @@ function createApiClient(options) {
|
|
|
59
62
|
}
|
|
60
63
|
return response.json();
|
|
61
64
|
}
|
|
65
|
+
async function listChangelog(externalId) {
|
|
66
|
+
const response = await fetcher(
|
|
67
|
+
`${endpoint}/api/feedback/v1/reports/widget/changelog/`,
|
|
68
|
+
{ method: "GET", headers: widgetHeaders(externalId) }
|
|
69
|
+
);
|
|
70
|
+
if (response.status === 404) return [];
|
|
71
|
+
if (!response.ok) {
|
|
72
|
+
const text = await response.text().catch(() => "");
|
|
73
|
+
throw new Error(`listChangelog failed: ${response.status} ${text}`);
|
|
74
|
+
}
|
|
75
|
+
return response.json();
|
|
76
|
+
}
|
|
62
77
|
async function getReport(reportId, externalId) {
|
|
63
78
|
const response = await fetcher(
|
|
64
79
|
`${endpoint}/api/feedback/v1/reports/widget/${reportId}/`,
|
|
@@ -109,7 +124,7 @@ function createApiClient(options) {
|
|
|
109
124
|
}
|
|
110
125
|
return response.json();
|
|
111
126
|
}
|
|
112
|
-
return { submitReport, listMine, getReport, addComment, closeAsResolved };
|
|
127
|
+
return { submitReport, listMine, listChangelog, getReport, addComment, closeAsResolved };
|
|
113
128
|
}
|
|
114
129
|
|
|
115
130
|
// src/capture/urlSanitizer.ts
|
|
@@ -470,6 +485,12 @@ var DEFAULT_STRINGS = {
|
|
|
470
485
|
"annotator.applying": "Applying\u2026",
|
|
471
486
|
"tab.send": "Send",
|
|
472
487
|
"tab.mine": "My reports",
|
|
488
|
+
"tab.changelog": "This week",
|
|
489
|
+
"changelog.empty.title": "Nothing resolved yet",
|
|
490
|
+
"changelog.empty.body": "Once a report you sent is fixed it will appear here, grouped by week.",
|
|
491
|
+
"changelog.week_of": "Week of {date}",
|
|
492
|
+
"changelog.resolved_one": "{count} resolved",
|
|
493
|
+
"changelog.resolved_many": "{count} resolved",
|
|
473
494
|
"mine.empty.title": "No reports yet",
|
|
474
495
|
"mine.empty.body": "Once you send feedback you can follow the thread here.",
|
|
475
496
|
"mine.refresh": "Refresh",
|
|
@@ -477,6 +498,11 @@ var DEFAULT_STRINGS = {
|
|
|
477
498
|
"mine.error": "Could not load your reports.",
|
|
478
499
|
"mine.replies_one": "1 reply",
|
|
479
500
|
"mine.replies_many": "{count} replies",
|
|
501
|
+
"mine.filter.empty": "No reports match this filter.",
|
|
502
|
+
"kpi.new": "New",
|
|
503
|
+
"kpi.in_progress": "In progress",
|
|
504
|
+
"kpi.awaiting_validation": "Awaiting you",
|
|
505
|
+
"kpi.resolution_rate": "Resolution rate",
|
|
480
506
|
"detail.back": "Back",
|
|
481
507
|
"detail.thread": "Conversation",
|
|
482
508
|
"detail.no_replies": "No replies yet \u2014 we\u2019ll let you know when an operator responds.",
|
|
@@ -495,6 +521,18 @@ var DEFAULT_STRINGS = {
|
|
|
495
521
|
"detail.author.staff": "Operator",
|
|
496
522
|
"detail.author.mcp": "Mhosaic Team",
|
|
497
523
|
"detail.author.system": "System",
|
|
524
|
+
"detail.tech.title": "What we received",
|
|
525
|
+
"detail.tech.errors_one": "error",
|
|
526
|
+
"detail.tech.errors_many": "errors",
|
|
527
|
+
"detail.tech.device": "Device",
|
|
528
|
+
"detail.tech.device.viewport": "Viewport",
|
|
529
|
+
"detail.tech.device.platform": "Platform",
|
|
530
|
+
"detail.tech.device.language": "Language",
|
|
531
|
+
"detail.tech.device.timezone": "Timezone",
|
|
532
|
+
"detail.tech.device.connection": "Connection",
|
|
533
|
+
"detail.tech.errors": "Runtime errors",
|
|
534
|
+
"detail.tech.console": "Console (last 20)",
|
|
535
|
+
"detail.tech.network": "Network (last 15)",
|
|
498
536
|
"status.new": "New",
|
|
499
537
|
"status.in_progress": "In progress",
|
|
500
538
|
"status.awaiting_validation": "Awaiting your validation",
|
|
@@ -549,6 +587,12 @@ var FRENCH_STRINGS = {
|
|
|
549
587
|
"annotator.applying": "Application\u2026",
|
|
550
588
|
"tab.send": "Envoyer",
|
|
551
589
|
"tab.mine": "Mes rapports",
|
|
590
|
+
"tab.changelog": "Cette semaine",
|
|
591
|
+
"changelog.empty.title": "Rien de r\xE9solu pour l\u2019instant",
|
|
592
|
+
"changelog.empty.body": "Quand un rapport que vous avez envoy\xE9 est corrig\xE9, il appara\xEEtra ici, regroup\xE9 par semaine.",
|
|
593
|
+
"changelog.week_of": "Semaine du {date}",
|
|
594
|
+
"changelog.resolved_one": "{count} r\xE9solu",
|
|
595
|
+
"changelog.resolved_many": "{count} r\xE9solus",
|
|
552
596
|
"mine.empty.title": "Aucun rapport",
|
|
553
597
|
"mine.empty.body": "Apr\xE8s votre premier envoi vous pourrez suivre la conversation ici.",
|
|
554
598
|
"mine.refresh": "Actualiser",
|
|
@@ -556,6 +600,11 @@ var FRENCH_STRINGS = {
|
|
|
556
600
|
"mine.error": "Impossible de charger vos rapports.",
|
|
557
601
|
"mine.replies_one": "1 r\xE9ponse",
|
|
558
602
|
"mine.replies_many": "{count} r\xE9ponses",
|
|
603
|
+
"mine.filter.empty": "Aucun rapport ne correspond \xE0 ce filtre.",
|
|
604
|
+
"kpi.new": "Nouveau",
|
|
605
|
+
"kpi.in_progress": "En cours",
|
|
606
|
+
"kpi.awaiting_validation": "\xC0 valider",
|
|
607
|
+
"kpi.resolution_rate": "Taux de r\xE9solution",
|
|
559
608
|
"detail.back": "Retour",
|
|
560
609
|
"detail.thread": "Conversation",
|
|
561
610
|
"detail.no_replies": "Pas encore de r\xE9ponse \u2014 vous serez notifi\xE9 d\xE8s qu\u2019un op\xE9rateur r\xE9pondra.",
|
|
@@ -574,6 +623,18 @@ var FRENCH_STRINGS = {
|
|
|
574
623
|
"detail.author.staff": "Op\xE9rateur",
|
|
575
624
|
"detail.author.mcp": "\xC9quipe Mhosaic",
|
|
576
625
|
"detail.author.system": "Syst\xE8me",
|
|
626
|
+
"detail.tech.title": "Ce que nous avons re\xE7u",
|
|
627
|
+
"detail.tech.errors_one": "erreur",
|
|
628
|
+
"detail.tech.errors_many": "erreurs",
|
|
629
|
+
"detail.tech.device": "Appareil",
|
|
630
|
+
"detail.tech.device.viewport": "Fen\xEAtre",
|
|
631
|
+
"detail.tech.device.platform": "Plateforme",
|
|
632
|
+
"detail.tech.device.language": "Langue",
|
|
633
|
+
"detail.tech.device.timezone": "Fuseau horaire",
|
|
634
|
+
"detail.tech.device.connection": "Connexion",
|
|
635
|
+
"detail.tech.errors": "Erreurs d\u2019ex\xE9cution",
|
|
636
|
+
"detail.tech.console": "Console (20 derniers)",
|
|
637
|
+
"detail.tech.network": "R\xE9seau (15 derniers)",
|
|
577
638
|
"status.new": "Nouveau",
|
|
578
639
|
"status.in_progress": "En cours",
|
|
579
640
|
"status.awaiting_validation": "En attente de validation",
|
|
@@ -603,10 +664,162 @@ function resolveStrings(overrides, options = {}) {
|
|
|
603
664
|
import { h, render } from "preact";
|
|
604
665
|
import { useCallback } from "preact/hooks";
|
|
605
666
|
|
|
667
|
+
// src/widget/ChangelogList.tsx
|
|
668
|
+
import { useEffect, useMemo, useRef, useState } from "preact/hooks";
|
|
669
|
+
|
|
670
|
+
// src/widget/ReportRow.tsx
|
|
671
|
+
import { jsx, jsxs } from "preact/jsx-runtime";
|
|
672
|
+
function statusClassName(status) {
|
|
673
|
+
return `pill pill-status pill-status--${status}`;
|
|
674
|
+
}
|
|
675
|
+
function severityClassName(severity) {
|
|
676
|
+
return `pill pill-severity pill-severity--${severity}`;
|
|
677
|
+
}
|
|
678
|
+
function typeClassName() {
|
|
679
|
+
return "pill pill-type";
|
|
680
|
+
}
|
|
681
|
+
function formatRelative(iso) {
|
|
682
|
+
const then = Date.parse(iso);
|
|
683
|
+
if (!Number.isFinite(then)) return "";
|
|
684
|
+
const seconds = Math.max(1, Math.round((Date.now() - then) / 1e3));
|
|
685
|
+
if (seconds < 60) return `${seconds}s`;
|
|
686
|
+
const minutes = Math.round(seconds / 60);
|
|
687
|
+
if (minutes < 60) return `${minutes}m`;
|
|
688
|
+
const hours = Math.round(minutes / 60);
|
|
689
|
+
if (hours < 48) return `${hours}h`;
|
|
690
|
+
const days = Math.round(hours / 24);
|
|
691
|
+
return `${days}d`;
|
|
692
|
+
}
|
|
693
|
+
function repliesLabel(count, strings) {
|
|
694
|
+
if (count === 1) return strings["mine.replies_one"];
|
|
695
|
+
return strings["mine.replies_many"].replace("{count}", String(count));
|
|
696
|
+
}
|
|
697
|
+
function ReportRow({ row, strings, onClick }) {
|
|
698
|
+
const preview = row.description.length > 120 ? row.description.slice(0, 117) + "\u2026" : row.description;
|
|
699
|
+
return /* @__PURE__ */ jsxs("button", { type: "button", class: "mine-row", onClick, children: [
|
|
700
|
+
/* @__PURE__ */ jsxs("div", { class: "mine-row-pills", children: [
|
|
701
|
+
/* @__PURE__ */ jsx("span", { class: statusClassName(row.status), children: strings[`status.${row.status}`] ?? row.status }),
|
|
702
|
+
/* @__PURE__ */ jsx("span", { class: typeClassName(), children: strings[`type.${row.feedback_type}`] }),
|
|
703
|
+
/* @__PURE__ */ jsx("span", { class: severityClassName(row.severity), children: strings[`severity.${row.severity}`] })
|
|
704
|
+
] }),
|
|
705
|
+
/* @__PURE__ */ jsx("div", { class: "mine-row-preview", children: preview }),
|
|
706
|
+
/* @__PURE__ */ jsxs("div", { class: "mine-row-meta", children: [
|
|
707
|
+
/* @__PURE__ */ jsx("span", { children: formatRelative(row.updated_at || row.created_at) }),
|
|
708
|
+
row.comment_count > 0 && /* @__PURE__ */ jsxs("span", { children: [
|
|
709
|
+
"\xB7 ",
|
|
710
|
+
repliesLabel(row.comment_count, strings)
|
|
711
|
+
] })
|
|
712
|
+
] })
|
|
713
|
+
] });
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
// src/widget/ChangelogList.tsx
|
|
717
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "preact/jsx-runtime";
|
|
718
|
+
var POLL_MS = 3e4;
|
|
719
|
+
function isoWeekKey(iso) {
|
|
720
|
+
const d = new Date(iso);
|
|
721
|
+
if (Number.isNaN(d.getTime())) return "";
|
|
722
|
+
const day = (d.getUTCDay() + 6) % 7;
|
|
723
|
+
const monday = new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate() - day));
|
|
724
|
+
return monday.toISOString().slice(0, 10);
|
|
725
|
+
}
|
|
726
|
+
function groupRowsByWeek(rows) {
|
|
727
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
728
|
+
for (const row of rows) {
|
|
729
|
+
const key = isoWeekKey(row.resolved_at);
|
|
730
|
+
if (!key) continue;
|
|
731
|
+
const existing = buckets.get(key);
|
|
732
|
+
if (existing) existing.push(row);
|
|
733
|
+
else buckets.set(key, [row]);
|
|
734
|
+
}
|
|
735
|
+
return Array.from(buckets.entries()).sort(([a], [b]) => a < b ? 1 : -1).map(([weekKey, weekRows]) => ({
|
|
736
|
+
weekKey,
|
|
737
|
+
label: formatWeekLabel(weekKey),
|
|
738
|
+
rows: weekRows
|
|
739
|
+
}));
|
|
740
|
+
}
|
|
741
|
+
function formatWeekLabel(weekKey) {
|
|
742
|
+
try {
|
|
743
|
+
return new Date(weekKey).toLocaleDateString(void 0, {
|
|
744
|
+
year: "numeric",
|
|
745
|
+
month: "short",
|
|
746
|
+
day: "numeric"
|
|
747
|
+
});
|
|
748
|
+
} catch {
|
|
749
|
+
return weekKey;
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
function ChangelogList({ api, externalId, strings, onSelect }) {
|
|
753
|
+
const [rows, setRows] = useState(null);
|
|
754
|
+
const [error, setError] = useState(null);
|
|
755
|
+
const [refreshing, setRefreshing] = useState(false);
|
|
756
|
+
const mountedRef = useRef(true);
|
|
757
|
+
const fetchRows = async () => {
|
|
758
|
+
setRefreshing(true);
|
|
759
|
+
setError(null);
|
|
760
|
+
try {
|
|
761
|
+
const next = await api.listChangelog(externalId);
|
|
762
|
+
if (!mountedRef.current) return;
|
|
763
|
+
setRows(next);
|
|
764
|
+
} catch (err) {
|
|
765
|
+
if (!mountedRef.current) return;
|
|
766
|
+
setError(err instanceof Error ? err.message : strings["mine.error"]);
|
|
767
|
+
} finally {
|
|
768
|
+
if (mountedRef.current) setRefreshing(false);
|
|
769
|
+
}
|
|
770
|
+
};
|
|
771
|
+
useEffect(() => {
|
|
772
|
+
mountedRef.current = true;
|
|
773
|
+
void fetchRows();
|
|
774
|
+
const timer = setInterval(() => {
|
|
775
|
+
void fetchRows();
|
|
776
|
+
}, POLL_MS);
|
|
777
|
+
return () => {
|
|
778
|
+
mountedRef.current = false;
|
|
779
|
+
clearInterval(timer);
|
|
780
|
+
};
|
|
781
|
+
}, [externalId]);
|
|
782
|
+
const groups = useMemo(() => rows ? groupRowsByWeek(rows) : [], [rows]);
|
|
783
|
+
const isLoading = rows === null && !error;
|
|
784
|
+
const isEmpty = rows !== null && rows.length === 0;
|
|
785
|
+
return /* @__PURE__ */ jsxs2("div", { class: "mine-list", children: [
|
|
786
|
+
/* @__PURE__ */ jsxs2("div", { class: "mine-list-header", children: [
|
|
787
|
+
/* @__PURE__ */ jsx2("h2", { children: strings["tab.changelog"] }),
|
|
788
|
+
/* @__PURE__ */ jsx2(
|
|
789
|
+
"button",
|
|
790
|
+
{
|
|
791
|
+
type: "button",
|
|
792
|
+
class: "btn",
|
|
793
|
+
onClick: () => {
|
|
794
|
+
void fetchRows();
|
|
795
|
+
},
|
|
796
|
+
disabled: refreshing,
|
|
797
|
+
children: refreshing ? strings["mine.loading"] : strings["mine.refresh"]
|
|
798
|
+
}
|
|
799
|
+
)
|
|
800
|
+
] }),
|
|
801
|
+
isLoading && /* @__PURE__ */ jsx2("div", { class: "mine-loading", children: strings["mine.loading"] }),
|
|
802
|
+
error && /* @__PURE__ */ jsx2("div", { class: "error", children: error }),
|
|
803
|
+
isEmpty && /* @__PURE__ */ jsxs2("div", { class: "mine-empty", children: [
|
|
804
|
+
/* @__PURE__ */ jsx2("strong", { children: strings["changelog.empty.title"] }),
|
|
805
|
+
/* @__PURE__ */ jsx2("p", { children: strings["changelog.empty.body"] })
|
|
806
|
+
] }),
|
|
807
|
+
groups.length > 0 && /* @__PURE__ */ jsx2("div", { class: "changelog-groups", children: groups.map((g) => /* @__PURE__ */ jsxs2("section", { class: "changelog-group", children: [
|
|
808
|
+
/* @__PURE__ */ jsxs2("header", { class: "changelog-group-header", children: [
|
|
809
|
+
/* @__PURE__ */ jsx2("span", { class: "changelog-group-marker", "aria-hidden": "true", children: "\u25CF" }),
|
|
810
|
+
/* @__PURE__ */ jsx2("span", { class: "changelog-group-label", children: strings["changelog.week_of"].replace("{date}", g.label) }),
|
|
811
|
+
/* @__PURE__ */ jsx2("span", { class: "changelog-group-rule", "aria-hidden": "true" }),
|
|
812
|
+
/* @__PURE__ */ jsx2("span", { class: "changelog-group-count", children: strings[g.rows.length === 1 ? "changelog.resolved_one" : "changelog.resolved_many"].replace("{count}", String(g.rows.length)) })
|
|
813
|
+
] }),
|
|
814
|
+
/* @__PURE__ */ jsx2("ul", { class: "mine-rows", children: g.rows.map((row) => /* @__PURE__ */ jsx2("li", { children: /* @__PURE__ */ jsx2(ReportRow, { row, strings, onClick: () => onSelect(row) }) })) })
|
|
815
|
+
] }, g.weekKey)) })
|
|
816
|
+
] });
|
|
817
|
+
}
|
|
818
|
+
|
|
606
819
|
// src/widget/Fab.tsx
|
|
607
|
-
import { jsx } from "preact/jsx-runtime";
|
|
820
|
+
import { jsx as jsx3 } from "preact/jsx-runtime";
|
|
608
821
|
function ChatBubbleIcon() {
|
|
609
|
-
return /* @__PURE__ */
|
|
822
|
+
return /* @__PURE__ */ jsx3(
|
|
610
823
|
"svg",
|
|
611
824
|
{
|
|
612
825
|
width: "24",
|
|
@@ -615,7 +828,7 @@ function ChatBubbleIcon() {
|
|
|
615
828
|
fill: "none",
|
|
616
829
|
"aria-hidden": "true",
|
|
617
830
|
focusable: "false",
|
|
618
|
-
children: /* @__PURE__ */
|
|
831
|
+
children: /* @__PURE__ */ jsx3(
|
|
619
832
|
"path",
|
|
620
833
|
{
|
|
621
834
|
d: "M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z",
|
|
@@ -626,15 +839,15 @@ function ChatBubbleIcon() {
|
|
|
626
839
|
);
|
|
627
840
|
}
|
|
628
841
|
function Fab({ label, onClick }) {
|
|
629
|
-
return /* @__PURE__ */
|
|
842
|
+
return /* @__PURE__ */ jsx3("button", { type: "button", class: "fab", "aria-label": label, title: label, onClick, children: /* @__PURE__ */ jsx3(ChatBubbleIcon, {}) });
|
|
630
843
|
}
|
|
631
844
|
|
|
632
845
|
// src/widget/Form.tsx
|
|
633
|
-
import { useEffect as
|
|
846
|
+
import { useEffect as useEffect3, useRef as useRef3, useState as useState3 } from "preact/hooks";
|
|
634
847
|
|
|
635
848
|
// src/widget/Annotator.tsx
|
|
636
|
-
import { useEffect, useRef, useState } from "preact/hooks";
|
|
637
|
-
import { jsx as
|
|
849
|
+
import { useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "preact/hooks";
|
|
850
|
+
import { jsx as jsx4, jsxs as jsxs3 } from "preact/jsx-runtime";
|
|
638
851
|
var COLORS = ["#ef4444", "#f59e0b", "#10b981", "#3b82f6", "#ffffff"];
|
|
639
852
|
function drawShape(ctx, shape) {
|
|
640
853
|
ctx.save();
|
|
@@ -693,27 +906,27 @@ function drawArrow(ctx, x1, y1, x2, y2) {
|
|
|
693
906
|
ctx.fill();
|
|
694
907
|
}
|
|
695
908
|
var Icon = {
|
|
696
|
-
rect: /* @__PURE__ */
|
|
697
|
-
arrow: /* @__PURE__ */
|
|
698
|
-
pencil: /* @__PURE__ */
|
|
699
|
-
text: /* @__PURE__ */
|
|
700
|
-
undo: /* @__PURE__ */
|
|
701
|
-
trash: /* @__PURE__ */
|
|
702
|
-
close: /* @__PURE__ */
|
|
909
|
+
rect: /* @__PURE__ */ jsx4("svg", { width: "16", height: "16", viewBox: "0 0 16 16", "aria-hidden": "true", children: /* @__PURE__ */ jsx4("rect", { x: "2", y: "3", width: "12", height: "10", fill: "none", stroke: "currentColor", "stroke-width": "1.5" }) }),
|
|
910
|
+
arrow: /* @__PURE__ */ jsx4("svg", { width: "16", height: "16", viewBox: "0 0 16 16", "aria-hidden": "true", children: /* @__PURE__ */ jsx4("path", { d: "M2 8h11M9 4l4 4-4 4", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round", "stroke-linejoin": "round" }) }),
|
|
911
|
+
pencil: /* @__PURE__ */ jsx4("svg", { width: "16", height: "16", viewBox: "0 0 16 16", "aria-hidden": "true", children: /* @__PURE__ */ jsx4("path", { d: "M11.5 2.5l2 2L5 13H3v-2l8.5-8.5z", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linejoin": "round" }) }),
|
|
912
|
+
text: /* @__PURE__ */ jsx4("svg", { width: "16", height: "16", viewBox: "0 0 16 16", "aria-hidden": "true", children: /* @__PURE__ */ jsx4("path", { d: "M3 3h10M8 3v10M5 13h6", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round" }) }),
|
|
913
|
+
undo: /* @__PURE__ */ jsx4("svg", { width: "14", height: "14", viewBox: "0 0 16 16", "aria-hidden": "true", children: /* @__PURE__ */ jsx4("path", { d: "M4 7l3-3M4 7l3 3M4 7h6a3 3 0 0 1 0 6H7", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round", "stroke-linejoin": "round" }) }),
|
|
914
|
+
trash: /* @__PURE__ */ jsx4("svg", { width: "14", height: "14", viewBox: "0 0 16 16", "aria-hidden": "true", children: /* @__PURE__ */ jsx4("path", { d: "M3 4h10M6 4V2.5h4V4M5 4l.5 9h5L11 4", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round", "stroke-linejoin": "round" }) }),
|
|
915
|
+
close: /* @__PURE__ */ jsx4("svg", { width: "16", height: "16", viewBox: "0 0 16 16", "aria-hidden": "true", children: /* @__PURE__ */ jsx4("path", { d: "M4 4l8 8M12 4l-8 8", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round" }) })
|
|
703
916
|
};
|
|
704
917
|
function Annotator({ imageBlob, strings, onSave, onCancel }) {
|
|
705
|
-
const canvasRef =
|
|
706
|
-
const containerRef =
|
|
707
|
-
const imageRef =
|
|
708
|
-
const [tool, setTool] =
|
|
709
|
-
const [color, setColor] =
|
|
710
|
-
const [shapes, setShapes] =
|
|
711
|
-
const isDrawingRef =
|
|
712
|
-
const [draftShape, setDraftShape] =
|
|
713
|
-
const [imageLoaded, setImageLoaded] =
|
|
714
|
-
const [saving, setSaving] =
|
|
715
|
-
const scaleRef =
|
|
716
|
-
|
|
918
|
+
const canvasRef = useRef2(null);
|
|
919
|
+
const containerRef = useRef2(null);
|
|
920
|
+
const imageRef = useRef2(null);
|
|
921
|
+
const [tool, setTool] = useState2("rectangle");
|
|
922
|
+
const [color, setColor] = useState2(COLORS[0]);
|
|
923
|
+
const [shapes, setShapes] = useState2([]);
|
|
924
|
+
const isDrawingRef = useRef2(false);
|
|
925
|
+
const [draftShape, setDraftShape] = useState2(null);
|
|
926
|
+
const [imageLoaded, setImageLoaded] = useState2(false);
|
|
927
|
+
const [saving, setSaving] = useState2(false);
|
|
928
|
+
const scaleRef = useRef2(1);
|
|
929
|
+
useEffect2(() => {
|
|
717
930
|
const onKey = (e) => {
|
|
718
931
|
if (e.key === "Escape") {
|
|
719
932
|
e.stopPropagation();
|
|
@@ -723,7 +936,7 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
|
|
|
723
936
|
window.addEventListener("keydown", onKey);
|
|
724
937
|
return () => window.removeEventListener("keydown", onKey);
|
|
725
938
|
}, [onCancel]);
|
|
726
|
-
|
|
939
|
+
useEffect2(() => {
|
|
727
940
|
const url = URL.createObjectURL(imageBlob);
|
|
728
941
|
const img = new Image();
|
|
729
942
|
img.onload = () => {
|
|
@@ -733,7 +946,7 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
|
|
|
733
946
|
img.src = url;
|
|
734
947
|
return () => URL.revokeObjectURL(url);
|
|
735
948
|
}, [imageBlob]);
|
|
736
|
-
|
|
949
|
+
useEffect2(() => {
|
|
737
950
|
if (!imageLoaded || !canvasRef.current || !imageRef.current || !containerRef.current) {
|
|
738
951
|
return;
|
|
739
952
|
}
|
|
@@ -750,7 +963,7 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
|
|
|
750
963
|
canvas.style.height = `${img.height * fitScale}px`;
|
|
751
964
|
redraw();
|
|
752
965
|
}, [imageLoaded]);
|
|
753
|
-
|
|
966
|
+
useEffect2(() => {
|
|
754
967
|
redraw();
|
|
755
968
|
}, [shapes, draftShape]);
|
|
756
969
|
function redraw() {
|
|
@@ -846,7 +1059,7 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
|
|
|
846
1059
|
{ id: "freehand", icon: Icon.pencil, label: strings["annotator.tool.freehand"] },
|
|
847
1060
|
{ id: "text", icon: Icon.text, label: strings["annotator.tool.text"] }
|
|
848
1061
|
];
|
|
849
|
-
return /* @__PURE__ */
|
|
1062
|
+
return /* @__PURE__ */ jsx4(
|
|
850
1063
|
"div",
|
|
851
1064
|
{
|
|
852
1065
|
class: "annotator-backdrop",
|
|
@@ -854,10 +1067,10 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
|
|
|
854
1067
|
onClick: (e) => {
|
|
855
1068
|
if (e.target === e.currentTarget) onCancel();
|
|
856
1069
|
},
|
|
857
|
-
children: /* @__PURE__ */
|
|
858
|
-
/* @__PURE__ */
|
|
859
|
-
/* @__PURE__ */
|
|
860
|
-
/* @__PURE__ */
|
|
1070
|
+
children: /* @__PURE__ */ jsxs3("div", { class: "annotator", role: "dialog", "aria-modal": "true", "aria-label": strings["annotator.title"], children: [
|
|
1071
|
+
/* @__PURE__ */ jsxs3("div", { class: "annotator-header", children: [
|
|
1072
|
+
/* @__PURE__ */ jsx4("span", { children: strings["annotator.title"] }),
|
|
1073
|
+
/* @__PURE__ */ jsx4(
|
|
861
1074
|
"button",
|
|
862
1075
|
{
|
|
863
1076
|
type: "button",
|
|
@@ -868,8 +1081,8 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
|
|
|
868
1081
|
}
|
|
869
1082
|
)
|
|
870
1083
|
] }),
|
|
871
|
-
/* @__PURE__ */
|
|
872
|
-
/* @__PURE__ */
|
|
1084
|
+
/* @__PURE__ */ jsxs3("div", { class: "annotator-toolbar", children: [
|
|
1085
|
+
/* @__PURE__ */ jsx4("div", { class: "annotator-tools", children: tools.map((t) => /* @__PURE__ */ jsx4(
|
|
873
1086
|
"button",
|
|
874
1087
|
{
|
|
875
1088
|
type: "button",
|
|
@@ -882,8 +1095,8 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
|
|
|
882
1095
|
},
|
|
883
1096
|
t.id
|
|
884
1097
|
)) }),
|
|
885
|
-
/* @__PURE__ */
|
|
886
|
-
/* @__PURE__ */
|
|
1098
|
+
/* @__PURE__ */ jsx4("span", { class: "annotator-sep" }),
|
|
1099
|
+
/* @__PURE__ */ jsx4("div", { class: "annotator-colors", children: COLORS.map((c) => /* @__PURE__ */ jsx4(
|
|
887
1100
|
"button",
|
|
888
1101
|
{
|
|
889
1102
|
type: "button",
|
|
@@ -895,8 +1108,8 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
|
|
|
895
1108
|
},
|
|
896
1109
|
c
|
|
897
1110
|
)) }),
|
|
898
|
-
/* @__PURE__ */
|
|
899
|
-
/* @__PURE__ */
|
|
1111
|
+
/* @__PURE__ */ jsx4("span", { class: "annotator-sep" }),
|
|
1112
|
+
/* @__PURE__ */ jsxs3(
|
|
900
1113
|
"button",
|
|
901
1114
|
{
|
|
902
1115
|
type: "button",
|
|
@@ -905,11 +1118,11 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
|
|
|
905
1118
|
disabled: shapes.length === 0,
|
|
906
1119
|
children: [
|
|
907
1120
|
Icon.undo,
|
|
908
|
-
/* @__PURE__ */
|
|
1121
|
+
/* @__PURE__ */ jsx4("span", { children: strings["annotator.undo"] })
|
|
909
1122
|
]
|
|
910
1123
|
}
|
|
911
1124
|
),
|
|
912
|
-
/* @__PURE__ */
|
|
1125
|
+
/* @__PURE__ */ jsxs3(
|
|
913
1126
|
"button",
|
|
914
1127
|
{
|
|
915
1128
|
type: "button",
|
|
@@ -918,18 +1131,18 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
|
|
|
918
1131
|
disabled: shapes.length === 0,
|
|
919
1132
|
children: [
|
|
920
1133
|
Icon.trash,
|
|
921
|
-
/* @__PURE__ */
|
|
1134
|
+
/* @__PURE__ */ jsx4("span", { children: strings["annotator.clear"] })
|
|
922
1135
|
]
|
|
923
1136
|
}
|
|
924
1137
|
),
|
|
925
|
-
/* @__PURE__ */
|
|
926
|
-
/* @__PURE__ */
|
|
1138
|
+
/* @__PURE__ */ jsx4("span", { class: "annotator-spacer" }),
|
|
1139
|
+
/* @__PURE__ */ jsxs3("span", { class: "annotator-count", children: [
|
|
927
1140
|
shapes.length,
|
|
928
1141
|
" ",
|
|
929
1142
|
strings["annotator.count_suffix"]
|
|
930
1143
|
] })
|
|
931
1144
|
] }),
|
|
932
|
-
/* @__PURE__ */
|
|
1145
|
+
/* @__PURE__ */ jsx4("div", { ref: containerRef, class: "annotator-canvas-wrap", children: !imageLoaded ? /* @__PURE__ */ jsx4("span", { class: "annotator-loading", children: strings["annotator.loading"] }) : /* @__PURE__ */ jsx4(
|
|
933
1146
|
"canvas",
|
|
934
1147
|
{
|
|
935
1148
|
ref: canvasRef,
|
|
@@ -940,9 +1153,9 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
|
|
|
940
1153
|
class: "annotator-canvas"
|
|
941
1154
|
}
|
|
942
1155
|
) }),
|
|
943
|
-
/* @__PURE__ */
|
|
944
|
-
/* @__PURE__ */
|
|
945
|
-
/* @__PURE__ */
|
|
1156
|
+
/* @__PURE__ */ jsxs3("div", { class: "annotator-footer", children: [
|
|
1157
|
+
/* @__PURE__ */ jsx4("button", { type: "button", class: "btn", onClick: onCancel, children: strings["form.cancel"] }),
|
|
1158
|
+
/* @__PURE__ */ jsx4(
|
|
946
1159
|
"button",
|
|
947
1160
|
{
|
|
948
1161
|
type: "button",
|
|
@@ -984,24 +1197,24 @@ function truncateUrl(url, maxLength = 80) {
|
|
|
984
1197
|
}
|
|
985
1198
|
|
|
986
1199
|
// src/widget/Form.tsx
|
|
987
|
-
import { jsx as
|
|
1200
|
+
import { jsx as jsx5, jsxs as jsxs4 } from "preact/jsx-runtime";
|
|
988
1201
|
var TYPES = ["bug", "feature", "question", "praise", "typo"];
|
|
989
1202
|
var SEVERITIES = ["blocker", "high", "medium", "low"];
|
|
990
1203
|
function Form({ strings, onSubmit, onCancel, status, errorMessage }) {
|
|
991
|
-
const [description, setDescription] =
|
|
992
|
-
const [feedbackType, setFeedbackType] =
|
|
993
|
-
const [severity, setSeverity] =
|
|
994
|
-
const [localError, setLocalError] =
|
|
995
|
-
const [screenshotBlob, setScreenshotBlob] =
|
|
996
|
-
const [screenshotPreview, setScreenshotPreview] =
|
|
997
|
-
const [isDragOver, setIsDragOver] =
|
|
998
|
-
const [annotatorOpen, setAnnotatorOpen] =
|
|
999
|
-
const fileInputRef =
|
|
1000
|
-
const dropZoneRef =
|
|
1204
|
+
const [description, setDescription] = useState3("");
|
|
1205
|
+
const [feedbackType, setFeedbackType] = useState3("bug");
|
|
1206
|
+
const [severity, setSeverity] = useState3("medium");
|
|
1207
|
+
const [localError, setLocalError] = useState3("");
|
|
1208
|
+
const [screenshotBlob, setScreenshotBlob] = useState3(null);
|
|
1209
|
+
const [screenshotPreview, setScreenshotPreview] = useState3(null);
|
|
1210
|
+
const [isDragOver, setIsDragOver] = useState3(false);
|
|
1211
|
+
const [annotatorOpen, setAnnotatorOpen] = useState3(false);
|
|
1212
|
+
const fileInputRef = useRef3(null);
|
|
1213
|
+
const dropZoneRef = useRef3(null);
|
|
1001
1214
|
const submitting = status === "submitting";
|
|
1002
1215
|
const submitLabel = submitting ? strings["form.submitting"] : strings["form.submit"];
|
|
1003
1216
|
const pageUrl = typeof window !== "undefined" ? window.location.href : "";
|
|
1004
|
-
|
|
1217
|
+
useEffect3(() => {
|
|
1005
1218
|
return () => {
|
|
1006
1219
|
if (screenshotPreview) URL.revokeObjectURL(screenshotPreview);
|
|
1007
1220
|
};
|
|
@@ -1049,7 +1262,7 @@ function Form({ strings, onSubmit, onCancel, status, errorMessage }) {
|
|
|
1049
1262
|
const file = e.dataTransfer?.files?.[0];
|
|
1050
1263
|
if (file) acceptFile(file);
|
|
1051
1264
|
};
|
|
1052
|
-
|
|
1265
|
+
useEffect3(() => {
|
|
1053
1266
|
const zone = dropZoneRef.current;
|
|
1054
1267
|
if (!zone) return;
|
|
1055
1268
|
const onPaste = (e) => {
|
|
@@ -1090,11 +1303,11 @@ function Form({ strings, onSubmit, onCancel, status, errorMessage }) {
|
|
|
1090
1303
|
if (screenshotBlob) values.screenshot = screenshotBlob;
|
|
1091
1304
|
onSubmit(values);
|
|
1092
1305
|
};
|
|
1093
|
-
return /* @__PURE__ */
|
|
1094
|
-
/* @__PURE__ */
|
|
1095
|
-
/* @__PURE__ */
|
|
1096
|
-
/* @__PURE__ */
|
|
1097
|
-
/* @__PURE__ */
|
|
1306
|
+
return /* @__PURE__ */ jsxs4("form", { onSubmit: handleSubmit, children: [
|
|
1307
|
+
/* @__PURE__ */ jsx5("h2", { children: strings["form.title"] }),
|
|
1308
|
+
/* @__PURE__ */ jsxs4("div", { class: "field", children: [
|
|
1309
|
+
/* @__PURE__ */ jsx5("label", { for: "mfb-desc", children: strings["form.description.label"] }),
|
|
1310
|
+
/* @__PURE__ */ jsx5(
|
|
1098
1311
|
"textarea",
|
|
1099
1312
|
{
|
|
1100
1313
|
id: "mfb-desc",
|
|
@@ -1104,35 +1317,35 @@ function Form({ strings, onSubmit, onCancel, status, errorMessage }) {
|
|
|
1104
1317
|
}
|
|
1105
1318
|
)
|
|
1106
1319
|
] }),
|
|
1107
|
-
/* @__PURE__ */
|
|
1108
|
-
/* @__PURE__ */
|
|
1109
|
-
/* @__PURE__ */
|
|
1110
|
-
/* @__PURE__ */
|
|
1320
|
+
/* @__PURE__ */ jsxs4("div", { class: "row", children: [
|
|
1321
|
+
/* @__PURE__ */ jsxs4("div", { class: "field", children: [
|
|
1322
|
+
/* @__PURE__ */ jsx5("label", { for: "mfb-type", children: strings["form.type.label"] }),
|
|
1323
|
+
/* @__PURE__ */ jsx5(
|
|
1111
1324
|
"select",
|
|
1112
1325
|
{
|
|
1113
1326
|
id: "mfb-type",
|
|
1114
1327
|
value: feedbackType,
|
|
1115
1328
|
onChange: (e) => setFeedbackType(e.target.value),
|
|
1116
|
-
children: TYPES.map((t) => /* @__PURE__ */
|
|
1329
|
+
children: TYPES.map((t) => /* @__PURE__ */ jsx5("option", { value: t, children: strings[`type.${t}`] }))
|
|
1117
1330
|
}
|
|
1118
1331
|
)
|
|
1119
1332
|
] }),
|
|
1120
|
-
/* @__PURE__ */
|
|
1121
|
-
/* @__PURE__ */
|
|
1122
|
-
/* @__PURE__ */
|
|
1333
|
+
/* @__PURE__ */ jsxs4("div", { class: "field", children: [
|
|
1334
|
+
/* @__PURE__ */ jsx5("label", { for: "mfb-sev", children: strings["form.severity.label"] }),
|
|
1335
|
+
/* @__PURE__ */ jsx5(
|
|
1123
1336
|
"select",
|
|
1124
1337
|
{
|
|
1125
1338
|
id: "mfb-sev",
|
|
1126
1339
|
value: severity,
|
|
1127
1340
|
onChange: (e) => setSeverity(e.target.value),
|
|
1128
|
-
children: SEVERITIES.map((s) => /* @__PURE__ */
|
|
1341
|
+
children: SEVERITIES.map((s) => /* @__PURE__ */ jsx5("option", { value: s, children: strings[`severity.${s}`] }))
|
|
1129
1342
|
}
|
|
1130
1343
|
)
|
|
1131
1344
|
] })
|
|
1132
1345
|
] }),
|
|
1133
|
-
/* @__PURE__ */
|
|
1134
|
-
/* @__PURE__ */
|
|
1135
|
-
/* @__PURE__ */
|
|
1346
|
+
/* @__PURE__ */ jsxs4("div", { class: "field", children: [
|
|
1347
|
+
/* @__PURE__ */ jsx5("label", { children: strings["form.screenshot.label"] }),
|
|
1348
|
+
/* @__PURE__ */ jsx5(
|
|
1136
1349
|
"input",
|
|
1137
1350
|
{
|
|
1138
1351
|
ref: fileInputRef,
|
|
@@ -1144,9 +1357,9 @@ function Form({ strings, onSubmit, onCancel, status, errorMessage }) {
|
|
|
1144
1357
|
tabIndex: -1
|
|
1145
1358
|
}
|
|
1146
1359
|
),
|
|
1147
|
-
screenshotPreview ? /* @__PURE__ */
|
|
1148
|
-
/* @__PURE__ */
|
|
1149
|
-
/* @__PURE__ */
|
|
1360
|
+
screenshotPreview ? /* @__PURE__ */ jsxs4("div", { class: "screenshot-preview", children: [
|
|
1361
|
+
/* @__PURE__ */ jsx5("img", { src: screenshotPreview, alt: "" }),
|
|
1362
|
+
/* @__PURE__ */ jsx5(
|
|
1150
1363
|
"button",
|
|
1151
1364
|
{
|
|
1152
1365
|
type: "button",
|
|
@@ -1156,7 +1369,7 @@ function Form({ strings, onSubmit, onCancel, status, errorMessage }) {
|
|
|
1156
1369
|
children: "\xD7"
|
|
1157
1370
|
}
|
|
1158
1371
|
),
|
|
1159
|
-
/* @__PURE__ */
|
|
1372
|
+
/* @__PURE__ */ jsx5(
|
|
1160
1373
|
"button",
|
|
1161
1374
|
{
|
|
1162
1375
|
type: "button",
|
|
@@ -1165,7 +1378,7 @@ function Form({ strings, onSubmit, onCancel, status, errorMessage }) {
|
|
|
1165
1378
|
children: strings["form.screenshot.annotate"]
|
|
1166
1379
|
}
|
|
1167
1380
|
)
|
|
1168
|
-
] }) : /* @__PURE__ */
|
|
1381
|
+
] }) : /* @__PURE__ */ jsxs4(
|
|
1169
1382
|
"div",
|
|
1170
1383
|
{
|
|
1171
1384
|
ref: dropZoneRef,
|
|
@@ -1184,28 +1397,28 @@ function Form({ strings, onSubmit, onCancel, status, errorMessage }) {
|
|
|
1184
1397
|
onDragLeave: handleDragLeave,
|
|
1185
1398
|
onDrop: handleDrop,
|
|
1186
1399
|
children: [
|
|
1187
|
-
/* @__PURE__ */
|
|
1188
|
-
/* @__PURE__ */
|
|
1400
|
+
/* @__PURE__ */ jsxs4("div", { class: "screenshot-cta", children: [
|
|
1401
|
+
/* @__PURE__ */ jsx5("strong", { children: strings["form.screenshot.cta_click"] }),
|
|
1189
1402
|
", ",
|
|
1190
1403
|
strings["form.screenshot.cta_rest"]
|
|
1191
1404
|
] }),
|
|
1192
|
-
/* @__PURE__ */
|
|
1405
|
+
/* @__PURE__ */ jsx5("div", { class: "screenshot-formats", children: strings["form.screenshot.formats"] })
|
|
1193
1406
|
]
|
|
1194
1407
|
}
|
|
1195
1408
|
)
|
|
1196
1409
|
] }),
|
|
1197
|
-
pageUrl && /* @__PURE__ */
|
|
1198
|
-
/* @__PURE__ */
|
|
1199
|
-
/* @__PURE__ */
|
|
1410
|
+
pageUrl && /* @__PURE__ */ jsxs4("div", { class: "page-context", title: pageUrl, children: [
|
|
1411
|
+
/* @__PURE__ */ jsx5("span", { class: "page-context-label", children: strings["form.context.label"] }),
|
|
1412
|
+
/* @__PURE__ */ jsx5("span", { class: "page-context-url", children: truncateUrl(pageUrl, 90) })
|
|
1200
1413
|
] }),
|
|
1201
|
-
localError && /* @__PURE__ */
|
|
1202
|
-
status === "error" && errorMessage && /* @__PURE__ */
|
|
1203
|
-
status === "success" && /* @__PURE__ */
|
|
1204
|
-
/* @__PURE__ */
|
|
1205
|
-
/* @__PURE__ */
|
|
1206
|
-
/* @__PURE__ */
|
|
1414
|
+
localError && /* @__PURE__ */ jsx5("div", { class: "error", children: localError }),
|
|
1415
|
+
status === "error" && errorMessage && /* @__PURE__ */ jsx5("div", { class: "error", children: errorMessage }),
|
|
1416
|
+
status === "success" && /* @__PURE__ */ jsx5("div", { class: "success", children: strings["form.success"] }),
|
|
1417
|
+
/* @__PURE__ */ jsxs4("div", { class: "actions", children: [
|
|
1418
|
+
/* @__PURE__ */ jsx5("button", { type: "button", class: "btn", onClick: onCancel, disabled: submitting, children: strings["form.cancel"] }),
|
|
1419
|
+
/* @__PURE__ */ jsx5("button", { type: "submit", class: "btn btn--primary", disabled: submitting, children: submitLabel })
|
|
1207
1420
|
] }),
|
|
1208
|
-
annotatorOpen && screenshotBlob && /* @__PURE__ */
|
|
1421
|
+
annotatorOpen && screenshotBlob && /* @__PURE__ */ jsx5(
|
|
1209
1422
|
Annotator,
|
|
1210
1423
|
{
|
|
1211
1424
|
imageBlob: screenshotBlob,
|
|
@@ -1218,62 +1431,104 @@ function Form({ strings, onSubmit, onCancel, status, errorMessage }) {
|
|
|
1218
1431
|
}
|
|
1219
1432
|
|
|
1220
1433
|
// src/widget/MineList.tsx
|
|
1221
|
-
import { useEffect as
|
|
1434
|
+
import { useEffect as useEffect4, useRef as useRef4, useState as useState4 } from "preact/hooks";
|
|
1222
1435
|
|
|
1223
|
-
// src/widget/
|
|
1224
|
-
import { jsx as
|
|
1225
|
-
function
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1436
|
+
// src/widget/KpiStrip.tsx
|
|
1437
|
+
import { jsx as jsx6, jsxs as jsxs5 } from "preact/jsx-runtime";
|
|
1438
|
+
function computeKpiCounts(rows) {
|
|
1439
|
+
const counts = {
|
|
1440
|
+
new: 0,
|
|
1441
|
+
in_progress: 0,
|
|
1442
|
+
awaiting_validation: 0,
|
|
1443
|
+
resolved: 0,
|
|
1444
|
+
total: 0
|
|
1445
|
+
};
|
|
1446
|
+
for (const row of rows) {
|
|
1447
|
+
counts.total += 1;
|
|
1448
|
+
switch (row.status) {
|
|
1449
|
+
case "new":
|
|
1450
|
+
counts.new += 1;
|
|
1451
|
+
break;
|
|
1452
|
+
case "in_progress":
|
|
1453
|
+
counts.in_progress += 1;
|
|
1454
|
+
break;
|
|
1455
|
+
case "awaiting_validation":
|
|
1456
|
+
counts.awaiting_validation += 1;
|
|
1457
|
+
break;
|
|
1458
|
+
case "closed":
|
|
1459
|
+
case "rejected":
|
|
1460
|
+
case "duplicate":
|
|
1461
|
+
case "wontfix":
|
|
1462
|
+
counts.resolved += 1;
|
|
1463
|
+
break;
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
return counts;
|
|
1245
1467
|
}
|
|
1246
|
-
function
|
|
1247
|
-
if (
|
|
1248
|
-
return
|
|
1468
|
+
function computeResolutionRate(counts) {
|
|
1469
|
+
if (counts.total === 0) return null;
|
|
1470
|
+
return Math.round(
|
|
1471
|
+
(counts.awaiting_validation + counts.resolved) / counts.total * 100
|
|
1472
|
+
);
|
|
1249
1473
|
}
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1474
|
+
var FILTER_FOR_STATUS = {
|
|
1475
|
+
new: "new",
|
|
1476
|
+
in_progress: "in_progress",
|
|
1477
|
+
awaiting_validation: "awaiting_validation",
|
|
1478
|
+
closed: "resolved",
|
|
1479
|
+
rejected: "resolved",
|
|
1480
|
+
duplicate: "resolved",
|
|
1481
|
+
wontfix: "resolved"
|
|
1482
|
+
};
|
|
1483
|
+
function rowsMatchingFilter(rows, filter) {
|
|
1484
|
+
if (filter === "all") return rows;
|
|
1485
|
+
return rows.filter((r) => FILTER_FOR_STATUS[r.status] === filter);
|
|
1486
|
+
}
|
|
1487
|
+
function KpiStrip({ rows, filter, onFilter, strings }) {
|
|
1488
|
+
const counts = computeKpiCounts(rows);
|
|
1489
|
+
const resolutionRate = computeResolutionRate(counts);
|
|
1490
|
+
const cells = [
|
|
1491
|
+
{ key: "new", label: strings["kpi.new"], value: String(counts.new) },
|
|
1492
|
+
{ key: "in_progress", label: strings["kpi.in_progress"], value: String(counts.in_progress) },
|
|
1493
|
+
{
|
|
1494
|
+
key: "awaiting_validation",
|
|
1495
|
+
label: strings["kpi.awaiting_validation"],
|
|
1496
|
+
value: String(counts.awaiting_validation)
|
|
1497
|
+
},
|
|
1498
|
+
{
|
|
1499
|
+
key: "resolved",
|
|
1500
|
+
label: strings["kpi.resolution_rate"],
|
|
1501
|
+
value: resolutionRate === null ? "\u2014" : `${resolutionRate}%`
|
|
1502
|
+
}
|
|
1503
|
+
];
|
|
1504
|
+
return /* @__PURE__ */ jsx6("div", { class: "kpi-strip", role: "toolbar", children: cells.map((c) => {
|
|
1505
|
+
const active = filter === c.key;
|
|
1506
|
+
const toggleTo = active ? "all" : c.key;
|
|
1507
|
+
return /* @__PURE__ */ jsxs5(
|
|
1508
|
+
"button",
|
|
1509
|
+
{
|
|
1510
|
+
type: "button",
|
|
1511
|
+
class: `kpi-cell kpi-cell--${c.key}${active ? " is-active" : ""}`,
|
|
1512
|
+
onClick: () => onFilter(toggleTo),
|
|
1513
|
+
"aria-pressed": active,
|
|
1514
|
+
children: [
|
|
1515
|
+
/* @__PURE__ */ jsx6("span", { class: "kpi-value", children: c.value }),
|
|
1516
|
+
/* @__PURE__ */ jsx6("span", { class: "kpi-label", children: c.label })
|
|
1517
|
+
]
|
|
1518
|
+
}
|
|
1519
|
+
);
|
|
1520
|
+
}) });
|
|
1267
1521
|
}
|
|
1268
1522
|
|
|
1269
1523
|
// src/widget/MineList.tsx
|
|
1270
|
-
import { jsx as
|
|
1271
|
-
var
|
|
1524
|
+
import { jsx as jsx7, jsxs as jsxs6 } from "preact/jsx-runtime";
|
|
1525
|
+
var POLL_MS2 = 3e4;
|
|
1272
1526
|
function MineList({ api, externalId, strings, onSelect }) {
|
|
1273
|
-
const [rows, setRows] =
|
|
1274
|
-
const [error, setError] =
|
|
1275
|
-
const [refreshing, setRefreshing] =
|
|
1276
|
-
const
|
|
1527
|
+
const [rows, setRows] = useState4(null);
|
|
1528
|
+
const [error, setError] = useState4(null);
|
|
1529
|
+
const [refreshing, setRefreshing] = useState4(false);
|
|
1530
|
+
const [filter, setFilter] = useState4("all");
|
|
1531
|
+
const mountedRef = useRef4(true);
|
|
1277
1532
|
const fetchRows = async () => {
|
|
1278
1533
|
setRefreshing(true);
|
|
1279
1534
|
setError(null);
|
|
@@ -1288,12 +1543,12 @@ function MineList({ api, externalId, strings, onSelect }) {
|
|
|
1288
1543
|
if (mountedRef.current) setRefreshing(false);
|
|
1289
1544
|
}
|
|
1290
1545
|
};
|
|
1291
|
-
|
|
1546
|
+
useEffect4(() => {
|
|
1292
1547
|
mountedRef.current = true;
|
|
1293
1548
|
void fetchRows();
|
|
1294
1549
|
const timer = setInterval(() => {
|
|
1295
1550
|
void fetchRows();
|
|
1296
|
-
},
|
|
1551
|
+
}, POLL_MS2);
|
|
1297
1552
|
return () => {
|
|
1298
1553
|
mountedRef.current = false;
|
|
1299
1554
|
clearInterval(timer);
|
|
@@ -1301,10 +1556,12 @@ function MineList({ api, externalId, strings, onSelect }) {
|
|
|
1301
1556
|
}, [externalId]);
|
|
1302
1557
|
const isEmpty = rows !== null && rows.length === 0;
|
|
1303
1558
|
const isLoading = rows === null && !error;
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1559
|
+
const visibleRows = rows ? rowsMatchingFilter(rows, filter) : null;
|
|
1560
|
+
const visibleEmpty = !!rows && rows.length > 0 && (visibleRows?.length ?? 0) === 0;
|
|
1561
|
+
return /* @__PURE__ */ jsxs6("div", { class: "mine-list", children: [
|
|
1562
|
+
/* @__PURE__ */ jsxs6("div", { class: "mine-list-header", children: [
|
|
1563
|
+
/* @__PURE__ */ jsx7("h2", { children: strings["tab.mine"] }),
|
|
1564
|
+
/* @__PURE__ */ jsx7(
|
|
1308
1565
|
"button",
|
|
1309
1566
|
{
|
|
1310
1567
|
type: "button",
|
|
@@ -1317,23 +1574,25 @@ function MineList({ api, externalId, strings, onSelect }) {
|
|
|
1317
1574
|
}
|
|
1318
1575
|
)
|
|
1319
1576
|
] }),
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
/* @__PURE__ */
|
|
1577
|
+
rows && rows.length > 0 && /* @__PURE__ */ jsx7(KpiStrip, { rows, filter, onFilter: setFilter, strings }),
|
|
1578
|
+
isLoading && /* @__PURE__ */ jsx7("div", { class: "mine-loading", children: strings["mine.loading"] }),
|
|
1579
|
+
error && /* @__PURE__ */ jsx7("div", { class: "error", children: error }),
|
|
1580
|
+
isEmpty && /* @__PURE__ */ jsxs6("div", { class: "mine-empty", children: [
|
|
1581
|
+
/* @__PURE__ */ jsx7("strong", { children: strings["mine.empty.title"] }),
|
|
1582
|
+
/* @__PURE__ */ jsx7("p", { children: strings["mine.empty.body"] })
|
|
1325
1583
|
] }),
|
|
1326
|
-
|
|
1584
|
+
visibleEmpty && /* @__PURE__ */ jsx7("div", { class: "mine-empty", children: /* @__PURE__ */ jsx7("p", { children: strings["mine.filter.empty"] }) }),
|
|
1585
|
+
visibleRows && visibleRows.length > 0 && /* @__PURE__ */ jsx7("ul", { class: "mine-rows", children: visibleRows.map((row) => /* @__PURE__ */ jsx7("li", { children: /* @__PURE__ */ jsx7(ReportRow, { row, strings, onClick: () => onSelect(row) }) })) })
|
|
1327
1586
|
] });
|
|
1328
1587
|
}
|
|
1329
1588
|
|
|
1330
1589
|
// src/widget/Modal.tsx
|
|
1331
|
-
import { useEffect as
|
|
1332
|
-
import { jsx as
|
|
1590
|
+
import { useEffect as useEffect5, useRef as useRef5 } from "preact/hooks";
|
|
1591
|
+
import { jsx as jsx8, jsxs as jsxs7 } from "preact/jsx-runtime";
|
|
1333
1592
|
function Modal({ onDismiss, children, closeLabel = "Close" }) {
|
|
1334
|
-
const modalRef =
|
|
1335
|
-
const previouslyFocused =
|
|
1336
|
-
|
|
1593
|
+
const modalRef = useRef5(null);
|
|
1594
|
+
const previouslyFocused = useRef5(null);
|
|
1595
|
+
useEffect5(() => {
|
|
1337
1596
|
previouslyFocused.current = document.activeElement;
|
|
1338
1597
|
const onKey = (e) => {
|
|
1339
1598
|
if (e.key !== "Escape") return;
|
|
@@ -1355,7 +1614,7 @@ function Modal({ onDismiss, children, closeLabel = "Close" }) {
|
|
|
1355
1614
|
if (prev && typeof prev.focus === "function") prev.focus();
|
|
1356
1615
|
};
|
|
1357
1616
|
}, [onDismiss]);
|
|
1358
|
-
return /* @__PURE__ */
|
|
1617
|
+
return /* @__PURE__ */ jsx8(
|
|
1359
1618
|
"div",
|
|
1360
1619
|
{
|
|
1361
1620
|
class: "backdrop",
|
|
@@ -1363,8 +1622,8 @@ function Modal({ onDismiss, children, closeLabel = "Close" }) {
|
|
|
1363
1622
|
onClick: (e) => {
|
|
1364
1623
|
if (e.target === e.currentTarget) onDismiss();
|
|
1365
1624
|
},
|
|
1366
|
-
children: /* @__PURE__ */
|
|
1367
|
-
/* @__PURE__ */
|
|
1625
|
+
children: /* @__PURE__ */ jsxs7("div", { ref: modalRef, class: "modal", role: "dialog", "aria-modal": "true", children: [
|
|
1626
|
+
/* @__PURE__ */ jsx8(
|
|
1368
1627
|
"button",
|
|
1369
1628
|
{
|
|
1370
1629
|
type: "button",
|
|
@@ -1381,10 +1640,10 @@ function Modal({ onDismiss, children, closeLabel = "Close" }) {
|
|
|
1381
1640
|
}
|
|
1382
1641
|
|
|
1383
1642
|
// src/widget/ReportDetailView.tsx
|
|
1384
|
-
import { useEffect as
|
|
1643
|
+
import { useEffect as useEffect6, useRef as useRef6, useState as useState5 } from "preact/hooks";
|
|
1385
1644
|
|
|
1386
1645
|
// src/widget/CommentBubble.tsx
|
|
1387
|
-
import { jsx as
|
|
1646
|
+
import { jsx as jsx9, jsxs as jsxs8 } from "preact/jsx-runtime";
|
|
1388
1647
|
function CommentBubble({ comment, strings }) {
|
|
1389
1648
|
const isMine = comment.is_mine;
|
|
1390
1649
|
const isAgent = !isMine && comment.author_source === "mcp";
|
|
@@ -1392,10 +1651,10 @@ function CommentBubble({ comment, strings }) {
|
|
|
1392
1651
|
const variant = isAgent ? "mcp" : isSystem ? "system" : "staff";
|
|
1393
1652
|
const labelKey = variant === "mcp" ? "detail.author.mcp" : variant === "system" ? "detail.author.system" : "detail.author.staff";
|
|
1394
1653
|
const label = comment.author_label || strings[labelKey];
|
|
1395
|
-
return /* @__PURE__ */
|
|
1396
|
-
!isMine && label && /* @__PURE__ */
|
|
1397
|
-
/* @__PURE__ */
|
|
1398
|
-
/* @__PURE__ */
|
|
1654
|
+
return /* @__PURE__ */ jsxs8("div", { class: `comment-bubble ${isMine ? "is-mine" : "is-other"}`, children: [
|
|
1655
|
+
!isMine && label && /* @__PURE__ */ jsx9("div", { class: `comment-author comment-author--${variant}`, children: label }),
|
|
1656
|
+
/* @__PURE__ */ jsx9("div", { class: "comment-body", children: comment.body }),
|
|
1657
|
+
/* @__PURE__ */ jsx9("div", { class: "comment-time", children: formatTime(comment.created_at) })
|
|
1399
1658
|
] });
|
|
1400
1659
|
}
|
|
1401
1660
|
function formatTime(iso) {
|
|
@@ -1410,8 +1669,8 @@ function formatTime(iso) {
|
|
|
1410
1669
|
}
|
|
1411
1670
|
|
|
1412
1671
|
// src/widget/ReportDetailView.tsx
|
|
1413
|
-
import { jsx as
|
|
1414
|
-
var
|
|
1672
|
+
import { Fragment, jsx as jsx10, jsxs as jsxs9 } from "preact/jsx-runtime";
|
|
1673
|
+
var POLL_MS3 = 3e4;
|
|
1415
1674
|
function ReportDetailView({
|
|
1416
1675
|
api,
|
|
1417
1676
|
externalId,
|
|
@@ -1419,12 +1678,12 @@ function ReportDetailView({
|
|
|
1419
1678
|
strings,
|
|
1420
1679
|
onBack
|
|
1421
1680
|
}) {
|
|
1422
|
-
const [detail, setDetail] =
|
|
1423
|
-
const [error, setError] =
|
|
1424
|
-
const [composeBody, setComposeBody] =
|
|
1425
|
-
const [sending, setSending] =
|
|
1426
|
-
const [closing, setClosing] =
|
|
1427
|
-
const mountedRef =
|
|
1681
|
+
const [detail, setDetail] = useState5(null);
|
|
1682
|
+
const [error, setError] = useState5(null);
|
|
1683
|
+
const [composeBody, setComposeBody] = useState5("");
|
|
1684
|
+
const [sending, setSending] = useState5(false);
|
|
1685
|
+
const [closing, setClosing] = useState5(false);
|
|
1686
|
+
const mountedRef = useRef6(true);
|
|
1428
1687
|
const fetchDetail = async () => {
|
|
1429
1688
|
try {
|
|
1430
1689
|
const next = await api.getReport(reportId, externalId);
|
|
@@ -1436,12 +1695,12 @@ function ReportDetailView({
|
|
|
1436
1695
|
setError(err instanceof Error ? err.message : "load_failed");
|
|
1437
1696
|
}
|
|
1438
1697
|
};
|
|
1439
|
-
|
|
1698
|
+
useEffect6(() => {
|
|
1440
1699
|
mountedRef.current = true;
|
|
1441
1700
|
void fetchDetail();
|
|
1442
1701
|
const timer = setInterval(() => {
|
|
1443
1702
|
void fetchDetail();
|
|
1444
|
-
},
|
|
1703
|
+
}, POLL_MS3);
|
|
1445
1704
|
return () => {
|
|
1446
1705
|
mountedRef.current = false;
|
|
1447
1706
|
clearInterval(timer);
|
|
@@ -1481,37 +1740,39 @@ function ReportDetailView({
|
|
|
1481
1740
|
}
|
|
1482
1741
|
};
|
|
1483
1742
|
if (!detail && !error) {
|
|
1484
|
-
return /* @__PURE__ */
|
|
1743
|
+
return /* @__PURE__ */ jsx10("div", { class: "mine-loading", children: strings["mine.loading"] });
|
|
1485
1744
|
}
|
|
1486
1745
|
if (!detail) {
|
|
1487
|
-
return /* @__PURE__ */
|
|
1746
|
+
return /* @__PURE__ */ jsx10("div", { class: "error", role: "alert", children: error });
|
|
1488
1747
|
}
|
|
1489
1748
|
const showCloseCta = detail.status === "awaiting_validation";
|
|
1490
|
-
return /* @__PURE__ */
|
|
1491
|
-
/* @__PURE__ */
|
|
1492
|
-
/* @__PURE__ */
|
|
1749
|
+
return /* @__PURE__ */ jsxs9("div", { class: "report-detail", children: [
|
|
1750
|
+
/* @__PURE__ */ jsxs9("div", { class: "report-detail-header", children: [
|
|
1751
|
+
/* @__PURE__ */ jsxs9("button", { type: "button", class: "btn", onClick: onBack, children: [
|
|
1493
1752
|
"\u2190 ",
|
|
1494
1753
|
strings["detail.back"]
|
|
1495
1754
|
] }),
|
|
1496
|
-
/* @__PURE__ */
|
|
1755
|
+
/* @__PURE__ */ jsx10("span", { class: `pill pill-status pill-status--${detail.status}`, children: strings[`status.${detail.status}`] ?? detail.status })
|
|
1497
1756
|
] }),
|
|
1498
|
-
/* @__PURE__ */
|
|
1499
|
-
/* @__PURE__ */
|
|
1500
|
-
/* @__PURE__ */
|
|
1501
|
-
detail.screenshot_url && /* @__PURE__ */
|
|
1757
|
+
/* @__PURE__ */ jsxs9("div", { class: "report-detail-body", children: [
|
|
1758
|
+
/* @__PURE__ */ jsx10(ContextBlock, { detail, strings }),
|
|
1759
|
+
/* @__PURE__ */ jsx10("p", { class: "report-detail-description", children: detail.description }),
|
|
1760
|
+
detail.screenshot_url && /* @__PURE__ */ jsx10(
|
|
1502
1761
|
"a",
|
|
1503
1762
|
{
|
|
1504
1763
|
class: "report-detail-screenshot",
|
|
1505
1764
|
href: detail.screenshot_url,
|
|
1506
1765
|
target: "_blank",
|
|
1507
1766
|
rel: "noopener noreferrer",
|
|
1508
|
-
children: /* @__PURE__ */
|
|
1767
|
+
children: /* @__PURE__ */ jsx10("img", { src: detail.screenshot_url, alt: "", loading: "lazy" })
|
|
1509
1768
|
}
|
|
1510
1769
|
),
|
|
1511
|
-
/* @__PURE__ */
|
|
1512
|
-
detail.comments.length === 0 ? /* @__PURE__ */
|
|
1513
|
-
/* @__PURE__ */
|
|
1514
|
-
|
|
1770
|
+
/* @__PURE__ */ jsx10("h3", { class: "report-detail-section", children: strings["detail.thread"] }),
|
|
1771
|
+
detail.comments.length === 0 ? /* @__PURE__ */ jsx10("p", { class: "report-detail-empty", children: strings["detail.no_replies"] }) : /* @__PURE__ */ jsx10("ul", { class: "report-comments", children: detail.comments.map((c) => /* @__PURE__ */ jsx10("li", { children: /* @__PURE__ */ jsx10(CommentBubble, { comment: c, strings }) })) }),
|
|
1772
|
+
detail.status_history && detail.status_history.length > 0 && /* @__PURE__ */ jsx10(StatusHistorySection, { rows: detail.status_history, strings }),
|
|
1773
|
+
detail.technical_context && /* @__PURE__ */ jsx10(TechnicalContextDrawer, { ctx: detail.technical_context, strings }),
|
|
1774
|
+
/* @__PURE__ */ jsxs9("div", { class: "report-compose", children: [
|
|
1775
|
+
/* @__PURE__ */ jsx10(
|
|
1515
1776
|
"textarea",
|
|
1516
1777
|
{
|
|
1517
1778
|
value: composeBody,
|
|
@@ -1520,8 +1781,8 @@ function ReportDetailView({
|
|
|
1520
1781
|
disabled: sending
|
|
1521
1782
|
}
|
|
1522
1783
|
),
|
|
1523
|
-
/* @__PURE__ */
|
|
1524
|
-
showCloseCta && /* @__PURE__ */
|
|
1784
|
+
/* @__PURE__ */ jsxs9("div", { class: "report-compose-actions", children: [
|
|
1785
|
+
showCloseCta && /* @__PURE__ */ jsx10(
|
|
1525
1786
|
"button",
|
|
1526
1787
|
{
|
|
1527
1788
|
type: "button",
|
|
@@ -1531,7 +1792,7 @@ function ReportDetailView({
|
|
|
1531
1792
|
children: closing ? strings["detail.close_busy"] : strings["detail.close_cta"]
|
|
1532
1793
|
}
|
|
1533
1794
|
),
|
|
1534
|
-
/* @__PURE__ */
|
|
1795
|
+
/* @__PURE__ */ jsx10(
|
|
1535
1796
|
"button",
|
|
1536
1797
|
{
|
|
1537
1798
|
type: "button",
|
|
@@ -1543,7 +1804,7 @@ function ReportDetailView({
|
|
|
1543
1804
|
)
|
|
1544
1805
|
] })
|
|
1545
1806
|
] }),
|
|
1546
|
-
error && /* @__PURE__ */
|
|
1807
|
+
error && /* @__PURE__ */ jsx10("div", { class: "error", children: error })
|
|
1547
1808
|
] })
|
|
1548
1809
|
] });
|
|
1549
1810
|
}
|
|
@@ -1554,19 +1815,19 @@ function appendComment(current, next) {
|
|
|
1554
1815
|
function ContextBlock({ detail, strings }) {
|
|
1555
1816
|
const captureKey = `detail.context.capture.${detail.capture_method}`;
|
|
1556
1817
|
const captureLabel = strings[captureKey] ?? detail.capture_method;
|
|
1557
|
-
return /* @__PURE__ */
|
|
1558
|
-
/* @__PURE__ */
|
|
1559
|
-
/* @__PURE__ */
|
|
1560
|
-
/* @__PURE__ */
|
|
1561
|
-
/* @__PURE__ */
|
|
1818
|
+
return /* @__PURE__ */ jsxs9("div", { class: "report-detail-context", children: [
|
|
1819
|
+
/* @__PURE__ */ jsxs9("div", { class: "report-detail-context-pills", children: [
|
|
1820
|
+
/* @__PURE__ */ jsx10("span", { class: "pill pill-type", children: strings[`type.${detail.feedback_type}`] }),
|
|
1821
|
+
/* @__PURE__ */ jsx10("span", { class: `pill pill-severity pill-severity--${detail.severity}`, children: strings[`severity.${detail.severity}`] }),
|
|
1822
|
+
/* @__PURE__ */ jsx10("span", { class: "pill pill-capture", children: captureLabel })
|
|
1562
1823
|
] }),
|
|
1563
|
-
/* @__PURE__ */
|
|
1564
|
-
/* @__PURE__ */
|
|
1565
|
-
/* @__PURE__ */
|
|
1824
|
+
/* @__PURE__ */ jsxs9("div", { class: "report-detail-context-line", children: [
|
|
1825
|
+
/* @__PURE__ */ jsx10("span", { class: "report-detail-context-label", children: strings["detail.context.submitted_at"] }),
|
|
1826
|
+
/* @__PURE__ */ jsx10("span", { children: formatSubmittedAt(detail.created_at) })
|
|
1566
1827
|
] }),
|
|
1567
|
-
detail.page_url && /* @__PURE__ */
|
|
1568
|
-
/* @__PURE__ */
|
|
1569
|
-
/* @__PURE__ */
|
|
1828
|
+
detail.page_url && /* @__PURE__ */ jsxs9("div", { class: "report-detail-context-line", title: detail.page_url, children: [
|
|
1829
|
+
/* @__PURE__ */ jsx10("span", { class: "report-detail-context-label", children: strings["detail.context.page"] }),
|
|
1830
|
+
/* @__PURE__ */ jsx10(
|
|
1570
1831
|
"a",
|
|
1571
1832
|
{
|
|
1572
1833
|
class: "report-detail-context-url",
|
|
@@ -1589,6 +1850,120 @@ function formatSubmittedAt(iso) {
|
|
|
1589
1850
|
return iso;
|
|
1590
1851
|
}
|
|
1591
1852
|
}
|
|
1853
|
+
var TECH_CONSOLE_LIMIT = 20;
|
|
1854
|
+
var TECH_NETWORK_LIMIT = 15;
|
|
1855
|
+
function TechnicalContextDrawer({ ctx, strings }) {
|
|
1856
|
+
const errors = ctx.errors ?? [];
|
|
1857
|
+
const consoleLogs = (ctx.consoleLogs ?? []).slice(-TECH_CONSOLE_LIMIT);
|
|
1858
|
+
const network = (ctx.networkRequests ?? []).slice(-TECH_NETWORK_LIMIT);
|
|
1859
|
+
const device = ctx.device;
|
|
1860
|
+
const hasAny = !!device || errors.length > 0 || consoleLogs.length > 0 || network.length > 0;
|
|
1861
|
+
if (!hasAny) return null;
|
|
1862
|
+
const errorCount = errors.length;
|
|
1863
|
+
const summary = errorCount > 0 ? `${strings["detail.tech.title"]} \xB7 ${errorCount} ${errorCount === 1 ? strings["detail.tech.errors_one"] : strings["detail.tech.errors_many"]}` : strings["detail.tech.title"];
|
|
1864
|
+
return /* @__PURE__ */ jsxs9("details", { class: "report-detail-tech", children: [
|
|
1865
|
+
/* @__PURE__ */ jsx10("summary", { children: summary }),
|
|
1866
|
+
/* @__PURE__ */ jsxs9("div", { class: "tech-body", children: [
|
|
1867
|
+
device && /* @__PURE__ */ jsx10(DeviceSection, { device, strings }),
|
|
1868
|
+
errors.length > 0 && /* @__PURE__ */ jsx10(ErrorsSection, { errors, strings }),
|
|
1869
|
+
consoleLogs.length > 0 && /* @__PURE__ */ jsx10(ConsoleSection, { logs: consoleLogs, strings }),
|
|
1870
|
+
network.length > 0 && /* @__PURE__ */ jsx10(NetworkSection, { rows: network, strings })
|
|
1871
|
+
] })
|
|
1872
|
+
] });
|
|
1873
|
+
}
|
|
1874
|
+
function DeviceSection({
|
|
1875
|
+
device,
|
|
1876
|
+
strings
|
|
1877
|
+
}) {
|
|
1878
|
+
const parts = [];
|
|
1879
|
+
if (device?.viewport) {
|
|
1880
|
+
const dpr = device.viewport.dpr ? ` @${device.viewport.dpr}x` : "";
|
|
1881
|
+
parts.push({
|
|
1882
|
+
label: strings["detail.tech.device.viewport"],
|
|
1883
|
+
value: `${device.viewport.w}\xD7${device.viewport.h}${dpr}`
|
|
1884
|
+
});
|
|
1885
|
+
}
|
|
1886
|
+
if (device?.platform) parts.push({ label: strings["detail.tech.device.platform"], value: device.platform });
|
|
1887
|
+
if (device?.language) parts.push({ label: strings["detail.tech.device.language"], value: device.language });
|
|
1888
|
+
if (device?.timezone) parts.push({ label: strings["detail.tech.device.timezone"], value: device.timezone });
|
|
1889
|
+
if (device?.connection) parts.push({ label: strings["detail.tech.device.connection"], value: device.connection });
|
|
1890
|
+
if (parts.length === 0) return null;
|
|
1891
|
+
return /* @__PURE__ */ jsxs9("div", { class: "tech-section", children: [
|
|
1892
|
+
/* @__PURE__ */ jsx10("h4", { children: strings["detail.tech.device"] }),
|
|
1893
|
+
/* @__PURE__ */ jsx10("dl", { class: "tech-device", children: parts.map((p) => /* @__PURE__ */ jsxs9(Fragment, { children: [
|
|
1894
|
+
/* @__PURE__ */ jsx10("dt", { children: p.label }),
|
|
1895
|
+
/* @__PURE__ */ jsx10("dd", { children: p.value })
|
|
1896
|
+
] })) })
|
|
1897
|
+
] });
|
|
1898
|
+
}
|
|
1899
|
+
function ErrorsSection({
|
|
1900
|
+
errors,
|
|
1901
|
+
strings
|
|
1902
|
+
}) {
|
|
1903
|
+
return /* @__PURE__ */ jsxs9("div", { class: "tech-section", children: [
|
|
1904
|
+
/* @__PURE__ */ jsx10("h4", { children: strings["detail.tech.errors"] }),
|
|
1905
|
+
/* @__PURE__ */ jsx10("ul", { class: "tech-errors", children: errors.map((e) => /* @__PURE__ */ jsxs9("li", { children: [
|
|
1906
|
+
/* @__PURE__ */ jsx10("div", { class: "tech-errors-msg", children: e.message }),
|
|
1907
|
+
e.stack && /* @__PURE__ */ jsx10("pre", { class: "tech-errors-stack", children: truncateStack(e.stack) })
|
|
1908
|
+
] })) })
|
|
1909
|
+
] });
|
|
1910
|
+
}
|
|
1911
|
+
function ConsoleSection({
|
|
1912
|
+
logs,
|
|
1913
|
+
strings
|
|
1914
|
+
}) {
|
|
1915
|
+
return /* @__PURE__ */ jsxs9("div", { class: "tech-section", children: [
|
|
1916
|
+
/* @__PURE__ */ jsx10("h4", { children: strings["detail.tech.console"] }),
|
|
1917
|
+
/* @__PURE__ */ jsx10("ul", { class: "tech-console", children: logs.map((l) => /* @__PURE__ */ jsxs9("li", { class: `tech-console-row tech-console-row--${l.level}`, children: [
|
|
1918
|
+
/* @__PURE__ */ jsx10("span", { class: "tech-console-level", children: l.level }),
|
|
1919
|
+
/* @__PURE__ */ jsx10("span", { class: "tech-console-msg", children: l.message })
|
|
1920
|
+
] })) })
|
|
1921
|
+
] });
|
|
1922
|
+
}
|
|
1923
|
+
function NetworkSection({
|
|
1924
|
+
rows,
|
|
1925
|
+
strings
|
|
1926
|
+
}) {
|
|
1927
|
+
return /* @__PURE__ */ jsxs9("div", { class: "tech-section", children: [
|
|
1928
|
+
/* @__PURE__ */ jsx10("h4", { children: strings["detail.tech.network"] }),
|
|
1929
|
+
/* @__PURE__ */ jsx10("ul", { class: "tech-network", children: rows.map((n) => {
|
|
1930
|
+
const failed = n.status >= 400 || n.status === 0;
|
|
1931
|
+
return /* @__PURE__ */ jsxs9("li", { class: `tech-network-row${failed ? " tech-network-row--fail" : ""}`, children: [
|
|
1932
|
+
/* @__PURE__ */ jsx10("span", { class: "tech-network-status", children: n.status || "\u2014" }),
|
|
1933
|
+
/* @__PURE__ */ jsx10("span", { class: "tech-network-method", children: n.method }),
|
|
1934
|
+
/* @__PURE__ */ jsx10("span", { class: "tech-network-url", title: n.url, children: truncateUrl(n.url, 56) }),
|
|
1935
|
+
/* @__PURE__ */ jsxs9("span", { class: "tech-network-time", children: [
|
|
1936
|
+
Math.round(n.durationMs),
|
|
1937
|
+
"ms"
|
|
1938
|
+
] })
|
|
1939
|
+
] });
|
|
1940
|
+
}) })
|
|
1941
|
+
] });
|
|
1942
|
+
}
|
|
1943
|
+
function truncateStack(stack) {
|
|
1944
|
+
const lines = stack.split("\n");
|
|
1945
|
+
if (lines.length <= 12) return stack;
|
|
1946
|
+
return lines.slice(0, 12).join("\n") + "\n \u2026";
|
|
1947
|
+
}
|
|
1948
|
+
function StatusHistorySection({ rows, strings }) {
|
|
1949
|
+
return /* @__PURE__ */ jsxs9("div", { class: "report-detail-history", children: [
|
|
1950
|
+
/* @__PURE__ */ jsx10("h3", { class: "report-detail-section", children: strings["detail.history"] }),
|
|
1951
|
+
/* @__PURE__ */ jsx10("ol", { class: "status-history", children: rows.map((r) => {
|
|
1952
|
+
const from = strings[`status.${r.from_status}`] ?? r.from_status;
|
|
1953
|
+
const to = strings[`status.${r.to_status}`] ?? r.to_status;
|
|
1954
|
+
const sourceKey = r.changed_by_source === "mcp" ? "detail.author.mcp" : r.changed_by_source === "system" ? "detail.author.system" : "detail.author.staff";
|
|
1955
|
+
return /* @__PURE__ */ jsxs9("li", { class: "status-history-row", children: [
|
|
1956
|
+
/* @__PURE__ */ jsx10("span", { class: "status-history-time", children: formatSubmittedAt(r.created_at) }),
|
|
1957
|
+
/* @__PURE__ */ jsxs9("span", { class: "status-history-transition", children: [
|
|
1958
|
+
/* @__PURE__ */ jsx10("span", { class: `pill pill-status pill-status--${r.from_status}`, children: from }),
|
|
1959
|
+
/* @__PURE__ */ jsx10("span", { class: "status-history-arrow", "aria-hidden": "true", children: "\u2192" }),
|
|
1960
|
+
/* @__PURE__ */ jsx10("span", { class: `pill pill-status pill-status--${r.to_status}`, children: to })
|
|
1961
|
+
] }),
|
|
1962
|
+
/* @__PURE__ */ jsx10("span", { class: `status-history-source status-history-source--${r.changed_by_source}`, children: strings[sourceKey] })
|
|
1963
|
+
] });
|
|
1964
|
+
}) })
|
|
1965
|
+
] });
|
|
1966
|
+
}
|
|
1592
1967
|
|
|
1593
1968
|
// src/widget/styles.ts
|
|
1594
1969
|
var WIDGET_STYLES = `
|
|
@@ -2310,10 +2685,259 @@ var WIDGET_STYLES = `
|
|
|
2310
2685
|
justify-content: flex-end;
|
|
2311
2686
|
gap: 6px;
|
|
2312
2687
|
}
|
|
2688
|
+
|
|
2689
|
+
/* Status history \u2014 read-only audit trail visible to the submitter.
|
|
2690
|
+
Timeline of who flipped the status and when (operator vs. agent vs.
|
|
2691
|
+
system). Bordered, neutral surface so it doesn't compete visually
|
|
2692
|
+
with the conversation thread above. */
|
|
2693
|
+
.report-detail-history {
|
|
2694
|
+
display: flex;
|
|
2695
|
+
flex-direction: column;
|
|
2696
|
+
gap: 6px;
|
|
2697
|
+
border: 1px solid var(--mfb-border);
|
|
2698
|
+
border-radius: var(--mfb-radius);
|
|
2699
|
+
background: var(--mfb-surface);
|
|
2700
|
+
padding: 8px 10px;
|
|
2701
|
+
}
|
|
2702
|
+
.report-detail-history .report-detail-section {
|
|
2703
|
+
margin: 0;
|
|
2704
|
+
font-size: 11px;
|
|
2705
|
+
text-transform: uppercase;
|
|
2706
|
+
letter-spacing: 0.04em;
|
|
2707
|
+
color: var(--mfb-text-muted);
|
|
2708
|
+
}
|
|
2709
|
+
.status-history {
|
|
2710
|
+
list-style: none;
|
|
2711
|
+
margin: 0;
|
|
2712
|
+
padding: 0;
|
|
2713
|
+
display: flex;
|
|
2714
|
+
flex-direction: column;
|
|
2715
|
+
gap: 4px;
|
|
2716
|
+
}
|
|
2717
|
+
.status-history-row {
|
|
2718
|
+
display: grid;
|
|
2719
|
+
grid-template-columns: auto 1fr auto;
|
|
2720
|
+
gap: 8px;
|
|
2721
|
+
align-items: center;
|
|
2722
|
+
font-size: 11px;
|
|
2723
|
+
}
|
|
2724
|
+
.status-history-time {
|
|
2725
|
+
color: var(--mfb-text-muted);
|
|
2726
|
+
font-variant-numeric: tabular-nums;
|
|
2727
|
+
white-space: nowrap;
|
|
2728
|
+
}
|
|
2729
|
+
.status-history-transition {
|
|
2730
|
+
display: inline-flex;
|
|
2731
|
+
gap: 4px;
|
|
2732
|
+
align-items: center;
|
|
2733
|
+
flex-wrap: wrap;
|
|
2734
|
+
}
|
|
2735
|
+
.status-history-arrow {
|
|
2736
|
+
color: var(--mfb-text-muted);
|
|
2737
|
+
font-size: 11px;
|
|
2738
|
+
}
|
|
2739
|
+
.status-history-source {
|
|
2740
|
+
font-size: 10px;
|
|
2741
|
+
text-transform: uppercase;
|
|
2742
|
+
letter-spacing: 0.04em;
|
|
2743
|
+
font-weight: 600;
|
|
2744
|
+
white-space: nowrap;
|
|
2745
|
+
}
|
|
2746
|
+
.status-history-source--mcp { color: #6b21a8; }
|
|
2747
|
+
.status-history-source--user { color: #1e40af; }
|
|
2748
|
+
.status-history-source--system { color: var(--mfb-text-muted); }
|
|
2749
|
+
|
|
2750
|
+
/* Transparency drawer \u2014 closed by default. The user can click open to
|
|
2751
|
+
verify what their browser sent: device, errors, console tail, network
|
|
2752
|
+
tail. Read-only; purely a trust-building gesture. */
|
|
2753
|
+
.report-detail-tech {
|
|
2754
|
+
border: 1px solid var(--mfb-border);
|
|
2755
|
+
border-radius: var(--mfb-radius);
|
|
2756
|
+
background: var(--mfb-surface);
|
|
2757
|
+
}
|
|
2758
|
+
.report-detail-tech > summary {
|
|
2759
|
+
cursor: pointer;
|
|
2760
|
+
padding: 8px 10px;
|
|
2761
|
+
font-size: 11px;
|
|
2762
|
+
text-transform: uppercase;
|
|
2763
|
+
letter-spacing: 0.04em;
|
|
2764
|
+
color: var(--mfb-text-muted);
|
|
2765
|
+
user-select: none;
|
|
2766
|
+
list-style: none;
|
|
2767
|
+
}
|
|
2768
|
+
.report-detail-tech > summary::-webkit-details-marker { display: none; }
|
|
2769
|
+
.report-detail-tech > summary::before {
|
|
2770
|
+
content: '\u25B8';
|
|
2771
|
+
display: inline-block;
|
|
2772
|
+
margin-right: 6px;
|
|
2773
|
+
transition: transform 120ms;
|
|
2774
|
+
}
|
|
2775
|
+
.report-detail-tech[open] > summary::before { transform: rotate(90deg); }
|
|
2776
|
+
.tech-body {
|
|
2777
|
+
padding: 0 10px 10px 10px;
|
|
2778
|
+
display: flex;
|
|
2779
|
+
flex-direction: column;
|
|
2780
|
+
gap: 10px;
|
|
2781
|
+
}
|
|
2782
|
+
.tech-section h4 {
|
|
2783
|
+
margin: 0 0 4px 0;
|
|
2784
|
+
font-size: 10px;
|
|
2785
|
+
text-transform: uppercase;
|
|
2786
|
+
letter-spacing: 0.04em;
|
|
2787
|
+
color: var(--mfb-text-muted);
|
|
2788
|
+
font-weight: 600;
|
|
2789
|
+
}
|
|
2790
|
+
.tech-device {
|
|
2791
|
+
display: grid;
|
|
2792
|
+
grid-template-columns: max-content 1fr;
|
|
2793
|
+
column-gap: 8px;
|
|
2794
|
+
row-gap: 2px;
|
|
2795
|
+
margin: 0;
|
|
2796
|
+
font-size: 11px;
|
|
2797
|
+
font-variant-numeric: tabular-nums;
|
|
2798
|
+
}
|
|
2799
|
+
.tech-device dt { color: var(--mfb-text-muted); }
|
|
2800
|
+
.tech-device dd { margin: 0; }
|
|
2801
|
+
.tech-errors, .tech-console, .tech-network {
|
|
2802
|
+
list-style: none;
|
|
2803
|
+
margin: 0;
|
|
2804
|
+
padding: 0;
|
|
2805
|
+
display: flex;
|
|
2806
|
+
flex-direction: column;
|
|
2807
|
+
gap: 2px;
|
|
2808
|
+
font-size: 11px;
|
|
2809
|
+
font-family: ui-monospace, "SF Mono", Menlo, monospace;
|
|
2810
|
+
max-height: 200px;
|
|
2811
|
+
overflow-y: auto;
|
|
2812
|
+
}
|
|
2813
|
+
.tech-errors li { padding: 4px 0; border-top: 1px solid var(--mfb-border); }
|
|
2814
|
+
.tech-errors li:first-child { border-top: 0; }
|
|
2815
|
+
.tech-errors-msg { color: #991b1b; font-weight: 600; }
|
|
2816
|
+
.tech-errors-stack {
|
|
2817
|
+
margin: 4px 0 0 0;
|
|
2818
|
+
padding: 4px 6px;
|
|
2819
|
+
background: var(--mfb-bg);
|
|
2820
|
+
border-radius: 4px;
|
|
2821
|
+
font-size: 10px;
|
|
2822
|
+
white-space: pre-wrap;
|
|
2823
|
+
color: var(--mfb-text-muted);
|
|
2824
|
+
max-height: 120px;
|
|
2825
|
+
overflow-y: auto;
|
|
2826
|
+
}
|
|
2827
|
+
.tech-console-row {
|
|
2828
|
+
display: grid;
|
|
2829
|
+
grid-template-columns: 48px 1fr;
|
|
2830
|
+
gap: 6px;
|
|
2831
|
+
padding: 1px 0;
|
|
2832
|
+
}
|
|
2833
|
+
.tech-console-level {
|
|
2834
|
+
text-transform: uppercase;
|
|
2835
|
+
font-size: 9px;
|
|
2836
|
+
font-weight: 600;
|
|
2837
|
+
align-self: center;
|
|
2838
|
+
color: var(--mfb-text-muted);
|
|
2839
|
+
}
|
|
2840
|
+
.tech-console-row--warn .tech-console-level { color: #92400e; }
|
|
2841
|
+
.tech-console-row--error .tech-console-level { color: #991b1b; }
|
|
2842
|
+
.tech-console-row--info .tech-console-level { color: #1e40af; }
|
|
2843
|
+
.tech-console-msg { word-break: break-word; }
|
|
2844
|
+
.tech-network-row {
|
|
2845
|
+
display: grid;
|
|
2846
|
+
grid-template-columns: 48px 56px 1fr 56px;
|
|
2847
|
+
gap: 6px;
|
|
2848
|
+
padding: 1px 0;
|
|
2849
|
+
}
|
|
2850
|
+
.tech-network-row--fail .tech-network-status { color: #991b1b; font-weight: 700; }
|
|
2851
|
+
.tech-network-status { font-variant-numeric: tabular-nums; }
|
|
2852
|
+
.tech-network-method { color: var(--mfb-text-muted); }
|
|
2853
|
+
.tech-network-url { word-break: break-all; }
|
|
2854
|
+
.tech-network-time { text-align: right; color: var(--mfb-text-muted); font-variant-numeric: tabular-nums; }
|
|
2855
|
+
|
|
2856
|
+
/* KPI strip \u2014 four pills above the My Reports list. Each is clickable
|
|
2857
|
+
to filter the rows below. Borrowed from thePnr's FeedbackKPICards. */
|
|
2858
|
+
.kpi-strip {
|
|
2859
|
+
display: grid;
|
|
2860
|
+
grid-template-columns: repeat(4, 1fr);
|
|
2861
|
+
gap: 6px;
|
|
2862
|
+
}
|
|
2863
|
+
.kpi-cell {
|
|
2864
|
+
appearance: none;
|
|
2865
|
+
background: var(--mfb-bg);
|
|
2866
|
+
border: 1px solid var(--mfb-border);
|
|
2867
|
+
border-radius: var(--mfb-radius);
|
|
2868
|
+
padding: 8px 6px;
|
|
2869
|
+
cursor: pointer;
|
|
2870
|
+
display: flex;
|
|
2871
|
+
flex-direction: column;
|
|
2872
|
+
align-items: flex-start;
|
|
2873
|
+
gap: 2px;
|
|
2874
|
+
font-family: inherit;
|
|
2875
|
+
color: inherit;
|
|
2876
|
+
transition: background 120ms, border-color 120ms;
|
|
2877
|
+
}
|
|
2878
|
+
.kpi-cell:hover { background: var(--mfb-surface); }
|
|
2879
|
+
.kpi-cell.is-active {
|
|
2880
|
+
border-color: var(--mfb-accent);
|
|
2881
|
+
background: rgba(59, 130, 246, 0.08);
|
|
2882
|
+
}
|
|
2883
|
+
.kpi-cell.is-active.kpi-cell--in_progress { border-color: #d97706; background: rgba(251, 191, 36, 0.10); }
|
|
2884
|
+
.kpi-cell.is-active.kpi-cell--awaiting_validation { border-color: #9333ea; background: rgba(168, 85, 247, 0.10); }
|
|
2885
|
+
.kpi-cell.is-active.kpi-cell--resolved { border-color: #059669; background: rgba(16, 185, 129, 0.10); }
|
|
2886
|
+
.kpi-value {
|
|
2887
|
+
font-size: 18px;
|
|
2888
|
+
font-weight: 700;
|
|
2889
|
+
font-variant-numeric: tabular-nums;
|
|
2890
|
+
line-height: 1;
|
|
2891
|
+
}
|
|
2892
|
+
.kpi-label {
|
|
2893
|
+
font-size: 10px;
|
|
2894
|
+
text-transform: uppercase;
|
|
2895
|
+
letter-spacing: 0.04em;
|
|
2896
|
+
color: var(--mfb-text-muted);
|
|
2897
|
+
}
|
|
2898
|
+
|
|
2899
|
+
/* Changelog ("This week") \u2014 week-grouped resolved-report timeline.
|
|
2900
|
+
Borrowed from thePnr's FeedbackChangelogView; the header per group
|
|
2901
|
+
reads "\u25CF Week of {date} \u2501\u2501\u2501 {n} resolved". */
|
|
2902
|
+
.changelog-groups {
|
|
2903
|
+
display: flex;
|
|
2904
|
+
flex-direction: column;
|
|
2905
|
+
gap: 12px;
|
|
2906
|
+
}
|
|
2907
|
+
.changelog-group {
|
|
2908
|
+
display: flex;
|
|
2909
|
+
flex-direction: column;
|
|
2910
|
+
gap: 6px;
|
|
2911
|
+
}
|
|
2912
|
+
.changelog-group-header {
|
|
2913
|
+
display: grid;
|
|
2914
|
+
grid-template-columns: auto auto 1fr auto;
|
|
2915
|
+
gap: 6px;
|
|
2916
|
+
align-items: center;
|
|
2917
|
+
font-size: 11px;
|
|
2918
|
+
text-transform: uppercase;
|
|
2919
|
+
letter-spacing: 0.04em;
|
|
2920
|
+
color: var(--mfb-text-muted);
|
|
2921
|
+
}
|
|
2922
|
+
.changelog-group-marker {
|
|
2923
|
+
color: var(--mfb-accent);
|
|
2924
|
+
font-size: 14px;
|
|
2925
|
+
line-height: 1;
|
|
2926
|
+
}
|
|
2927
|
+
.changelog-group-label { font-weight: 600; }
|
|
2928
|
+
.changelog-group-rule {
|
|
2929
|
+
height: 1px;
|
|
2930
|
+
background: var(--mfb-border);
|
|
2931
|
+
align-self: center;
|
|
2932
|
+
}
|
|
2933
|
+
.changelog-group-count {
|
|
2934
|
+
font-variant-numeric: tabular-nums;
|
|
2935
|
+
font-weight: 600;
|
|
2936
|
+
}
|
|
2313
2937
|
`;
|
|
2314
2938
|
|
|
2315
2939
|
// src/widget/mount.tsx
|
|
2316
|
-
import { Fragment, jsx as
|
|
2940
|
+
import { Fragment as Fragment2, jsx as jsx11, jsxs as jsxs10 } from "preact/jsx-runtime";
|
|
2317
2941
|
function mountWidget(options) {
|
|
2318
2942
|
const shadow = options.host.attachShadow({ mode: "open" });
|
|
2319
2943
|
const style = document.createElement("style");
|
|
@@ -2360,22 +2984,22 @@ function mountWidget(options) {
|
|
|
2360
2984
|
const externalId = options.getExternalId?.();
|
|
2361
2985
|
const fabVisible = options.showFAB && (options.getExternalId === void 0 ? true : Boolean(externalId));
|
|
2362
2986
|
const showMineTab = Boolean(options.api && externalId);
|
|
2363
|
-
return /* @__PURE__ */
|
|
2364
|
-
fabVisible && /* @__PURE__ */
|
|
2987
|
+
return /* @__PURE__ */ jsxs10(Fragment2, { children: [
|
|
2988
|
+
fabVisible && /* @__PURE__ */ jsx11(
|
|
2365
2989
|
Fab,
|
|
2366
2990
|
{
|
|
2367
2991
|
label: options.strings["fab.label"],
|
|
2368
2992
|
onClick: () => rerender({ ...currentState, open: true })
|
|
2369
2993
|
}
|
|
2370
2994
|
),
|
|
2371
|
-
state.open && /* @__PURE__ */
|
|
2995
|
+
state.open && /* @__PURE__ */ jsxs10(
|
|
2372
2996
|
Modal,
|
|
2373
2997
|
{
|
|
2374
2998
|
onDismiss: () => rerender(clearSelected({ ...currentState, open: false, status: "idle" })),
|
|
2375
2999
|
closeLabel: options.strings["form.close"],
|
|
2376
3000
|
children: [
|
|
2377
|
-
showMineTab && /* @__PURE__ */
|
|
2378
|
-
/* @__PURE__ */
|
|
3001
|
+
showMineTab && /* @__PURE__ */ jsxs10("div", { class: "tab-strip", role: "tablist", children: [
|
|
3002
|
+
/* @__PURE__ */ jsx11(
|
|
2379
3003
|
"button",
|
|
2380
3004
|
{
|
|
2381
3005
|
type: "button",
|
|
@@ -2386,7 +3010,7 @@ function mountWidget(options) {
|
|
|
2386
3010
|
children: options.strings["tab.send"]
|
|
2387
3011
|
}
|
|
2388
3012
|
),
|
|
2389
|
-
/* @__PURE__ */
|
|
3013
|
+
/* @__PURE__ */ jsx11(
|
|
2390
3014
|
"button",
|
|
2391
3015
|
{
|
|
2392
3016
|
type: "button",
|
|
@@ -2396,9 +3020,20 @@ function mountWidget(options) {
|
|
|
2396
3020
|
onClick: () => rerender(clearSelected({ ...currentState, tab: "mine" })),
|
|
2397
3021
|
children: options.strings["tab.mine"]
|
|
2398
3022
|
}
|
|
3023
|
+
),
|
|
3024
|
+
/* @__PURE__ */ jsx11(
|
|
3025
|
+
"button",
|
|
3026
|
+
{
|
|
3027
|
+
type: "button",
|
|
3028
|
+
role: "tab",
|
|
3029
|
+
"aria-selected": state.tab === "changelog",
|
|
3030
|
+
class: `tab-button ${state.tab === "changelog" ? "is-active" : ""}`,
|
|
3031
|
+
onClick: () => rerender(clearSelected({ ...currentState, tab: "changelog" })),
|
|
3032
|
+
children: options.strings["tab.changelog"]
|
|
3033
|
+
}
|
|
2399
3034
|
)
|
|
2400
3035
|
] }),
|
|
2401
|
-
state.tab === "send" && /* @__PURE__ */
|
|
3036
|
+
state.tab === "send" && /* @__PURE__ */ jsx11(
|
|
2402
3037
|
Form,
|
|
2403
3038
|
{
|
|
2404
3039
|
strings: options.strings,
|
|
@@ -2408,7 +3043,7 @@ function mountWidget(options) {
|
|
|
2408
3043
|
...state.error !== void 0 && { errorMessage: state.error }
|
|
2409
3044
|
}
|
|
2410
3045
|
),
|
|
2411
|
-
state.tab === "mine" && options.api && externalId && !state.selectedReportId && /* @__PURE__ */
|
|
3046
|
+
state.tab === "mine" && options.api && externalId && !state.selectedReportId && /* @__PURE__ */ jsx11(
|
|
2412
3047
|
MineList,
|
|
2413
3048
|
{
|
|
2414
3049
|
api: options.api,
|
|
@@ -2417,7 +3052,7 @@ function mountWidget(options) {
|
|
|
2417
3052
|
onSelect: (row) => rerender({ ...currentState, selectedReportId: row.id })
|
|
2418
3053
|
}
|
|
2419
3054
|
),
|
|
2420
|
-
state.tab === "mine" && options.api && externalId && state.selectedReportId && /* @__PURE__ */
|
|
3055
|
+
(state.tab === "mine" || state.tab === "changelog") && options.api && externalId && state.selectedReportId && /* @__PURE__ */ jsx11(
|
|
2421
3056
|
ReportDetailView,
|
|
2422
3057
|
{
|
|
2423
3058
|
api: options.api,
|
|
@@ -2426,6 +3061,15 @@ function mountWidget(options) {
|
|
|
2426
3061
|
strings: options.strings,
|
|
2427
3062
|
onBack: () => rerender(clearSelected({ ...currentState }))
|
|
2428
3063
|
}
|
|
3064
|
+
),
|
|
3065
|
+
state.tab === "changelog" && options.api && externalId && !state.selectedReportId && /* @__PURE__ */ jsx11(
|
|
3066
|
+
ChangelogList,
|
|
3067
|
+
{
|
|
3068
|
+
api: options.api,
|
|
3069
|
+
externalId,
|
|
3070
|
+
strings: options.strings,
|
|
3071
|
+
onSelect: (row) => rerender({ ...currentState, selectedReportId: row.id })
|
|
3072
|
+
}
|
|
2429
3073
|
)
|
|
2430
3074
|
]
|
|
2431
3075
|
}
|
|
@@ -2498,6 +3142,9 @@ function createFeedback(config) {
|
|
|
2498
3142
|
capture_method: screenshot ? manualScreenshot ? "manual" : "html2canvas" : "none",
|
|
2499
3143
|
technical_context
|
|
2500
3144
|
};
|
|
3145
|
+
if ("0.8.0") {
|
|
3146
|
+
payload.widget_version = "0.8.0";
|
|
3147
|
+
}
|
|
2501
3148
|
if (screenshot) payload.screenshot = screenshot;
|
|
2502
3149
|
if (values.synthetic) payload.synthetic = true;
|
|
2503
3150
|
if (user?.id !== void 0 && user.id !== null && user.id !== "") {
|
|
@@ -2582,4 +3229,4 @@ function createFeedback(config) {
|
|
|
2582
3229
|
export {
|
|
2583
3230
|
createFeedback
|
|
2584
3231
|
};
|
|
2585
|
-
//# sourceMappingURL=chunk-
|
|
3232
|
+
//# sourceMappingURL=chunk-XHK6VAU2.mjs.map
|