@mhosaic/feedback 0.18.1 → 0.20.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-5FVEQYA5.mjs → chunk-YV7LMW22.mjs} +129 -11
- package/dist/chunk-YV7LMW22.mjs.map +1 -0
- package/dist/embed.min.js +4 -4
- package/dist/embed.min.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/react.mjs +1 -1
- package/dist/widget.min.js +5 -5
- package/dist/widget.min.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-5FVEQYA5.mjs.map +0 -1
|
@@ -169,6 +169,24 @@ function createApiClient(options) {
|
|
|
169
169
|
}
|
|
170
170
|
return response.json();
|
|
171
171
|
}
|
|
172
|
+
async function reopenUnresolved(reportId, externalId) {
|
|
173
|
+
const response = await fetcher(
|
|
174
|
+
`${endpoint}/api/feedback/v1/reports/widget/${reportId}/`,
|
|
175
|
+
{
|
|
176
|
+
method: "PATCH",
|
|
177
|
+
headers: {
|
|
178
|
+
...widgetHeaders(externalId),
|
|
179
|
+
"Content-Type": "application/json"
|
|
180
|
+
},
|
|
181
|
+
body: JSON.stringify({ status: "in_progress" })
|
|
182
|
+
}
|
|
183
|
+
);
|
|
184
|
+
if (!response.ok) {
|
|
185
|
+
const text = await response.text().catch(() => "");
|
|
186
|
+
throw new Error(`reopenUnresolved failed: ${response.status} ${text}`);
|
|
187
|
+
}
|
|
188
|
+
return response.json();
|
|
189
|
+
}
|
|
172
190
|
function boardQueryString(filters) {
|
|
173
191
|
if (!filters) return "";
|
|
174
192
|
const params = new URLSearchParams();
|
|
@@ -177,6 +195,7 @@ function createApiClient(options) {
|
|
|
177
195
|
filters.severity?.forEach((s) => params.append("severity", s));
|
|
178
196
|
if (filters.q) params.set("q", filters.q);
|
|
179
197
|
if (filters.mine) params.set("mine", "1");
|
|
198
|
+
if (filters.ordering) params.set("ordering", filters.ordering);
|
|
180
199
|
if (filters.page && filters.page > 1) params.set("page", String(filters.page));
|
|
181
200
|
const qs = params.toString();
|
|
182
201
|
return qs ? `?${qs}` : "";
|
|
@@ -238,6 +257,7 @@ function createApiClient(options) {
|
|
|
238
257
|
getReport,
|
|
239
258
|
addComment,
|
|
240
259
|
closeAsResolved,
|
|
260
|
+
reopenUnresolved,
|
|
241
261
|
listBoard,
|
|
242
262
|
fetchBoardKpis
|
|
243
263
|
};
|
|
@@ -585,6 +605,12 @@ var DEFAULT_STRINGS = {
|
|
|
585
605
|
"board.kpi.closed": "Closed",
|
|
586
606
|
"board.kpi.rejected": "Rejected",
|
|
587
607
|
"board.kpi.resolution_rate": "Resolution rate",
|
|
608
|
+
"board.sort": "Sort",
|
|
609
|
+
"board.sort.recent": "Newest first",
|
|
610
|
+
"board.sort.oldest": "Oldest first",
|
|
611
|
+
"board.sort.updated": "Recently active",
|
|
612
|
+
"board.sort.severity": "Severity",
|
|
613
|
+
"board.sort.status": "Status",
|
|
588
614
|
"board.filter.status": "Status",
|
|
589
615
|
"board.filter.type": "Type",
|
|
590
616
|
"board.filter.severity": "Severity",
|
|
@@ -594,7 +620,8 @@ var DEFAULT_STRINGS = {
|
|
|
594
620
|
"board.list.empty.title": "Nothing here yet",
|
|
595
621
|
"board.list.empty.description": "When reports land, they\u2019ll show up here.",
|
|
596
622
|
"board.list.loading": "Loading\u2026",
|
|
597
|
-
"board.list.error": "Couldn\u2019t load reports.
|
|
623
|
+
"board.list.error": "Couldn\u2019t load reports.",
|
|
624
|
+
"board.retry": "Try again",
|
|
598
625
|
"board.list.you": "you",
|
|
599
626
|
"board.list.count": "{n} of {total}",
|
|
600
627
|
"board.detail.empty": "Pick a report on the left to see its full thread.",
|
|
@@ -629,12 +656,15 @@ var DEFAULT_STRINGS = {
|
|
|
629
656
|
"detail.compose_sending": "Sending\u2026",
|
|
630
657
|
"detail.close_cta": "Mark as resolved",
|
|
631
658
|
"detail.close_busy": "Marking\u2026",
|
|
659
|
+
"detail.reopen_cta": "Still not fixed",
|
|
660
|
+
"detail.reopen_busy": "Reopening\u2026",
|
|
632
661
|
"detail.teammate_notice": "Viewing a teammate\u2019s report \u2014 replies are private to the submitter.",
|
|
633
662
|
"detail.load_failed.title": "Report not available",
|
|
634
663
|
"detail.load_failed.body": "It may have been deleted, or you no longer have access to it.",
|
|
635
664
|
"detail.load_failed.cta": "Back",
|
|
636
665
|
"detail.send_failed": "Couldn\u2019t send your reply. Try again.",
|
|
637
666
|
"detail.close_failed": "Couldn\u2019t mark as resolved. Try again.",
|
|
667
|
+
"detail.reopen_failed": "Couldn\u2019t reopen the report. Try again.",
|
|
638
668
|
"detail.history": "Status history",
|
|
639
669
|
"detail.context.submitted_at": "Submitted",
|
|
640
670
|
"detail.context.page": "Page",
|
|
@@ -724,6 +754,12 @@ var FRENCH_STRINGS = {
|
|
|
724
754
|
"board.kpi.closed": "Ferm\xE9",
|
|
725
755
|
"board.kpi.rejected": "Refus\xE9",
|
|
726
756
|
"board.kpi.resolution_rate": "Taux de r\xE9solution",
|
|
757
|
+
"board.sort": "Trier",
|
|
758
|
+
"board.sort.recent": "Plus r\xE9cents",
|
|
759
|
+
"board.sort.oldest": "Plus anciens",
|
|
760
|
+
"board.sort.updated": "Activit\xE9 r\xE9cente",
|
|
761
|
+
"board.sort.severity": "S\xE9v\xE9rit\xE9",
|
|
762
|
+
"board.sort.status": "Statut",
|
|
727
763
|
"board.filter.status": "Statut",
|
|
728
764
|
"board.filter.type": "Type",
|
|
729
765
|
"board.filter.severity": "S\xE9v\xE9rit\xE9",
|
|
@@ -733,7 +769,8 @@ var FRENCH_STRINGS = {
|
|
|
733
769
|
"board.list.empty.title": "Rien \xE0 voir ici",
|
|
734
770
|
"board.list.empty.description": "Les rapports appara\xEEtront ici d\xE8s qu\u2019ils arrivent.",
|
|
735
771
|
"board.list.loading": "Chargement\u2026",
|
|
736
|
-
"board.list.error": "Impossible de charger les rapports.
|
|
772
|
+
"board.list.error": "Impossible de charger les rapports.",
|
|
773
|
+
"board.retry": "R\xE9essayer",
|
|
737
774
|
"board.list.you": "vous",
|
|
738
775
|
"board.list.count": "{n} sur {total}",
|
|
739
776
|
"board.detail.empty": "S\xE9lectionnez un rapport \xE0 gauche pour voir le fil complet.",
|
|
@@ -768,12 +805,15 @@ var FRENCH_STRINGS = {
|
|
|
768
805
|
"detail.compose_sending": "Envoi\u2026",
|
|
769
806
|
"detail.close_cta": "Marquer comme r\xE9solu",
|
|
770
807
|
"detail.close_busy": "Validation\u2026",
|
|
808
|
+
"detail.reopen_cta": "Toujours pas r\xE9gl\xE9",
|
|
809
|
+
"detail.reopen_busy": "R\xE9ouverture\u2026",
|
|
771
810
|
"detail.teammate_notice": "Vous consultez le rapport d\u2019un co\xE9quipier \u2014 les r\xE9ponses sont priv\xE9es au soumetteur.",
|
|
772
811
|
"detail.load_failed.title": "Rapport indisponible",
|
|
773
812
|
"detail.load_failed.body": "Il a peut-\xEAtre \xE9t\xE9 supprim\xE9, ou vous n\u2019y avez plus acc\xE8s.",
|
|
774
813
|
"detail.load_failed.cta": "Retour",
|
|
775
814
|
"detail.send_failed": "Impossible d\u2019envoyer votre r\xE9ponse. R\xE9essayez.",
|
|
776
815
|
"detail.close_failed": "Impossible de marquer comme r\xE9solu. R\xE9essayez.",
|
|
816
|
+
"detail.reopen_failed": "Impossible de rouvrir le rapport. R\xE9essayez.",
|
|
777
817
|
"detail.history": "Historique du statut",
|
|
778
818
|
"detail.context.submitted_at": "Envoy\xE9",
|
|
779
819
|
"detail.context.page": "Page",
|
|
@@ -828,6 +868,25 @@ import { useCallback, useEffect as useEffect8, useState as useState7 } from "pre
|
|
|
828
868
|
// src/widget/BoardView.tsx
|
|
829
869
|
import { useEffect as useEffect2, useMemo, useState as useState2 } from "preact/hooks";
|
|
830
870
|
|
|
871
|
+
// src/widget/boardViewStore.ts
|
|
872
|
+
var PREFIX = "mhosaic.boardView.";
|
|
873
|
+
function loadBoardView(externalId) {
|
|
874
|
+
try {
|
|
875
|
+
const raw = localStorage.getItem(PREFIX + externalId);
|
|
876
|
+
if (!raw) return {};
|
|
877
|
+
const parsed = JSON.parse(raw);
|
|
878
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
879
|
+
} catch {
|
|
880
|
+
return {};
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
function saveBoardView(externalId, view) {
|
|
884
|
+
try {
|
|
885
|
+
localStorage.setItem(PREFIX + externalId, JSON.stringify(view));
|
|
886
|
+
} catch {
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
|
|
831
890
|
// src/widget/ReportDetailView.tsx
|
|
832
891
|
import { useEffect, useRef, useState } from "preact/hooks";
|
|
833
892
|
|
|
@@ -913,6 +972,7 @@ function ReportDetailView({
|
|
|
913
972
|
const [composeBody, setComposeBody] = useState("");
|
|
914
973
|
const [sending, setSending] = useState(false);
|
|
915
974
|
const [closing, setClosing] = useState(false);
|
|
975
|
+
const [reopening, setReopening] = useState(false);
|
|
916
976
|
const mountedRef = useRef(true);
|
|
917
977
|
const fetchDetail = async () => {
|
|
918
978
|
try {
|
|
@@ -958,7 +1018,7 @@ function ReportDetailView({
|
|
|
958
1018
|
}
|
|
959
1019
|
};
|
|
960
1020
|
const handleClose = async () => {
|
|
961
|
-
if (closing) return;
|
|
1021
|
+
if (closing || reopening) return;
|
|
962
1022
|
setClosing(true);
|
|
963
1023
|
try {
|
|
964
1024
|
const next = await api.closeAsResolved(reportId, externalId);
|
|
@@ -973,6 +1033,22 @@ function ReportDetailView({
|
|
|
973
1033
|
if (mountedRef.current) setClosing(false);
|
|
974
1034
|
}
|
|
975
1035
|
};
|
|
1036
|
+
const handleReopen = async () => {
|
|
1037
|
+
if (closing || reopening) return;
|
|
1038
|
+
setReopening(true);
|
|
1039
|
+
try {
|
|
1040
|
+
const next = await api.reopenUnresolved(reportId, externalId);
|
|
1041
|
+
if (!mountedRef.current) return;
|
|
1042
|
+
setDetail(next);
|
|
1043
|
+
} catch (err) {
|
|
1044
|
+
if (typeof console !== "undefined")
|
|
1045
|
+
console.warn("[mhosaic] reopenUnresolved:", err);
|
|
1046
|
+
if (!mountedRef.current) return;
|
|
1047
|
+
setError(strings["detail.reopen_failed"]);
|
|
1048
|
+
} finally {
|
|
1049
|
+
if (mountedRef.current) setReopening(false);
|
|
1050
|
+
}
|
|
1051
|
+
};
|
|
976
1052
|
if (!detail && !error) {
|
|
977
1053
|
return /* @__PURE__ */ jsx2("div", { class: "mine-loading", children: strings["mine.loading"] });
|
|
978
1054
|
}
|
|
@@ -1050,10 +1126,20 @@ function ReportDetailView({
|
|
|
1050
1126
|
type: "button",
|
|
1051
1127
|
class: "btn",
|
|
1052
1128
|
onClick: handleClose,
|
|
1053
|
-
disabled: closing,
|
|
1129
|
+
disabled: closing || reopening,
|
|
1054
1130
|
children: closing ? strings["detail.close_busy"] : strings["detail.close_cta"]
|
|
1055
1131
|
}
|
|
1056
1132
|
),
|
|
1133
|
+
showCloseCta && /* @__PURE__ */ jsx2(
|
|
1134
|
+
"button",
|
|
1135
|
+
{
|
|
1136
|
+
type: "button",
|
|
1137
|
+
class: "btn btn--ghost",
|
|
1138
|
+
onClick: handleReopen,
|
|
1139
|
+
disabled: closing || reopening,
|
|
1140
|
+
children: reopening ? strings["detail.reopen_busy"] : strings["detail.reopen_cta"]
|
|
1141
|
+
}
|
|
1142
|
+
),
|
|
1057
1143
|
/* @__PURE__ */ jsx2(
|
|
1058
1144
|
"button",
|
|
1059
1145
|
{
|
|
@@ -1239,6 +1325,7 @@ function StatusHistorySection({ rows, strings }) {
|
|
|
1239
1325
|
// src/widget/BoardView.tsx
|
|
1240
1326
|
import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "preact/jsx-runtime";
|
|
1241
1327
|
var POLL_MS2 = 3e4;
|
|
1328
|
+
var SORT_OPTIONS = ["recent", "oldest", "updated", "severity", "status"];
|
|
1242
1329
|
var STATUSES = [
|
|
1243
1330
|
"new",
|
|
1244
1331
|
"in_progress",
|
|
@@ -1249,13 +1336,17 @@ var STATUSES = [
|
|
|
1249
1336
|
var TYPES = ["bug", "feature", "question", "praise", "typo"];
|
|
1250
1337
|
var SEVERITIES = ["blocker", "high", "medium", "low"];
|
|
1251
1338
|
function BoardView({ api, externalId, strings }) {
|
|
1252
|
-
const [filters, setFilters] = useState2(
|
|
1339
|
+
const [filters, setFilters] = useState2(() => loadBoardView(externalId));
|
|
1340
|
+
useEffect2(() => {
|
|
1341
|
+
saveBoardView(externalId, filters);
|
|
1342
|
+
}, [externalId, filtersHash(filters)]);
|
|
1253
1343
|
const [page, setPage] = useState2(null);
|
|
1254
1344
|
const [kpis, setKpis] = useState2(null);
|
|
1255
1345
|
const [loading, setLoading] = useState2(true);
|
|
1256
1346
|
const [error, setError] = useState2(null);
|
|
1257
1347
|
const [selectedId, setSelectedId] = useState2(null);
|
|
1258
1348
|
const [detailKey, setDetailKey] = useState2(0);
|
|
1349
|
+
const [reloadTick, setReloadTick] = useState2(0);
|
|
1259
1350
|
const activeFilterCount = useMemo(() => {
|
|
1260
1351
|
return (filters.status?.length ?? 0) + (filters.type?.length ?? 0) + (filters.severity?.length ?? 0) + (filters.q ? 1 : 0) + (filters.mine ? 1 : 0);
|
|
1261
1352
|
}, [filters]);
|
|
@@ -1286,7 +1377,7 @@ function BoardView({ api, externalId, strings }) {
|
|
|
1286
1377
|
cancelled = true;
|
|
1287
1378
|
if (timer) clearTimeout(timer);
|
|
1288
1379
|
};
|
|
1289
|
-
}, [api, externalId, filtersHash(filters)]);
|
|
1380
|
+
}, [api, externalId, filtersHash(filters), reloadTick]);
|
|
1290
1381
|
const selectedRow = useMemo(() => {
|
|
1291
1382
|
if (!selectedId || !page) return null;
|
|
1292
1383
|
return page.results.find((r) => r.id === selectedId) ?? null;
|
|
@@ -1308,7 +1399,22 @@ function BoardView({ api, externalId, strings }) {
|
|
|
1308
1399
|
),
|
|
1309
1400
|
/* @__PURE__ */ jsxs3("div", { class: "board-body", children: [
|
|
1310
1401
|
/* @__PURE__ */ jsxs3("div", { class: "board-list-wrap", "aria-busy": loading && !page, children: [
|
|
1311
|
-
error && /* @__PURE__ */
|
|
1402
|
+
error && /* @__PURE__ */ jsxs3("div", { class: "board-error", role: "alert", children: [
|
|
1403
|
+
/* @__PURE__ */ jsx3("span", { children: strings["board.list.error"] }),
|
|
1404
|
+
/* @__PURE__ */ jsx3(
|
|
1405
|
+
"button",
|
|
1406
|
+
{
|
|
1407
|
+
type: "button",
|
|
1408
|
+
class: "btn btn--ghost",
|
|
1409
|
+
onClick: () => {
|
|
1410
|
+
setError(null);
|
|
1411
|
+
setLoading(true);
|
|
1412
|
+
setReloadTick((t) => t + 1);
|
|
1413
|
+
},
|
|
1414
|
+
children: strings["board.retry"]
|
|
1415
|
+
}
|
|
1416
|
+
)
|
|
1417
|
+
] }),
|
|
1312
1418
|
!error && loading && !page && /* @__PURE__ */ jsx3(BoardListSkeleton, {}),
|
|
1313
1419
|
!error && page && page.results.length === 0 && !loading && /* @__PURE__ */ jsx3(BoardEmpty, { strings }),
|
|
1314
1420
|
!error && page && page.results.length > 0 && /* @__PURE__ */ jsx3(
|
|
@@ -1425,6 +1531,7 @@ function BoardFilters({
|
|
|
1425
1531
|
onChange({ ...filters, severity: [value] });
|
|
1426
1532
|
}
|
|
1427
1533
|
};
|
|
1534
|
+
const setOrdering = (value) => onChange({ ...filters, ordering: value });
|
|
1428
1535
|
const setSearch = (q) => onChange({ ...filters, q });
|
|
1429
1536
|
const toggleMine = () => {
|
|
1430
1537
|
if (filters.mine) {
|
|
@@ -1437,6 +1544,16 @@ function BoardFilters({
|
|
|
1437
1544
|
};
|
|
1438
1545
|
const clear = () => onChange({});
|
|
1439
1546
|
return /* @__PURE__ */ jsxs3("div", { class: "board-filters", children: [
|
|
1547
|
+
/* @__PURE__ */ jsx3(
|
|
1548
|
+
"select",
|
|
1549
|
+
{
|
|
1550
|
+
class: "board-filter-select",
|
|
1551
|
+
"aria-label": strings["board.sort"],
|
|
1552
|
+
value: filters.ordering ?? "recent",
|
|
1553
|
+
onChange: (e) => setOrdering(e.target.value),
|
|
1554
|
+
children: SORT_OPTIONS.map((o) => /* @__PURE__ */ jsx3("option", { value: o, children: strings[`board.sort.${o}`] }, o))
|
|
1555
|
+
}
|
|
1556
|
+
),
|
|
1440
1557
|
/* @__PURE__ */ jsxs3(
|
|
1441
1558
|
"select",
|
|
1442
1559
|
{
|
|
@@ -1600,7 +1717,8 @@ function filtersHash(f) {
|
|
|
1600
1717
|
t: f.type?.slice().sort(),
|
|
1601
1718
|
sv: f.severity?.slice().sort(),
|
|
1602
1719
|
q: f.q ?? "",
|
|
1603
|
-
m: Boolean(f.mine)
|
|
1720
|
+
m: Boolean(f.mine),
|
|
1721
|
+
o: f.ordering ?? ""
|
|
1604
1722
|
});
|
|
1605
1723
|
}
|
|
1606
1724
|
function formatRelative(iso) {
|
|
@@ -4759,8 +4877,8 @@ function createFeedback(config) {
|
|
|
4759
4877
|
capture_method: captureMethod,
|
|
4760
4878
|
technical_context
|
|
4761
4879
|
};
|
|
4762
|
-
if ("0.
|
|
4763
|
-
payload.widget_version = "0.
|
|
4880
|
+
if ("0.20.0") {
|
|
4881
|
+
payload.widget_version = "0.20.0";
|
|
4764
4882
|
}
|
|
4765
4883
|
if (manualScreenshot) payload.screenshot = manualScreenshot;
|
|
4766
4884
|
if (values.synthetic) payload.synthetic = true;
|
|
@@ -4853,4 +4971,4 @@ function createFeedback(config) {
|
|
|
4853
4971
|
export {
|
|
4854
4972
|
createFeedback
|
|
4855
4973
|
};
|
|
4856
|
-
//# sourceMappingURL=chunk-
|
|
4974
|
+
//# sourceMappingURL=chunk-YV7LMW22.mjs.map
|