@mhosaic/feedback 0.30.0 → 0.32.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-5F36UFXG.mjs → chunk-YNDBZR37.mjs} +264 -63
- package/dist/chunk-YNDBZR37.mjs.map +1 -0
- package/dist/embed.min.js +19 -5
- 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 +20 -6
- package/dist/widget.min.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-5F36UFXG.mjs.map +0 -1
|
@@ -153,10 +153,14 @@ function createApiClient(options) {
|
|
|
153
153
|
if (response.status === 404) return [];
|
|
154
154
|
return readJsonArray(response, "listChangelog");
|
|
155
155
|
}
|
|
156
|
-
async function getReport(reportId, externalId) {
|
|
156
|
+
async function getReport(reportId, externalId, opts) {
|
|
157
157
|
const response = await fetcher(
|
|
158
158
|
`${endpoint}/api/feedback/v1/reports/widget/${reportId}/`,
|
|
159
|
-
{
|
|
159
|
+
{
|
|
160
|
+
method: "GET",
|
|
161
|
+
headers: widgetHeaders(externalId),
|
|
162
|
+
...opts?.signal ? { signal: opts.signal } : {}
|
|
163
|
+
}
|
|
160
164
|
);
|
|
161
165
|
return readJsonObject(response, "getReport");
|
|
162
166
|
}
|
|
@@ -268,10 +272,14 @@ function createApiClient(options) {
|
|
|
268
272
|
const qs = params.toString();
|
|
269
273
|
return qs ? `?${qs}` : "";
|
|
270
274
|
}
|
|
271
|
-
async function listBoard(externalId, filters) {
|
|
275
|
+
async function listBoard(externalId, filters, opts) {
|
|
272
276
|
const response = await fetcher(
|
|
273
277
|
`${endpoint}/api/feedback/v1/reports/widget/board/${boardQueryString(filters)}`,
|
|
274
|
-
{
|
|
278
|
+
{
|
|
279
|
+
method: "GET",
|
|
280
|
+
headers: widgetHeaders(externalId),
|
|
281
|
+
...opts?.signal ? { signal: opts.signal } : {}
|
|
282
|
+
}
|
|
275
283
|
);
|
|
276
284
|
if (response.status === 404) {
|
|
277
285
|
return { count: 0, next: null, previous: null, results: [] };
|
|
@@ -282,10 +290,14 @@ function createApiClient(options) {
|
|
|
282
290
|
}
|
|
283
291
|
return page;
|
|
284
292
|
}
|
|
285
|
-
async function fetchBoardKpis(externalId, filters) {
|
|
293
|
+
async function fetchBoardKpis(externalId, filters, opts) {
|
|
286
294
|
const response = await fetcher(
|
|
287
295
|
`${endpoint}/api/feedback/v1/reports/widget/board/kpis/${boardQueryString(filters)}`,
|
|
288
|
-
{
|
|
296
|
+
{
|
|
297
|
+
method: "GET",
|
|
298
|
+
headers: widgetHeaders(externalId),
|
|
299
|
+
...opts?.signal ? { signal: opts.signal } : {}
|
|
300
|
+
}
|
|
289
301
|
);
|
|
290
302
|
if (response.status === 404) {
|
|
291
303
|
return { total: 0, by_status: {}, resolution_rate: 0, scope: "mine" };
|
|
@@ -705,6 +717,7 @@ var DEFAULT_STRINGS = {
|
|
|
705
717
|
"board.retry": "Try again",
|
|
706
718
|
"board.list.you": "you",
|
|
707
719
|
"board.list.count": "{n} of {total}",
|
|
720
|
+
"board.list.loadMore": "Show more",
|
|
708
721
|
"board.detail.empty": "Pick a report on the left to see its full thread.",
|
|
709
722
|
"board.detail.by": "By",
|
|
710
723
|
"board.detail.confirm_resolution": "Confirm resolution",
|
|
@@ -891,6 +904,7 @@ var FRENCH_STRINGS = {
|
|
|
891
904
|
"board.retry": "R\xE9essayer",
|
|
892
905
|
"board.list.you": "vous",
|
|
893
906
|
"board.list.count": "{n} sur {total}",
|
|
907
|
+
"board.list.loadMore": "Afficher plus",
|
|
894
908
|
"board.detail.empty": "S\xE9lectionnez un rapport \xE0 gauche pour voir le fil complet.",
|
|
895
909
|
"board.detail.by": "Par",
|
|
896
910
|
"board.detail.confirm_resolution": "Confirmer la r\xE9solution",
|
|
@@ -1038,6 +1052,26 @@ function onLocationChange(cb) {
|
|
|
1038
1052
|
// src/widget/BoardView.tsx
|
|
1039
1053
|
import { useEffect as useEffect2, useMemo, useState as useState2 } from "preact/hooks";
|
|
1040
1054
|
|
|
1055
|
+
// src/widget/boardCache.ts
|
|
1056
|
+
var TTL_MS = 6e4;
|
|
1057
|
+
var MAX_ENTRIES = 50;
|
|
1058
|
+
var cache = /* @__PURE__ */ new Map();
|
|
1059
|
+
function boardCacheKey(externalId, fetchKey) {
|
|
1060
|
+
return `${externalId}|${fetchKey}`;
|
|
1061
|
+
}
|
|
1062
|
+
function readBoardCache(key) {
|
|
1063
|
+
const hit = cache.get(key);
|
|
1064
|
+
return hit && Date.now() - hit.at < TTL_MS ? hit : null;
|
|
1065
|
+
}
|
|
1066
|
+
function writeBoardCache(key, page, kpis) {
|
|
1067
|
+
if (!cache.has(key) && cache.size >= MAX_ENTRIES) {
|
|
1068
|
+
const oldest = cache.keys().next().value;
|
|
1069
|
+
if (oldest !== void 0) cache.delete(oldest);
|
|
1070
|
+
}
|
|
1071
|
+
cache.delete(key);
|
|
1072
|
+
cache.set(key, { page, kpis, at: Date.now() });
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1041
1075
|
// src/widget/boardViewStore.ts
|
|
1042
1076
|
var PREFIX = "mhosaic.boardView.";
|
|
1043
1077
|
function loadBoardView(externalId) {
|
|
@@ -1146,7 +1180,7 @@ function linkifySeq(text, onOpenSeq) {
|
|
|
1146
1180
|
|
|
1147
1181
|
// src/widget/CommentBubble.tsx
|
|
1148
1182
|
import { jsx, jsxs as jsxs2 } from "preact/jsx-runtime";
|
|
1149
|
-
function CommentBubble({ comment, strings, onOpenSeq }) {
|
|
1183
|
+
function CommentBubble({ comment, strings, onOpenSeq, stableUrl }) {
|
|
1150
1184
|
const isMine = comment.is_mine;
|
|
1151
1185
|
const isAgent = !isMine && comment.author_source === "mcp";
|
|
1152
1186
|
const isSystem = !isMine && comment.author_source === "system";
|
|
@@ -1157,17 +1191,20 @@ function CommentBubble({ comment, strings, onOpenSeq }) {
|
|
|
1157
1191
|
return /* @__PURE__ */ jsxs2("div", { class: `comment-bubble ${isMine ? "is-mine" : "is-other"}`, children: [
|
|
1158
1192
|
!isMine && label && /* @__PURE__ */ jsx("div", { class: `comment-author comment-author--${variant}`, children: label }),
|
|
1159
1193
|
comment.body && /* @__PURE__ */ jsx("div", { class: "comment-body", children: linkifySeq(comment.body, onOpenSeq) }),
|
|
1160
|
-
images.length > 0 && /* @__PURE__ */ jsx("div", { class: "comment-attachments", children: images.map((a) =>
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1194
|
+
images.length > 0 && /* @__PURE__ */ jsx("div", { class: "comment-attachments", children: images.map((a) => {
|
|
1195
|
+
const src = stableUrl ? stableUrl(`att:${a.id}`, a.url) : a.url;
|
|
1196
|
+
return /* @__PURE__ */ jsx(
|
|
1197
|
+
"a",
|
|
1198
|
+
{
|
|
1199
|
+
class: "comment-attachment",
|
|
1200
|
+
href: src,
|
|
1201
|
+
target: "_blank",
|
|
1202
|
+
rel: "noopener noreferrer",
|
|
1203
|
+
children: /* @__PURE__ */ jsx("img", { src, alt: strings["detail.attachment_alt"], loading: "lazy" })
|
|
1204
|
+
},
|
|
1205
|
+
a.id
|
|
1206
|
+
);
|
|
1207
|
+
}) }),
|
|
1171
1208
|
/* @__PURE__ */ jsx("div", { class: "comment-time", children: formatTime(comment.created_at) })
|
|
1172
1209
|
] });
|
|
1173
1210
|
}
|
|
@@ -1234,7 +1271,8 @@ function ReportDetailView({
|
|
|
1234
1271
|
canModerate = true,
|
|
1235
1272
|
variant = "modal",
|
|
1236
1273
|
onOpenSeq,
|
|
1237
|
-
buildPermalink
|
|
1274
|
+
buildPermalink,
|
|
1275
|
+
seed
|
|
1238
1276
|
}) {
|
|
1239
1277
|
const [detail, setDetail] = useState(null);
|
|
1240
1278
|
const [error, setError] = useState(null);
|
|
@@ -1251,6 +1289,20 @@ function ReportDetailView({
|
|
|
1251
1289
|
const [attachError, setAttachError] = useState("");
|
|
1252
1290
|
const fileInputRef = useRef(null);
|
|
1253
1291
|
const mountedRef = useRef(true);
|
|
1292
|
+
const urlPins = useRef(/* @__PURE__ */ new Map());
|
|
1293
|
+
const [, bumpPinTick] = useState(0);
|
|
1294
|
+
const PIN_MAX_AGE_MS = 45 * 6e4;
|
|
1295
|
+
const pinUrl = (key, url) => {
|
|
1296
|
+
if (!url) return url;
|
|
1297
|
+
const pin = urlPins.current.get(key);
|
|
1298
|
+
if (pin && Date.now() - pin.at < PIN_MAX_AGE_MS) return pin.url;
|
|
1299
|
+
urlPins.current.set(key, { url, at: Date.now() });
|
|
1300
|
+
return url;
|
|
1301
|
+
};
|
|
1302
|
+
const unpinUrl = (key) => {
|
|
1303
|
+
urlPins.current.delete(key);
|
|
1304
|
+
bumpPinTick((t) => t + 1);
|
|
1305
|
+
};
|
|
1254
1306
|
const shotsRef = useRef(composeShots);
|
|
1255
1307
|
shotsRef.current = composeShots;
|
|
1256
1308
|
useEffect(() => {
|
|
@@ -1421,6 +1473,39 @@ function ReportDetailView({
|
|
|
1421
1473
|
}
|
|
1422
1474
|
};
|
|
1423
1475
|
if (!detail && !error) {
|
|
1476
|
+
if (seed) {
|
|
1477
|
+
const seedPill = /* @__PURE__ */ jsx2("span", { class: `pill pill-status pill-status--${seed.status}`, children: strings[`status.${seed.status}`] ?? seed.status });
|
|
1478
|
+
return /* @__PURE__ */ jsxs3("div", { class: `report-detail variant-${variant} is-seeded`, children: [
|
|
1479
|
+
variant === "modal" && /* @__PURE__ */ jsxs3("div", { class: "report-detail-header", children: [
|
|
1480
|
+
/* @__PURE__ */ jsxs3("button", { type: "button", class: "btn", onClick: onBack, children: [
|
|
1481
|
+
"\u2190 ",
|
|
1482
|
+
strings["detail.back"]
|
|
1483
|
+
] }),
|
|
1484
|
+
seedPill
|
|
1485
|
+
] }),
|
|
1486
|
+
variant === "board" && /* @__PURE__ */ jsxs3("div", { class: "report-detail-header report-detail-header--board", children: [
|
|
1487
|
+
/* @__PURE__ */ jsxs3(
|
|
1488
|
+
"button",
|
|
1489
|
+
{
|
|
1490
|
+
type: "button",
|
|
1491
|
+
class: "btn btn--ghost report-detail-back",
|
|
1492
|
+
onClick: onBack,
|
|
1493
|
+
"aria-label": strings["board.back"],
|
|
1494
|
+
children: [
|
|
1495
|
+
"\u2190 ",
|
|
1496
|
+
strings["board.back"]
|
|
1497
|
+
]
|
|
1498
|
+
}
|
|
1499
|
+
),
|
|
1500
|
+
seedPill
|
|
1501
|
+
] }),
|
|
1502
|
+
/* @__PURE__ */ jsxs3("div", { class: "report-detail-body", children: [
|
|
1503
|
+
/* @__PURE__ */ jsx2("div", { class: "report-detail-description-row", children: /* @__PURE__ */ jsx2("p", { class: "report-detail-description", children: linkifySeq(seed.description, onOpenSeq) }) }),
|
|
1504
|
+
/* @__PURE__ */ jsx2("h3", { class: "report-detail-section", children: strings["detail.thread"] }),
|
|
1505
|
+
/* @__PURE__ */ jsx2("div", { class: "mine-loading", children: strings["mine.loading"] })
|
|
1506
|
+
] })
|
|
1507
|
+
] });
|
|
1508
|
+
}
|
|
1424
1509
|
return /* @__PURE__ */ jsx2("div", { class: "mine-loading", children: strings["mine.loading"] });
|
|
1425
1510
|
}
|
|
1426
1511
|
if (!detail) {
|
|
@@ -1530,7 +1615,7 @@ function ReportDetailView({
|
|
|
1530
1615
|
children: copied ? strings["detail.copied"] : strings["detail.copy_link"]
|
|
1531
1616
|
}
|
|
1532
1617
|
) }),
|
|
1533
|
-
(detail.screenshots?.length ? detail.screenshots.map((s) => s.url) : [detail.screenshot_url]).
|
|
1618
|
+
(detail.screenshots?.length ? detail.screenshots.map((s) => s.url) : [detail.screenshot_url]).map((url, i) => ({ url: pinUrl(`shot:${i}`, url ?? null), pinKey: `shot:${i}` })).filter((s) => Boolean(s.url)).map(({ url, pinKey }) => {
|
|
1534
1619
|
const safeHref = safeExternalHref(url);
|
|
1535
1620
|
return safeHref ? /* @__PURE__ */ jsx2(
|
|
1536
1621
|
"a",
|
|
@@ -1539,9 +1624,9 @@ function ReportDetailView({
|
|
|
1539
1624
|
href: safeHref,
|
|
1540
1625
|
target: "_blank",
|
|
1541
1626
|
rel: "noopener noreferrer",
|
|
1542
|
-
children: /* @__PURE__ */ jsx2("img", { src: url, alt: "", loading: "lazy" })
|
|
1627
|
+
children: /* @__PURE__ */ jsx2("img", { src: url, alt: "", loading: "lazy", onError: () => unpinUrl(pinKey) })
|
|
1543
1628
|
}
|
|
1544
|
-
) : /* @__PURE__ */ jsx2("div", { class: "report-detail-screenshot", children: /* @__PURE__ */ jsx2("img", { src: url, alt: "", loading: "lazy" }) });
|
|
1629
|
+
) : /* @__PURE__ */ jsx2("div", { class: "report-detail-screenshot", children: /* @__PURE__ */ jsx2("img", { src: url, alt: "", loading: "lazy", onError: () => unpinUrl(pinKey) }) });
|
|
1545
1630
|
}),
|
|
1546
1631
|
/* @__PURE__ */ jsx2("h3", { class: "report-detail-section", children: strings["detail.thread"] }),
|
|
1547
1632
|
detail.comments.length === 0 ? /* @__PURE__ */ jsx2("p", { class: "report-detail-empty", children: strings["detail.no_replies"] }) : /* @__PURE__ */ jsx2("ul", { class: "report-comments", children: detail.comments.map((c) => /* @__PURE__ */ jsx2("li", { children: /* @__PURE__ */ jsx2(
|
|
@@ -1549,7 +1634,8 @@ function ReportDetailView({
|
|
|
1549
1634
|
{
|
|
1550
1635
|
comment: c,
|
|
1551
1636
|
strings,
|
|
1552
|
-
...onOpenSeq && { onOpenSeq }
|
|
1637
|
+
...onOpenSeq && { onOpenSeq },
|
|
1638
|
+
stableUrl: (key, url) => pinUrl(key, url) ?? url
|
|
1553
1639
|
}
|
|
1554
1640
|
) })) }),
|
|
1555
1641
|
detail.status_history && detail.status_history.length > 0 && /* @__PURE__ */ jsx2(StatusHistorySection, { rows: detail.status_history, strings }),
|
|
@@ -1833,10 +1919,13 @@ function BoardView({
|
|
|
1833
1919
|
const [page, setPage] = useState2(null);
|
|
1834
1920
|
const [kpis, setKpis] = useState2(null);
|
|
1835
1921
|
const [loading, setLoading] = useState2(true);
|
|
1922
|
+
const [stale, setStale] = useState2(false);
|
|
1836
1923
|
const [error, setError] = useState2(null);
|
|
1837
1924
|
const [selectedId, setSelectedId] = useState2(initialSelectedId ?? null);
|
|
1838
1925
|
const [detailKey, setDetailKey] = useState2(0);
|
|
1839
1926
|
const [reloadTick, setReloadTick] = useState2(0);
|
|
1927
|
+
const [extra, setExtra] = useState2(null);
|
|
1928
|
+
const [loadingMore, setLoadingMore] = useState2(false);
|
|
1840
1929
|
const [qDraft, setQDraft] = useState2(filters.q ?? "");
|
|
1841
1930
|
const debouncedQ = useDebouncedValue(qDraft, SEARCH_DEBOUNCE_MS);
|
|
1842
1931
|
useEffect2(() => {
|
|
@@ -1866,16 +1955,36 @@ function BoardView({
|
|
|
1866
1955
|
}, [filtersHash(filters), thisPage, reloadTick]);
|
|
1867
1956
|
useEffect2(() => {
|
|
1868
1957
|
let cancelled = false;
|
|
1869
|
-
|
|
1958
|
+
const controller = new AbortController();
|
|
1959
|
+
const cacheKey = boardCacheKey(externalId, JSON.stringify(fetchFilters));
|
|
1960
|
+
const cached = readBoardCache(cacheKey);
|
|
1961
|
+
if (cached) {
|
|
1962
|
+
setPage(cached.page);
|
|
1963
|
+
setKpis(cached.kpis);
|
|
1964
|
+
setStale(false);
|
|
1965
|
+
setLoading(false);
|
|
1966
|
+
} else {
|
|
1967
|
+
setPage(
|
|
1968
|
+
(prev) => prev ? { ...prev, results: applyLocalFilters(prev.results, fetchFilters) } : null
|
|
1969
|
+
);
|
|
1970
|
+
setStale(true);
|
|
1971
|
+
setLoading(true);
|
|
1972
|
+
}
|
|
1973
|
+
setExtra(null);
|
|
1974
|
+
setLoadingMore(false);
|
|
1870
1975
|
const poll = startPoll(async () => {
|
|
1871
1976
|
try {
|
|
1872
|
-
const
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1977
|
+
const list = await api.listBoard(externalId, fetchFilters, {
|
|
1978
|
+
signal: controller.signal
|
|
1979
|
+
});
|
|
1980
|
+
const k = list.kpis ?? await api.fetchBoardKpis(externalId, fetchFilters, {
|
|
1981
|
+
signal: controller.signal
|
|
1982
|
+
});
|
|
1876
1983
|
if (cancelled) return true;
|
|
1984
|
+
writeBoardCache(cacheKey, list, k);
|
|
1877
1985
|
setPage(list);
|
|
1878
1986
|
setKpis(k);
|
|
1987
|
+
setStale(false);
|
|
1879
1988
|
setError(null);
|
|
1880
1989
|
return true;
|
|
1881
1990
|
} catch (e) {
|
|
@@ -1889,17 +1998,41 @@ function BoardView({
|
|
|
1889
1998
|
return () => {
|
|
1890
1999
|
cancelled = true;
|
|
1891
2000
|
poll.stop();
|
|
2001
|
+
controller.abort();
|
|
1892
2002
|
};
|
|
1893
2003
|
}, [api, externalId, JSON.stringify(fetchFilters)]);
|
|
2004
|
+
const visibleRows = useMemo(() => {
|
|
2005
|
+
if (!page) return [];
|
|
2006
|
+
if (!extra || extra.rows.length === 0) return page.results;
|
|
2007
|
+
const seen = new Set(page.results.map((r) => r.id));
|
|
2008
|
+
return [...page.results, ...extra.rows.filter((r) => !seen.has(r.id))];
|
|
2009
|
+
}, [page, extra]);
|
|
2010
|
+
const hasMore = extra ? extra.hasMore : Boolean(page?.next);
|
|
2011
|
+
const loadMore = async () => {
|
|
2012
|
+
if (loadingMore || !page) return;
|
|
2013
|
+
const pageNum = extra ? extra.nextPage : 2;
|
|
2014
|
+
setLoadingMore(true);
|
|
2015
|
+
try {
|
|
2016
|
+
const res = await api.listBoard(externalId, { ...fetchFilters, page: pageNum });
|
|
2017
|
+
setExtra((prev) => ({
|
|
2018
|
+
rows: [...prev?.rows ?? [], ...res.results],
|
|
2019
|
+
nextPage: pageNum + 1,
|
|
2020
|
+
hasMore: res.next !== null
|
|
2021
|
+
}));
|
|
2022
|
+
} catch {
|
|
2023
|
+
} finally {
|
|
2024
|
+
setLoadingMore(false);
|
|
2025
|
+
}
|
|
2026
|
+
};
|
|
1894
2027
|
const selectedRow = useMemo(() => {
|
|
1895
|
-
if (!selectedId
|
|
1896
|
-
return
|
|
1897
|
-
}, [selectedId,
|
|
2028
|
+
if (!selectedId) return null;
|
|
2029
|
+
return visibleRows.find((r) => r.id === selectedId) ?? null;
|
|
2030
|
+
}, [selectedId, visibleRows]);
|
|
1898
2031
|
const onPickRow = (row) => {
|
|
1899
2032
|
setSelectedId(row.id);
|
|
1900
2033
|
setDetailKey((k) => k + 1);
|
|
1901
2034
|
};
|
|
1902
|
-
return /* @__PURE__ */ jsxs4("div", { class:
|
|
2035
|
+
return /* @__PURE__ */ jsxs4("div", { class: `board-view ${stale ? "is-stale" : ""}`, children: [
|
|
1903
2036
|
/* @__PURE__ */ jsx3(BoardHeader, { strings, kpis, thisPage }),
|
|
1904
2037
|
/* @__PURE__ */ jsx3(
|
|
1905
2038
|
BoardFilters,
|
|
@@ -1915,7 +2048,7 @@ function BoardView({
|
|
|
1915
2048
|
}
|
|
1916
2049
|
),
|
|
1917
2050
|
/* @__PURE__ */ jsxs4("div", { class: "board-body", children: [
|
|
1918
|
-
/* @__PURE__ */ jsxs4("div", { class: "board-list-wrap", "aria-busy": loading
|
|
2051
|
+
/* @__PURE__ */ jsxs4("div", { class: "board-list-wrap", "aria-busy": loading, children: [
|
|
1919
2052
|
error && /* @__PURE__ */ jsxs4("div", { class: "board-error", role: "alert", children: [
|
|
1920
2053
|
/* @__PURE__ */ jsx3("span", { children: error }),
|
|
1921
2054
|
/* @__PURE__ */ jsx3(
|
|
@@ -1933,7 +2066,7 @@ function BoardView({
|
|
|
1933
2066
|
)
|
|
1934
2067
|
] }),
|
|
1935
2068
|
!error && loading && !page && /* @__PURE__ */ jsx3(BoardListSkeleton, {}),
|
|
1936
|
-
!error && page &&
|
|
2069
|
+
!error && page && visibleRows.length === 0 && (!loading || stale) && /* @__PURE__ */ jsx3(
|
|
1937
2070
|
BoardEmpty,
|
|
1938
2071
|
{
|
|
1939
2072
|
strings,
|
|
@@ -1941,27 +2074,40 @@ function BoardView({
|
|
|
1941
2074
|
onAllPages: () => setThisPage(false)
|
|
1942
2075
|
}
|
|
1943
2076
|
),
|
|
1944
|
-
!error && page &&
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
2077
|
+
!error && page && visibleRows.length > 0 && /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
2078
|
+
/* @__PURE__ */ jsx3(
|
|
2079
|
+
BoardList,
|
|
2080
|
+
{
|
|
2081
|
+
rows: visibleRows,
|
|
2082
|
+
selectedId,
|
|
2083
|
+
onPick: onPickRow,
|
|
2084
|
+
strings,
|
|
2085
|
+
total: page.count,
|
|
2086
|
+
thisPage
|
|
2087
|
+
}
|
|
2088
|
+
),
|
|
2089
|
+
hasMore && !stale && /* @__PURE__ */ jsx3(
|
|
2090
|
+
"button",
|
|
2091
|
+
{
|
|
2092
|
+
type: "button",
|
|
2093
|
+
class: "btn btn--ghost board-load-more",
|
|
2094
|
+
onClick: () => void loadMore(),
|
|
2095
|
+
disabled: loadingMore,
|
|
2096
|
+
children: strings["board.list.loadMore"]
|
|
2097
|
+
}
|
|
2098
|
+
)
|
|
2099
|
+
] })
|
|
1955
2100
|
] }),
|
|
1956
|
-
/* @__PURE__ */ jsx3("div", { class: `board-detail-wrap ${
|
|
2101
|
+
/* @__PURE__ */ jsx3("div", { class: `board-detail-wrap ${selectedId ? "has-selection" : ""}`, children: selectedId ? /* @__PURE__ */ jsx3(
|
|
1957
2102
|
ReportDetailView,
|
|
1958
2103
|
{
|
|
1959
2104
|
api,
|
|
1960
2105
|
externalId,
|
|
1961
|
-
reportId:
|
|
2106
|
+
reportId: selectedId,
|
|
1962
2107
|
strings,
|
|
1963
2108
|
onBack: () => setSelectedId(null),
|
|
1964
|
-
canModerate: selectedRow.can_moderate ?? selectedRow.is_mine,
|
|
2109
|
+
canModerate: selectedRow ? selectedRow.can_moderate ?? selectedRow.is_mine : false,
|
|
2110
|
+
...selectedRow ? { seed: selectedRow } : {},
|
|
1965
2111
|
variant: "board",
|
|
1966
2112
|
onOpenSeq: (seq) => {
|
|
1967
2113
|
void api.getReportBySeq(seq, externalId).then((r) => {
|
|
@@ -2283,6 +2429,33 @@ function useDebouncedValue(value, ms) {
|
|
|
2283
2429
|
}, [value, ms]);
|
|
2284
2430
|
return debounced;
|
|
2285
2431
|
}
|
|
2432
|
+
var SEVERITY_RANK = { blocker: 0, high: 1, medium: 2, low: 3 };
|
|
2433
|
+
function applyLocalFilters(rows, f) {
|
|
2434
|
+
let out = rows;
|
|
2435
|
+
if (f.status?.length) out = out.filter((r) => f.status.includes(r.status));
|
|
2436
|
+
if (f.type?.length) out = out.filter((r) => f.type.includes(r.feedback_type));
|
|
2437
|
+
if (f.severity?.length) out = out.filter((r) => f.severity.includes(r.severity));
|
|
2438
|
+
if (f.mine) out = out.filter((r) => r.is_mine);
|
|
2439
|
+
const sorted = out.slice();
|
|
2440
|
+
switch (f.ordering ?? "recent") {
|
|
2441
|
+
case "oldest":
|
|
2442
|
+
sorted.sort((a, b) => a.created_at.localeCompare(b.created_at));
|
|
2443
|
+
break;
|
|
2444
|
+
case "updated":
|
|
2445
|
+
sorted.sort((a, b) => b.updated_at.localeCompare(a.updated_at));
|
|
2446
|
+
break;
|
|
2447
|
+
case "severity":
|
|
2448
|
+
sorted.sort(
|
|
2449
|
+
(a, b) => (SEVERITY_RANK[a.severity] ?? 9) - (SEVERITY_RANK[b.severity] ?? 9)
|
|
2450
|
+
);
|
|
2451
|
+
break;
|
|
2452
|
+
case "status":
|
|
2453
|
+
break;
|
|
2454
|
+
default:
|
|
2455
|
+
sorted.sort((a, b) => b.created_at.localeCompare(a.created_at));
|
|
2456
|
+
}
|
|
2457
|
+
return sorted;
|
|
2458
|
+
}
|
|
2286
2459
|
function filtersHash(f) {
|
|
2287
2460
|
return JSON.stringify({
|
|
2288
2461
|
s: f.status?.slice().sort(),
|
|
@@ -2338,7 +2511,7 @@ function repliesLabel(count, strings) {
|
|
|
2338
2511
|
if (count === 1) return strings["mine.replies_one"];
|
|
2339
2512
|
return strings["mine.replies_many"].replace("{count}", String(count));
|
|
2340
2513
|
}
|
|
2341
|
-
function ReportRow({ row, strings, onClick }) {
|
|
2514
|
+
function ReportRow({ row, strings, onClick, extraMeta }) {
|
|
2342
2515
|
const preview = row.description.length > 120 ? row.description.slice(0, 117) + "\u2026" : row.description;
|
|
2343
2516
|
return /* @__PURE__ */ jsxs5("button", { type: "button", class: "mine-row", onClick, children: [
|
|
2344
2517
|
/* @__PURE__ */ jsxs5("div", { class: "mine-row-pills", children: [
|
|
@@ -2352,6 +2525,10 @@ function ReportRow({ row, strings, onClick }) {
|
|
|
2352
2525
|
row.comment_count > 0 && /* @__PURE__ */ jsxs5("span", { children: [
|
|
2353
2526
|
"\xB7 ",
|
|
2354
2527
|
repliesLabel(row.comment_count, strings)
|
|
2528
|
+
] }),
|
|
2529
|
+
extraMeta && /* @__PURE__ */ jsxs5("span", { children: [
|
|
2530
|
+
"\xB7 ",
|
|
2531
|
+
extraMeta
|
|
2355
2532
|
] })
|
|
2356
2533
|
] })
|
|
2357
2534
|
] });
|
|
@@ -2456,7 +2633,15 @@ function ChangelogList({ api, externalId, strings, onSelect }) {
|
|
|
2456
2633
|
/* @__PURE__ */ jsx5("span", { class: "changelog-group-rule", "aria-hidden": "true" }),
|
|
2457
2634
|
/* @__PURE__ */ jsx5("span", { class: "changelog-group-count", children: strings[g.rows.length === 1 ? "changelog.resolved_one" : "changelog.resolved_many"].replace("{count}", String(g.rows.length)) })
|
|
2458
2635
|
] }),
|
|
2459
|
-
/* @__PURE__ */ jsx5("ul", { class: "mine-rows", children: g.rows.map((row) => /* @__PURE__ */ jsx5("li", { children: /* @__PURE__ */ jsx5(
|
|
2636
|
+
/* @__PURE__ */ jsx5("ul", { class: "mine-rows", children: g.rows.map((row) => /* @__PURE__ */ jsx5("li", { children: /* @__PURE__ */ jsx5(
|
|
2637
|
+
ReportRow,
|
|
2638
|
+
{
|
|
2639
|
+
row,
|
|
2640
|
+
strings,
|
|
2641
|
+
onClick: () => onSelect(row),
|
|
2642
|
+
extraMeta: row.pr_number ? `PR #${row.pr_number}` : void 0
|
|
2643
|
+
}
|
|
2644
|
+
) })) })
|
|
2460
2645
|
] }, g.weekKey)) })
|
|
2461
2646
|
] });
|
|
2462
2647
|
}
|
|
@@ -3633,30 +3818,32 @@ function PagePeekPanel({
|
|
|
3633
3818
|
|
|
3634
3819
|
// src/widget/pageSignal.ts
|
|
3635
3820
|
import { useCallback, useEffect as useEffect9, useState as useState8 } from "preact/hooks";
|
|
3636
|
-
var
|
|
3821
|
+
var TTL_MS2 = 6e4;
|
|
3637
3822
|
function computeOpenCount(kpis) {
|
|
3638
3823
|
const s = kpis.by_status;
|
|
3639
3824
|
return (s.new ?? 0) + (s.in_progress ?? 0) + (s.awaiting_validation ?? 0);
|
|
3640
3825
|
}
|
|
3641
|
-
var
|
|
3826
|
+
var cache2 = /* @__PURE__ */ new Map();
|
|
3642
3827
|
var inflight = /* @__PURE__ */ new Map();
|
|
3643
3828
|
function invalidatePageSignal() {
|
|
3644
|
-
|
|
3829
|
+
cache2.clear();
|
|
3645
3830
|
}
|
|
3646
|
-
function
|
|
3831
|
+
function fetchPageKpis(api, externalId, path) {
|
|
3647
3832
|
const key = `${externalId}|${path}`;
|
|
3648
|
-
const hit =
|
|
3649
|
-
if (hit && Date.now() - hit.fetchedAt <
|
|
3833
|
+
const hit = cache2.get(key);
|
|
3834
|
+
if (hit && Date.now() - hit.fetchedAt < TTL_MS2) return Promise.resolve(hit.kpis);
|
|
3650
3835
|
const pending = inflight.get(key);
|
|
3651
3836
|
if (pending) return pending;
|
|
3652
3837
|
const p = api.fetchBoardKpis(externalId, { pagePath: path }).then((kpis) => {
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
return open;
|
|
3838
|
+
cache2.set(key, { kpis, fetchedAt: Date.now() });
|
|
3839
|
+
return kpis;
|
|
3656
3840
|
}).finally(() => inflight.delete(key));
|
|
3657
3841
|
inflight.set(key, p);
|
|
3658
3842
|
return p;
|
|
3659
3843
|
}
|
|
3844
|
+
function fetchOpen(api, externalId, path) {
|
|
3845
|
+
return fetchPageKpis(api, externalId, path).then(computeOpenCount);
|
|
3846
|
+
}
|
|
3660
3847
|
function usePageOpenCount(opts) {
|
|
3661
3848
|
const { api, externalId, getCurrentPage, enabled } = opts;
|
|
3662
3849
|
const [open, setOpen] = useState8(0);
|
|
@@ -5450,6 +5637,20 @@ button.report-detail-edit {
|
|
|
5450
5637
|
min-height: 0;
|
|
5451
5638
|
overflow: hidden;
|
|
5452
5639
|
}
|
|
5640
|
+
/* Stale-while-revalidate: a query change keeps the previous rows visible
|
|
5641
|
+
* (locally pre-filtered where the row data allows) and dims them while the
|
|
5642
|
+
* server confirms \u2014 the instant local response is the #352 "never inert"
|
|
5643
|
+
* guarantee, the dim is the "still confirming" affordance. */
|
|
5644
|
+
.board-view.is-stale .board-list,
|
|
5645
|
+
.board-view.is-stale .board-list-count,
|
|
5646
|
+
.board-view.is-stale .board-kpi-strip {
|
|
5647
|
+
opacity: 0.55;
|
|
5648
|
+
transition: opacity 0.15s ease;
|
|
5649
|
+
}
|
|
5650
|
+
.board-load-more {
|
|
5651
|
+
margin: 4px auto 8px;
|
|
5652
|
+
flex: 0 0 auto;
|
|
5653
|
+
}
|
|
5453
5654
|
.board-list-count {
|
|
5454
5655
|
font-size: var(--mfb-text-xs);
|
|
5455
5656
|
color: var(--mfb-text-muted);
|
|
@@ -5771,7 +5972,7 @@ function mountWidget(options) {
|
|
|
5771
5972
|
function openWidget(externalId) {
|
|
5772
5973
|
rerender({ ...currentState, open: true, tab: "send" });
|
|
5773
5974
|
if (options.openToCurrentPageFeedback && options.api && externalId) {
|
|
5774
|
-
options.api
|
|
5975
|
+
fetchPageKpis(options.api, externalId, currentPagePath(options)).then((kpis) => {
|
|
5775
5976
|
if (kpis.total > 0 && currentState.open && currentState.tab === "send") {
|
|
5776
5977
|
rerender({ ...currentState, tab: "board" });
|
|
5777
5978
|
}
|
|
@@ -6203,8 +6404,8 @@ function createFeedback(config) {
|
|
|
6203
6404
|
capture_method: captureMethod,
|
|
6204
6405
|
technical_context
|
|
6205
6406
|
};
|
|
6206
|
-
if ("0.
|
|
6207
|
-
payload.widget_version = "0.
|
|
6407
|
+
if ("0.32.0") {
|
|
6408
|
+
payload.widget_version = "0.32.0";
|
|
6208
6409
|
}
|
|
6209
6410
|
if (manualScreenshots?.length) {
|
|
6210
6411
|
payload.screenshots = manualScreenshots;
|
|
@@ -6330,4 +6531,4 @@ function createFeedback(config) {
|
|
|
6330
6531
|
export {
|
|
6331
6532
|
createFeedback
|
|
6332
6533
|
};
|
|
6333
|
-
//# sourceMappingURL=chunk-
|
|
6534
|
+
//# sourceMappingURL=chunk-YNDBZR37.mjs.map
|