@mhosaic/feedback 0.31.0 → 0.33.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-AA24TFKX.mjs → chunk-RR6UB75B.mjs} +684 -406
- package/dist/chunk-RR6UB75B.mjs.map +1 -0
- package/dist/{chunk-IP4Y5EZE.mjs → chunk-WROPP3IB.mjs} +17 -1
- package/dist/chunk-WROPP3IB.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/loader/react.mjs +1 -1
- package/dist/loader.min.js +1 -1
- package/dist/loader.min.js.map +1 -1
- package/dist/loader.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-AA24TFKX.mjs.map +0 -1
- package/dist/chunk-IP4Y5EZE.mjs.map +0 -1
|
@@ -145,6 +145,32 @@ function createApiClient(options) {
|
|
|
145
145
|
if (response.status === 404) return [];
|
|
146
146
|
return readJsonArray(response, "listMine");
|
|
147
147
|
}
|
|
148
|
+
async function diagnose(opts) {
|
|
149
|
+
const onLine = opts?.onLine ?? (typeof navigator !== "undefined" ? navigator.onLine : true);
|
|
150
|
+
if (onLine === false) return "offline";
|
|
151
|
+
const probeUrl = `${endpoint}/api/feedback/v1/widget-manifest/?pk=${encodeURIComponent(options.apiKey)}`;
|
|
152
|
+
let simple;
|
|
153
|
+
try {
|
|
154
|
+
simple = await fetcher(probeUrl, { method: "GET", redirect: "manual" });
|
|
155
|
+
} catch {
|
|
156
|
+
return "blocked";
|
|
157
|
+
}
|
|
158
|
+
if (simple.type === "opaqueredirect" || simple.status >= 300 && simple.status < 400) {
|
|
159
|
+
return "intercepted";
|
|
160
|
+
}
|
|
161
|
+
let authed;
|
|
162
|
+
try {
|
|
163
|
+
authed = await fetcher(probeUrl, {
|
|
164
|
+
method: "GET",
|
|
165
|
+
headers: { Authorization: `Bearer ${options.apiKey}` }
|
|
166
|
+
});
|
|
167
|
+
} catch {
|
|
168
|
+
return "preflight";
|
|
169
|
+
}
|
|
170
|
+
if (authed.status === 401 || authed.status === 403) return "key";
|
|
171
|
+
if (authed.status === 429) return "throttle";
|
|
172
|
+
return "healthy";
|
|
173
|
+
}
|
|
148
174
|
async function listChangelog(externalId) {
|
|
149
175
|
const response = await fetcher(
|
|
150
176
|
`${endpoint}/api/feedback/v1/reports/widget/changelog/`,
|
|
@@ -153,10 +179,14 @@ function createApiClient(options) {
|
|
|
153
179
|
if (response.status === 404) return [];
|
|
154
180
|
return readJsonArray(response, "listChangelog");
|
|
155
181
|
}
|
|
156
|
-
async function getReport(reportId, externalId) {
|
|
182
|
+
async function getReport(reportId, externalId, opts) {
|
|
157
183
|
const response = await fetcher(
|
|
158
184
|
`${endpoint}/api/feedback/v1/reports/widget/${reportId}/`,
|
|
159
|
-
{
|
|
185
|
+
{
|
|
186
|
+
method: "GET",
|
|
187
|
+
headers: widgetHeaders(externalId),
|
|
188
|
+
...opts?.signal ? { signal: opts.signal } : {}
|
|
189
|
+
}
|
|
160
190
|
);
|
|
161
191
|
return readJsonObject(response, "getReport");
|
|
162
192
|
}
|
|
@@ -268,10 +298,14 @@ function createApiClient(options) {
|
|
|
268
298
|
const qs = params.toString();
|
|
269
299
|
return qs ? `?${qs}` : "";
|
|
270
300
|
}
|
|
271
|
-
async function listBoard(externalId, filters) {
|
|
301
|
+
async function listBoard(externalId, filters, opts) {
|
|
272
302
|
const response = await fetcher(
|
|
273
303
|
`${endpoint}/api/feedback/v1/reports/widget/board/${boardQueryString(filters)}`,
|
|
274
|
-
{
|
|
304
|
+
{
|
|
305
|
+
method: "GET",
|
|
306
|
+
headers: widgetHeaders(externalId),
|
|
307
|
+
...opts?.signal ? { signal: opts.signal } : {}
|
|
308
|
+
}
|
|
275
309
|
);
|
|
276
310
|
if (response.status === 404) {
|
|
277
311
|
return { count: 0, next: null, previous: null, results: [] };
|
|
@@ -282,10 +316,14 @@ function createApiClient(options) {
|
|
|
282
316
|
}
|
|
283
317
|
return page;
|
|
284
318
|
}
|
|
285
|
-
async function fetchBoardKpis(externalId, filters) {
|
|
319
|
+
async function fetchBoardKpis(externalId, filters, opts) {
|
|
286
320
|
const response = await fetcher(
|
|
287
321
|
`${endpoint}/api/feedback/v1/reports/widget/board/kpis/${boardQueryString(filters)}`,
|
|
288
|
-
{
|
|
322
|
+
{
|
|
323
|
+
method: "GET",
|
|
324
|
+
headers: widgetHeaders(externalId),
|
|
325
|
+
...opts?.signal ? { signal: opts.signal } : {}
|
|
326
|
+
}
|
|
289
327
|
);
|
|
290
328
|
if (response.status === 404) {
|
|
291
329
|
return { total: 0, by_status: {}, resolution_rate: 0, scope: "mine" };
|
|
@@ -318,6 +356,14 @@ function createApiClient(options) {
|
|
|
318
356
|
checkVisibility,
|
|
319
357
|
listMine,
|
|
320
358
|
listChangelog,
|
|
359
|
+
diagnose,
|
|
360
|
+
endpointHost: (() => {
|
|
361
|
+
try {
|
|
362
|
+
return new URL(endpoint).host;
|
|
363
|
+
} catch {
|
|
364
|
+
return endpoint;
|
|
365
|
+
}
|
|
366
|
+
})(),
|
|
321
367
|
getReport,
|
|
322
368
|
getReportBySeq,
|
|
323
369
|
addComment,
|
|
@@ -705,6 +751,7 @@ var DEFAULT_STRINGS = {
|
|
|
705
751
|
"board.retry": "Try again",
|
|
706
752
|
"board.list.you": "you",
|
|
707
753
|
"board.list.count": "{n} of {total}",
|
|
754
|
+
"board.list.loadMore": "Show more",
|
|
708
755
|
"board.detail.empty": "Pick a report on the left to see its full thread.",
|
|
709
756
|
"board.detail.by": "By",
|
|
710
757
|
"board.detail.confirm_resolution": "Confirm resolution",
|
|
@@ -726,6 +773,15 @@ var DEFAULT_STRINGS = {
|
|
|
726
773
|
"mine.empty.title": "No reports yet",
|
|
727
774
|
"mine.empty.body": "Once you send feedback you can follow the thread here.",
|
|
728
775
|
"mine.refresh": "Refresh",
|
|
776
|
+
"diag.run": "Diagnose",
|
|
777
|
+
"diag.running": "Checking the connection\u2026",
|
|
778
|
+
"diag.offline": "You appear to be offline. Reconnect and retry.",
|
|
779
|
+
"diag.blocked": "The feedback service is unreachable from this network. A firewall or proxy is blocking it \u2014 ask IT to allow {host}.",
|
|
780
|
+
"diag.intercepted": "Your corporate network (SSL-inspection proxy) is intercepting widget traffic. Ask IT to add {host} to the inspection bypass list.",
|
|
781
|
+
"diag.preflight": "A network proxy is blocking the widget\u2019s secure requests (CORS preflight). Ask IT to allow {host}.",
|
|
782
|
+
"diag.key": "The widget\u2019s access key was rejected. Contact the site team.",
|
|
783
|
+
"diag.throttle": "Too many requests right now \u2014 wait a moment and retry.",
|
|
784
|
+
"diag.healthy": "The service is reachable \u2014 this looks like a temporary hiccup. Retry.",
|
|
729
785
|
"mine.loading": "Loading\u2026",
|
|
730
786
|
"mine.error": "Could not load your reports.",
|
|
731
787
|
"mine.jump.placeholder": "#",
|
|
@@ -891,6 +947,7 @@ var FRENCH_STRINGS = {
|
|
|
891
947
|
"board.retry": "R\xE9essayer",
|
|
892
948
|
"board.list.you": "vous",
|
|
893
949
|
"board.list.count": "{n} sur {total}",
|
|
950
|
+
"board.list.loadMore": "Afficher plus",
|
|
894
951
|
"board.detail.empty": "S\xE9lectionnez un rapport \xE0 gauche pour voir le fil complet.",
|
|
895
952
|
"board.detail.by": "Par",
|
|
896
953
|
"board.detail.confirm_resolution": "Confirmer la r\xE9solution",
|
|
@@ -912,6 +969,15 @@ var FRENCH_STRINGS = {
|
|
|
912
969
|
"mine.empty.title": "Aucun rapport",
|
|
913
970
|
"mine.empty.body": "Apr\xE8s votre premier envoi vous pourrez suivre la conversation ici.",
|
|
914
971
|
"mine.refresh": "Actualiser",
|
|
972
|
+
"diag.run": "Diagnostiquer",
|
|
973
|
+
"diag.running": "V\xE9rification de la connexion\u2026",
|
|
974
|
+
"diag.offline": "Vous semblez hors ligne. Reconnectez-vous puis r\xE9essayez.",
|
|
975
|
+
"diag.blocked": "Le service de feedback est injoignable depuis ce r\xE9seau. Un pare-feu ou proxy le bloque \u2014 demandez \xE0 votre TI d\u2019autoriser {host}.",
|
|
976
|
+
"diag.intercepted": "Votre r\xE9seau d\u2019entreprise (inspection SSL) intercepte le trafic du widget. Demandez \xE0 votre TI d\u2019ajouter {host} \xE0 la liste d\u2019exclusion.",
|
|
977
|
+
"diag.preflight": "Un proxy r\xE9seau bloque les requ\xEAtes s\xE9curis\xE9es du widget (pr\xE9flight CORS). Demandez \xE0 votre TI d\u2019autoriser {host}.",
|
|
978
|
+
"diag.key": "La cl\xE9 d\u2019acc\xE8s du widget a \xE9t\xE9 refus\xE9e. Contactez l\u2019\xE9quipe du site.",
|
|
979
|
+
"diag.throttle": "Trop de requ\xEAtes pour le moment \u2014 patientez puis r\xE9essayez.",
|
|
980
|
+
"diag.healthy": "Le service est joignable \u2014 le probl\xE8me semble passager. R\xE9essayez.",
|
|
915
981
|
"mine.loading": "Chargement\u2026",
|
|
916
982
|
"mine.error": "Impossible de charger vos rapports.",
|
|
917
983
|
"mine.jump.placeholder": "#",
|
|
@@ -1003,7 +1069,7 @@ function resolveStrings(overrides, options = {}) {
|
|
|
1003
1069
|
|
|
1004
1070
|
// src/widget/mount.tsx
|
|
1005
1071
|
import { h, render } from "preact";
|
|
1006
|
-
import { useCallback as useCallback2, useEffect as useEffect10, useRef as useRef7, useState as
|
|
1072
|
+
import { useCallback as useCallback2, useEffect as useEffect10, useRef as useRef7, useState as useState10 } from "preact/hooks";
|
|
1007
1073
|
|
|
1008
1074
|
// src/widget/currentPage.ts
|
|
1009
1075
|
function currentPagePath(opts) {
|
|
@@ -1036,7 +1102,50 @@ function onLocationChange(cb) {
|
|
|
1036
1102
|
}
|
|
1037
1103
|
|
|
1038
1104
|
// src/widget/BoardView.tsx
|
|
1039
|
-
import { useEffect as useEffect2, useMemo, useState as
|
|
1105
|
+
import { useEffect as useEffect2, useMemo, useState as useState3 } from "preact/hooks";
|
|
1106
|
+
|
|
1107
|
+
// src/widget/DiagnosticHint.tsx
|
|
1108
|
+
import { useState } from "preact/hooks";
|
|
1109
|
+
import { jsx } from "preact/jsx-runtime";
|
|
1110
|
+
function DiagnosticHint({ api, strings, host }) {
|
|
1111
|
+
const [state, setState] = useState("idle");
|
|
1112
|
+
const run = async () => {
|
|
1113
|
+
setState("running");
|
|
1114
|
+
try {
|
|
1115
|
+
setState(await api.diagnose());
|
|
1116
|
+
} catch {
|
|
1117
|
+
setState("blocked");
|
|
1118
|
+
}
|
|
1119
|
+
};
|
|
1120
|
+
if (state === "idle") {
|
|
1121
|
+
return /* @__PURE__ */ jsx("button", { type: "button", class: "btn diag-btn", onClick: () => void run(), children: strings["diag.run"] });
|
|
1122
|
+
}
|
|
1123
|
+
if (state === "running") {
|
|
1124
|
+
return /* @__PURE__ */ jsx("div", { class: "diag-result", children: strings["diag.running"] });
|
|
1125
|
+
}
|
|
1126
|
+
const key = `diag.${state}`;
|
|
1127
|
+
return /* @__PURE__ */ jsx("div", { class: "diag-result", children: (strings[key] ?? state).replace("{host}", host) });
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
// src/widget/boardCache.ts
|
|
1131
|
+
var TTL_MS = 6e4;
|
|
1132
|
+
var MAX_ENTRIES = 50;
|
|
1133
|
+
var cache = /* @__PURE__ */ new Map();
|
|
1134
|
+
function boardCacheKey(externalId, fetchKey) {
|
|
1135
|
+
return `${externalId}|${fetchKey}`;
|
|
1136
|
+
}
|
|
1137
|
+
function readBoardCache(key) {
|
|
1138
|
+
const hit = cache.get(key);
|
|
1139
|
+
return hit && Date.now() - hit.at < TTL_MS ? hit : null;
|
|
1140
|
+
}
|
|
1141
|
+
function writeBoardCache(key, page, kpis) {
|
|
1142
|
+
if (!cache.has(key) && cache.size >= MAX_ENTRIES) {
|
|
1143
|
+
const oldest = cache.keys().next().value;
|
|
1144
|
+
if (oldest !== void 0) cache.delete(oldest);
|
|
1145
|
+
}
|
|
1146
|
+
cache.delete(key);
|
|
1147
|
+
cache.set(key, { page, kpis, at: Date.now() });
|
|
1148
|
+
}
|
|
1040
1149
|
|
|
1041
1150
|
// src/widget/boardViewStore.ts
|
|
1042
1151
|
var PREFIX = "mhosaic.boardView.";
|
|
@@ -1108,7 +1217,7 @@ function rateLimitMessage(err, strings) {
|
|
|
1108
1217
|
}
|
|
1109
1218
|
|
|
1110
1219
|
// src/widget/ReportDetailView.tsx
|
|
1111
|
-
import { useEffect, useRef, useState } from "preact/hooks";
|
|
1220
|
+
import { useEffect, useRef, useState as useState2 } from "preact/hooks";
|
|
1112
1221
|
|
|
1113
1222
|
// src/widget/linkifySeq.tsx
|
|
1114
1223
|
import { jsxs } from "preact/jsx-runtime";
|
|
@@ -1145,8 +1254,8 @@ function linkifySeq(text, onOpenSeq) {
|
|
|
1145
1254
|
}
|
|
1146
1255
|
|
|
1147
1256
|
// src/widget/CommentBubble.tsx
|
|
1148
|
-
import { jsx, jsxs as jsxs2 } from "preact/jsx-runtime";
|
|
1149
|
-
function CommentBubble({ comment, strings, onOpenSeq }) {
|
|
1257
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "preact/jsx-runtime";
|
|
1258
|
+
function CommentBubble({ comment, strings, onOpenSeq, stableUrl }) {
|
|
1150
1259
|
const isMine = comment.is_mine;
|
|
1151
1260
|
const isAgent = !isMine && comment.author_source === "mcp";
|
|
1152
1261
|
const isSystem = !isMine && comment.author_source === "system";
|
|
@@ -1155,20 +1264,23 @@ function CommentBubble({ comment, strings, onOpenSeq }) {
|
|
|
1155
1264
|
const label = comment.author_label || strings[labelKey];
|
|
1156
1265
|
const images = (comment.attachments ?? []).filter((a) => a.url);
|
|
1157
1266
|
return /* @__PURE__ */ jsxs2("div", { class: `comment-bubble ${isMine ? "is-mine" : "is-other"}`, children: [
|
|
1158
|
-
!isMine && label && /* @__PURE__ */
|
|
1159
|
-
comment.body && /* @__PURE__ */
|
|
1160
|
-
images.length > 0 && /* @__PURE__ */
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1267
|
+
!isMine && label && /* @__PURE__ */ jsx2("div", { class: `comment-author comment-author--${variant}`, children: label }),
|
|
1268
|
+
comment.body && /* @__PURE__ */ jsx2("div", { class: "comment-body", children: linkifySeq(comment.body, onOpenSeq) }),
|
|
1269
|
+
images.length > 0 && /* @__PURE__ */ jsx2("div", { class: "comment-attachments", children: images.map((a) => {
|
|
1270
|
+
const src = stableUrl ? stableUrl(`att:${a.id}`, a.url) : a.url;
|
|
1271
|
+
return /* @__PURE__ */ jsx2(
|
|
1272
|
+
"a",
|
|
1273
|
+
{
|
|
1274
|
+
class: "comment-attachment",
|
|
1275
|
+
href: src,
|
|
1276
|
+
target: "_blank",
|
|
1277
|
+
rel: "noopener noreferrer",
|
|
1278
|
+
children: /* @__PURE__ */ jsx2("img", { src, alt: strings["detail.attachment_alt"], loading: "lazy" })
|
|
1279
|
+
},
|
|
1280
|
+
a.id
|
|
1281
|
+
);
|
|
1282
|
+
}) }),
|
|
1283
|
+
/* @__PURE__ */ jsx2("div", { class: "comment-time", children: formatTime(comment.created_at) })
|
|
1172
1284
|
] });
|
|
1173
1285
|
}
|
|
1174
1286
|
function formatTime(iso) {
|
|
@@ -1223,7 +1335,7 @@ function safeExternalHref(url) {
|
|
|
1223
1335
|
}
|
|
1224
1336
|
|
|
1225
1337
|
// src/widget/ReportDetailView.tsx
|
|
1226
|
-
import { Fragment, jsx as
|
|
1338
|
+
import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "preact/jsx-runtime";
|
|
1227
1339
|
var POLL_MS = 3e4;
|
|
1228
1340
|
function ReportDetailView({
|
|
1229
1341
|
api,
|
|
@@ -1234,23 +1346,38 @@ function ReportDetailView({
|
|
|
1234
1346
|
canModerate = true,
|
|
1235
1347
|
variant = "modal",
|
|
1236
1348
|
onOpenSeq,
|
|
1237
|
-
buildPermalink
|
|
1349
|
+
buildPermalink,
|
|
1350
|
+
seed
|
|
1238
1351
|
}) {
|
|
1239
|
-
const [detail, setDetail] =
|
|
1240
|
-
const [error, setError] =
|
|
1241
|
-
const [copied, setCopied] =
|
|
1242
|
-
const [editing, setEditing] =
|
|
1243
|
-
const [editBody, setEditBody] =
|
|
1244
|
-
const [savingEdit, setSavingEdit] =
|
|
1245
|
-
const [editError, setEditError] =
|
|
1246
|
-
const [composeBody, setComposeBody] =
|
|
1247
|
-
const [sending, setSending] =
|
|
1248
|
-
const [closing, setClosing] =
|
|
1249
|
-
const [reopening, setReopening] =
|
|
1250
|
-
const [composeShots, setComposeShots] =
|
|
1251
|
-
const [attachError, setAttachError] =
|
|
1352
|
+
const [detail, setDetail] = useState2(null);
|
|
1353
|
+
const [error, setError] = useState2(null);
|
|
1354
|
+
const [copied, setCopied] = useState2(false);
|
|
1355
|
+
const [editing, setEditing] = useState2(false);
|
|
1356
|
+
const [editBody, setEditBody] = useState2("");
|
|
1357
|
+
const [savingEdit, setSavingEdit] = useState2(false);
|
|
1358
|
+
const [editError, setEditError] = useState2(false);
|
|
1359
|
+
const [composeBody, setComposeBody] = useState2("");
|
|
1360
|
+
const [sending, setSending] = useState2(false);
|
|
1361
|
+
const [closing, setClosing] = useState2(false);
|
|
1362
|
+
const [reopening, setReopening] = useState2(false);
|
|
1363
|
+
const [composeShots, setComposeShots] = useState2([]);
|
|
1364
|
+
const [attachError, setAttachError] = useState2("");
|
|
1252
1365
|
const fileInputRef = useRef(null);
|
|
1253
1366
|
const mountedRef = useRef(true);
|
|
1367
|
+
const urlPins = useRef(/* @__PURE__ */ new Map());
|
|
1368
|
+
const [, bumpPinTick] = useState2(0);
|
|
1369
|
+
const PIN_MAX_AGE_MS = 45 * 6e4;
|
|
1370
|
+
const pinUrl = (key, url) => {
|
|
1371
|
+
if (!url) return url;
|
|
1372
|
+
const pin = urlPins.current.get(key);
|
|
1373
|
+
if (pin && Date.now() - pin.at < PIN_MAX_AGE_MS) return pin.url;
|
|
1374
|
+
urlPins.current.set(key, { url, at: Date.now() });
|
|
1375
|
+
return url;
|
|
1376
|
+
};
|
|
1377
|
+
const unpinUrl = (key) => {
|
|
1378
|
+
urlPins.current.delete(key);
|
|
1379
|
+
bumpPinTick((t) => t + 1);
|
|
1380
|
+
};
|
|
1254
1381
|
const shotsRef = useRef(composeShots);
|
|
1255
1382
|
shotsRef.current = composeShots;
|
|
1256
1383
|
useEffect(() => {
|
|
@@ -1421,13 +1548,46 @@ function ReportDetailView({
|
|
|
1421
1548
|
}
|
|
1422
1549
|
};
|
|
1423
1550
|
if (!detail && !error) {
|
|
1424
|
-
|
|
1551
|
+
if (seed) {
|
|
1552
|
+
const seedPill = /* @__PURE__ */ jsx3("span", { class: `pill pill-status pill-status--${seed.status}`, children: strings[`status.${seed.status}`] ?? seed.status });
|
|
1553
|
+
return /* @__PURE__ */ jsxs3("div", { class: `report-detail variant-${variant} is-seeded`, children: [
|
|
1554
|
+
variant === "modal" && /* @__PURE__ */ jsxs3("div", { class: "report-detail-header", children: [
|
|
1555
|
+
/* @__PURE__ */ jsxs3("button", { type: "button", class: "btn", onClick: onBack, children: [
|
|
1556
|
+
"\u2190 ",
|
|
1557
|
+
strings["detail.back"]
|
|
1558
|
+
] }),
|
|
1559
|
+
seedPill
|
|
1560
|
+
] }),
|
|
1561
|
+
variant === "board" && /* @__PURE__ */ jsxs3("div", { class: "report-detail-header report-detail-header--board", children: [
|
|
1562
|
+
/* @__PURE__ */ jsxs3(
|
|
1563
|
+
"button",
|
|
1564
|
+
{
|
|
1565
|
+
type: "button",
|
|
1566
|
+
class: "btn btn--ghost report-detail-back",
|
|
1567
|
+
onClick: onBack,
|
|
1568
|
+
"aria-label": strings["board.back"],
|
|
1569
|
+
children: [
|
|
1570
|
+
"\u2190 ",
|
|
1571
|
+
strings["board.back"]
|
|
1572
|
+
]
|
|
1573
|
+
}
|
|
1574
|
+
),
|
|
1575
|
+
seedPill
|
|
1576
|
+
] }),
|
|
1577
|
+
/* @__PURE__ */ jsxs3("div", { class: "report-detail-body", children: [
|
|
1578
|
+
/* @__PURE__ */ jsx3("div", { class: "report-detail-description-row", children: /* @__PURE__ */ jsx3("p", { class: "report-detail-description", children: linkifySeq(seed.description, onOpenSeq) }) }),
|
|
1579
|
+
/* @__PURE__ */ jsx3("h3", { class: "report-detail-section", children: strings["detail.thread"] }),
|
|
1580
|
+
/* @__PURE__ */ jsx3("div", { class: "mine-loading", children: strings["mine.loading"] })
|
|
1581
|
+
] })
|
|
1582
|
+
] });
|
|
1583
|
+
}
|
|
1584
|
+
return /* @__PURE__ */ jsx3("div", { class: "mine-loading", children: strings["mine.loading"] });
|
|
1425
1585
|
}
|
|
1426
1586
|
if (!detail) {
|
|
1427
1587
|
if (error && error !== "load_failed") {
|
|
1428
1588
|
return /* @__PURE__ */ jsxs3("div", { class: "report-detail-empty-state", role: "alert", children: [
|
|
1429
|
-
/* @__PURE__ */
|
|
1430
|
-
/* @__PURE__ */
|
|
1589
|
+
/* @__PURE__ */ jsx3("p", { class: "report-detail-empty-state-body", children: error }),
|
|
1590
|
+
/* @__PURE__ */ jsx3("button", { type: "button", class: "btn", onClick: () => void fetchDetail(), children: strings["board.retry"] }),
|
|
1431
1591
|
/* @__PURE__ */ jsxs3("button", { type: "button", class: "btn", onClick: onBack, children: [
|
|
1432
1592
|
"\u2190 ",
|
|
1433
1593
|
strings["detail.load_failed.cta"]
|
|
@@ -1435,8 +1595,8 @@ function ReportDetailView({
|
|
|
1435
1595
|
] });
|
|
1436
1596
|
}
|
|
1437
1597
|
return /* @__PURE__ */ jsxs3("div", { class: "report-detail-empty-state", role: "alert", children: [
|
|
1438
|
-
/* @__PURE__ */
|
|
1439
|
-
/* @__PURE__ */
|
|
1598
|
+
/* @__PURE__ */ jsx3("p", { class: "report-detail-empty-state-title", children: strings["detail.load_failed.title"] }),
|
|
1599
|
+
/* @__PURE__ */ jsx3("p", { class: "report-detail-empty-state-body", children: strings["detail.load_failed.body"] }),
|
|
1440
1600
|
/* @__PURE__ */ jsxs3("button", { type: "button", class: "btn", onClick: onBack, children: [
|
|
1441
1601
|
"\u2190 ",
|
|
1442
1602
|
strings["detail.load_failed.cta"]
|
|
@@ -1453,7 +1613,7 @@ function ReportDetailView({
|
|
|
1453
1613
|
"\u2190 ",
|
|
1454
1614
|
strings["detail.back"]
|
|
1455
1615
|
] }),
|
|
1456
|
-
/* @__PURE__ */
|
|
1616
|
+
/* @__PURE__ */ jsx3("span", { class: `pill pill-status pill-status--${detail.status}`, children: strings[`status.${detail.status}`] ?? detail.status })
|
|
1457
1617
|
] }),
|
|
1458
1618
|
variant === "board" && /* @__PURE__ */ jsxs3("div", { class: "report-detail-header report-detail-header--board", children: [
|
|
1459
1619
|
/* @__PURE__ */ jsxs3(
|
|
@@ -1469,12 +1629,12 @@ function ReportDetailView({
|
|
|
1469
1629
|
]
|
|
1470
1630
|
}
|
|
1471
1631
|
),
|
|
1472
|
-
/* @__PURE__ */
|
|
1632
|
+
/* @__PURE__ */ jsx3("span", { class: `pill pill-status pill-status--${detail.status}`, children: strings[`status.${detail.status}`] ?? detail.status })
|
|
1473
1633
|
] }),
|
|
1474
1634
|
/* @__PURE__ */ jsxs3("div", { class: "report-detail-body", children: [
|
|
1475
|
-
/* @__PURE__ */
|
|
1635
|
+
/* @__PURE__ */ jsx3(ContextBlock, { detail, strings }),
|
|
1476
1636
|
editing ? /* @__PURE__ */ jsxs3("div", { class: "report-detail-edit", children: [
|
|
1477
|
-
/* @__PURE__ */
|
|
1637
|
+
/* @__PURE__ */ jsx3(
|
|
1478
1638
|
"textarea",
|
|
1479
1639
|
{
|
|
1480
1640
|
class: "report-detail-edit-input",
|
|
@@ -1486,9 +1646,9 @@ function ReportDetailView({
|
|
|
1486
1646
|
disabled: savingEdit
|
|
1487
1647
|
}
|
|
1488
1648
|
),
|
|
1489
|
-
editError && /* @__PURE__ */
|
|
1649
|
+
editError && /* @__PURE__ */ jsx3("p", { class: "error", role: "alert", children: strings["detail.edit_failed"] }),
|
|
1490
1650
|
/* @__PURE__ */ jsxs3("div", { class: "report-detail-edit-actions", children: [
|
|
1491
|
-
/* @__PURE__ */
|
|
1651
|
+
/* @__PURE__ */ jsx3(
|
|
1492
1652
|
"button",
|
|
1493
1653
|
{
|
|
1494
1654
|
type: "button",
|
|
@@ -1498,7 +1658,7 @@ function ReportDetailView({
|
|
|
1498
1658
|
children: strings["detail.edit_cancel"]
|
|
1499
1659
|
}
|
|
1500
1660
|
),
|
|
1501
|
-
/* @__PURE__ */
|
|
1661
|
+
/* @__PURE__ */ jsx3(
|
|
1502
1662
|
"button",
|
|
1503
1663
|
{
|
|
1504
1664
|
type: "button",
|
|
@@ -1510,8 +1670,8 @@ function ReportDetailView({
|
|
|
1510
1670
|
)
|
|
1511
1671
|
] })
|
|
1512
1672
|
] }) : /* @__PURE__ */ jsxs3("div", { class: "report-detail-description-row", children: [
|
|
1513
|
-
/* @__PURE__ */
|
|
1514
|
-
isMine && /* @__PURE__ */
|
|
1673
|
+
/* @__PURE__ */ jsx3("p", { class: "report-detail-description", children: linkifySeq(detail.description, onOpenSeq) }),
|
|
1674
|
+
isMine && /* @__PURE__ */ jsx3(
|
|
1515
1675
|
"button",
|
|
1516
1676
|
{
|
|
1517
1677
|
type: "button",
|
|
@@ -1521,7 +1681,7 @@ function ReportDetailView({
|
|
|
1521
1681
|
}
|
|
1522
1682
|
)
|
|
1523
1683
|
] }),
|
|
1524
|
-
buildPermalink && /* @__PURE__ */
|
|
1684
|
+
buildPermalink && /* @__PURE__ */ jsx3("div", { class: "report-detail-permalink", children: /* @__PURE__ */ jsx3(
|
|
1525
1685
|
"button",
|
|
1526
1686
|
{
|
|
1527
1687
|
type: "button",
|
|
@@ -1530,32 +1690,33 @@ function ReportDetailView({
|
|
|
1530
1690
|
children: copied ? strings["detail.copied"] : strings["detail.copy_link"]
|
|
1531
1691
|
}
|
|
1532
1692
|
) }),
|
|
1533
|
-
(detail.screenshots?.length ? detail.screenshots.map((s) => s.url) : [detail.screenshot_url]).
|
|
1693
|
+
(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
1694
|
const safeHref = safeExternalHref(url);
|
|
1535
|
-
return safeHref ? /* @__PURE__ */
|
|
1695
|
+
return safeHref ? /* @__PURE__ */ jsx3(
|
|
1536
1696
|
"a",
|
|
1537
1697
|
{
|
|
1538
1698
|
class: "report-detail-screenshot",
|
|
1539
1699
|
href: safeHref,
|
|
1540
1700
|
target: "_blank",
|
|
1541
1701
|
rel: "noopener noreferrer",
|
|
1542
|
-
children: /* @__PURE__ */
|
|
1702
|
+
children: /* @__PURE__ */ jsx3("img", { src: url, alt: "", loading: "lazy", onError: () => unpinUrl(pinKey) })
|
|
1543
1703
|
}
|
|
1544
|
-
) : /* @__PURE__ */
|
|
1704
|
+
) : /* @__PURE__ */ jsx3("div", { class: "report-detail-screenshot", children: /* @__PURE__ */ jsx3("img", { src: url, alt: "", loading: "lazy", onError: () => unpinUrl(pinKey) }) });
|
|
1545
1705
|
}),
|
|
1546
|
-
/* @__PURE__ */
|
|
1547
|
-
detail.comments.length === 0 ? /* @__PURE__ */
|
|
1706
|
+
/* @__PURE__ */ jsx3("h3", { class: "report-detail-section", children: strings["detail.thread"] }),
|
|
1707
|
+
detail.comments.length === 0 ? /* @__PURE__ */ jsx3("p", { class: "report-detail-empty", children: strings["detail.no_replies"] }) : /* @__PURE__ */ jsx3("ul", { class: "report-comments", children: detail.comments.map((c) => /* @__PURE__ */ jsx3("li", { children: /* @__PURE__ */ jsx3(
|
|
1548
1708
|
CommentBubble,
|
|
1549
1709
|
{
|
|
1550
1710
|
comment: c,
|
|
1551
1711
|
strings,
|
|
1552
|
-
...onOpenSeq && { onOpenSeq }
|
|
1712
|
+
...onOpenSeq && { onOpenSeq },
|
|
1713
|
+
stableUrl: (key, url) => pinUrl(key, url) ?? url
|
|
1553
1714
|
}
|
|
1554
1715
|
) })) }),
|
|
1555
|
-
detail.status_history && detail.status_history.length > 0 && /* @__PURE__ */
|
|
1556
|
-
detail.technical_context && /* @__PURE__ */
|
|
1716
|
+
detail.status_history && detail.status_history.length > 0 && /* @__PURE__ */ jsx3(StatusHistorySection, { rows: detail.status_history, strings }),
|
|
1717
|
+
detail.technical_context && /* @__PURE__ */ jsx3(TechnicalContextDrawer, { ctx: detail.technical_context, strings }),
|
|
1557
1718
|
showComposeBox ? /* @__PURE__ */ jsxs3("div", { class: "report-compose", children: [
|
|
1558
|
-
/* @__PURE__ */
|
|
1719
|
+
/* @__PURE__ */ jsx3(
|
|
1559
1720
|
"textarea",
|
|
1560
1721
|
{
|
|
1561
1722
|
value: composeBody,
|
|
@@ -1564,9 +1725,9 @@ function ReportDetailView({
|
|
|
1564
1725
|
disabled: sending
|
|
1565
1726
|
}
|
|
1566
1727
|
),
|
|
1567
|
-
composeShots.length > 0 && /* @__PURE__ */
|
|
1568
|
-
/* @__PURE__ */
|
|
1569
|
-
/* @__PURE__ */
|
|
1728
|
+
composeShots.length > 0 && /* @__PURE__ */ jsx3("div", { class: "compose-attachments", children: composeShots.map((s, i) => /* @__PURE__ */ jsxs3("div", { class: "compose-attachment", children: [
|
|
1729
|
+
/* @__PURE__ */ jsx3("img", { src: s.preview, alt: strings["detail.attachment_alt"] }),
|
|
1730
|
+
/* @__PURE__ */ jsx3(
|
|
1570
1731
|
"button",
|
|
1571
1732
|
{
|
|
1572
1733
|
type: "button",
|
|
@@ -1578,8 +1739,8 @@ function ReportDetailView({
|
|
|
1578
1739
|
}
|
|
1579
1740
|
)
|
|
1580
1741
|
] }, s.preview)) }),
|
|
1581
|
-
attachError && /* @__PURE__ */
|
|
1582
|
-
/* @__PURE__ */
|
|
1742
|
+
attachError && /* @__PURE__ */ jsx3("p", { class: "compose-attach-error", role: "alert", children: attachError }),
|
|
1743
|
+
/* @__PURE__ */ jsx3(
|
|
1583
1744
|
"input",
|
|
1584
1745
|
{
|
|
1585
1746
|
ref: fileInputRef,
|
|
@@ -1592,7 +1753,7 @@ function ReportDetailView({
|
|
|
1592
1753
|
}
|
|
1593
1754
|
),
|
|
1594
1755
|
/* @__PURE__ */ jsxs3("div", { class: "report-compose-actions", children: [
|
|
1595
|
-
/* @__PURE__ */
|
|
1756
|
+
/* @__PURE__ */ jsx3(
|
|
1596
1757
|
"button",
|
|
1597
1758
|
{
|
|
1598
1759
|
type: "button",
|
|
@@ -1602,7 +1763,7 @@ function ReportDetailView({
|
|
|
1602
1763
|
children: strings["detail.attach_add"]
|
|
1603
1764
|
}
|
|
1604
1765
|
),
|
|
1605
|
-
showCloseCta && /* @__PURE__ */
|
|
1766
|
+
showCloseCta && /* @__PURE__ */ jsx3(
|
|
1606
1767
|
"button",
|
|
1607
1768
|
{
|
|
1608
1769
|
type: "button",
|
|
@@ -1612,7 +1773,7 @@ function ReportDetailView({
|
|
|
1612
1773
|
children: closing ? strings["detail.close_busy"] : strings["detail.close_cta"]
|
|
1613
1774
|
}
|
|
1614
1775
|
),
|
|
1615
|
-
showCloseCta && /* @__PURE__ */
|
|
1776
|
+
showCloseCta && /* @__PURE__ */ jsx3(
|
|
1616
1777
|
"button",
|
|
1617
1778
|
{
|
|
1618
1779
|
type: "button",
|
|
@@ -1622,7 +1783,7 @@ function ReportDetailView({
|
|
|
1622
1783
|
children: reopening ? strings["detail.reopen_busy"] : strings["detail.reopen_cta"]
|
|
1623
1784
|
}
|
|
1624
1785
|
),
|
|
1625
|
-
/* @__PURE__ */
|
|
1786
|
+
/* @__PURE__ */ jsx3(
|
|
1626
1787
|
"button",
|
|
1627
1788
|
{
|
|
1628
1789
|
type: "button",
|
|
@@ -1638,9 +1799,9 @@ function ReportDetailView({
|
|
|
1638
1799
|
// to the submitter, so we hide the compose box entirely (action
|
|
1639
1800
|
// is genuinely unavailable) and surface a short notice so the
|
|
1640
1801
|
// viewer understands why.
|
|
1641
|
-
/* @__PURE__ */
|
|
1802
|
+
/* @__PURE__ */ jsx3("p", { class: "report-detail-teammate-notice", role: "note", children: strings["detail.teammate_notice"] })
|
|
1642
1803
|
),
|
|
1643
|
-
error && /* @__PURE__ */
|
|
1804
|
+
error && /* @__PURE__ */ jsx3("div", { class: "error", children: error })
|
|
1644
1805
|
] })
|
|
1645
1806
|
] });
|
|
1646
1807
|
}
|
|
@@ -1653,19 +1814,19 @@ function ContextBlock({ detail, strings }) {
|
|
|
1653
1814
|
const captureLabel = strings[captureKey] ?? detail.capture_method;
|
|
1654
1815
|
return /* @__PURE__ */ jsxs3("div", { class: "report-detail-context", children: [
|
|
1655
1816
|
/* @__PURE__ */ jsxs3("div", { class: "report-detail-context-pills", children: [
|
|
1656
|
-
/* @__PURE__ */
|
|
1657
|
-
/* @__PURE__ */
|
|
1658
|
-
/* @__PURE__ */
|
|
1817
|
+
/* @__PURE__ */ jsx3("span", { class: "pill pill-type", children: strings[`type.${detail.feedback_type}`] }),
|
|
1818
|
+
/* @__PURE__ */ jsx3("span", { class: `pill pill-severity pill-severity--${detail.severity}`, children: strings[`severity.${detail.severity}`] }),
|
|
1819
|
+
/* @__PURE__ */ jsx3("span", { class: "pill pill-capture", children: captureLabel })
|
|
1659
1820
|
] }),
|
|
1660
1821
|
/* @__PURE__ */ jsxs3("div", { class: "report-detail-context-line", children: [
|
|
1661
|
-
/* @__PURE__ */
|
|
1662
|
-
/* @__PURE__ */
|
|
1822
|
+
/* @__PURE__ */ jsx3("span", { class: "report-detail-context-label", children: strings["detail.context.submitted_at"] }),
|
|
1823
|
+
/* @__PURE__ */ jsx3("span", { children: formatSubmittedAt(detail.created_at) })
|
|
1663
1824
|
] }),
|
|
1664
1825
|
detail.page_url && (() => {
|
|
1665
1826
|
const safeHref = safeExternalHref(detail.page_url);
|
|
1666
1827
|
return /* @__PURE__ */ jsxs3("div", { class: "report-detail-context-line", title: detail.page_url, children: [
|
|
1667
|
-
/* @__PURE__ */
|
|
1668
|
-
safeHref ? /* @__PURE__ */
|
|
1828
|
+
/* @__PURE__ */ jsx3("span", { class: "report-detail-context-label", children: strings["detail.context.page"] }),
|
|
1829
|
+
safeHref ? /* @__PURE__ */ jsx3(
|
|
1669
1830
|
"a",
|
|
1670
1831
|
{
|
|
1671
1832
|
class: "report-detail-context-url",
|
|
@@ -1674,7 +1835,7 @@ function ContextBlock({ detail, strings }) {
|
|
|
1674
1835
|
rel: "noopener noreferrer",
|
|
1675
1836
|
children: truncateUrl(detail.page_url, 64)
|
|
1676
1837
|
}
|
|
1677
|
-
) : /* @__PURE__ */
|
|
1838
|
+
) : /* @__PURE__ */ jsx3("span", { class: "report-detail-context-url", children: truncateUrl(detail.page_url, 64) })
|
|
1678
1839
|
] });
|
|
1679
1840
|
})()
|
|
1680
1841
|
] });
|
|
@@ -1701,12 +1862,12 @@ function TechnicalContextDrawer({ ctx, strings }) {
|
|
|
1701
1862
|
const errorCount = errors.length;
|
|
1702
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"];
|
|
1703
1864
|
return /* @__PURE__ */ jsxs3("details", { class: "report-detail-tech", children: [
|
|
1704
|
-
/* @__PURE__ */
|
|
1865
|
+
/* @__PURE__ */ jsx3("summary", { children: summary }),
|
|
1705
1866
|
/* @__PURE__ */ jsxs3("div", { class: "tech-body", children: [
|
|
1706
|
-
device && /* @__PURE__ */
|
|
1707
|
-
errors.length > 0 && /* @__PURE__ */
|
|
1708
|
-
consoleLogs.length > 0 && /* @__PURE__ */
|
|
1709
|
-
network.length > 0 && /* @__PURE__ */
|
|
1867
|
+
device && /* @__PURE__ */ jsx3(DeviceSection, { device, strings }),
|
|
1868
|
+
errors.length > 0 && /* @__PURE__ */ jsx3(ErrorsSection, { errors, strings }),
|
|
1869
|
+
consoleLogs.length > 0 && /* @__PURE__ */ jsx3(ConsoleSection, { logs: consoleLogs, strings }),
|
|
1870
|
+
network.length > 0 && /* @__PURE__ */ jsx3(NetworkSection, { rows: network, strings })
|
|
1710
1871
|
] })
|
|
1711
1872
|
] });
|
|
1712
1873
|
}
|
|
@@ -1728,10 +1889,10 @@ function DeviceSection({
|
|
|
1728
1889
|
if (device?.connection) parts.push({ label: strings["detail.tech.device.connection"], value: device.connection });
|
|
1729
1890
|
if (parts.length === 0) return null;
|
|
1730
1891
|
return /* @__PURE__ */ jsxs3("div", { class: "tech-section", children: [
|
|
1731
|
-
/* @__PURE__ */
|
|
1732
|
-
/* @__PURE__ */
|
|
1733
|
-
/* @__PURE__ */
|
|
1734
|
-
/* @__PURE__ */
|
|
1892
|
+
/* @__PURE__ */ jsx3("h4", { children: strings["detail.tech.device"] }),
|
|
1893
|
+
/* @__PURE__ */ jsx3("dl", { class: "tech-device", children: parts.map((p) => /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
1894
|
+
/* @__PURE__ */ jsx3("dt", { children: p.label }),
|
|
1895
|
+
/* @__PURE__ */ jsx3("dd", { children: p.value })
|
|
1735
1896
|
] })) })
|
|
1736
1897
|
] });
|
|
1737
1898
|
}
|
|
@@ -1740,10 +1901,10 @@ function ErrorsSection({
|
|
|
1740
1901
|
strings
|
|
1741
1902
|
}) {
|
|
1742
1903
|
return /* @__PURE__ */ jsxs3("div", { class: "tech-section", children: [
|
|
1743
|
-
/* @__PURE__ */
|
|
1744
|
-
/* @__PURE__ */
|
|
1745
|
-
/* @__PURE__ */
|
|
1746
|
-
e.stack && /* @__PURE__ */
|
|
1904
|
+
/* @__PURE__ */ jsx3("h4", { children: strings["detail.tech.errors"] }),
|
|
1905
|
+
/* @__PURE__ */ jsx3("ul", { class: "tech-errors", children: errors.map((e) => /* @__PURE__ */ jsxs3("li", { children: [
|
|
1906
|
+
/* @__PURE__ */ jsx3("div", { class: "tech-errors-msg", children: e.message }),
|
|
1907
|
+
e.stack && /* @__PURE__ */ jsx3("pre", { class: "tech-errors-stack", children: truncateStack(e.stack) })
|
|
1747
1908
|
] })) })
|
|
1748
1909
|
] });
|
|
1749
1910
|
}
|
|
@@ -1752,10 +1913,10 @@ function ConsoleSection({
|
|
|
1752
1913
|
strings
|
|
1753
1914
|
}) {
|
|
1754
1915
|
return /* @__PURE__ */ jsxs3("div", { class: "tech-section", children: [
|
|
1755
|
-
/* @__PURE__ */
|
|
1756
|
-
/* @__PURE__ */
|
|
1757
|
-
/* @__PURE__ */
|
|
1758
|
-
/* @__PURE__ */
|
|
1916
|
+
/* @__PURE__ */ jsx3("h4", { children: strings["detail.tech.console"] }),
|
|
1917
|
+
/* @__PURE__ */ jsx3("ul", { class: "tech-console", children: logs.map((l) => /* @__PURE__ */ jsxs3("li", { class: `tech-console-row tech-console-row--${l.level}`, children: [
|
|
1918
|
+
/* @__PURE__ */ jsx3("span", { class: "tech-console-level", children: l.level }),
|
|
1919
|
+
/* @__PURE__ */ jsx3("span", { class: "tech-console-msg", children: l.message })
|
|
1759
1920
|
] })) })
|
|
1760
1921
|
] });
|
|
1761
1922
|
}
|
|
@@ -1764,13 +1925,13 @@ function NetworkSection({
|
|
|
1764
1925
|
strings
|
|
1765
1926
|
}) {
|
|
1766
1927
|
return /* @__PURE__ */ jsxs3("div", { class: "tech-section", children: [
|
|
1767
|
-
/* @__PURE__ */
|
|
1768
|
-
/* @__PURE__ */
|
|
1928
|
+
/* @__PURE__ */ jsx3("h4", { children: strings["detail.tech.network"] }),
|
|
1929
|
+
/* @__PURE__ */ jsx3("ul", { class: "tech-network", children: rows.map((n) => {
|
|
1769
1930
|
const failed = n.status >= 400 || n.status === 0;
|
|
1770
1931
|
return /* @__PURE__ */ jsxs3("li", { class: `tech-network-row${failed ? " tech-network-row--fail" : ""}`, children: [
|
|
1771
|
-
/* @__PURE__ */
|
|
1772
|
-
/* @__PURE__ */
|
|
1773
|
-
/* @__PURE__ */
|
|
1932
|
+
/* @__PURE__ */ jsx3("span", { class: "tech-network-status", children: n.status || "\u2014" }),
|
|
1933
|
+
/* @__PURE__ */ jsx3("span", { class: "tech-network-method", children: n.method }),
|
|
1934
|
+
/* @__PURE__ */ jsx3("span", { class: "tech-network-url", title: n.url, children: truncateUrl(n.url, 56) }),
|
|
1774
1935
|
/* @__PURE__ */ jsxs3("span", { class: "tech-network-time", children: [
|
|
1775
1936
|
Math.round(n.durationMs),
|
|
1776
1937
|
"ms"
|
|
@@ -1786,26 +1947,26 @@ function truncateStack(stack) {
|
|
|
1786
1947
|
}
|
|
1787
1948
|
function StatusHistorySection({ rows, strings }) {
|
|
1788
1949
|
return /* @__PURE__ */ jsxs3("div", { class: "report-detail-history", children: [
|
|
1789
|
-
/* @__PURE__ */
|
|
1790
|
-
/* @__PURE__ */
|
|
1950
|
+
/* @__PURE__ */ jsx3("h3", { class: "report-detail-section", children: strings["detail.history"] }),
|
|
1951
|
+
/* @__PURE__ */ jsx3("ol", { class: "status-history", children: rows.map((r) => {
|
|
1791
1952
|
const from = strings[`status.${r.from_status}`] ?? r.from_status;
|
|
1792
1953
|
const to = strings[`status.${r.to_status}`] ?? r.to_status;
|
|
1793
1954
|
const sourceKey = r.changed_by_source === "mcp" ? "detail.author.mcp" : r.changed_by_source === "system" ? "detail.author.system" : "detail.author.staff";
|
|
1794
1955
|
return /* @__PURE__ */ jsxs3("li", { class: "status-history-row", children: [
|
|
1795
|
-
/* @__PURE__ */
|
|
1956
|
+
/* @__PURE__ */ jsx3("span", { class: "status-history-time", children: formatSubmittedAt(r.created_at) }),
|
|
1796
1957
|
/* @__PURE__ */ jsxs3("span", { class: "status-history-transition", children: [
|
|
1797
|
-
/* @__PURE__ */
|
|
1798
|
-
/* @__PURE__ */
|
|
1799
|
-
/* @__PURE__ */
|
|
1958
|
+
/* @__PURE__ */ jsx3("span", { class: `pill pill-status pill-status--${r.from_status}`, children: from }),
|
|
1959
|
+
/* @__PURE__ */ jsx3("span", { class: "status-history-arrow", "aria-hidden": "true", children: "\u2192" }),
|
|
1960
|
+
/* @__PURE__ */ jsx3("span", { class: `pill pill-status pill-status--${r.to_status}`, children: to })
|
|
1800
1961
|
] }),
|
|
1801
|
-
/* @__PURE__ */
|
|
1962
|
+
/* @__PURE__ */ jsx3("span", { class: `status-history-source status-history-source--${r.changed_by_source}`, children: strings[sourceKey] })
|
|
1802
1963
|
] });
|
|
1803
1964
|
}) })
|
|
1804
1965
|
] });
|
|
1805
1966
|
}
|
|
1806
1967
|
|
|
1807
1968
|
// src/widget/BoardView.tsx
|
|
1808
|
-
import { Fragment as Fragment2, jsx as
|
|
1969
|
+
import { Fragment as Fragment2, jsx as jsx4, jsxs as jsxs4 } from "preact/jsx-runtime";
|
|
1809
1970
|
var POLL_MS2 = 3e4;
|
|
1810
1971
|
var SEARCH_DEBOUNCE_MS = 300;
|
|
1811
1972
|
var SORT_OPTIONS = ["recent", "oldest", "updated", "severity", "status"];
|
|
@@ -1825,19 +1986,22 @@ function BoardView({
|
|
|
1825
1986
|
getCurrentPage,
|
|
1826
1987
|
initialSelectedId
|
|
1827
1988
|
}) {
|
|
1828
|
-
const [filters, setFilters] =
|
|
1829
|
-
const [thisPage, setThisPage] =
|
|
1989
|
+
const [filters, setFilters] = useState3(() => loadBoardView(externalId));
|
|
1990
|
+
const [thisPage, setThisPage] = useState3(true);
|
|
1830
1991
|
useEffect2(() => {
|
|
1831
1992
|
saveBoardView(externalId, filters);
|
|
1832
1993
|
}, [externalId, filtersHash(filters)]);
|
|
1833
|
-
const [page, setPage] =
|
|
1834
|
-
const [kpis, setKpis] =
|
|
1835
|
-
const [loading, setLoading] =
|
|
1836
|
-
const [
|
|
1837
|
-
const [
|
|
1838
|
-
const [
|
|
1839
|
-
const [
|
|
1840
|
-
const [
|
|
1994
|
+
const [page, setPage] = useState3(null);
|
|
1995
|
+
const [kpis, setKpis] = useState3(null);
|
|
1996
|
+
const [loading, setLoading] = useState3(true);
|
|
1997
|
+
const [stale, setStale] = useState3(false);
|
|
1998
|
+
const [error, setError] = useState3(null);
|
|
1999
|
+
const [selectedId, setSelectedId] = useState3(initialSelectedId ?? null);
|
|
2000
|
+
const [detailKey, setDetailKey] = useState3(0);
|
|
2001
|
+
const [reloadTick, setReloadTick] = useState3(0);
|
|
2002
|
+
const [extra, setExtra] = useState3(null);
|
|
2003
|
+
const [loadingMore, setLoadingMore] = useState3(false);
|
|
2004
|
+
const [qDraft, setQDraft] = useState3(filters.q ?? "");
|
|
1841
2005
|
const debouncedQ = useDebouncedValue(qDraft, SEARCH_DEBOUNCE_MS);
|
|
1842
2006
|
useEffect2(() => {
|
|
1843
2007
|
setFilters((f) => {
|
|
@@ -1866,18 +2030,36 @@ function BoardView({
|
|
|
1866
2030
|
}, [filtersHash(filters), thisPage, reloadTick]);
|
|
1867
2031
|
useEffect2(() => {
|
|
1868
2032
|
let cancelled = false;
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
2033
|
+
const controller = new AbortController();
|
|
2034
|
+
const cacheKey = boardCacheKey(externalId, JSON.stringify(fetchFilters));
|
|
2035
|
+
const cached = readBoardCache(cacheKey);
|
|
2036
|
+
if (cached) {
|
|
2037
|
+
setPage(cached.page);
|
|
2038
|
+
setKpis(cached.kpis);
|
|
2039
|
+
setStale(false);
|
|
2040
|
+
setLoading(false);
|
|
2041
|
+
} else {
|
|
2042
|
+
setPage(
|
|
2043
|
+
(prev) => prev ? { ...prev, results: applyLocalFilters(prev.results, fetchFilters) } : null
|
|
2044
|
+
);
|
|
2045
|
+
setStale(true);
|
|
2046
|
+
setLoading(true);
|
|
2047
|
+
}
|
|
2048
|
+
setExtra(null);
|
|
2049
|
+
setLoadingMore(false);
|
|
1872
2050
|
const poll = startPoll(async () => {
|
|
1873
2051
|
try {
|
|
1874
|
-
const
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
2052
|
+
const list = await api.listBoard(externalId, fetchFilters, {
|
|
2053
|
+
signal: controller.signal
|
|
2054
|
+
});
|
|
2055
|
+
const k = list.kpis ?? await api.fetchBoardKpis(externalId, fetchFilters, {
|
|
2056
|
+
signal: controller.signal
|
|
2057
|
+
});
|
|
1878
2058
|
if (cancelled) return true;
|
|
2059
|
+
writeBoardCache(cacheKey, list, k);
|
|
1879
2060
|
setPage(list);
|
|
1880
2061
|
setKpis(k);
|
|
2062
|
+
setStale(false);
|
|
1881
2063
|
setError(null);
|
|
1882
2064
|
return true;
|
|
1883
2065
|
} catch (e) {
|
|
@@ -1891,19 +2073,43 @@ function BoardView({
|
|
|
1891
2073
|
return () => {
|
|
1892
2074
|
cancelled = true;
|
|
1893
2075
|
poll.stop();
|
|
2076
|
+
controller.abort();
|
|
1894
2077
|
};
|
|
1895
2078
|
}, [api, externalId, JSON.stringify(fetchFilters)]);
|
|
2079
|
+
const visibleRows = useMemo(() => {
|
|
2080
|
+
if (!page) return [];
|
|
2081
|
+
if (!extra || extra.rows.length === 0) return page.results;
|
|
2082
|
+
const seen = new Set(page.results.map((r) => r.id));
|
|
2083
|
+
return [...page.results, ...extra.rows.filter((r) => !seen.has(r.id))];
|
|
2084
|
+
}, [page, extra]);
|
|
2085
|
+
const hasMore = extra ? extra.hasMore : Boolean(page?.next);
|
|
2086
|
+
const loadMore = async () => {
|
|
2087
|
+
if (loadingMore || !page) return;
|
|
2088
|
+
const pageNum = extra ? extra.nextPage : 2;
|
|
2089
|
+
setLoadingMore(true);
|
|
2090
|
+
try {
|
|
2091
|
+
const res = await api.listBoard(externalId, { ...fetchFilters, page: pageNum });
|
|
2092
|
+
setExtra((prev) => ({
|
|
2093
|
+
rows: [...prev?.rows ?? [], ...res.results],
|
|
2094
|
+
nextPage: pageNum + 1,
|
|
2095
|
+
hasMore: res.next !== null
|
|
2096
|
+
}));
|
|
2097
|
+
} catch {
|
|
2098
|
+
} finally {
|
|
2099
|
+
setLoadingMore(false);
|
|
2100
|
+
}
|
|
2101
|
+
};
|
|
1896
2102
|
const selectedRow = useMemo(() => {
|
|
1897
|
-
if (!selectedId
|
|
1898
|
-
return
|
|
1899
|
-
}, [selectedId,
|
|
2103
|
+
if (!selectedId) return null;
|
|
2104
|
+
return visibleRows.find((r) => r.id === selectedId) ?? null;
|
|
2105
|
+
}, [selectedId, visibleRows]);
|
|
1900
2106
|
const onPickRow = (row) => {
|
|
1901
2107
|
setSelectedId(row.id);
|
|
1902
2108
|
setDetailKey((k) => k + 1);
|
|
1903
2109
|
};
|
|
1904
|
-
return /* @__PURE__ */ jsxs4("div", { class:
|
|
1905
|
-
/* @__PURE__ */
|
|
1906
|
-
/* @__PURE__ */
|
|
2110
|
+
return /* @__PURE__ */ jsxs4("div", { class: `board-view ${stale ? "is-stale" : ""}`, children: [
|
|
2111
|
+
/* @__PURE__ */ jsx4(BoardHeader, { strings, kpis, thisPage }),
|
|
2112
|
+
/* @__PURE__ */ jsx4(
|
|
1907
2113
|
BoardFilters,
|
|
1908
2114
|
{
|
|
1909
2115
|
filters,
|
|
@@ -1917,10 +2123,10 @@ function BoardView({
|
|
|
1917
2123
|
}
|
|
1918
2124
|
),
|
|
1919
2125
|
/* @__PURE__ */ jsxs4("div", { class: "board-body", children: [
|
|
1920
|
-
/* @__PURE__ */ jsxs4("div", { class: "board-list-wrap", "aria-busy": loading
|
|
2126
|
+
/* @__PURE__ */ jsxs4("div", { class: "board-list-wrap", "aria-busy": loading, children: [
|
|
1921
2127
|
error && /* @__PURE__ */ jsxs4("div", { class: "board-error", role: "alert", children: [
|
|
1922
|
-
/* @__PURE__ */
|
|
1923
|
-
/* @__PURE__ */
|
|
2128
|
+
/* @__PURE__ */ jsx4("span", { children: error }),
|
|
2129
|
+
/* @__PURE__ */ jsx4(
|
|
1924
2130
|
"button",
|
|
1925
2131
|
{
|
|
1926
2132
|
type: "button",
|
|
@@ -1932,10 +2138,11 @@ function BoardView({
|
|
|
1932
2138
|
},
|
|
1933
2139
|
children: strings["board.retry"]
|
|
1934
2140
|
}
|
|
1935
|
-
)
|
|
2141
|
+
),
|
|
2142
|
+
/* @__PURE__ */ jsx4(DiagnosticHint, { api, strings, host: api.endpointHost })
|
|
1936
2143
|
] }),
|
|
1937
|
-
!error && loading && !page && /* @__PURE__ */
|
|
1938
|
-
!error && page &&
|
|
2144
|
+
!error && loading && !page && /* @__PURE__ */ jsx4(BoardListSkeleton, {}),
|
|
2145
|
+
!error && page && visibleRows.length === 0 && (!loading || stale) && /* @__PURE__ */ jsx4(
|
|
1939
2146
|
BoardEmpty,
|
|
1940
2147
|
{
|
|
1941
2148
|
strings,
|
|
@@ -1943,27 +2150,40 @@ function BoardView({
|
|
|
1943
2150
|
onAllPages: () => setThisPage(false)
|
|
1944
2151
|
}
|
|
1945
2152
|
),
|
|
1946
|
-
!error && page &&
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
2153
|
+
!error && page && visibleRows.length > 0 && /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
2154
|
+
/* @__PURE__ */ jsx4(
|
|
2155
|
+
BoardList,
|
|
2156
|
+
{
|
|
2157
|
+
rows: visibleRows,
|
|
2158
|
+
selectedId,
|
|
2159
|
+
onPick: onPickRow,
|
|
2160
|
+
strings,
|
|
2161
|
+
total: page.count,
|
|
2162
|
+
thisPage
|
|
2163
|
+
}
|
|
2164
|
+
),
|
|
2165
|
+
hasMore && !stale && /* @__PURE__ */ jsx4(
|
|
2166
|
+
"button",
|
|
2167
|
+
{
|
|
2168
|
+
type: "button",
|
|
2169
|
+
class: "btn btn--ghost board-load-more",
|
|
2170
|
+
onClick: () => void loadMore(),
|
|
2171
|
+
disabled: loadingMore,
|
|
2172
|
+
children: strings["board.list.loadMore"]
|
|
2173
|
+
}
|
|
2174
|
+
)
|
|
2175
|
+
] })
|
|
1957
2176
|
] }),
|
|
1958
|
-
/* @__PURE__ */
|
|
2177
|
+
/* @__PURE__ */ jsx4("div", { class: `board-detail-wrap ${selectedId ? "has-selection" : ""}`, children: selectedId ? /* @__PURE__ */ jsx4(
|
|
1959
2178
|
ReportDetailView,
|
|
1960
2179
|
{
|
|
1961
2180
|
api,
|
|
1962
2181
|
externalId,
|
|
1963
|
-
reportId:
|
|
2182
|
+
reportId: selectedId,
|
|
1964
2183
|
strings,
|
|
1965
2184
|
onBack: () => setSelectedId(null),
|
|
1966
|
-
canModerate: selectedRow.can_moderate ?? selectedRow.is_mine,
|
|
2185
|
+
canModerate: selectedRow ? selectedRow.can_moderate ?? selectedRow.is_mine : false,
|
|
2186
|
+
...selectedRow ? { seed: selectedRow } : {},
|
|
1967
2187
|
variant: "board",
|
|
1968
2188
|
onOpenSeq: (seq) => {
|
|
1969
2189
|
void api.getReportBySeq(seq, externalId).then((r) => {
|
|
@@ -1975,8 +2195,8 @@ function BoardView({
|
|
|
1975
2195
|
},
|
|
1976
2196
|
detailKey
|
|
1977
2197
|
) : /* @__PURE__ */ jsxs4("div", { class: "board-detail-empty", children: [
|
|
1978
|
-
/* @__PURE__ */
|
|
1979
|
-
/* @__PURE__ */
|
|
2198
|
+
/* @__PURE__ */ jsx4(DetailEmptyIllustration, {}),
|
|
2199
|
+
/* @__PURE__ */ jsx4("p", { children: strings["board.detail.empty"] })
|
|
1980
2200
|
] }) })
|
|
1981
2201
|
] })
|
|
1982
2202
|
] });
|
|
@@ -1989,13 +2209,13 @@ function BoardHeader({
|
|
|
1989
2209
|
const scopeLabel = thisPage ? strings["board.scope.onThisPage"] : kpis?.scope === "project" ? strings["board.scope.project"] : strings["board.scope.mine"];
|
|
1990
2210
|
return /* @__PURE__ */ jsxs4("header", { class: "board-header", children: [
|
|
1991
2211
|
/* @__PURE__ */ jsxs4("div", { class: "board-header-title", children: [
|
|
1992
|
-
/* @__PURE__ */
|
|
2212
|
+
/* @__PURE__ */ jsx4("span", { class: "board-header-emoji", "aria-hidden": "true", children: "\u{1F4CB}" }),
|
|
1993
2213
|
/* @__PURE__ */ jsxs4("div", { children: [
|
|
1994
|
-
/* @__PURE__ */
|
|
1995
|
-
/* @__PURE__ */
|
|
2214
|
+
/* @__PURE__ */ jsx4("h2", { class: "board-header-h", children: strings["tab.board"] }),
|
|
2215
|
+
/* @__PURE__ */ jsx4("p", { class: "board-header-sub", children: kpis ? `${kpis.total} \xB7 ${scopeLabel}` : scopeLabel })
|
|
1996
2216
|
] })
|
|
1997
2217
|
] }),
|
|
1998
|
-
/* @__PURE__ */
|
|
2218
|
+
/* @__PURE__ */ jsx4(BoardKpiStrip, { kpis, strings })
|
|
1999
2219
|
] });
|
|
2000
2220
|
}
|
|
2001
2221
|
function BoardKpiStrip({
|
|
@@ -2028,9 +2248,9 @@ function BoardKpiStrip({
|
|
|
2028
2248
|
tone: "tone-rate"
|
|
2029
2249
|
}
|
|
2030
2250
|
];
|
|
2031
|
-
return /* @__PURE__ */
|
|
2032
|
-
/* @__PURE__ */
|
|
2033
|
-
/* @__PURE__ */
|
|
2251
|
+
return /* @__PURE__ */ jsx4("div", { class: `board-kpi-strip ${kpis ? "is-ready" : "is-loading"}`, "aria-live": "polite", children: cells.map((c) => /* @__PURE__ */ jsxs4("div", { class: `board-kpi-card ${c.tone}`, children: [
|
|
2252
|
+
/* @__PURE__ */ jsx4("div", { class: "board-kpi-value", children: c.value }),
|
|
2253
|
+
/* @__PURE__ */ jsx4("div", { class: "board-kpi-label", children: c.label })
|
|
2034
2254
|
] }, c.key)) });
|
|
2035
2255
|
}
|
|
2036
2256
|
function BoardFilters({
|
|
@@ -2083,7 +2303,7 @@ function BoardFilters({
|
|
|
2083
2303
|
const clear = () => onChange({});
|
|
2084
2304
|
return /* @__PURE__ */ jsxs4("div", { class: "board-filters", children: [
|
|
2085
2305
|
/* @__PURE__ */ jsxs4("div", { class: "board-scope", role: "group", "aria-label": strings["board.scope.thisPage"], children: [
|
|
2086
|
-
/* @__PURE__ */
|
|
2306
|
+
/* @__PURE__ */ jsx4(
|
|
2087
2307
|
"button",
|
|
2088
2308
|
{
|
|
2089
2309
|
type: "button",
|
|
@@ -2093,7 +2313,7 @@ function BoardFilters({
|
|
|
2093
2313
|
children: strings["board.scope.thisPage"]
|
|
2094
2314
|
}
|
|
2095
2315
|
),
|
|
2096
|
-
/* @__PURE__ */
|
|
2316
|
+
/* @__PURE__ */ jsx4(
|
|
2097
2317
|
"button",
|
|
2098
2318
|
{
|
|
2099
2319
|
type: "button",
|
|
@@ -2104,14 +2324,14 @@ function BoardFilters({
|
|
|
2104
2324
|
}
|
|
2105
2325
|
)
|
|
2106
2326
|
] }),
|
|
2107
|
-
/* @__PURE__ */
|
|
2327
|
+
/* @__PURE__ */ jsx4(
|
|
2108
2328
|
"select",
|
|
2109
2329
|
{
|
|
2110
2330
|
class: "board-filter-select",
|
|
2111
2331
|
"aria-label": strings["board.sort"],
|
|
2112
2332
|
value: filters.ordering ?? "recent",
|
|
2113
2333
|
onChange: (e) => setOrdering(e.target.value),
|
|
2114
|
-
children: SORT_OPTIONS.map((o) => /* @__PURE__ */
|
|
2334
|
+
children: SORT_OPTIONS.map((o) => /* @__PURE__ */ jsx4("option", { value: o, children: strings[`board.sort.${o}`] }, o))
|
|
2115
2335
|
}
|
|
2116
2336
|
),
|
|
2117
2337
|
/* @__PURE__ */ jsxs4(
|
|
@@ -2122,8 +2342,8 @@ function BoardFilters({
|
|
|
2122
2342
|
value: filters.status?.[0] ?? "",
|
|
2123
2343
|
onChange: (e) => setStatus(e.target.value),
|
|
2124
2344
|
children: [
|
|
2125
|
-
/* @__PURE__ */
|
|
2126
|
-
STATUSES.map((s) => /* @__PURE__ */
|
|
2345
|
+
/* @__PURE__ */ jsx4("option", { value: "", children: strings["board.filter.status"] }),
|
|
2346
|
+
STATUSES.map((s) => /* @__PURE__ */ jsx4("option", { value: s, children: strings[`board.kpi.${s}`] ?? s }, s))
|
|
2127
2347
|
]
|
|
2128
2348
|
}
|
|
2129
2349
|
),
|
|
@@ -2135,8 +2355,8 @@ function BoardFilters({
|
|
|
2135
2355
|
value: filters.type?.[0] ?? "",
|
|
2136
2356
|
onChange: (e) => setType(e.target.value),
|
|
2137
2357
|
children: [
|
|
2138
|
-
/* @__PURE__ */
|
|
2139
|
-
TYPES.map((t) => /* @__PURE__ */
|
|
2358
|
+
/* @__PURE__ */ jsx4("option", { value: "", children: strings["board.filter.type"] }),
|
|
2359
|
+
TYPES.map((t) => /* @__PURE__ */ jsx4("option", { value: t, children: strings[`type.${t}`] ?? t }, t))
|
|
2140
2360
|
]
|
|
2141
2361
|
}
|
|
2142
2362
|
),
|
|
@@ -2148,12 +2368,12 @@ function BoardFilters({
|
|
|
2148
2368
|
value: filters.severity?.[0] ?? "",
|
|
2149
2369
|
onChange: (e) => setSeverity(e.target.value),
|
|
2150
2370
|
children: [
|
|
2151
|
-
/* @__PURE__ */
|
|
2152
|
-
SEVERITIES.map((s) => /* @__PURE__ */
|
|
2371
|
+
/* @__PURE__ */ jsx4("option", { value: "", children: strings["board.filter.severity"] }),
|
|
2372
|
+
SEVERITIES.map((s) => /* @__PURE__ */ jsx4("option", { value: s, children: strings[`severity.${s}`] ?? s }, s))
|
|
2153
2373
|
]
|
|
2154
2374
|
}
|
|
2155
2375
|
),
|
|
2156
|
-
/* @__PURE__ */
|
|
2376
|
+
/* @__PURE__ */ jsx4(
|
|
2157
2377
|
"input",
|
|
2158
2378
|
{
|
|
2159
2379
|
type: "search",
|
|
@@ -2164,7 +2384,7 @@ function BoardFilters({
|
|
|
2164
2384
|
}
|
|
2165
2385
|
),
|
|
2166
2386
|
/* @__PURE__ */ jsxs4("label", { class: "board-filter-toggle", children: [
|
|
2167
|
-
/* @__PURE__ */
|
|
2387
|
+
/* @__PURE__ */ jsx4(
|
|
2168
2388
|
"input",
|
|
2169
2389
|
{
|
|
2170
2390
|
type: "checkbox",
|
|
@@ -2172,10 +2392,10 @@ function BoardFilters({
|
|
|
2172
2392
|
onChange: toggleMine
|
|
2173
2393
|
}
|
|
2174
2394
|
),
|
|
2175
|
-
/* @__PURE__ */
|
|
2395
|
+
/* @__PURE__ */ jsx4("span", { children: strings["board.filter.mine"] })
|
|
2176
2396
|
] }),
|
|
2177
2397
|
activeCount > 0 && /* @__PURE__ */ jsxs4("button", { type: "button", class: "board-filter-clear", onClick: clear, children: [
|
|
2178
|
-
/* @__PURE__ */
|
|
2398
|
+
/* @__PURE__ */ jsx4(CloseIcon, {}),
|
|
2179
2399
|
strings["board.filter.clear"]
|
|
2180
2400
|
] })
|
|
2181
2401
|
] });
|
|
@@ -2189,8 +2409,8 @@ function BoardList({
|
|
|
2189
2409
|
thisPage
|
|
2190
2410
|
}) {
|
|
2191
2411
|
return /* @__PURE__ */ jsxs4(Fragment2, { children: [
|
|
2192
|
-
/* @__PURE__ */
|
|
2193
|
-
/* @__PURE__ */
|
|
2412
|
+
/* @__PURE__ */ jsx4("div", { class: "board-list-count", children: strings[thisPage ? "board.list.count.page" : "board.list.count"].replace("{n}", String(rows.length)).replace("{total}", String(total)) }),
|
|
2413
|
+
/* @__PURE__ */ jsx4("ul", { class: "board-list", role: "list", children: rows.map((row) => /* @__PURE__ */ jsx4(
|
|
2194
2414
|
BoardRowCard,
|
|
2195
2415
|
{
|
|
2196
2416
|
row,
|
|
@@ -2209,7 +2429,7 @@ function BoardRowCard({
|
|
|
2209
2429
|
strings
|
|
2210
2430
|
}) {
|
|
2211
2431
|
const author = row.is_mine ? strings["board.list.you"] : row.author_label || "\u2014";
|
|
2212
|
-
return /* @__PURE__ */
|
|
2432
|
+
return /* @__PURE__ */ jsx4("li", { children: /* @__PURE__ */ jsxs4(
|
|
2213
2433
|
"button",
|
|
2214
2434
|
{
|
|
2215
2435
|
type: "button",
|
|
@@ -2218,18 +2438,18 @@ function BoardRowCard({
|
|
|
2218
2438
|
"aria-pressed": selected,
|
|
2219
2439
|
children: [
|
|
2220
2440
|
/* @__PURE__ */ jsxs4("div", { class: "board-row-badges", children: [
|
|
2221
|
-
/* @__PURE__ */
|
|
2222
|
-
/* @__PURE__ */
|
|
2441
|
+
/* @__PURE__ */ jsx4("span", { class: `badge status-${row.status}`, children: strings[`status.${row.status}`] ?? row.status }),
|
|
2442
|
+
/* @__PURE__ */ jsx4("span", { class: `badge severity-${row.severity}`, children: strings[`severity.${row.severity}`] ?? row.severity })
|
|
2223
2443
|
] }),
|
|
2224
|
-
/* @__PURE__ */
|
|
2444
|
+
/* @__PURE__ */ jsx4("div", { class: "board-row-description", children: row.description }),
|
|
2225
2445
|
/* @__PURE__ */ jsxs4("div", { class: "board-row-meta", children: [
|
|
2226
|
-
/* @__PURE__ */
|
|
2446
|
+
/* @__PURE__ */ jsx4("span", { class: "board-row-author", children: author }),
|
|
2227
2447
|
row.comment_count > 0 && /* @__PURE__ */ jsxs4("span", { class: "board-row-comments", children: [
|
|
2228
|
-
/* @__PURE__ */
|
|
2448
|
+
/* @__PURE__ */ jsx4(CommentIcon, {}),
|
|
2229
2449
|
" ",
|
|
2230
2450
|
row.comment_count
|
|
2231
2451
|
] }),
|
|
2232
|
-
/* @__PURE__ */
|
|
2452
|
+
/* @__PURE__ */ jsx4("span", { class: "board-row-time", children: formatRelative(row.created_at) })
|
|
2233
2453
|
] })
|
|
2234
2454
|
]
|
|
2235
2455
|
}
|
|
@@ -2241,50 +2461,77 @@ function BoardEmpty({
|
|
|
2241
2461
|
onAllPages
|
|
2242
2462
|
}) {
|
|
2243
2463
|
return /* @__PURE__ */ jsxs4("div", { class: "board-empty", children: [
|
|
2244
|
-
/* @__PURE__ */
|
|
2245
|
-
/* @__PURE__ */
|
|
2246
|
-
/* @__PURE__ */
|
|
2247
|
-
thisPage && /* @__PURE__ */
|
|
2464
|
+
/* @__PURE__ */ jsx4(EmptyIllustration, {}),
|
|
2465
|
+
/* @__PURE__ */ jsx4("h3", { children: strings[thisPage ? "board.list.empty.page.title" : "board.list.empty.title"] }),
|
|
2466
|
+
/* @__PURE__ */ jsx4("p", { children: strings[thisPage ? "board.list.empty.page.description" : "board.list.empty.description"] }),
|
|
2467
|
+
thisPage && /* @__PURE__ */ jsx4("button", { type: "button", class: "btn btn--ghost", onClick: onAllPages, children: strings["board.scope.allPages"] })
|
|
2248
2468
|
] });
|
|
2249
2469
|
}
|
|
2250
2470
|
function BoardListSkeleton() {
|
|
2251
|
-
return /* @__PURE__ */
|
|
2252
|
-
/* @__PURE__ */
|
|
2253
|
-
/* @__PURE__ */
|
|
2254
|
-
/* @__PURE__ */
|
|
2471
|
+
return /* @__PURE__ */ jsx4("ul", { class: "board-list board-list-skeleton", "aria-hidden": "true", children: Array.from({ length: 5 }).map((_, i) => /* @__PURE__ */ jsx4("li", { children: /* @__PURE__ */ jsxs4("div", { class: "board-row skeleton-row", children: [
|
|
2472
|
+
/* @__PURE__ */ jsx4("div", { class: "skeleton-line skeleton-badges" }),
|
|
2473
|
+
/* @__PURE__ */ jsx4("div", { class: "skeleton-line skeleton-text" }),
|
|
2474
|
+
/* @__PURE__ */ jsx4("div", { class: "skeleton-line skeleton-text short" })
|
|
2255
2475
|
] }) }, i)) });
|
|
2256
2476
|
}
|
|
2257
2477
|
function CommentIcon() {
|
|
2258
|
-
return /* @__PURE__ */
|
|
2478
|
+
return /* @__PURE__ */ jsx4("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx4("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" }) });
|
|
2259
2479
|
}
|
|
2260
2480
|
function CloseIcon() {
|
|
2261
2481
|
return /* @__PURE__ */ jsxs4("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true", children: [
|
|
2262
|
-
/* @__PURE__ */
|
|
2263
|
-
/* @__PURE__ */
|
|
2482
|
+
/* @__PURE__ */ jsx4("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
2483
|
+
/* @__PURE__ */ jsx4("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
|
|
2264
2484
|
] });
|
|
2265
2485
|
}
|
|
2266
2486
|
function EmptyIllustration() {
|
|
2267
2487
|
return /* @__PURE__ */ jsxs4("svg", { width: "64", height: "64", viewBox: "0 0 64 64", fill: "none", "aria-hidden": "true", children: [
|
|
2268
|
-
/* @__PURE__ */
|
|
2269
|
-
/* @__PURE__ */
|
|
2488
|
+
/* @__PURE__ */ jsx4("circle", { cx: "32", cy: "32", r: "28", stroke: "currentColor", "stroke-opacity": "0.18", "stroke-width": "2" }),
|
|
2489
|
+
/* @__PURE__ */ jsx4("path", { d: "M22 32h20M32 22v20", stroke: "currentColor", "stroke-opacity": "0.35", "stroke-width": "2", "stroke-linecap": "round" })
|
|
2270
2490
|
] });
|
|
2271
2491
|
}
|
|
2272
2492
|
function DetailEmptyIllustration() {
|
|
2273
2493
|
return /* @__PURE__ */ jsxs4("svg", { width: "80", height: "80", viewBox: "0 0 64 64", fill: "none", "aria-hidden": "true", children: [
|
|
2274
|
-
/* @__PURE__ */
|
|
2275
|
-
/* @__PURE__ */
|
|
2276
|
-
/* @__PURE__ */
|
|
2277
|
-
/* @__PURE__ */
|
|
2494
|
+
/* @__PURE__ */ jsx4("rect", { x: "10", y: "12", width: "44", height: "36", rx: "4", stroke: "currentColor", "stroke-opacity": "0.22", "stroke-width": "2" }),
|
|
2495
|
+
/* @__PURE__ */ jsx4("line", { x1: "18", y1: "22", x2: "46", y2: "22", stroke: "currentColor", "stroke-opacity": "0.22", "stroke-width": "2", "stroke-linecap": "round" }),
|
|
2496
|
+
/* @__PURE__ */ jsx4("line", { x1: "18", y1: "30", x2: "38", y2: "30", stroke: "currentColor", "stroke-opacity": "0.18", "stroke-width": "2", "stroke-linecap": "round" }),
|
|
2497
|
+
/* @__PURE__ */ jsx4("line", { x1: "18", y1: "38", x2: "32", y2: "38", stroke: "currentColor", "stroke-opacity": "0.14", "stroke-width": "2", "stroke-linecap": "round" })
|
|
2278
2498
|
] });
|
|
2279
2499
|
}
|
|
2280
2500
|
function useDebouncedValue(value, ms) {
|
|
2281
|
-
const [debounced, setDebounced] =
|
|
2501
|
+
const [debounced, setDebounced] = useState3(value);
|
|
2282
2502
|
useEffect2(() => {
|
|
2283
2503
|
const t = setTimeout(() => setDebounced(value), ms);
|
|
2284
2504
|
return () => clearTimeout(t);
|
|
2285
2505
|
}, [value, ms]);
|
|
2286
2506
|
return debounced;
|
|
2287
2507
|
}
|
|
2508
|
+
var SEVERITY_RANK = { blocker: 0, high: 1, medium: 2, low: 3 };
|
|
2509
|
+
function applyLocalFilters(rows, f) {
|
|
2510
|
+
let out = rows;
|
|
2511
|
+
if (f.status?.length) out = out.filter((r) => f.status.includes(r.status));
|
|
2512
|
+
if (f.type?.length) out = out.filter((r) => f.type.includes(r.feedback_type));
|
|
2513
|
+
if (f.severity?.length) out = out.filter((r) => f.severity.includes(r.severity));
|
|
2514
|
+
if (f.mine) out = out.filter((r) => r.is_mine);
|
|
2515
|
+
const sorted = out.slice();
|
|
2516
|
+
switch (f.ordering ?? "recent") {
|
|
2517
|
+
case "oldest":
|
|
2518
|
+
sorted.sort((a, b) => a.created_at.localeCompare(b.created_at));
|
|
2519
|
+
break;
|
|
2520
|
+
case "updated":
|
|
2521
|
+
sorted.sort((a, b) => b.updated_at.localeCompare(a.updated_at));
|
|
2522
|
+
break;
|
|
2523
|
+
case "severity":
|
|
2524
|
+
sorted.sort(
|
|
2525
|
+
(a, b) => (SEVERITY_RANK[a.severity] ?? 9) - (SEVERITY_RANK[b.severity] ?? 9)
|
|
2526
|
+
);
|
|
2527
|
+
break;
|
|
2528
|
+
case "status":
|
|
2529
|
+
break;
|
|
2530
|
+
default:
|
|
2531
|
+
sorted.sort((a, b) => b.created_at.localeCompare(a.created_at));
|
|
2532
|
+
}
|
|
2533
|
+
return sorted;
|
|
2534
|
+
}
|
|
2288
2535
|
function filtersHash(f) {
|
|
2289
2536
|
return JSON.stringify({
|
|
2290
2537
|
s: f.status?.slice().sort(),
|
|
@@ -2311,10 +2558,10 @@ function formatRelative(iso) {
|
|
|
2311
2558
|
}
|
|
2312
2559
|
|
|
2313
2560
|
// src/widget/ChangelogList.tsx
|
|
2314
|
-
import { useEffect as useEffect3, useMemo as useMemo2, useRef as useRef2, useState as
|
|
2561
|
+
import { useEffect as useEffect3, useMemo as useMemo2, useRef as useRef2, useState as useState4 } from "preact/hooks";
|
|
2315
2562
|
|
|
2316
2563
|
// src/widget/ReportRow.tsx
|
|
2317
|
-
import { jsx as
|
|
2564
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "preact/jsx-runtime";
|
|
2318
2565
|
function statusClassName(status) {
|
|
2319
2566
|
return `pill pill-status pill-status--${status}`;
|
|
2320
2567
|
}
|
|
@@ -2340,27 +2587,31 @@ function repliesLabel(count, strings) {
|
|
|
2340
2587
|
if (count === 1) return strings["mine.replies_one"];
|
|
2341
2588
|
return strings["mine.replies_many"].replace("{count}", String(count));
|
|
2342
2589
|
}
|
|
2343
|
-
function ReportRow({ row, strings, onClick }) {
|
|
2590
|
+
function ReportRow({ row, strings, onClick, extraMeta }) {
|
|
2344
2591
|
const preview = row.description.length > 120 ? row.description.slice(0, 117) + "\u2026" : row.description;
|
|
2345
2592
|
return /* @__PURE__ */ jsxs5("button", { type: "button", class: "mine-row", onClick, children: [
|
|
2346
2593
|
/* @__PURE__ */ jsxs5("div", { class: "mine-row-pills", children: [
|
|
2347
|
-
/* @__PURE__ */
|
|
2348
|
-
/* @__PURE__ */
|
|
2349
|
-
/* @__PURE__ */
|
|
2594
|
+
/* @__PURE__ */ jsx5("span", { class: statusClassName(row.status), children: strings[`status.${row.status}`] ?? row.status }),
|
|
2595
|
+
/* @__PURE__ */ jsx5("span", { class: typeClassName(), children: strings[`type.${row.feedback_type}`] }),
|
|
2596
|
+
/* @__PURE__ */ jsx5("span", { class: severityClassName(row.severity), children: strings[`severity.${row.severity}`] })
|
|
2350
2597
|
] }),
|
|
2351
|
-
/* @__PURE__ */
|
|
2598
|
+
/* @__PURE__ */ jsx5("div", { class: "mine-row-preview", children: preview }),
|
|
2352
2599
|
/* @__PURE__ */ jsxs5("div", { class: "mine-row-meta", children: [
|
|
2353
|
-
/* @__PURE__ */
|
|
2600
|
+
/* @__PURE__ */ jsx5("span", { children: formatRelative2(row.updated_at || row.created_at) }),
|
|
2354
2601
|
row.comment_count > 0 && /* @__PURE__ */ jsxs5("span", { children: [
|
|
2355
2602
|
"\xB7 ",
|
|
2356
2603
|
repliesLabel(row.comment_count, strings)
|
|
2604
|
+
] }),
|
|
2605
|
+
extraMeta && /* @__PURE__ */ jsxs5("span", { children: [
|
|
2606
|
+
"\xB7 ",
|
|
2607
|
+
extraMeta
|
|
2357
2608
|
] })
|
|
2358
2609
|
] })
|
|
2359
2610
|
] });
|
|
2360
2611
|
}
|
|
2361
2612
|
|
|
2362
2613
|
// src/widget/ChangelogList.tsx
|
|
2363
|
-
import { jsx as
|
|
2614
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "preact/jsx-runtime";
|
|
2364
2615
|
var POLL_MS3 = 3e4;
|
|
2365
2616
|
function isoWeekKey(iso) {
|
|
2366
2617
|
const d = new Date(iso);
|
|
@@ -2396,9 +2647,9 @@ function formatWeekLabel(weekKey) {
|
|
|
2396
2647
|
}
|
|
2397
2648
|
}
|
|
2398
2649
|
function ChangelogList({ api, externalId, strings, onSelect }) {
|
|
2399
|
-
const [rows, setRows] =
|
|
2400
|
-
const [error, setError] =
|
|
2401
|
-
const [refreshing, setRefreshing] =
|
|
2650
|
+
const [rows, setRows] = useState4(null);
|
|
2651
|
+
const [error, setError] = useState4(null);
|
|
2652
|
+
const [refreshing, setRefreshing] = useState4(false);
|
|
2402
2653
|
const mountedRef = useRef2(true);
|
|
2403
2654
|
const fetchRows = async () => {
|
|
2404
2655
|
setRefreshing(true);
|
|
@@ -2431,8 +2682,8 @@ function ChangelogList({ api, externalId, strings, onSelect }) {
|
|
|
2431
2682
|
const isEmpty = rows !== null && rows.length === 0;
|
|
2432
2683
|
return /* @__PURE__ */ jsxs6("div", { class: "mine-list", children: [
|
|
2433
2684
|
/* @__PURE__ */ jsxs6("div", { class: "mine-list-header", children: [
|
|
2434
|
-
/* @__PURE__ */
|
|
2435
|
-
/* @__PURE__ */
|
|
2685
|
+
/* @__PURE__ */ jsx6("h2", { children: strings["tab.changelog"] }),
|
|
2686
|
+
/* @__PURE__ */ jsx6(
|
|
2436
2687
|
"button",
|
|
2437
2688
|
{
|
|
2438
2689
|
type: "button",
|
|
@@ -2445,26 +2696,34 @@ function ChangelogList({ api, externalId, strings, onSelect }) {
|
|
|
2445
2696
|
}
|
|
2446
2697
|
)
|
|
2447
2698
|
] }),
|
|
2448
|
-
isLoading && /* @__PURE__ */
|
|
2449
|
-
error && /* @__PURE__ */
|
|
2699
|
+
isLoading && /* @__PURE__ */ jsx6("div", { class: "mine-loading", children: strings["mine.loading"] }),
|
|
2700
|
+
error && /* @__PURE__ */ jsx6("div", { class: "error", children: error }),
|
|
2450
2701
|
isEmpty && /* @__PURE__ */ jsxs6("div", { class: "mine-empty", children: [
|
|
2451
|
-
/* @__PURE__ */
|
|
2452
|
-
/* @__PURE__ */
|
|
2702
|
+
/* @__PURE__ */ jsx6("strong", { children: strings["changelog.empty.title"] }),
|
|
2703
|
+
/* @__PURE__ */ jsx6("p", { children: strings["changelog.empty.body"] })
|
|
2453
2704
|
] }),
|
|
2454
|
-
groups.length > 0 && /* @__PURE__ */
|
|
2705
|
+
groups.length > 0 && /* @__PURE__ */ jsx6("div", { class: "changelog-groups", children: groups.map((g) => /* @__PURE__ */ jsxs6("section", { class: "changelog-group", children: [
|
|
2455
2706
|
/* @__PURE__ */ jsxs6("header", { class: "changelog-group-header", children: [
|
|
2456
|
-
/* @__PURE__ */
|
|
2457
|
-
/* @__PURE__ */
|
|
2458
|
-
/* @__PURE__ */
|
|
2459
|
-
/* @__PURE__ */
|
|
2707
|
+
/* @__PURE__ */ jsx6("span", { class: "changelog-group-marker", "aria-hidden": "true", children: "\u25CF" }),
|
|
2708
|
+
/* @__PURE__ */ jsx6("span", { class: "changelog-group-label", children: strings["changelog.week_of"].replace("{date}", g.label) }),
|
|
2709
|
+
/* @__PURE__ */ jsx6("span", { class: "changelog-group-rule", "aria-hidden": "true" }),
|
|
2710
|
+
/* @__PURE__ */ jsx6("span", { class: "changelog-group-count", children: strings[g.rows.length === 1 ? "changelog.resolved_one" : "changelog.resolved_many"].replace("{count}", String(g.rows.length)) })
|
|
2460
2711
|
] }),
|
|
2461
|
-
/* @__PURE__ */
|
|
2712
|
+
/* @__PURE__ */ jsx6("ul", { class: "mine-rows", children: g.rows.map((row) => /* @__PURE__ */ jsx6("li", { children: /* @__PURE__ */ jsx6(
|
|
2713
|
+
ReportRow,
|
|
2714
|
+
{
|
|
2715
|
+
row,
|
|
2716
|
+
strings,
|
|
2717
|
+
onClick: () => onSelect(row),
|
|
2718
|
+
extraMeta: row.pr_number ? `PR #${row.pr_number}` : void 0
|
|
2719
|
+
}
|
|
2720
|
+
) })) })
|
|
2462
2721
|
] }, g.weekKey)) })
|
|
2463
2722
|
] });
|
|
2464
2723
|
}
|
|
2465
2724
|
|
|
2466
2725
|
// src/widget/Fab.tsx
|
|
2467
|
-
import { jsx as
|
|
2726
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "preact/jsx-runtime";
|
|
2468
2727
|
function BugIcon() {
|
|
2469
2728
|
return /* @__PURE__ */ jsxs7(
|
|
2470
2729
|
"svg",
|
|
@@ -2480,34 +2739,34 @@ function BugIcon() {
|
|
|
2480
2739
|
"aria-hidden": "true",
|
|
2481
2740
|
focusable: "false",
|
|
2482
2741
|
children: [
|
|
2483
|
-
/* @__PURE__ */
|
|
2484
|
-
/* @__PURE__ */
|
|
2485
|
-
/* @__PURE__ */
|
|
2486
|
-
/* @__PURE__ */
|
|
2487
|
-
/* @__PURE__ */
|
|
2488
|
-
/* @__PURE__ */
|
|
2489
|
-
/* @__PURE__ */
|
|
2490
|
-
/* @__PURE__ */
|
|
2491
|
-
/* @__PURE__ */
|
|
2492
|
-
/* @__PURE__ */
|
|
2493
|
-
/* @__PURE__ */
|
|
2742
|
+
/* @__PURE__ */ jsx7("path", { d: "m8 2 1.88 1.88" }),
|
|
2743
|
+
/* @__PURE__ */ jsx7("path", { d: "M14.12 3.88 16 2" }),
|
|
2744
|
+
/* @__PURE__ */ jsx7("path", { d: "M9 7.13v-1a3.003 3.003 0 1 1 6 0v1" }),
|
|
2745
|
+
/* @__PURE__ */ jsx7("path", { d: "M12 20c-3.3 0-6-2.7-6-6v-3a4 4 0 0 1 4-4h4a4 4 0 0 1 4 4v3c0 3.3-2.7 6-6 6" }),
|
|
2746
|
+
/* @__PURE__ */ jsx7("path", { d: "M12 20v-9" }),
|
|
2747
|
+
/* @__PURE__ */ jsx7("path", { d: "M6.53 9C4.6 8.8 3 7.1 3 5" }),
|
|
2748
|
+
/* @__PURE__ */ jsx7("path", { d: "M6 13H2" }),
|
|
2749
|
+
/* @__PURE__ */ jsx7("path", { d: "M3 21c0-2.1 1.7-3.9 3.8-4" }),
|
|
2750
|
+
/* @__PURE__ */ jsx7("path", { d: "M20.97 5c0 2.1-1.6 3.8-3.5 4" }),
|
|
2751
|
+
/* @__PURE__ */ jsx7("path", { d: "M22 13h-4" }),
|
|
2752
|
+
/* @__PURE__ */ jsx7("path", { d: "M17.2 17c2.1.1 3.8 1.9 3.8 4" })
|
|
2494
2753
|
]
|
|
2495
2754
|
}
|
|
2496
2755
|
);
|
|
2497
2756
|
}
|
|
2498
2757
|
function Fab({ label, onClick, count }) {
|
|
2499
2758
|
return /* @__PURE__ */ jsxs7("button", { type: "button", class: "fab", "aria-label": label, title: label, onClick, children: [
|
|
2500
|
-
/* @__PURE__ */
|
|
2501
|
-
count !== void 0 && count > 0 && /* @__PURE__ */
|
|
2759
|
+
/* @__PURE__ */ jsx7(BugIcon, {}),
|
|
2760
|
+
count !== void 0 && count > 0 && /* @__PURE__ */ jsx7("span", { class: "fab-badge", "aria-hidden": "true", children: count > 9 ? "9+" : String(count) })
|
|
2502
2761
|
] });
|
|
2503
2762
|
}
|
|
2504
2763
|
|
|
2505
2764
|
// src/widget/Form.tsx
|
|
2506
|
-
import { useEffect as useEffect5, useRef as useRef4, useState as
|
|
2765
|
+
import { useEffect as useEffect5, useRef as useRef4, useState as useState6 } from "preact/hooks";
|
|
2507
2766
|
|
|
2508
2767
|
// src/widget/Annotator.tsx
|
|
2509
|
-
import { useEffect as useEffect4, useLayoutEffect, useRef as useRef3, useState as
|
|
2510
|
-
import { jsx as
|
|
2768
|
+
import { useEffect as useEffect4, useLayoutEffect, useRef as useRef3, useState as useState5 } from "preact/hooks";
|
|
2769
|
+
import { jsx as jsx8, jsxs as jsxs8 } from "preact/jsx-runtime";
|
|
2511
2770
|
var COLORS = ["#ef4444", "#f59e0b", "#10b981", "#3b82f6", "#ffffff"];
|
|
2512
2771
|
var HIGHLIGHT_ALPHA = 0.35;
|
|
2513
2772
|
function drawShape(ctx, shape, sourceImage) {
|
|
@@ -2629,43 +2888,43 @@ function drawBlur(ctx, shape, sourceImage) {
|
|
|
2629
2888
|
ctx.imageSmoothingEnabled = true;
|
|
2630
2889
|
}
|
|
2631
2890
|
var Icon = {
|
|
2632
|
-
rect: /* @__PURE__ */
|
|
2633
|
-
arrow: /* @__PURE__ */
|
|
2634
|
-
pencil: /* @__PURE__ */
|
|
2635
|
-
text: /* @__PURE__ */
|
|
2891
|
+
rect: /* @__PURE__ */ jsx8("svg", { width: "16", height: "16", viewBox: "0 0 16 16", "aria-hidden": "true", children: /* @__PURE__ */ jsx8("rect", { x: "2", y: "3", width: "12", height: "10", fill: "none", stroke: "currentColor", "stroke-width": "1.5" }) }),
|
|
2892
|
+
arrow: /* @__PURE__ */ jsx8("svg", { width: "16", height: "16", viewBox: "0 0 16 16", "aria-hidden": "true", children: /* @__PURE__ */ jsx8("path", { d: "M2 8h11M9 4l4 4-4 4", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round", "stroke-linejoin": "round" }) }),
|
|
2893
|
+
pencil: /* @__PURE__ */ jsx8("svg", { width: "16", height: "16", viewBox: "0 0 16 16", "aria-hidden": "true", children: /* @__PURE__ */ jsx8("path", { d: "M11.5 2.5l2 2L5 13H3v-2l8.5-8.5z", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linejoin": "round" }) }),
|
|
2894
|
+
text: /* @__PURE__ */ jsx8("svg", { width: "16", height: "16", viewBox: "0 0 16 16", "aria-hidden": "true", children: /* @__PURE__ */ jsx8("path", { d: "M3 3h10M8 3v10M5 13h6", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round" }) }),
|
|
2636
2895
|
highlight: /* @__PURE__ */ jsxs8("svg", { width: "16", height: "16", viewBox: "0 0 16 16", "aria-hidden": "true", children: [
|
|
2637
|
-
/* @__PURE__ */
|
|
2638
|
-
/* @__PURE__ */
|
|
2896
|
+
/* @__PURE__ */ jsx8("path", { d: "M3 13l3-3L11 5l-2-2-5 5-3 3v2h2zM10 4l2 2", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round", "stroke-linejoin": "round" }),
|
|
2897
|
+
/* @__PURE__ */ jsx8("path", { d: "M2 14h12", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round" })
|
|
2639
2898
|
] }),
|
|
2640
2899
|
blur: /* @__PURE__ */ jsxs8("svg", { width: "16", height: "16", viewBox: "0 0 16 16", "aria-hidden": "true", children: [
|
|
2641
|
-
/* @__PURE__ */
|
|
2642
|
-
/* @__PURE__ */
|
|
2643
|
-
/* @__PURE__ */
|
|
2644
|
-
/* @__PURE__ */
|
|
2645
|
-
/* @__PURE__ */
|
|
2646
|
-
/* @__PURE__ */
|
|
2647
|
-
/* @__PURE__ */
|
|
2648
|
-
/* @__PURE__ */
|
|
2649
|
-
/* @__PURE__ */
|
|
2900
|
+
/* @__PURE__ */ jsx8("rect", { x: "2", y: "2", width: "4", height: "4", fill: "currentColor", opacity: "0.85" }),
|
|
2901
|
+
/* @__PURE__ */ jsx8("rect", { x: "7", y: "2", width: "3", height: "3", fill: "currentColor", opacity: "0.55" }),
|
|
2902
|
+
/* @__PURE__ */ jsx8("rect", { x: "11", y: "2", width: "3", height: "4", fill: "currentColor", opacity: "0.4" }),
|
|
2903
|
+
/* @__PURE__ */ jsx8("rect", { x: "2", y: "7", width: "3", height: "3", fill: "currentColor", opacity: "0.6" }),
|
|
2904
|
+
/* @__PURE__ */ jsx8("rect", { x: "6", y: "7", width: "3", height: "3", fill: "currentColor", opacity: "0.85" }),
|
|
2905
|
+
/* @__PURE__ */ jsx8("rect", { x: "10", y: "7", width: "4", height: "3", fill: "currentColor", opacity: "0.5" }),
|
|
2906
|
+
/* @__PURE__ */ jsx8("rect", { x: "2", y: "11", width: "4", height: "3", fill: "currentColor", opacity: "0.4" }),
|
|
2907
|
+
/* @__PURE__ */ jsx8("rect", { x: "7", y: "11", width: "3", height: "3", fill: "currentColor", opacity: "0.65" }),
|
|
2908
|
+
/* @__PURE__ */ jsx8("rect", { x: "11", y: "11", width: "3", height: "3", fill: "currentColor", opacity: "0.85" })
|
|
2650
2909
|
] }),
|
|
2651
|
-
undo: /* @__PURE__ */
|
|
2652
|
-
redo: /* @__PURE__ */
|
|
2653
|
-
trash: /* @__PURE__ */
|
|
2654
|
-
close: /* @__PURE__ */
|
|
2910
|
+
undo: /* @__PURE__ */ jsx8("svg", { width: "14", height: "14", viewBox: "0 0 16 16", "aria-hidden": "true", children: /* @__PURE__ */ jsx8("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" }) }),
|
|
2911
|
+
redo: /* @__PURE__ */ jsx8("svg", { width: "14", height: "14", viewBox: "0 0 16 16", "aria-hidden": "true", children: /* @__PURE__ */ jsx8("path", { d: "M12 7l-3-3M12 7l-3 3M12 7H6a3 3 0 0 0 0 6h2", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round", "stroke-linejoin": "round" }) }),
|
|
2912
|
+
trash: /* @__PURE__ */ jsx8("svg", { width: "14", height: "14", viewBox: "0 0 16 16", "aria-hidden": "true", children: /* @__PURE__ */ jsx8("path", { d: "M3 4h10M6 4V2.5h4V4M5 4l.5 9h5L11 4", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round", "stroke-linejoin": "round" }) }),
|
|
2913
|
+
close: /* @__PURE__ */ jsx8("svg", { width: "16", height: "16", viewBox: "0 0 16 16", "aria-hidden": "true", children: /* @__PURE__ */ jsx8("path", { d: "M4 4l8 8M12 4l-8 8", fill: "none", stroke: "currentColor", "stroke-width": "1.5", "stroke-linecap": "round" }) })
|
|
2655
2914
|
};
|
|
2656
2915
|
function Annotator({ imageBlob, strings, onSave, onCancel }) {
|
|
2657
2916
|
const canvasRef = useRef3(null);
|
|
2658
2917
|
const containerRef = useRef3(null);
|
|
2659
2918
|
const imageRef = useRef3(null);
|
|
2660
|
-
const [tool, setTool] =
|
|
2661
|
-
const [color, setColor] =
|
|
2662
|
-
const [shapes, setShapes] =
|
|
2663
|
-
const [past, setPast] =
|
|
2664
|
-
const [future, setFuture] =
|
|
2919
|
+
const [tool, setTool] = useState5("rectangle");
|
|
2920
|
+
const [color, setColor] = useState5(COLORS[0]);
|
|
2921
|
+
const [shapes, setShapes] = useState5([]);
|
|
2922
|
+
const [past, setPast] = useState5([]);
|
|
2923
|
+
const [future, setFuture] = useState5([]);
|
|
2665
2924
|
const isDrawingRef = useRef3(false);
|
|
2666
|
-
const [draftShape, setDraftShape] =
|
|
2667
|
-
const [imageLoaded, setImageLoaded] =
|
|
2668
|
-
const [saving, setSaving] =
|
|
2925
|
+
const [draftShape, setDraftShape] = useState5(null);
|
|
2926
|
+
const [imageLoaded, setImageLoaded] = useState5(false);
|
|
2927
|
+
const [saving, setSaving] = useState5(false);
|
|
2669
2928
|
const scaleRef = useRef3(1);
|
|
2670
2929
|
function addShape(shape) {
|
|
2671
2930
|
setShapes((s) => {
|
|
@@ -2885,7 +3144,7 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
|
|
|
2885
3144
|
{ id: "highlight", icon: Icon.highlight, label: strings["annotator.tool.highlight"] },
|
|
2886
3145
|
{ id: "blur", icon: Icon.blur, label: strings["annotator.tool.blur"] }
|
|
2887
3146
|
];
|
|
2888
|
-
return /* @__PURE__ */
|
|
3147
|
+
return /* @__PURE__ */ jsx8(
|
|
2889
3148
|
"div",
|
|
2890
3149
|
{
|
|
2891
3150
|
class: "annotator-backdrop",
|
|
@@ -2895,8 +3154,8 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
|
|
|
2895
3154
|
},
|
|
2896
3155
|
children: /* @__PURE__ */ jsxs8("div", { class: "annotator", role: "dialog", "aria-modal": "true", "aria-label": strings["annotator.title"], children: [
|
|
2897
3156
|
/* @__PURE__ */ jsxs8("div", { class: "annotator-header", children: [
|
|
2898
|
-
/* @__PURE__ */
|
|
2899
|
-
/* @__PURE__ */
|
|
3157
|
+
/* @__PURE__ */ jsx8("span", { children: strings["annotator.title"] }),
|
|
3158
|
+
/* @__PURE__ */ jsx8(
|
|
2900
3159
|
"button",
|
|
2901
3160
|
{
|
|
2902
3161
|
type: "button",
|
|
@@ -2908,7 +3167,7 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
|
|
|
2908
3167
|
)
|
|
2909
3168
|
] }),
|
|
2910
3169
|
/* @__PURE__ */ jsxs8("div", { class: "annotator-toolbar", children: [
|
|
2911
|
-
/* @__PURE__ */
|
|
3170
|
+
/* @__PURE__ */ jsx8("div", { class: "annotator-tools", children: tools.map((t) => /* @__PURE__ */ jsx8(
|
|
2912
3171
|
"button",
|
|
2913
3172
|
{
|
|
2914
3173
|
type: "button",
|
|
@@ -2921,9 +3180,9 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
|
|
|
2921
3180
|
},
|
|
2922
3181
|
t.id
|
|
2923
3182
|
)) }),
|
|
2924
|
-
/* @__PURE__ */
|
|
3183
|
+
/* @__PURE__ */ jsx8("span", { class: "annotator-sep" }),
|
|
2925
3184
|
/* @__PURE__ */ jsxs8("div", { class: "annotator-colors", children: [
|
|
2926
|
-
COLORS.map((c) => /* @__PURE__ */
|
|
3185
|
+
COLORS.map((c) => /* @__PURE__ */ jsx8(
|
|
2927
3186
|
"button",
|
|
2928
3187
|
{
|
|
2929
3188
|
type: "button",
|
|
@@ -2935,7 +3194,7 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
|
|
|
2935
3194
|
},
|
|
2936
3195
|
c
|
|
2937
3196
|
)),
|
|
2938
|
-
/* @__PURE__ */
|
|
3197
|
+
/* @__PURE__ */ jsx8("label", { class: "annotator-color annotator-color-picker", title: strings["annotator.color_picker"], children: /* @__PURE__ */ jsx8(
|
|
2939
3198
|
"input",
|
|
2940
3199
|
{
|
|
2941
3200
|
type: "color",
|
|
@@ -2945,7 +3204,7 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
|
|
|
2945
3204
|
}
|
|
2946
3205
|
) })
|
|
2947
3206
|
] }),
|
|
2948
|
-
/* @__PURE__ */
|
|
3207
|
+
/* @__PURE__ */ jsx8("span", { class: "annotator-sep" }),
|
|
2949
3208
|
/* @__PURE__ */ jsxs8(
|
|
2950
3209
|
"button",
|
|
2951
3210
|
{
|
|
@@ -2956,7 +3215,7 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
|
|
|
2956
3215
|
title: strings["annotator.undo"],
|
|
2957
3216
|
children: [
|
|
2958
3217
|
Icon.undo,
|
|
2959
|
-
/* @__PURE__ */
|
|
3218
|
+
/* @__PURE__ */ jsx8("span", { children: strings["annotator.undo"] })
|
|
2960
3219
|
]
|
|
2961
3220
|
}
|
|
2962
3221
|
),
|
|
@@ -2970,7 +3229,7 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
|
|
|
2970
3229
|
title: strings["annotator.redo"],
|
|
2971
3230
|
children: [
|
|
2972
3231
|
Icon.redo,
|
|
2973
|
-
/* @__PURE__ */
|
|
3232
|
+
/* @__PURE__ */ jsx8("span", { children: strings["annotator.redo"] })
|
|
2974
3233
|
]
|
|
2975
3234
|
}
|
|
2976
3235
|
),
|
|
@@ -2983,18 +3242,18 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
|
|
|
2983
3242
|
disabled: shapes.length === 0,
|
|
2984
3243
|
children: [
|
|
2985
3244
|
Icon.trash,
|
|
2986
|
-
/* @__PURE__ */
|
|
3245
|
+
/* @__PURE__ */ jsx8("span", { children: strings["annotator.clear"] })
|
|
2987
3246
|
]
|
|
2988
3247
|
}
|
|
2989
3248
|
),
|
|
2990
|
-
/* @__PURE__ */
|
|
3249
|
+
/* @__PURE__ */ jsx8("span", { class: "annotator-spacer" }),
|
|
2991
3250
|
/* @__PURE__ */ jsxs8("span", { class: "annotator-count", children: [
|
|
2992
3251
|
shapes.length,
|
|
2993
3252
|
" ",
|
|
2994
3253
|
strings["annotator.count_suffix"]
|
|
2995
3254
|
] })
|
|
2996
3255
|
] }),
|
|
2997
|
-
/* @__PURE__ */
|
|
3256
|
+
/* @__PURE__ */ jsx8("div", { ref: containerRef, class: "annotator-canvas-wrap", children: !imageLoaded ? /* @__PURE__ */ jsx8("span", { class: "annotator-loading", children: strings["annotator.loading"] }) : /* @__PURE__ */ jsx8(
|
|
2998
3257
|
"canvas",
|
|
2999
3258
|
{
|
|
3000
3259
|
ref: canvasRef,
|
|
@@ -3006,8 +3265,8 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
|
|
|
3006
3265
|
}
|
|
3007
3266
|
) }),
|
|
3008
3267
|
/* @__PURE__ */ jsxs8("div", { class: "annotator-footer", children: [
|
|
3009
|
-
/* @__PURE__ */
|
|
3010
|
-
/* @__PURE__ */
|
|
3268
|
+
/* @__PURE__ */ jsx8("button", { type: "button", class: "btn", onClick: onCancel, children: strings["form.cancel"] }),
|
|
3269
|
+
/* @__PURE__ */ jsx8(
|
|
3011
3270
|
"button",
|
|
3012
3271
|
{
|
|
3013
3272
|
type: "button",
|
|
@@ -3024,7 +3283,7 @@ function Annotator({ imageBlob, strings, onSave, onCancel }) {
|
|
|
3024
3283
|
}
|
|
3025
3284
|
|
|
3026
3285
|
// src/widget/Form.tsx
|
|
3027
|
-
import { jsx as
|
|
3286
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "preact/jsx-runtime";
|
|
3028
3287
|
var TYPES2 = ["bug", "feature", "question", "praise", "typo"];
|
|
3029
3288
|
var SEVERITIES2 = ["blocker", "high", "medium", "low"];
|
|
3030
3289
|
function Form({
|
|
@@ -3036,13 +3295,13 @@ function Form({
|
|
|
3036
3295
|
submittedSeq,
|
|
3037
3296
|
onDirtyChange
|
|
3038
3297
|
}) {
|
|
3039
|
-
const [description, setDescription] =
|
|
3040
|
-
const [feedbackType, setFeedbackType] =
|
|
3041
|
-
const [severity, setSeverity] =
|
|
3042
|
-
const [localError, setLocalError] =
|
|
3043
|
-
const [shots, setShots] =
|
|
3044
|
-
const [isDragOver, setIsDragOver] =
|
|
3045
|
-
const [annotatingIndex, setAnnotatingIndex] =
|
|
3298
|
+
const [description, setDescription] = useState6("");
|
|
3299
|
+
const [feedbackType, setFeedbackType] = useState6("bug");
|
|
3300
|
+
const [severity, setSeverity] = useState6("medium");
|
|
3301
|
+
const [localError, setLocalError] = useState6("");
|
|
3302
|
+
const [shots, setShots] = useState6([]);
|
|
3303
|
+
const [isDragOver, setIsDragOver] = useState6(false);
|
|
3304
|
+
const [annotatingIndex, setAnnotatingIndex] = useState6(null);
|
|
3046
3305
|
const fileInputRef = useRef4(null);
|
|
3047
3306
|
const dropZoneRef = useRef4(null);
|
|
3048
3307
|
const submitting = status === "submitting";
|
|
@@ -3164,10 +3423,10 @@ function Form({
|
|
|
3164
3423
|
onSubmit(values);
|
|
3165
3424
|
};
|
|
3166
3425
|
return /* @__PURE__ */ jsxs9("form", { onSubmit: handleSubmit, children: [
|
|
3167
|
-
/* @__PURE__ */
|
|
3426
|
+
/* @__PURE__ */ jsx9("h2", { children: strings["form.title"] }),
|
|
3168
3427
|
/* @__PURE__ */ jsxs9("div", { class: "field", children: [
|
|
3169
|
-
/* @__PURE__ */
|
|
3170
|
-
/* @__PURE__ */
|
|
3428
|
+
/* @__PURE__ */ jsx9("label", { for: "mfb-desc", children: strings["form.description.label"] }),
|
|
3429
|
+
/* @__PURE__ */ jsx9(
|
|
3171
3430
|
"textarea",
|
|
3172
3431
|
{
|
|
3173
3432
|
id: "mfb-desc",
|
|
@@ -3179,33 +3438,33 @@ function Form({
|
|
|
3179
3438
|
] }),
|
|
3180
3439
|
/* @__PURE__ */ jsxs9("div", { class: "row", children: [
|
|
3181
3440
|
/* @__PURE__ */ jsxs9("div", { class: "field", children: [
|
|
3182
|
-
/* @__PURE__ */
|
|
3183
|
-
/* @__PURE__ */
|
|
3441
|
+
/* @__PURE__ */ jsx9("label", { for: "mfb-type", children: strings["form.type.label"] }),
|
|
3442
|
+
/* @__PURE__ */ jsx9(
|
|
3184
3443
|
"select",
|
|
3185
3444
|
{
|
|
3186
3445
|
id: "mfb-type",
|
|
3187
3446
|
value: feedbackType,
|
|
3188
3447
|
onChange: (e) => setFeedbackType(e.target.value),
|
|
3189
|
-
children: TYPES2.map((t) => /* @__PURE__ */
|
|
3448
|
+
children: TYPES2.map((t) => /* @__PURE__ */ jsx9("option", { value: t, children: strings[`type.${t}`] }))
|
|
3190
3449
|
}
|
|
3191
3450
|
)
|
|
3192
3451
|
] }),
|
|
3193
3452
|
/* @__PURE__ */ jsxs9("div", { class: "field", children: [
|
|
3194
|
-
/* @__PURE__ */
|
|
3195
|
-
/* @__PURE__ */
|
|
3453
|
+
/* @__PURE__ */ jsx9("label", { for: "mfb-sev", children: strings["form.severity.label"] }),
|
|
3454
|
+
/* @__PURE__ */ jsx9(
|
|
3196
3455
|
"select",
|
|
3197
3456
|
{
|
|
3198
3457
|
id: "mfb-sev",
|
|
3199
3458
|
value: severity,
|
|
3200
3459
|
onChange: (e) => setSeverity(e.target.value),
|
|
3201
|
-
children: SEVERITIES2.map((s) => /* @__PURE__ */
|
|
3460
|
+
children: SEVERITIES2.map((s) => /* @__PURE__ */ jsx9("option", { value: s, children: strings[`severity.${s}`] }))
|
|
3202
3461
|
}
|
|
3203
3462
|
)
|
|
3204
3463
|
] })
|
|
3205
3464
|
] }),
|
|
3206
3465
|
/* @__PURE__ */ jsxs9("div", { class: "field", children: [
|
|
3207
|
-
/* @__PURE__ */
|
|
3208
|
-
/* @__PURE__ */
|
|
3466
|
+
/* @__PURE__ */ jsx9("label", { children: strings["form.screenshot.label"] }),
|
|
3467
|
+
/* @__PURE__ */ jsx9(
|
|
3209
3468
|
"input",
|
|
3210
3469
|
{
|
|
3211
3470
|
ref: fileInputRef,
|
|
@@ -3218,8 +3477,8 @@ function Form({
|
|
|
3218
3477
|
tabIndex: -1
|
|
3219
3478
|
}
|
|
3220
3479
|
),
|
|
3221
|
-
shots.length > 0 && /* @__PURE__ */
|
|
3222
|
-
/* @__PURE__ */
|
|
3480
|
+
shots.length > 0 && /* @__PURE__ */ jsx9("div", { class: "screenshot-strip", children: shots.map((shot, index) => /* @__PURE__ */ jsxs9("div", { class: "screenshot-preview", children: [
|
|
3481
|
+
/* @__PURE__ */ jsx9("img", { src: shot.preview, alt: "" }),
|
|
3223
3482
|
/* @__PURE__ */ jsxs9("div", { class: "screenshot-preview-actions", children: [
|
|
3224
3483
|
/* @__PURE__ */ jsxs9(
|
|
3225
3484
|
"button",
|
|
@@ -3229,15 +3488,15 @@ function Form({
|
|
|
3229
3488
|
onClick: () => setAnnotatingIndex(index),
|
|
3230
3489
|
children: [
|
|
3231
3490
|
/* @__PURE__ */ jsxs9("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true", children: [
|
|
3232
|
-
/* @__PURE__ */
|
|
3233
|
-
/* @__PURE__ */
|
|
3491
|
+
/* @__PURE__ */ jsx9("path", { d: "M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" }),
|
|
3492
|
+
/* @__PURE__ */ jsx9("path", { d: "M18.5 2.5a2.12 2.12 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z" })
|
|
3234
3493
|
] }),
|
|
3235
3494
|
strings["form.screenshot.annotate"]
|
|
3236
3495
|
]
|
|
3237
3496
|
}
|
|
3238
3497
|
),
|
|
3239
3498
|
/* @__PURE__ */ jsxs9("button", { type: "button", class: "btn", onClick: () => removeShot(index), children: [
|
|
3240
|
-
/* @__PURE__ */
|
|
3499
|
+
/* @__PURE__ */ jsx9("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "2", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true", children: /* @__PURE__ */ jsx9("path", { d: "M3 6h18M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2m3 0v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6h14z" }) }),
|
|
3241
3500
|
strings["form.screenshot.remove"]
|
|
3242
3501
|
] })
|
|
3243
3502
|
] })
|
|
@@ -3262,33 +3521,33 @@ function Form({
|
|
|
3262
3521
|
onDrop: handleDrop,
|
|
3263
3522
|
children: [
|
|
3264
3523
|
/* @__PURE__ */ jsxs9("svg", { class: "screenshot-icon", width: "28", height: "28", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", "stroke-width": "1.6", "stroke-linecap": "round", "stroke-linejoin": "round", "aria-hidden": "true", children: [
|
|
3265
|
-
/* @__PURE__ */
|
|
3266
|
-
/* @__PURE__ */
|
|
3267
|
-
/* @__PURE__ */
|
|
3524
|
+
/* @__PURE__ */ jsx9("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
|
|
3525
|
+
/* @__PURE__ */ jsx9("polyline", { points: "17 8 12 3 7 8" }),
|
|
3526
|
+
/* @__PURE__ */ jsx9("line", { x1: "12", y1: "3", x2: "12", y2: "15" })
|
|
3268
3527
|
] }),
|
|
3269
3528
|
/* @__PURE__ */ jsxs9("div", { class: "screenshot-cta", children: [
|
|
3270
|
-
/* @__PURE__ */
|
|
3529
|
+
/* @__PURE__ */ jsx9("strong", { children: strings["form.screenshot.cta_click"] }),
|
|
3271
3530
|
", ",
|
|
3272
3531
|
strings["form.screenshot.cta_rest"]
|
|
3273
3532
|
] }),
|
|
3274
|
-
/* @__PURE__ */
|
|
3533
|
+
/* @__PURE__ */ jsx9("div", { class: "screenshot-formats", children: strings["form.screenshot.formats"] })
|
|
3275
3534
|
]
|
|
3276
3535
|
}
|
|
3277
3536
|
)
|
|
3278
3537
|
] }),
|
|
3279
3538
|
pageUrl && /* @__PURE__ */ jsxs9("div", { class: "page-context", title: pageUrl, children: [
|
|
3280
|
-
/* @__PURE__ */
|
|
3281
|
-
/* @__PURE__ */
|
|
3539
|
+
/* @__PURE__ */ jsx9("span", { class: "page-context-label", children: strings["form.context.label"] }),
|
|
3540
|
+
/* @__PURE__ */ jsx9("span", { class: "page-context-url", children: truncateUrl(pageUrl, 90) })
|
|
3282
3541
|
] }),
|
|
3283
|
-
/* @__PURE__ */
|
|
3284
|
-
localError && /* @__PURE__ */
|
|
3285
|
-
status === "error" && errorMessage && /* @__PURE__ */
|
|
3286
|
-
status === "success" && /* @__PURE__ */
|
|
3542
|
+
/* @__PURE__ */ jsx9("div", { class: "capture-notice", children: strings["form.capture.notice"] }),
|
|
3543
|
+
localError && /* @__PURE__ */ jsx9("div", { class: "error", children: localError }),
|
|
3544
|
+
status === "error" && errorMessage && /* @__PURE__ */ jsx9("div", { class: "error", children: errorMessage }),
|
|
3545
|
+
status === "success" && /* @__PURE__ */ jsx9("div", { class: "success", children: submittedSeq !== void 0 ? strings["form.success.seq"].replace("{seq}", String(submittedSeq)) : strings["form.success"] }),
|
|
3287
3546
|
/* @__PURE__ */ jsxs9("div", { class: "actions", children: [
|
|
3288
|
-
/* @__PURE__ */
|
|
3289
|
-
/* @__PURE__ */
|
|
3547
|
+
/* @__PURE__ */ jsx9("button", { type: "button", class: "btn", onClick: onCancel, disabled: submitting, children: strings["form.cancel"] }),
|
|
3548
|
+
/* @__PURE__ */ jsx9("button", { type: "submit", class: "btn btn--primary", disabled: submitting, children: submitLabel })
|
|
3290
3549
|
] }),
|
|
3291
|
-
annotatingIndex !== null && shots[annotatingIndex] && /* @__PURE__ */
|
|
3550
|
+
annotatingIndex !== null && shots[annotatingIndex] && /* @__PURE__ */ jsx9(
|
|
3292
3551
|
Annotator,
|
|
3293
3552
|
{
|
|
3294
3553
|
imageBlob: shots[annotatingIndex].blob,
|
|
@@ -3301,10 +3560,10 @@ function Form({
|
|
|
3301
3560
|
}
|
|
3302
3561
|
|
|
3303
3562
|
// src/widget/MineList.tsx
|
|
3304
|
-
import { useEffect as useEffect6, useRef as useRef5, useState as
|
|
3563
|
+
import { useEffect as useEffect6, useRef as useRef5, useState as useState7 } from "preact/hooks";
|
|
3305
3564
|
|
|
3306
3565
|
// src/widget/KpiStrip.tsx
|
|
3307
|
-
import { jsx as
|
|
3566
|
+
import { jsx as jsx10, jsxs as jsxs10 } from "preact/jsx-runtime";
|
|
3308
3567
|
function computeKpiCounts(rows) {
|
|
3309
3568
|
const counts = {
|
|
3310
3569
|
new: 0,
|
|
@@ -3371,7 +3630,7 @@ function KpiStrip({ rows, filter, onFilter, strings }) {
|
|
|
3371
3630
|
value: resolutionRate === null ? "\u2014" : `${resolutionRate}%`
|
|
3372
3631
|
}
|
|
3373
3632
|
];
|
|
3374
|
-
return /* @__PURE__ */
|
|
3633
|
+
return /* @__PURE__ */ jsx10("div", { class: "kpi-strip", role: "toolbar", children: cells.map((c) => {
|
|
3375
3634
|
const active = filter === c.key;
|
|
3376
3635
|
const toggleTo = active ? "all" : c.key;
|
|
3377
3636
|
return /* @__PURE__ */ jsxs10(
|
|
@@ -3382,8 +3641,8 @@ function KpiStrip({ rows, filter, onFilter, strings }) {
|
|
|
3382
3641
|
onClick: () => onFilter(toggleTo),
|
|
3383
3642
|
"aria-pressed": active,
|
|
3384
3643
|
children: [
|
|
3385
|
-
/* @__PURE__ */
|
|
3386
|
-
/* @__PURE__ */
|
|
3644
|
+
/* @__PURE__ */ jsx10("span", { class: "kpi-value", children: c.value }),
|
|
3645
|
+
/* @__PURE__ */ jsx10("span", { class: "kpi-label", children: c.label })
|
|
3387
3646
|
]
|
|
3388
3647
|
}
|
|
3389
3648
|
);
|
|
@@ -3391,16 +3650,16 @@ function KpiStrip({ rows, filter, onFilter, strings }) {
|
|
|
3391
3650
|
}
|
|
3392
3651
|
|
|
3393
3652
|
// src/widget/MineList.tsx
|
|
3394
|
-
import { jsx as
|
|
3653
|
+
import { jsx as jsx11, jsxs as jsxs11 } from "preact/jsx-runtime";
|
|
3395
3654
|
var POLL_MS4 = 3e4;
|
|
3396
3655
|
function MineList({ api, externalId, strings, onSelect, onOpenSeq }) {
|
|
3397
|
-
const [rows, setRows] =
|
|
3398
|
-
const [error, setError] =
|
|
3399
|
-
const [refreshing, setRefreshing] =
|
|
3400
|
-
const [filter, setFilter] =
|
|
3401
|
-
const [jumpValue, setJumpValue] =
|
|
3402
|
-
const [jumpError, setJumpError] =
|
|
3403
|
-
const [jumping, setJumping] =
|
|
3656
|
+
const [rows, setRows] = useState7(null);
|
|
3657
|
+
const [error, setError] = useState7(null);
|
|
3658
|
+
const [refreshing, setRefreshing] = useState7(false);
|
|
3659
|
+
const [filter, setFilter] = useState7("all");
|
|
3660
|
+
const [jumpValue, setJumpValue] = useState7("");
|
|
3661
|
+
const [jumpError, setJumpError] = useState7(false);
|
|
3662
|
+
const [jumping, setJumping] = useState7(false);
|
|
3404
3663
|
const mountedRef = useRef5(true);
|
|
3405
3664
|
const handleJump = async (e) => {
|
|
3406
3665
|
e.preventDefault();
|
|
@@ -3452,10 +3711,10 @@ function MineList({ api, externalId, strings, onSelect, onOpenSeq }) {
|
|
|
3452
3711
|
const visibleEmpty = !!rows && rows.length > 0 && (visibleRows?.length ?? 0) === 0;
|
|
3453
3712
|
return /* @__PURE__ */ jsxs11("div", { class: "mine-list", children: [
|
|
3454
3713
|
/* @__PURE__ */ jsxs11("div", { class: "mine-list-header", children: [
|
|
3455
|
-
/* @__PURE__ */
|
|
3714
|
+
/* @__PURE__ */ jsx11("h2", { children: strings["tab.mine"] }),
|
|
3456
3715
|
/* @__PURE__ */ jsxs11("div", { class: "mine-list-header-actions", children: [
|
|
3457
3716
|
onOpenSeq && /* @__PURE__ */ jsxs11("form", { class: "mine-jump", onSubmit: handleJump, children: [
|
|
3458
|
-
/* @__PURE__ */
|
|
3717
|
+
/* @__PURE__ */ jsx11(
|
|
3459
3718
|
"input",
|
|
3460
3719
|
{
|
|
3461
3720
|
type: "text",
|
|
@@ -3470,9 +3729,9 @@ function MineList({ api, externalId, strings, onSelect, onOpenSeq }) {
|
|
|
3470
3729
|
disabled: jumping
|
|
3471
3730
|
}
|
|
3472
3731
|
),
|
|
3473
|
-
/* @__PURE__ */
|
|
3732
|
+
/* @__PURE__ */ jsx11("button", { type: "submit", class: "btn", disabled: jumping || !jumpValue.trim(), children: strings["mine.jump.go"] })
|
|
3474
3733
|
] }),
|
|
3475
|
-
/* @__PURE__ */
|
|
3734
|
+
/* @__PURE__ */ jsx11(
|
|
3476
3735
|
"button",
|
|
3477
3736
|
{
|
|
3478
3737
|
type: "button",
|
|
@@ -3486,22 +3745,25 @@ function MineList({ api, externalId, strings, onSelect, onOpenSeq }) {
|
|
|
3486
3745
|
)
|
|
3487
3746
|
] })
|
|
3488
3747
|
] }),
|
|
3489
|
-
jumpError && /* @__PURE__ */
|
|
3490
|
-
rows && rows.length > 0 && /* @__PURE__ */
|
|
3491
|
-
isLoading && /* @__PURE__ */
|
|
3492
|
-
error && /* @__PURE__ */
|
|
3748
|
+
jumpError && /* @__PURE__ */ jsx11("div", { class: "mine-jump-error error", role: "alert", children: strings["mine.jump.not_found"] }),
|
|
3749
|
+
rows && rows.length > 0 && /* @__PURE__ */ jsx11(KpiStrip, { rows, filter, onFilter: setFilter, strings }),
|
|
3750
|
+
isLoading && /* @__PURE__ */ jsx11("div", { class: "mine-loading", children: strings["mine.loading"] }),
|
|
3751
|
+
error && /* @__PURE__ */ jsxs11("div", { class: "error", children: [
|
|
3752
|
+
error,
|
|
3753
|
+
/* @__PURE__ */ jsx11(DiagnosticHint, { api, strings, host: api.endpointHost })
|
|
3754
|
+
] }),
|
|
3493
3755
|
isEmpty && /* @__PURE__ */ jsxs11("div", { class: "mine-empty", children: [
|
|
3494
|
-
/* @__PURE__ */
|
|
3495
|
-
/* @__PURE__ */
|
|
3756
|
+
/* @__PURE__ */ jsx11("strong", { children: strings["mine.empty.title"] }),
|
|
3757
|
+
/* @__PURE__ */ jsx11("p", { children: strings["mine.empty.body"] })
|
|
3496
3758
|
] }),
|
|
3497
|
-
visibleEmpty && /* @__PURE__ */
|
|
3498
|
-
visibleRows && visibleRows.length > 0 && /* @__PURE__ */
|
|
3759
|
+
visibleEmpty && /* @__PURE__ */ jsx11("div", { class: "mine-empty", children: /* @__PURE__ */ jsx11("p", { children: strings["mine.filter.empty"] }) }),
|
|
3760
|
+
visibleRows && visibleRows.length > 0 && /* @__PURE__ */ jsx11("ul", { class: "mine-rows", children: visibleRows.map((row) => /* @__PURE__ */ jsx11("li", { children: /* @__PURE__ */ jsx11(ReportRow, { row, strings, onClick: () => onSelect(row) }) })) })
|
|
3499
3761
|
] });
|
|
3500
3762
|
}
|
|
3501
3763
|
|
|
3502
3764
|
// src/widget/Modal.tsx
|
|
3503
3765
|
import { useEffect as useEffect7, useRef as useRef6 } from "preact/hooks";
|
|
3504
|
-
import { jsx as
|
|
3766
|
+
import { jsx as jsx12, jsxs as jsxs12 } from "preact/jsx-runtime";
|
|
3505
3767
|
function Modal({ onDismiss, children, closeLabel = "Close", expanded = false }) {
|
|
3506
3768
|
const modalRef = useRef6(null);
|
|
3507
3769
|
const previouslyFocused = useRef6(null);
|
|
@@ -3527,7 +3789,7 @@ function Modal({ onDismiss, children, closeLabel = "Close", expanded = false })
|
|
|
3527
3789
|
if (prev && typeof prev.focus === "function") prev.focus();
|
|
3528
3790
|
};
|
|
3529
3791
|
}, [onDismiss]);
|
|
3530
|
-
return /* @__PURE__ */
|
|
3792
|
+
return /* @__PURE__ */ jsx12(
|
|
3531
3793
|
"div",
|
|
3532
3794
|
{
|
|
3533
3795
|
class: `backdrop ${expanded ? "is-expanded" : ""}`,
|
|
@@ -3543,7 +3805,7 @@ function Modal({ onDismiss, children, closeLabel = "Close", expanded = false })
|
|
|
3543
3805
|
role: "dialog",
|
|
3544
3806
|
"aria-modal": "true",
|
|
3545
3807
|
children: [
|
|
3546
|
-
/* @__PURE__ */
|
|
3808
|
+
/* @__PURE__ */ jsx12(
|
|
3547
3809
|
"button",
|
|
3548
3810
|
{
|
|
3549
3811
|
type: "button",
|
|
@@ -3562,7 +3824,7 @@ function Modal({ onDismiss, children, closeLabel = "Close", expanded = false })
|
|
|
3562
3824
|
}
|
|
3563
3825
|
|
|
3564
3826
|
// src/widget/PageActivityStrip.tsx
|
|
3565
|
-
import { jsx as
|
|
3827
|
+
import { jsx as jsx13, jsxs as jsxs13 } from "preact/jsx-runtime";
|
|
3566
3828
|
function PageActivityStrip({
|
|
3567
3829
|
open,
|
|
3568
3830
|
strings,
|
|
@@ -3571,14 +3833,14 @@ function PageActivityStrip({
|
|
|
3571
3833
|
if (open === 0) return null;
|
|
3572
3834
|
const text = open === 1 ? strings["page.strip.text.one"] : strings["page.strip.text"].replace("{n}", String(open));
|
|
3573
3835
|
return /* @__PURE__ */ jsxs13("div", { class: "page-strip", children: [
|
|
3574
|
-
/* @__PURE__ */
|
|
3575
|
-
/* @__PURE__ */
|
|
3836
|
+
/* @__PURE__ */ jsx13("span", { children: text }),
|
|
3837
|
+
/* @__PURE__ */ jsx13("button", { type: "button", class: "page-strip-view", onClick: onView, children: strings["page.strip.view"] })
|
|
3576
3838
|
] });
|
|
3577
3839
|
}
|
|
3578
3840
|
|
|
3579
3841
|
// src/widget/PagePeekPanel.tsx
|
|
3580
|
-
import { useEffect as useEffect8, useState as
|
|
3581
|
-
import { jsx as
|
|
3842
|
+
import { useEffect as useEffect8, useState as useState8 } from "preact/hooks";
|
|
3843
|
+
import { jsx as jsx14, jsxs as jsxs14 } from "preact/jsx-runtime";
|
|
3582
3844
|
function PagePeekPanel({
|
|
3583
3845
|
api,
|
|
3584
3846
|
externalId,
|
|
@@ -3589,8 +3851,8 @@ function PagePeekPanel({
|
|
|
3589
3851
|
onPickRow,
|
|
3590
3852
|
onSend
|
|
3591
3853
|
}) {
|
|
3592
|
-
const [rows, setRows] =
|
|
3593
|
-
const [failed, setFailed] =
|
|
3854
|
+
const [rows, setRows] = useState8(null);
|
|
3855
|
+
const [failed, setFailed] = useState8(false);
|
|
3594
3856
|
useEffect8(() => {
|
|
3595
3857
|
let cancelled = false;
|
|
3596
3858
|
const pagePath = currentPagePath(getCurrentPage !== void 0 ? { getCurrentPage } : {});
|
|
@@ -3607,11 +3869,11 @@ function PagePeekPanel({
|
|
|
3607
3869
|
};
|
|
3608
3870
|
}, [api, externalId]);
|
|
3609
3871
|
return /* @__PURE__ */ jsxs14("div", { class: "page-peek", role: "region", "aria-label": strings["page.peek.title"], children: [
|
|
3610
|
-
/* @__PURE__ */
|
|
3872
|
+
/* @__PURE__ */ jsx14("div", { class: "page-peek-title", children: strings["page.peek.title"] }),
|
|
3611
3873
|
rows === null && !failed && /* @__PURE__ */ jsxs14("div", { class: "page-peek-skeleton", "aria-hidden": "true", children: [
|
|
3612
|
-
/* @__PURE__ */
|
|
3874
|
+
/* @__PURE__ */ jsx14("div", {}),
|
|
3613
3875
|
" ",
|
|
3614
|
-
/* @__PURE__ */
|
|
3876
|
+
/* @__PURE__ */ jsx14("div", {})
|
|
3615
3877
|
] }),
|
|
3616
3878
|
rows !== null && rows.map((row) => /* @__PURE__ */ jsxs14(
|
|
3617
3879
|
"button",
|
|
@@ -3620,49 +3882,51 @@ function PagePeekPanel({
|
|
|
3620
3882
|
class: "page-peek-row",
|
|
3621
3883
|
onClick: () => onPickRow?.(row.id),
|
|
3622
3884
|
children: [
|
|
3623
|
-
/* @__PURE__ */
|
|
3624
|
-
/* @__PURE__ */
|
|
3885
|
+
/* @__PURE__ */ jsx14("span", { class: `page-peek-dot status-${row.status}`, "aria-hidden": "true" }),
|
|
3886
|
+
/* @__PURE__ */ jsx14("span", { class: "page-peek-desc", children: row.description })
|
|
3625
3887
|
]
|
|
3626
3888
|
},
|
|
3627
3889
|
row.id
|
|
3628
3890
|
)),
|
|
3629
3891
|
/* @__PURE__ */ jsxs14("div", { class: "page-peek-footer", children: [
|
|
3630
|
-
/* @__PURE__ */
|
|
3631
|
-
/* @__PURE__ */
|
|
3892
|
+
/* @__PURE__ */ jsx14("button", { type: "button", class: "page-peek-viewall", onClick: onViewAll, children: open === 1 ? strings["page.peek.viewAll.one"] : strings["page.peek.viewAll"].replace("{n}", String(open)) }),
|
|
3893
|
+
/* @__PURE__ */ jsx14("button", { type: "button", class: "page-peek-send", onClick: onSend, children: strings["fab.label"] })
|
|
3632
3894
|
] })
|
|
3633
3895
|
] });
|
|
3634
3896
|
}
|
|
3635
3897
|
|
|
3636
3898
|
// src/widget/pageSignal.ts
|
|
3637
|
-
import { useCallback, useEffect as useEffect9, useState as
|
|
3638
|
-
var
|
|
3899
|
+
import { useCallback, useEffect as useEffect9, useState as useState9 } from "preact/hooks";
|
|
3900
|
+
var TTL_MS2 = 6e4;
|
|
3639
3901
|
function computeOpenCount(kpis) {
|
|
3640
3902
|
const s = kpis.by_status;
|
|
3641
3903
|
return (s.new ?? 0) + (s.in_progress ?? 0) + (s.awaiting_validation ?? 0);
|
|
3642
3904
|
}
|
|
3643
|
-
var
|
|
3905
|
+
var cache2 = /* @__PURE__ */ new Map();
|
|
3644
3906
|
var inflight = /* @__PURE__ */ new Map();
|
|
3645
3907
|
function invalidatePageSignal() {
|
|
3646
|
-
|
|
3908
|
+
cache2.clear();
|
|
3647
3909
|
}
|
|
3648
|
-
function
|
|
3910
|
+
function fetchPageKpis(api, externalId, path) {
|
|
3649
3911
|
const key = `${externalId}|${path}`;
|
|
3650
|
-
const hit =
|
|
3651
|
-
if (hit && Date.now() - hit.fetchedAt <
|
|
3912
|
+
const hit = cache2.get(key);
|
|
3913
|
+
if (hit && Date.now() - hit.fetchedAt < TTL_MS2) return Promise.resolve(hit.kpis);
|
|
3652
3914
|
const pending = inflight.get(key);
|
|
3653
3915
|
if (pending) return pending;
|
|
3654
3916
|
const p = api.fetchBoardKpis(externalId, { pagePath: path }).then((kpis) => {
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
return open;
|
|
3917
|
+
cache2.set(key, { kpis, fetchedAt: Date.now() });
|
|
3918
|
+
return kpis;
|
|
3658
3919
|
}).finally(() => inflight.delete(key));
|
|
3659
3920
|
inflight.set(key, p);
|
|
3660
3921
|
return p;
|
|
3661
3922
|
}
|
|
3923
|
+
function fetchOpen(api, externalId, path) {
|
|
3924
|
+
return fetchPageKpis(api, externalId, path).then(computeOpenCount);
|
|
3925
|
+
}
|
|
3662
3926
|
function usePageOpenCount(opts) {
|
|
3663
3927
|
const { api, externalId, getCurrentPage, enabled } = opts;
|
|
3664
|
-
const [open, setOpen] =
|
|
3665
|
-
const [tick, setTick] =
|
|
3928
|
+
const [open, setOpen] = useState9(0);
|
|
3929
|
+
const [tick, setTick] = useState9(0);
|
|
3666
3930
|
const refresh = useCallback(() => {
|
|
3667
3931
|
invalidatePageSignal();
|
|
3668
3932
|
setTick((t) => t + 1);
|
|
@@ -5452,6 +5716,20 @@ button.report-detail-edit {
|
|
|
5452
5716
|
min-height: 0;
|
|
5453
5717
|
overflow: hidden;
|
|
5454
5718
|
}
|
|
5719
|
+
/* Stale-while-revalidate: a query change keeps the previous rows visible
|
|
5720
|
+
* (locally pre-filtered where the row data allows) and dims them while the
|
|
5721
|
+
* server confirms \u2014 the instant local response is the #352 "never inert"
|
|
5722
|
+
* guarantee, the dim is the "still confirming" affordance. */
|
|
5723
|
+
.board-view.is-stale .board-list,
|
|
5724
|
+
.board-view.is-stale .board-list-count,
|
|
5725
|
+
.board-view.is-stale .board-kpi-strip {
|
|
5726
|
+
opacity: 0.55;
|
|
5727
|
+
transition: opacity 0.15s ease;
|
|
5728
|
+
}
|
|
5729
|
+
.board-load-more {
|
|
5730
|
+
margin: 4px auto 8px;
|
|
5731
|
+
flex: 0 0 auto;
|
|
5732
|
+
}
|
|
5455
5733
|
.board-list-count {
|
|
5456
5734
|
font-size: var(--mfb-text-xs);
|
|
5457
5735
|
color: var(--mfb-text-muted);
|
|
@@ -5743,7 +6021,7 @@ button.report-detail-edit {
|
|
|
5743
6021
|
`;
|
|
5744
6022
|
|
|
5745
6023
|
// src/widget/mount.tsx
|
|
5746
|
-
import { Fragment as Fragment3, jsx as
|
|
6024
|
+
import { Fragment as Fragment3, jsx as jsx15, jsxs as jsxs15 } from "preact/jsx-runtime";
|
|
5747
6025
|
function computeFabVisible(opts, externalId, visibilityAllowed) {
|
|
5748
6026
|
if (!opts.showFAB) return false;
|
|
5749
6027
|
if (opts.getExternalId !== void 0 && !externalId) return false;
|
|
@@ -5773,7 +6051,7 @@ function mountWidget(options) {
|
|
|
5773
6051
|
function openWidget(externalId) {
|
|
5774
6052
|
rerender({ ...currentState, open: true, tab: "send" });
|
|
5775
6053
|
if (options.openToCurrentPageFeedback && options.api && externalId) {
|
|
5776
|
-
options.api
|
|
6054
|
+
fetchPageKpis(options.api, externalId, currentPagePath(options)).then((kpis) => {
|
|
5777
6055
|
if (kpis.total > 0 && currentState.open && currentState.tab === "send") {
|
|
5778
6056
|
rerender({ ...currentState, tab: "board" });
|
|
5779
6057
|
}
|
|
@@ -5841,7 +6119,7 @@ function mountWidget(options) {
|
|
|
5841
6119
|
enabled: pageActivityEnabled
|
|
5842
6120
|
});
|
|
5843
6121
|
const fabLabel = pageSignal.open === 1 ? options.strings["page.badge.aria.one"] : pageSignal.open > 0 ? options.strings["page.badge.aria"].replace("{n}", String(pageSignal.open)) : options.strings["fab.label"];
|
|
5844
|
-
const [visibilityAllowed, setVisibilityAllowed] =
|
|
6122
|
+
const [visibilityAllowed, setVisibilityAllowed] = useState10(
|
|
5845
6123
|
!options.requiresVisibilityCheck
|
|
5846
6124
|
);
|
|
5847
6125
|
useEffect10(() => {
|
|
@@ -5865,7 +6143,7 @@ function mountWidget(options) {
|
|
|
5865
6143
|
}, [externalId]);
|
|
5866
6144
|
const fabVisible = computeFabVisible(options, externalId, visibilityAllowed);
|
|
5867
6145
|
const showMineTab = Boolean(options.api && externalId);
|
|
5868
|
-
const [peekOpen, setPeekOpen] =
|
|
6146
|
+
const [peekOpen, setPeekOpen] = useState10(false);
|
|
5869
6147
|
const peekTimers = useRef7({});
|
|
5870
6148
|
const peekEnter = () => {
|
|
5871
6149
|
if (peekTimers.current.close) clearTimeout(peekTimers.current.close);
|
|
@@ -5889,7 +6167,7 @@ function mountWidget(options) {
|
|
|
5889
6167
|
onFocusOut: () => setPeekOpen(false),
|
|
5890
6168
|
onKeyDown: (e) => e.key === "Escape" && setPeekOpen(false),
|
|
5891
6169
|
children: [
|
|
5892
|
-
peekOpen && !state.open && pageActivityEnabled && pageSignal.open > 0 && options.api && externalId && /* @__PURE__ */
|
|
6170
|
+
peekOpen && !state.open && pageActivityEnabled && pageSignal.open > 0 && options.api && externalId && /* @__PURE__ */ jsx15(
|
|
5893
6171
|
PagePeekPanel,
|
|
5894
6172
|
{
|
|
5895
6173
|
api: options.api,
|
|
@@ -5913,7 +6191,7 @@ function mountWidget(options) {
|
|
|
5913
6191
|
}
|
|
5914
6192
|
}
|
|
5915
6193
|
),
|
|
5916
|
-
/* @__PURE__ */
|
|
6194
|
+
/* @__PURE__ */ jsx15(
|
|
5917
6195
|
Fab,
|
|
5918
6196
|
{
|
|
5919
6197
|
label: fabLabel,
|
|
@@ -5945,11 +6223,11 @@ function mountWidget(options) {
|
|
|
5945
6223
|
closeLabel: options.strings["form.close"],
|
|
5946
6224
|
expanded: state.tab === "board",
|
|
5947
6225
|
children: [
|
|
5948
|
-
state.confirmingDiscard && /* @__PURE__ */
|
|
5949
|
-
/* @__PURE__ */
|
|
5950
|
-
/* @__PURE__ */
|
|
6226
|
+
state.confirmingDiscard && /* @__PURE__ */ jsx15("div", { class: "discard-confirm", role: "alertdialog", "aria-modal": "true", children: /* @__PURE__ */ jsxs15("div", { class: "discard-confirm-card", children: [
|
|
6227
|
+
/* @__PURE__ */ jsx15("p", { class: "discard-confirm-title", children: options.strings["form.discard.title"] }),
|
|
6228
|
+
/* @__PURE__ */ jsx15("p", { class: "discard-confirm-body", children: options.strings["form.discard.body"] }),
|
|
5951
6229
|
/* @__PURE__ */ jsxs15("div", { class: "discard-confirm-actions", children: [
|
|
5952
|
-
/* @__PURE__ */
|
|
6230
|
+
/* @__PURE__ */ jsx15(
|
|
5953
6231
|
"button",
|
|
5954
6232
|
{
|
|
5955
6233
|
type: "button",
|
|
@@ -5958,7 +6236,7 @@ function mountWidget(options) {
|
|
|
5958
6236
|
children: options.strings["form.discard.keep"]
|
|
5959
6237
|
}
|
|
5960
6238
|
),
|
|
5961
|
-
/* @__PURE__ */
|
|
6239
|
+
/* @__PURE__ */ jsx15(
|
|
5962
6240
|
"button",
|
|
5963
6241
|
{
|
|
5964
6242
|
type: "button",
|
|
@@ -5980,7 +6258,7 @@ function mountWidget(options) {
|
|
|
5980
6258
|
] })
|
|
5981
6259
|
] }) }),
|
|
5982
6260
|
showMineTab && /* @__PURE__ */ jsxs15("div", { class: "tab-strip", role: "tablist", children: [
|
|
5983
|
-
/* @__PURE__ */
|
|
6261
|
+
/* @__PURE__ */ jsx15(
|
|
5984
6262
|
"button",
|
|
5985
6263
|
{
|
|
5986
6264
|
type: "button",
|
|
@@ -5991,7 +6269,7 @@ function mountWidget(options) {
|
|
|
5991
6269
|
children: options.strings["tab.send"]
|
|
5992
6270
|
}
|
|
5993
6271
|
),
|
|
5994
|
-
/* @__PURE__ */
|
|
6272
|
+
/* @__PURE__ */ jsx15(
|
|
5995
6273
|
"button",
|
|
5996
6274
|
{
|
|
5997
6275
|
type: "button",
|
|
@@ -6002,7 +6280,7 @@ function mountWidget(options) {
|
|
|
6002
6280
|
children: options.strings["tab.mine"]
|
|
6003
6281
|
}
|
|
6004
6282
|
),
|
|
6005
|
-
/* @__PURE__ */
|
|
6283
|
+
/* @__PURE__ */ jsx15(
|
|
6006
6284
|
"button",
|
|
6007
6285
|
{
|
|
6008
6286
|
type: "button",
|
|
@@ -6013,7 +6291,7 @@ function mountWidget(options) {
|
|
|
6013
6291
|
children: options.strings["tab.changelog"]
|
|
6014
6292
|
}
|
|
6015
6293
|
),
|
|
6016
|
-
/* @__PURE__ */
|
|
6294
|
+
/* @__PURE__ */ jsx15(
|
|
6017
6295
|
"button",
|
|
6018
6296
|
{
|
|
6019
6297
|
type: "button",
|
|
@@ -6026,7 +6304,7 @@ function mountWidget(options) {
|
|
|
6026
6304
|
)
|
|
6027
6305
|
] }),
|
|
6028
6306
|
state.tab === "send" && /* @__PURE__ */ jsxs15(Fragment3, { children: [
|
|
6029
|
-
showMineTab && pageActivityEnabled && /* @__PURE__ */
|
|
6307
|
+
showMineTab && pageActivityEnabled && /* @__PURE__ */ jsx15(
|
|
6030
6308
|
PageActivityStrip,
|
|
6031
6309
|
{
|
|
6032
6310
|
open: pageSignal.open,
|
|
@@ -6034,7 +6312,7 @@ function mountWidget(options) {
|
|
|
6034
6312
|
onView: () => rerender(clearSelected({ ...currentState, tab: "board" }))
|
|
6035
6313
|
}
|
|
6036
6314
|
),
|
|
6037
|
-
/* @__PURE__ */
|
|
6315
|
+
/* @__PURE__ */ jsx15(
|
|
6038
6316
|
Form,
|
|
6039
6317
|
{
|
|
6040
6318
|
strings: options.strings,
|
|
@@ -6051,7 +6329,7 @@ function mountWidget(options) {
|
|
|
6051
6329
|
}
|
|
6052
6330
|
)
|
|
6053
6331
|
] }),
|
|
6054
|
-
state.tab === "mine" && options.api && externalId && !state.selectedReportId && /* @__PURE__ */
|
|
6332
|
+
state.tab === "mine" && options.api && externalId && !state.selectedReportId && /* @__PURE__ */ jsx15(
|
|
6055
6333
|
MineList,
|
|
6056
6334
|
{
|
|
6057
6335
|
api: options.api,
|
|
@@ -6061,7 +6339,7 @@ function mountWidget(options) {
|
|
|
6061
6339
|
onOpenSeq: openSeq
|
|
6062
6340
|
}
|
|
6063
6341
|
),
|
|
6064
|
-
(state.tab === "mine" || state.tab === "changelog") && options.api && externalId && state.selectedReportId && /* @__PURE__ */
|
|
6342
|
+
(state.tab === "mine" || state.tab === "changelog") && options.api && externalId && state.selectedReportId && /* @__PURE__ */ jsx15(
|
|
6065
6343
|
ReportDetailView,
|
|
6066
6344
|
{
|
|
6067
6345
|
api: options.api,
|
|
@@ -6073,7 +6351,7 @@ function mountWidget(options) {
|
|
|
6073
6351
|
...buildPermalink && { buildPermalink }
|
|
6074
6352
|
}
|
|
6075
6353
|
),
|
|
6076
|
-
state.tab === "changelog" && options.api && externalId && !state.selectedReportId && /* @__PURE__ */
|
|
6354
|
+
state.tab === "changelog" && options.api && externalId && !state.selectedReportId && /* @__PURE__ */ jsx15(
|
|
6077
6355
|
ChangelogList,
|
|
6078
6356
|
{
|
|
6079
6357
|
api: options.api,
|
|
@@ -6084,7 +6362,7 @@ function mountWidget(options) {
|
|
|
6084
6362
|
),
|
|
6085
6363
|
state.tab === "board" && options.api && externalId && // BoardView owns its own master/detail navigation — no
|
|
6086
6364
|
// selectedReportId routing through the modal-level state.
|
|
6087
|
-
/* @__PURE__ */
|
|
6365
|
+
/* @__PURE__ */ jsx15(
|
|
6088
6366
|
BoardView,
|
|
6089
6367
|
{
|
|
6090
6368
|
api: options.api,
|
|
@@ -6205,8 +6483,8 @@ function createFeedback(config) {
|
|
|
6205
6483
|
capture_method: captureMethod,
|
|
6206
6484
|
technical_context
|
|
6207
6485
|
};
|
|
6208
|
-
if ("0.
|
|
6209
|
-
payload.widget_version = "0.
|
|
6486
|
+
if ("0.33.0") {
|
|
6487
|
+
payload.widget_version = "0.33.0";
|
|
6210
6488
|
}
|
|
6211
6489
|
if (manualScreenshots?.length) {
|
|
6212
6490
|
payload.screenshots = manualScreenshots;
|
|
@@ -6332,4 +6610,4 @@ function createFeedback(config) {
|
|
|
6332
6610
|
export {
|
|
6333
6611
|
createFeedback
|
|
6334
6612
|
};
|
|
6335
|
-
//# sourceMappingURL=chunk-
|
|
6613
|
+
//# sourceMappingURL=chunk-RR6UB75B.mjs.map
|