@mhosaic/feedback 0.7.3 → 0.9.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-2VBGH64F.mjs → chunk-KO5NHJ7J.mjs} +867 -226
- package/dist/chunk-KO5NHJ7J.mjs.map +1 -0
- package/dist/embed.min.js +257 -5
- package/dist/embed.min.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/react.mjs +1 -1
- package/package.json +1 -1
- package/dist/chunk-2VBGH64F.mjs.map +0 -1
|
@@ -62,6 +62,18 @@ function createApiClient(options) {
|
|
|
62
62
|
}
|
|
63
63
|
return response.json();
|
|
64
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
|
+
}
|
|
65
77
|
async function getReport(reportId, externalId) {
|
|
66
78
|
const response = await fetcher(
|
|
67
79
|
`${endpoint}/api/feedback/v1/reports/widget/${reportId}/`,
|
|
@@ -112,7 +124,7 @@ function createApiClient(options) {
|
|
|
112
124
|
}
|
|
113
125
|
return response.json();
|
|
114
126
|
}
|
|
115
|
-
return { submitReport, listMine, getReport, addComment, closeAsResolved };
|
|
127
|
+
return { submitReport, listMine, listChangelog, getReport, addComment, closeAsResolved };
|
|
116
128
|
}
|
|
117
129
|
|
|
118
130
|
// src/capture/urlSanitizer.ts
|
|
@@ -473,6 +485,12 @@ var DEFAULT_STRINGS = {
|
|
|
473
485
|
"annotator.applying": "Applying\u2026",
|
|
474
486
|
"tab.send": "Send",
|
|
475
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",
|
|
476
494
|
"mine.empty.title": "No reports yet",
|
|
477
495
|
"mine.empty.body": "Once you send feedback you can follow the thread here.",
|
|
478
496
|
"mine.refresh": "Refresh",
|
|
@@ -480,6 +498,11 @@ var DEFAULT_STRINGS = {
|
|
|
480
498
|
"mine.error": "Could not load your reports.",
|
|
481
499
|
"mine.replies_one": "1 reply",
|
|
482
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",
|
|
483
506
|
"detail.back": "Back",
|
|
484
507
|
"detail.thread": "Conversation",
|
|
485
508
|
"detail.no_replies": "No replies yet \u2014 we\u2019ll let you know when an operator responds.",
|
|
@@ -498,6 +521,18 @@ var DEFAULT_STRINGS = {
|
|
|
498
521
|
"detail.author.staff": "Operator",
|
|
499
522
|
"detail.author.mcp": "Mhosaic Team",
|
|
500
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)",
|
|
501
536
|
"status.new": "New",
|
|
502
537
|
"status.in_progress": "In progress",
|
|
503
538
|
"status.awaiting_validation": "Awaiting your validation",
|
|
@@ -552,6 +587,12 @@ var FRENCH_STRINGS = {
|
|
|
552
587
|
"annotator.applying": "Application\u2026",
|
|
553
588
|
"tab.send": "Envoyer",
|
|
554
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",
|
|
555
596
|
"mine.empty.title": "Aucun rapport",
|
|
556
597
|
"mine.empty.body": "Apr\xE8s votre premier envoi vous pourrez suivre la conversation ici.",
|
|
557
598
|
"mine.refresh": "Actualiser",
|
|
@@ -559,6 +600,11 @@ var FRENCH_STRINGS = {
|
|
|
559
600
|
"mine.error": "Impossible de charger vos rapports.",
|
|
560
601
|
"mine.replies_one": "1 r\xE9ponse",
|
|
561
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",
|
|
562
608
|
"detail.back": "Retour",
|
|
563
609
|
"detail.thread": "Conversation",
|
|
564
610
|
"detail.no_replies": "Pas encore de r\xE9ponse \u2014 vous serez notifi\xE9 d\xE8s qu\u2019un op\xE9rateur r\xE9pondra.",
|
|
@@ -577,6 +623,18 @@ var FRENCH_STRINGS = {
|
|
|
577
623
|
"detail.author.staff": "Op\xE9rateur",
|
|
578
624
|
"detail.author.mcp": "\xC9quipe Mhosaic",
|
|
579
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)",
|
|
580
638
|
"status.new": "Nouveau",
|
|
581
639
|
"status.in_progress": "En cours",
|
|
582
640
|
"status.awaiting_validation": "En attente de validation",
|
|
@@ -606,10 +664,162 @@ function resolveStrings(overrides, options = {}) {
|
|
|
606
664
|
import { h, render } from "preact";
|
|
607
665
|
import { useCallback } from "preact/hooks";
|
|
608
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
|
+
|
|
609
819
|
// src/widget/Fab.tsx
|
|
610
|
-
import { jsx } from "preact/jsx-runtime";
|
|
820
|
+
import { jsx as jsx3 } from "preact/jsx-runtime";
|
|
611
821
|
function ChatBubbleIcon() {
|
|
612
|
-
return /* @__PURE__ */
|
|
822
|
+
return /* @__PURE__ */ jsx3(
|
|
613
823
|
"svg",
|
|
614
824
|
{
|
|
615
825
|
width: "24",
|
|
@@ -618,7 +828,7 @@ function ChatBubbleIcon() {
|
|
|
618
828
|
fill: "none",
|
|
619
829
|
"aria-hidden": "true",
|
|
620
830
|
focusable: "false",
|
|
621
|
-
children: /* @__PURE__ */
|
|
831
|
+
children: /* @__PURE__ */ jsx3(
|
|
622
832
|
"path",
|
|
623
833
|
{
|
|
624
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",
|
|
@@ -629,15 +839,15 @@ function ChatBubbleIcon() {
|
|
|
629
839
|
);
|
|
630
840
|
}
|
|
631
841
|
function Fab({ label, onClick }) {
|
|
632
|
-
return /* @__PURE__ */
|
|
842
|
+
return /* @__PURE__ */ jsx3("button", { type: "button", class: "fab", "aria-label": label, title: label, onClick, children: /* @__PURE__ */ jsx3(ChatBubbleIcon, {}) });
|
|
633
843
|
}
|
|
634
844
|
|
|
635
845
|
// src/widget/Form.tsx
|
|
636
|
-
import { useEffect as
|
|
846
|
+
import { useEffect as useEffect3, useRef as useRef3, useState as useState3 } from "preact/hooks";
|
|
637
847
|
|
|
638
848
|
// src/widget/Annotator.tsx
|
|
639
|
-
import { useEffect, useRef, useState } from "preact/hooks";
|
|
640
|
-
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";
|
|
641
851
|
var COLORS = ["#ef4444", "#f59e0b", "#10b981", "#3b82f6", "#ffffff"];
|
|
642
852
|
function drawShape(ctx, shape) {
|
|
643
853
|
ctx.save();
|
|
@@ -696,27 +906,27 @@ function drawArrow(ctx, x1, y1, x2, y2) {
|
|
|
696
906
|
ctx.fill();
|
|
697
907
|
}
|
|
698
908
|
var Icon = {
|
|
699
|
-
rect: /* @__PURE__ */
|
|
700
|
-
arrow: /* @__PURE__ */
|
|
701
|
-
pencil: /* @__PURE__ */
|
|
702
|
-
text: /* @__PURE__ */
|
|
703
|
-
undo: /* @__PURE__ */
|
|
704
|
-
trash: /* @__PURE__ */
|
|
705
|
-
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" }) })
|
|
706
916
|
};
|
|
707
917
|
function Annotator({ imageBlob, strings, onSave, onCancel }) {
|
|
708
|
-
const canvasRef =
|
|
709
|
-
const containerRef =
|
|
710
|
-
const imageRef =
|
|
711
|
-
const [tool, setTool] =
|
|
712
|
-
const [color, setColor] =
|
|
713
|
-
const [shapes, setShapes] =
|
|
714
|
-
const isDrawingRef =
|
|
715
|
-
const [draftShape, setDraftShape] =
|
|
716
|
-
const [imageLoaded, setImageLoaded] =
|
|
717
|
-
const [saving, setSaving] =
|
|
718
|
-
const scaleRef =
|
|
719
|
-
|
|
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(() => {
|
|
720
930
|
const onKey = (e) => {
|
|
721
931
|
if (e.key === "Escape") {
|
|
722
932
|
e.stopPropagation();
|
|
@@ -726,7 +936,7 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
|
|
|
726
936
|
window.addEventListener("keydown", onKey);
|
|
727
937
|
return () => window.removeEventListener("keydown", onKey);
|
|
728
938
|
}, [onCancel]);
|
|
729
|
-
|
|
939
|
+
useEffect2(() => {
|
|
730
940
|
const url = URL.createObjectURL(imageBlob);
|
|
731
941
|
const img = new Image();
|
|
732
942
|
img.onload = () => {
|
|
@@ -736,7 +946,7 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
|
|
|
736
946
|
img.src = url;
|
|
737
947
|
return () => URL.revokeObjectURL(url);
|
|
738
948
|
}, [imageBlob]);
|
|
739
|
-
|
|
949
|
+
useEffect2(() => {
|
|
740
950
|
if (!imageLoaded || !canvasRef.current || !imageRef.current || !containerRef.current) {
|
|
741
951
|
return;
|
|
742
952
|
}
|
|
@@ -753,7 +963,7 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
|
|
|
753
963
|
canvas.style.height = `${img.height * fitScale}px`;
|
|
754
964
|
redraw();
|
|
755
965
|
}, [imageLoaded]);
|
|
756
|
-
|
|
966
|
+
useEffect2(() => {
|
|
757
967
|
redraw();
|
|
758
968
|
}, [shapes, draftShape]);
|
|
759
969
|
function redraw() {
|
|
@@ -849,7 +1059,7 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
|
|
|
849
1059
|
{ id: "freehand", icon: Icon.pencil, label: strings["annotator.tool.freehand"] },
|
|
850
1060
|
{ id: "text", icon: Icon.text, label: strings["annotator.tool.text"] }
|
|
851
1061
|
];
|
|
852
|
-
return /* @__PURE__ */
|
|
1062
|
+
return /* @__PURE__ */ jsx4(
|
|
853
1063
|
"div",
|
|
854
1064
|
{
|
|
855
1065
|
class: "annotator-backdrop",
|
|
@@ -857,10 +1067,10 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
|
|
|
857
1067
|
onClick: (e) => {
|
|
858
1068
|
if (e.target === e.currentTarget) onCancel();
|
|
859
1069
|
},
|
|
860
|
-
children: /* @__PURE__ */
|
|
861
|
-
/* @__PURE__ */
|
|
862
|
-
/* @__PURE__ */
|
|
863
|
-
/* @__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(
|
|
864
1074
|
"button",
|
|
865
1075
|
{
|
|
866
1076
|
type: "button",
|
|
@@ -871,8 +1081,8 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
|
|
|
871
1081
|
}
|
|
872
1082
|
)
|
|
873
1083
|
] }),
|
|
874
|
-
/* @__PURE__ */
|
|
875
|
-
/* @__PURE__ */
|
|
1084
|
+
/* @__PURE__ */ jsxs3("div", { class: "annotator-toolbar", children: [
|
|
1085
|
+
/* @__PURE__ */ jsx4("div", { class: "annotator-tools", children: tools.map((t) => /* @__PURE__ */ jsx4(
|
|
876
1086
|
"button",
|
|
877
1087
|
{
|
|
878
1088
|
type: "button",
|
|
@@ -885,8 +1095,8 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
|
|
|
885
1095
|
},
|
|
886
1096
|
t.id
|
|
887
1097
|
)) }),
|
|
888
|
-
/* @__PURE__ */
|
|
889
|
-
/* @__PURE__ */
|
|
1098
|
+
/* @__PURE__ */ jsx4("span", { class: "annotator-sep" }),
|
|
1099
|
+
/* @__PURE__ */ jsx4("div", { class: "annotator-colors", children: COLORS.map((c) => /* @__PURE__ */ jsx4(
|
|
890
1100
|
"button",
|
|
891
1101
|
{
|
|
892
1102
|
type: "button",
|
|
@@ -898,8 +1108,8 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
|
|
|
898
1108
|
},
|
|
899
1109
|
c
|
|
900
1110
|
)) }),
|
|
901
|
-
/* @__PURE__ */
|
|
902
|
-
/* @__PURE__ */
|
|
1111
|
+
/* @__PURE__ */ jsx4("span", { class: "annotator-sep" }),
|
|
1112
|
+
/* @__PURE__ */ jsxs3(
|
|
903
1113
|
"button",
|
|
904
1114
|
{
|
|
905
1115
|
type: "button",
|
|
@@ -908,11 +1118,11 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
|
|
|
908
1118
|
disabled: shapes.length === 0,
|
|
909
1119
|
children: [
|
|
910
1120
|
Icon.undo,
|
|
911
|
-
/* @__PURE__ */
|
|
1121
|
+
/* @__PURE__ */ jsx4("span", { children: strings["annotator.undo"] })
|
|
912
1122
|
]
|
|
913
1123
|
}
|
|
914
1124
|
),
|
|
915
|
-
/* @__PURE__ */
|
|
1125
|
+
/* @__PURE__ */ jsxs3(
|
|
916
1126
|
"button",
|
|
917
1127
|
{
|
|
918
1128
|
type: "button",
|
|
@@ -921,18 +1131,18 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
|
|
|
921
1131
|
disabled: shapes.length === 0,
|
|
922
1132
|
children: [
|
|
923
1133
|
Icon.trash,
|
|
924
|
-
/* @__PURE__ */
|
|
1134
|
+
/* @__PURE__ */ jsx4("span", { children: strings["annotator.clear"] })
|
|
925
1135
|
]
|
|
926
1136
|
}
|
|
927
1137
|
),
|
|
928
|
-
/* @__PURE__ */
|
|
929
|
-
/* @__PURE__ */
|
|
1138
|
+
/* @__PURE__ */ jsx4("span", { class: "annotator-spacer" }),
|
|
1139
|
+
/* @__PURE__ */ jsxs3("span", { class: "annotator-count", children: [
|
|
930
1140
|
shapes.length,
|
|
931
1141
|
" ",
|
|
932
1142
|
strings["annotator.count_suffix"]
|
|
933
1143
|
] })
|
|
934
1144
|
] }),
|
|
935
|
-
/* @__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(
|
|
936
1146
|
"canvas",
|
|
937
1147
|
{
|
|
938
1148
|
ref: canvasRef,
|
|
@@ -943,9 +1153,9 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
|
|
|
943
1153
|
class: "annotator-canvas"
|
|
944
1154
|
}
|
|
945
1155
|
) }),
|
|
946
|
-
/* @__PURE__ */
|
|
947
|
-
/* @__PURE__ */
|
|
948
|
-
/* @__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(
|
|
949
1159
|
"button",
|
|
950
1160
|
{
|
|
951
1161
|
type: "button",
|
|
@@ -987,24 +1197,24 @@ function truncateUrl(url, maxLength = 80) {
|
|
|
987
1197
|
}
|
|
988
1198
|
|
|
989
1199
|
// src/widget/Form.tsx
|
|
990
|
-
import { jsx as
|
|
1200
|
+
import { jsx as jsx5, jsxs as jsxs4 } from "preact/jsx-runtime";
|
|
991
1201
|
var TYPES = ["bug", "feature", "question", "praise", "typo"];
|
|
992
1202
|
var SEVERITIES = ["blocker", "high", "medium", "low"];
|
|
993
1203
|
function Form({ strings, onSubmit, onCancel, status, errorMessage }) {
|
|
994
|
-
const [description, setDescription] =
|
|
995
|
-
const [feedbackType, setFeedbackType] =
|
|
996
|
-
const [severity, setSeverity] =
|
|
997
|
-
const [localError, setLocalError] =
|
|
998
|
-
const [screenshotBlob, setScreenshotBlob] =
|
|
999
|
-
const [screenshotPreview, setScreenshotPreview] =
|
|
1000
|
-
const [isDragOver, setIsDragOver] =
|
|
1001
|
-
const [annotatorOpen, setAnnotatorOpen] =
|
|
1002
|
-
const fileInputRef =
|
|
1003
|
-
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);
|
|
1004
1214
|
const submitting = status === "submitting";
|
|
1005
1215
|
const submitLabel = submitting ? strings["form.submitting"] : strings["form.submit"];
|
|
1006
1216
|
const pageUrl = typeof window !== "undefined" ? window.location.href : "";
|
|
1007
|
-
|
|
1217
|
+
useEffect3(() => {
|
|
1008
1218
|
return () => {
|
|
1009
1219
|
if (screenshotPreview) URL.revokeObjectURL(screenshotPreview);
|
|
1010
1220
|
};
|
|
@@ -1052,7 +1262,7 @@ function Form({ strings, onSubmit, onCancel, status, errorMessage }) {
|
|
|
1052
1262
|
const file = e.dataTransfer?.files?.[0];
|
|
1053
1263
|
if (file) acceptFile(file);
|
|
1054
1264
|
};
|
|
1055
|
-
|
|
1265
|
+
useEffect3(() => {
|
|
1056
1266
|
const zone = dropZoneRef.current;
|
|
1057
1267
|
if (!zone) return;
|
|
1058
1268
|
const onPaste = (e) => {
|
|
@@ -1093,11 +1303,11 @@ function Form({ strings, onSubmit, onCancel, status, errorMessage }) {
|
|
|
1093
1303
|
if (screenshotBlob) values.screenshot = screenshotBlob;
|
|
1094
1304
|
onSubmit(values);
|
|
1095
1305
|
};
|
|
1096
|
-
return /* @__PURE__ */
|
|
1097
|
-
/* @__PURE__ */
|
|
1098
|
-
/* @__PURE__ */
|
|
1099
|
-
/* @__PURE__ */
|
|
1100
|
-
/* @__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(
|
|
1101
1311
|
"textarea",
|
|
1102
1312
|
{
|
|
1103
1313
|
id: "mfb-desc",
|
|
@@ -1107,35 +1317,35 @@ function Form({ strings, onSubmit, onCancel, status, errorMessage }) {
|
|
|
1107
1317
|
}
|
|
1108
1318
|
)
|
|
1109
1319
|
] }),
|
|
1110
|
-
/* @__PURE__ */
|
|
1111
|
-
/* @__PURE__ */
|
|
1112
|
-
/* @__PURE__ */
|
|
1113
|
-
/* @__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(
|
|
1114
1324
|
"select",
|
|
1115
1325
|
{
|
|
1116
1326
|
id: "mfb-type",
|
|
1117
1327
|
value: feedbackType,
|
|
1118
1328
|
onChange: (e) => setFeedbackType(e.target.value),
|
|
1119
|
-
children: TYPES.map((t) => /* @__PURE__ */
|
|
1329
|
+
children: TYPES.map((t) => /* @__PURE__ */ jsx5("option", { value: t, children: strings[`type.${t}`] }))
|
|
1120
1330
|
}
|
|
1121
1331
|
)
|
|
1122
1332
|
] }),
|
|
1123
|
-
/* @__PURE__ */
|
|
1124
|
-
/* @__PURE__ */
|
|
1125
|
-
/* @__PURE__ */
|
|
1333
|
+
/* @__PURE__ */ jsxs4("div", { class: "field", children: [
|
|
1334
|
+
/* @__PURE__ */ jsx5("label", { for: "mfb-sev", children: strings["form.severity.label"] }),
|
|
1335
|
+
/* @__PURE__ */ jsx5(
|
|
1126
1336
|
"select",
|
|
1127
1337
|
{
|
|
1128
1338
|
id: "mfb-sev",
|
|
1129
1339
|
value: severity,
|
|
1130
1340
|
onChange: (e) => setSeverity(e.target.value),
|
|
1131
|
-
children: SEVERITIES.map((s) => /* @__PURE__ */
|
|
1341
|
+
children: SEVERITIES.map((s) => /* @__PURE__ */ jsx5("option", { value: s, children: strings[`severity.${s}`] }))
|
|
1132
1342
|
}
|
|
1133
1343
|
)
|
|
1134
1344
|
] })
|
|
1135
1345
|
] }),
|
|
1136
|
-
/* @__PURE__ */
|
|
1137
|
-
/* @__PURE__ */
|
|
1138
|
-
/* @__PURE__ */
|
|
1346
|
+
/* @__PURE__ */ jsxs4("div", { class: "field", children: [
|
|
1347
|
+
/* @__PURE__ */ jsx5("label", { children: strings["form.screenshot.label"] }),
|
|
1348
|
+
/* @__PURE__ */ jsx5(
|
|
1139
1349
|
"input",
|
|
1140
1350
|
{
|
|
1141
1351
|
ref: fileInputRef,
|
|
@@ -1147,9 +1357,9 @@ function Form({ strings, onSubmit, onCancel, status, errorMessage }) {
|
|
|
1147
1357
|
tabIndex: -1
|
|
1148
1358
|
}
|
|
1149
1359
|
),
|
|
1150
|
-
screenshotPreview ? /* @__PURE__ */
|
|
1151
|
-
/* @__PURE__ */
|
|
1152
|
-
/* @__PURE__ */
|
|
1360
|
+
screenshotPreview ? /* @__PURE__ */ jsxs4("div", { class: "screenshot-preview", children: [
|
|
1361
|
+
/* @__PURE__ */ jsx5("img", { src: screenshotPreview, alt: "" }),
|
|
1362
|
+
/* @__PURE__ */ jsx5(
|
|
1153
1363
|
"button",
|
|
1154
1364
|
{
|
|
1155
1365
|
type: "button",
|
|
@@ -1159,7 +1369,7 @@ function Form({ strings, onSubmit, onCancel, status, errorMessage }) {
|
|
|
1159
1369
|
children: "\xD7"
|
|
1160
1370
|
}
|
|
1161
1371
|
),
|
|
1162
|
-
/* @__PURE__ */
|
|
1372
|
+
/* @__PURE__ */ jsx5(
|
|
1163
1373
|
"button",
|
|
1164
1374
|
{
|
|
1165
1375
|
type: "button",
|
|
@@ -1168,7 +1378,7 @@ function Form({ strings, onSubmit, onCancel, status, errorMessage }) {
|
|
|
1168
1378
|
children: strings["form.screenshot.annotate"]
|
|
1169
1379
|
}
|
|
1170
1380
|
)
|
|
1171
|
-
] }) : /* @__PURE__ */
|
|
1381
|
+
] }) : /* @__PURE__ */ jsxs4(
|
|
1172
1382
|
"div",
|
|
1173
1383
|
{
|
|
1174
1384
|
ref: dropZoneRef,
|
|
@@ -1187,28 +1397,28 @@ function Form({ strings, onSubmit, onCancel, status, errorMessage }) {
|
|
|
1187
1397
|
onDragLeave: handleDragLeave,
|
|
1188
1398
|
onDrop: handleDrop,
|
|
1189
1399
|
children: [
|
|
1190
|
-
/* @__PURE__ */
|
|
1191
|
-
/* @__PURE__ */
|
|
1400
|
+
/* @__PURE__ */ jsxs4("div", { class: "screenshot-cta", children: [
|
|
1401
|
+
/* @__PURE__ */ jsx5("strong", { children: strings["form.screenshot.cta_click"] }),
|
|
1192
1402
|
", ",
|
|
1193
1403
|
strings["form.screenshot.cta_rest"]
|
|
1194
1404
|
] }),
|
|
1195
|
-
/* @__PURE__ */
|
|
1405
|
+
/* @__PURE__ */ jsx5("div", { class: "screenshot-formats", children: strings["form.screenshot.formats"] })
|
|
1196
1406
|
]
|
|
1197
1407
|
}
|
|
1198
1408
|
)
|
|
1199
1409
|
] }),
|
|
1200
|
-
pageUrl && /* @__PURE__ */
|
|
1201
|
-
/* @__PURE__ */
|
|
1202
|
-
/* @__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) })
|
|
1203
1413
|
] }),
|
|
1204
|
-
localError && /* @__PURE__ */
|
|
1205
|
-
status === "error" && errorMessage && /* @__PURE__ */
|
|
1206
|
-
status === "success" && /* @__PURE__ */
|
|
1207
|
-
/* @__PURE__ */
|
|
1208
|
-
/* @__PURE__ */
|
|
1209
|
-
/* @__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 })
|
|
1210
1420
|
] }),
|
|
1211
|
-
annotatorOpen && screenshotBlob && /* @__PURE__ */
|
|
1421
|
+
annotatorOpen && screenshotBlob && /* @__PURE__ */ jsx5(
|
|
1212
1422
|
Annotator,
|
|
1213
1423
|
{
|
|
1214
1424
|
imageBlob: screenshotBlob,
|
|
@@ -1221,62 +1431,104 @@ function Form({ strings, onSubmit, onCancel, status, errorMessage }) {
|
|
|
1221
1431
|
}
|
|
1222
1432
|
|
|
1223
1433
|
// src/widget/MineList.tsx
|
|
1224
|
-
import { useEffect as
|
|
1434
|
+
import { useEffect as useEffect4, useRef as useRef4, useState as useState4 } from "preact/hooks";
|
|
1225
1435
|
|
|
1226
|
-
// src/widget/
|
|
1227
|
-
import { jsx as
|
|
1228
|
-
function
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
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;
|
|
1248
1467
|
}
|
|
1249
|
-
function
|
|
1250
|
-
if (
|
|
1251
|
-
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
|
+
);
|
|
1252
1473
|
}
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
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
|
+
}) });
|
|
1270
1521
|
}
|
|
1271
1522
|
|
|
1272
1523
|
// src/widget/MineList.tsx
|
|
1273
|
-
import { jsx as
|
|
1274
|
-
var
|
|
1524
|
+
import { jsx as jsx7, jsxs as jsxs6 } from "preact/jsx-runtime";
|
|
1525
|
+
var POLL_MS2 = 3e4;
|
|
1275
1526
|
function MineList({ api, externalId, strings, onSelect }) {
|
|
1276
|
-
const [rows, setRows] =
|
|
1277
|
-
const [error, setError] =
|
|
1278
|
-
const [refreshing, setRefreshing] =
|
|
1279
|
-
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);
|
|
1280
1532
|
const fetchRows = async () => {
|
|
1281
1533
|
setRefreshing(true);
|
|
1282
1534
|
setError(null);
|
|
@@ -1291,12 +1543,12 @@ function MineList({ api, externalId, strings, onSelect }) {
|
|
|
1291
1543
|
if (mountedRef.current) setRefreshing(false);
|
|
1292
1544
|
}
|
|
1293
1545
|
};
|
|
1294
|
-
|
|
1546
|
+
useEffect4(() => {
|
|
1295
1547
|
mountedRef.current = true;
|
|
1296
1548
|
void fetchRows();
|
|
1297
1549
|
const timer = setInterval(() => {
|
|
1298
1550
|
void fetchRows();
|
|
1299
|
-
},
|
|
1551
|
+
}, POLL_MS2);
|
|
1300
1552
|
return () => {
|
|
1301
1553
|
mountedRef.current = false;
|
|
1302
1554
|
clearInterval(timer);
|
|
@@ -1304,10 +1556,12 @@ function MineList({ api, externalId, strings, onSelect }) {
|
|
|
1304
1556
|
}, [externalId]);
|
|
1305
1557
|
const isEmpty = rows !== null && rows.length === 0;
|
|
1306
1558
|
const isLoading = rows === null && !error;
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
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(
|
|
1311
1565
|
"button",
|
|
1312
1566
|
{
|
|
1313
1567
|
type: "button",
|
|
@@ -1320,23 +1574,25 @@ function MineList({ api, externalId, strings, onSelect }) {
|
|
|
1320
1574
|
}
|
|
1321
1575
|
)
|
|
1322
1576
|
] }),
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
/* @__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"] })
|
|
1328
1583
|
] }),
|
|
1329
|
-
|
|
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) }) })) })
|
|
1330
1586
|
] });
|
|
1331
1587
|
}
|
|
1332
1588
|
|
|
1333
1589
|
// src/widget/Modal.tsx
|
|
1334
|
-
import { useEffect as
|
|
1335
|
-
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";
|
|
1336
1592
|
function Modal({ onDismiss, children, closeLabel = "Close" }) {
|
|
1337
|
-
const modalRef =
|
|
1338
|
-
const previouslyFocused =
|
|
1339
|
-
|
|
1593
|
+
const modalRef = useRef5(null);
|
|
1594
|
+
const previouslyFocused = useRef5(null);
|
|
1595
|
+
useEffect5(() => {
|
|
1340
1596
|
previouslyFocused.current = document.activeElement;
|
|
1341
1597
|
const onKey = (e) => {
|
|
1342
1598
|
if (e.key !== "Escape") return;
|
|
@@ -1358,7 +1614,7 @@ function Modal({ onDismiss, children, closeLabel = "Close" }) {
|
|
|
1358
1614
|
if (prev && typeof prev.focus === "function") prev.focus();
|
|
1359
1615
|
};
|
|
1360
1616
|
}, [onDismiss]);
|
|
1361
|
-
return /* @__PURE__ */
|
|
1617
|
+
return /* @__PURE__ */ jsx8(
|
|
1362
1618
|
"div",
|
|
1363
1619
|
{
|
|
1364
1620
|
class: "backdrop",
|
|
@@ -1366,8 +1622,8 @@ function Modal({ onDismiss, children, closeLabel = "Close" }) {
|
|
|
1366
1622
|
onClick: (e) => {
|
|
1367
1623
|
if (e.target === e.currentTarget) onDismiss();
|
|
1368
1624
|
},
|
|
1369
|
-
children: /* @__PURE__ */
|
|
1370
|
-
/* @__PURE__ */
|
|
1625
|
+
children: /* @__PURE__ */ jsxs7("div", { ref: modalRef, class: "modal", role: "dialog", "aria-modal": "true", children: [
|
|
1626
|
+
/* @__PURE__ */ jsx8(
|
|
1371
1627
|
"button",
|
|
1372
1628
|
{
|
|
1373
1629
|
type: "button",
|
|
@@ -1384,10 +1640,10 @@ function Modal({ onDismiss, children, closeLabel = "Close" }) {
|
|
|
1384
1640
|
}
|
|
1385
1641
|
|
|
1386
1642
|
// src/widget/ReportDetailView.tsx
|
|
1387
|
-
import { useEffect as
|
|
1643
|
+
import { useEffect as useEffect6, useRef as useRef6, useState as useState5 } from "preact/hooks";
|
|
1388
1644
|
|
|
1389
1645
|
// src/widget/CommentBubble.tsx
|
|
1390
|
-
import { jsx as
|
|
1646
|
+
import { jsx as jsx9, jsxs as jsxs8 } from "preact/jsx-runtime";
|
|
1391
1647
|
function CommentBubble({ comment, strings }) {
|
|
1392
1648
|
const isMine = comment.is_mine;
|
|
1393
1649
|
const isAgent = !isMine && comment.author_source === "mcp";
|
|
@@ -1395,10 +1651,10 @@ function CommentBubble({ comment, strings }) {
|
|
|
1395
1651
|
const variant = isAgent ? "mcp" : isSystem ? "system" : "staff";
|
|
1396
1652
|
const labelKey = variant === "mcp" ? "detail.author.mcp" : variant === "system" ? "detail.author.system" : "detail.author.staff";
|
|
1397
1653
|
const label = comment.author_label || strings[labelKey];
|
|
1398
|
-
return /* @__PURE__ */
|
|
1399
|
-
!isMine && label && /* @__PURE__ */
|
|
1400
|
-
/* @__PURE__ */
|
|
1401
|
-
/* @__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) })
|
|
1402
1658
|
] });
|
|
1403
1659
|
}
|
|
1404
1660
|
function formatTime(iso) {
|
|
@@ -1413,8 +1669,8 @@ function formatTime(iso) {
|
|
|
1413
1669
|
}
|
|
1414
1670
|
|
|
1415
1671
|
// src/widget/ReportDetailView.tsx
|
|
1416
|
-
import { jsx as
|
|
1417
|
-
var
|
|
1672
|
+
import { Fragment, jsx as jsx10, jsxs as jsxs9 } from "preact/jsx-runtime";
|
|
1673
|
+
var POLL_MS3 = 3e4;
|
|
1418
1674
|
function ReportDetailView({
|
|
1419
1675
|
api,
|
|
1420
1676
|
externalId,
|
|
@@ -1422,12 +1678,12 @@ function ReportDetailView({
|
|
|
1422
1678
|
strings,
|
|
1423
1679
|
onBack
|
|
1424
1680
|
}) {
|
|
1425
|
-
const [detail, setDetail] =
|
|
1426
|
-
const [error, setError] =
|
|
1427
|
-
const [composeBody, setComposeBody] =
|
|
1428
|
-
const [sending, setSending] =
|
|
1429
|
-
const [closing, setClosing] =
|
|
1430
|
-
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);
|
|
1431
1687
|
const fetchDetail = async () => {
|
|
1432
1688
|
try {
|
|
1433
1689
|
const next = await api.getReport(reportId, externalId);
|
|
@@ -1439,12 +1695,12 @@ function ReportDetailView({
|
|
|
1439
1695
|
setError(err instanceof Error ? err.message : "load_failed");
|
|
1440
1696
|
}
|
|
1441
1697
|
};
|
|
1442
|
-
|
|
1698
|
+
useEffect6(() => {
|
|
1443
1699
|
mountedRef.current = true;
|
|
1444
1700
|
void fetchDetail();
|
|
1445
1701
|
const timer = setInterval(() => {
|
|
1446
1702
|
void fetchDetail();
|
|
1447
|
-
},
|
|
1703
|
+
}, POLL_MS3);
|
|
1448
1704
|
return () => {
|
|
1449
1705
|
mountedRef.current = false;
|
|
1450
1706
|
clearInterval(timer);
|
|
@@ -1484,37 +1740,39 @@ function ReportDetailView({
|
|
|
1484
1740
|
}
|
|
1485
1741
|
};
|
|
1486
1742
|
if (!detail && !error) {
|
|
1487
|
-
return /* @__PURE__ */
|
|
1743
|
+
return /* @__PURE__ */ jsx10("div", { class: "mine-loading", children: strings["mine.loading"] });
|
|
1488
1744
|
}
|
|
1489
1745
|
if (!detail) {
|
|
1490
|
-
return /* @__PURE__ */
|
|
1746
|
+
return /* @__PURE__ */ jsx10("div", { class: "error", role: "alert", children: error });
|
|
1491
1747
|
}
|
|
1492
1748
|
const showCloseCta = detail.status === "awaiting_validation";
|
|
1493
|
-
return /* @__PURE__ */
|
|
1494
|
-
/* @__PURE__ */
|
|
1495
|
-
/* @__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: [
|
|
1496
1752
|
"\u2190 ",
|
|
1497
1753
|
strings["detail.back"]
|
|
1498
1754
|
] }),
|
|
1499
|
-
/* @__PURE__ */
|
|
1755
|
+
/* @__PURE__ */ jsx10("span", { class: `pill pill-status pill-status--${detail.status}`, children: strings[`status.${detail.status}`] ?? detail.status })
|
|
1500
1756
|
] }),
|
|
1501
|
-
/* @__PURE__ */
|
|
1502
|
-
/* @__PURE__ */
|
|
1503
|
-
/* @__PURE__ */
|
|
1504
|
-
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(
|
|
1505
1761
|
"a",
|
|
1506
1762
|
{
|
|
1507
1763
|
class: "report-detail-screenshot",
|
|
1508
1764
|
href: detail.screenshot_url,
|
|
1509
1765
|
target: "_blank",
|
|
1510
1766
|
rel: "noopener noreferrer",
|
|
1511
|
-
children: /* @__PURE__ */
|
|
1767
|
+
children: /* @__PURE__ */ jsx10("img", { src: detail.screenshot_url, alt: "", loading: "lazy" })
|
|
1512
1768
|
}
|
|
1513
1769
|
),
|
|
1514
|
-
/* @__PURE__ */
|
|
1515
|
-
detail.comments.length === 0 ? /* @__PURE__ */
|
|
1516
|
-
/* @__PURE__ */
|
|
1517
|
-
|
|
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(
|
|
1518
1776
|
"textarea",
|
|
1519
1777
|
{
|
|
1520
1778
|
value: composeBody,
|
|
@@ -1523,8 +1781,8 @@ function ReportDetailView({
|
|
|
1523
1781
|
disabled: sending
|
|
1524
1782
|
}
|
|
1525
1783
|
),
|
|
1526
|
-
/* @__PURE__ */
|
|
1527
|
-
showCloseCta && /* @__PURE__ */
|
|
1784
|
+
/* @__PURE__ */ jsxs9("div", { class: "report-compose-actions", children: [
|
|
1785
|
+
showCloseCta && /* @__PURE__ */ jsx10(
|
|
1528
1786
|
"button",
|
|
1529
1787
|
{
|
|
1530
1788
|
type: "button",
|
|
@@ -1534,7 +1792,7 @@ function ReportDetailView({
|
|
|
1534
1792
|
children: closing ? strings["detail.close_busy"] : strings["detail.close_cta"]
|
|
1535
1793
|
}
|
|
1536
1794
|
),
|
|
1537
|
-
/* @__PURE__ */
|
|
1795
|
+
/* @__PURE__ */ jsx10(
|
|
1538
1796
|
"button",
|
|
1539
1797
|
{
|
|
1540
1798
|
type: "button",
|
|
@@ -1546,7 +1804,7 @@ function ReportDetailView({
|
|
|
1546
1804
|
)
|
|
1547
1805
|
] })
|
|
1548
1806
|
] }),
|
|
1549
|
-
error && /* @__PURE__ */
|
|
1807
|
+
error && /* @__PURE__ */ jsx10("div", { class: "error", children: error })
|
|
1550
1808
|
] })
|
|
1551
1809
|
] });
|
|
1552
1810
|
}
|
|
@@ -1557,19 +1815,19 @@ function appendComment(current, next) {
|
|
|
1557
1815
|
function ContextBlock({ detail, strings }) {
|
|
1558
1816
|
const captureKey = `detail.context.capture.${detail.capture_method}`;
|
|
1559
1817
|
const captureLabel = strings[captureKey] ?? detail.capture_method;
|
|
1560
|
-
return /* @__PURE__ */
|
|
1561
|
-
/* @__PURE__ */
|
|
1562
|
-
/* @__PURE__ */
|
|
1563
|
-
/* @__PURE__ */
|
|
1564
|
-
/* @__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 })
|
|
1565
1823
|
] }),
|
|
1566
|
-
/* @__PURE__ */
|
|
1567
|
-
/* @__PURE__ */
|
|
1568
|
-
/* @__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) })
|
|
1569
1827
|
] }),
|
|
1570
|
-
detail.page_url && /* @__PURE__ */
|
|
1571
|
-
/* @__PURE__ */
|
|
1572
|
-
/* @__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(
|
|
1573
1831
|
"a",
|
|
1574
1832
|
{
|
|
1575
1833
|
class: "report-detail-context-url",
|
|
@@ -1592,6 +1850,120 @@ function formatSubmittedAt(iso) {
|
|
|
1592
1850
|
return iso;
|
|
1593
1851
|
}
|
|
1594
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
|
+
}
|
|
1595
1967
|
|
|
1596
1968
|
// src/widget/styles.ts
|
|
1597
1969
|
var WIDGET_STYLES = `
|
|
@@ -2313,10 +2685,259 @@ var WIDGET_STYLES = `
|
|
|
2313
2685
|
justify-content: flex-end;
|
|
2314
2686
|
gap: 6px;
|
|
2315
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
|
+
}
|
|
2316
2937
|
`;
|
|
2317
2938
|
|
|
2318
2939
|
// src/widget/mount.tsx
|
|
2319
|
-
import { Fragment, jsx as
|
|
2940
|
+
import { Fragment as Fragment2, jsx as jsx11, jsxs as jsxs10 } from "preact/jsx-runtime";
|
|
2320
2941
|
function mountWidget(options) {
|
|
2321
2942
|
const shadow = options.host.attachShadow({ mode: "open" });
|
|
2322
2943
|
const style = document.createElement("style");
|
|
@@ -2363,22 +2984,22 @@ function mountWidget(options) {
|
|
|
2363
2984
|
const externalId = options.getExternalId?.();
|
|
2364
2985
|
const fabVisible = options.showFAB && (options.getExternalId === void 0 ? true : Boolean(externalId));
|
|
2365
2986
|
const showMineTab = Boolean(options.api && externalId);
|
|
2366
|
-
return /* @__PURE__ */
|
|
2367
|
-
fabVisible && /* @__PURE__ */
|
|
2987
|
+
return /* @__PURE__ */ jsxs10(Fragment2, { children: [
|
|
2988
|
+
fabVisible && /* @__PURE__ */ jsx11(
|
|
2368
2989
|
Fab,
|
|
2369
2990
|
{
|
|
2370
2991
|
label: options.strings["fab.label"],
|
|
2371
2992
|
onClick: () => rerender({ ...currentState, open: true })
|
|
2372
2993
|
}
|
|
2373
2994
|
),
|
|
2374
|
-
state.open && /* @__PURE__ */
|
|
2995
|
+
state.open && /* @__PURE__ */ jsxs10(
|
|
2375
2996
|
Modal,
|
|
2376
2997
|
{
|
|
2377
2998
|
onDismiss: () => rerender(clearSelected({ ...currentState, open: false, status: "idle" })),
|
|
2378
2999
|
closeLabel: options.strings["form.close"],
|
|
2379
3000
|
children: [
|
|
2380
|
-
showMineTab && /* @__PURE__ */
|
|
2381
|
-
/* @__PURE__ */
|
|
3001
|
+
showMineTab && /* @__PURE__ */ jsxs10("div", { class: "tab-strip", role: "tablist", children: [
|
|
3002
|
+
/* @__PURE__ */ jsx11(
|
|
2382
3003
|
"button",
|
|
2383
3004
|
{
|
|
2384
3005
|
type: "button",
|
|
@@ -2389,7 +3010,7 @@ function mountWidget(options) {
|
|
|
2389
3010
|
children: options.strings["tab.send"]
|
|
2390
3011
|
}
|
|
2391
3012
|
),
|
|
2392
|
-
/* @__PURE__ */
|
|
3013
|
+
/* @__PURE__ */ jsx11(
|
|
2393
3014
|
"button",
|
|
2394
3015
|
{
|
|
2395
3016
|
type: "button",
|
|
@@ -2399,9 +3020,20 @@ function mountWidget(options) {
|
|
|
2399
3020
|
onClick: () => rerender(clearSelected({ ...currentState, tab: "mine" })),
|
|
2400
3021
|
children: options.strings["tab.mine"]
|
|
2401
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
|
+
}
|
|
2402
3034
|
)
|
|
2403
3035
|
] }),
|
|
2404
|
-
state.tab === "send" && /* @__PURE__ */
|
|
3036
|
+
state.tab === "send" && /* @__PURE__ */ jsx11(
|
|
2405
3037
|
Form,
|
|
2406
3038
|
{
|
|
2407
3039
|
strings: options.strings,
|
|
@@ -2411,7 +3043,7 @@ function mountWidget(options) {
|
|
|
2411
3043
|
...state.error !== void 0 && { errorMessage: state.error }
|
|
2412
3044
|
}
|
|
2413
3045
|
),
|
|
2414
|
-
state.tab === "mine" && options.api && externalId && !state.selectedReportId && /* @__PURE__ */
|
|
3046
|
+
state.tab === "mine" && options.api && externalId && !state.selectedReportId && /* @__PURE__ */ jsx11(
|
|
2415
3047
|
MineList,
|
|
2416
3048
|
{
|
|
2417
3049
|
api: options.api,
|
|
@@ -2420,7 +3052,7 @@ function mountWidget(options) {
|
|
|
2420
3052
|
onSelect: (row) => rerender({ ...currentState, selectedReportId: row.id })
|
|
2421
3053
|
}
|
|
2422
3054
|
),
|
|
2423
|
-
state.tab === "mine" && options.api && externalId && state.selectedReportId && /* @__PURE__ */
|
|
3055
|
+
(state.tab === "mine" || state.tab === "changelog") && options.api && externalId && state.selectedReportId && /* @__PURE__ */ jsx11(
|
|
2424
3056
|
ReportDetailView,
|
|
2425
3057
|
{
|
|
2426
3058
|
api: options.api,
|
|
@@ -2429,6 +3061,15 @@ function mountWidget(options) {
|
|
|
2429
3061
|
strings: options.strings,
|
|
2430
3062
|
onBack: () => rerender(clearSelected({ ...currentState }))
|
|
2431
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
|
+
}
|
|
2432
3073
|
)
|
|
2433
3074
|
]
|
|
2434
3075
|
}
|
|
@@ -2501,8 +3142,8 @@ function createFeedback(config) {
|
|
|
2501
3142
|
capture_method: screenshot ? manualScreenshot ? "manual" : "html2canvas" : "none",
|
|
2502
3143
|
technical_context
|
|
2503
3144
|
};
|
|
2504
|
-
if ("0.
|
|
2505
|
-
payload.widget_version = "0.
|
|
3145
|
+
if ("0.9.0") {
|
|
3146
|
+
payload.widget_version = "0.9.0";
|
|
2506
3147
|
}
|
|
2507
3148
|
if (screenshot) payload.screenshot = screenshot;
|
|
2508
3149
|
if (values.synthetic) payload.synthetic = true;
|
|
@@ -2588,4 +3229,4 @@ function createFeedback(config) {
|
|
|
2588
3229
|
export {
|
|
2589
3230
|
createFeedback
|
|
2590
3231
|
};
|
|
2591
|
-
//# sourceMappingURL=chunk-
|
|
3232
|
+
//# sourceMappingURL=chunk-KO5NHJ7J.mjs.map
|