@bbearai/react 0.5.0 → 0.5.2
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/index.js +568 -72
- package/dist/index.mjs +542 -46
- package/package.json +4 -2
package/dist/index.mjs
CHANGED
|
@@ -304,11 +304,80 @@ function BugBearProvider({ config, children, enabled = true }) {
|
|
|
304
304
|
}
|
|
305
305
|
|
|
306
306
|
// src/BugBearPanel.tsx
|
|
307
|
-
import { useState as useState11, useRef as
|
|
307
|
+
import { useState as useState11, useRef as useRef4, useEffect as useEffect11, useCallback as useCallback6 } from "react";
|
|
308
308
|
import { createPortal } from "react-dom";
|
|
309
309
|
|
|
310
310
|
// src/widget/navigation.ts
|
|
311
|
-
import { useReducer } from "react";
|
|
311
|
+
import { useReducer, useEffect as useEffect2, useRef as useRef2 } from "react";
|
|
312
|
+
var NAV_STORAGE_KEY = "bugbear-nav-screen";
|
|
313
|
+
function serializeScreen(screen) {
|
|
314
|
+
switch (screen.name) {
|
|
315
|
+
case "HOME":
|
|
316
|
+
case "TEST_LIST":
|
|
317
|
+
case "MESSAGE_LIST":
|
|
318
|
+
case "COMPOSE_MESSAGE":
|
|
319
|
+
case "PROFILE":
|
|
320
|
+
case "REPORT_SUCCESS":
|
|
321
|
+
return JSON.stringify({ name: screen.name });
|
|
322
|
+
case "TEST_DETAIL":
|
|
323
|
+
return JSON.stringify({ name: screen.name, testId: screen.testId });
|
|
324
|
+
case "REPORT":
|
|
325
|
+
return JSON.stringify({ name: screen.name });
|
|
326
|
+
case "ISSUE_LIST":
|
|
327
|
+
return JSON.stringify({ name: screen.name, category: screen.category });
|
|
328
|
+
// Complex screens — save their parent list instead
|
|
329
|
+
case "THREAD_DETAIL":
|
|
330
|
+
return JSON.stringify({ name: "MESSAGE_LIST" });
|
|
331
|
+
case "ISSUE_DETAIL":
|
|
332
|
+
return JSON.stringify({ name: "ISSUE_LIST", category: "open" });
|
|
333
|
+
case "TEST_FEEDBACK":
|
|
334
|
+
return JSON.stringify({ name: "TEST_LIST" });
|
|
335
|
+
default:
|
|
336
|
+
return null;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
function deserializeScreen(json) {
|
|
340
|
+
try {
|
|
341
|
+
const parsed = JSON.parse(json);
|
|
342
|
+
if (!parsed || !parsed.name) return null;
|
|
343
|
+
switch (parsed.name) {
|
|
344
|
+
case "HOME":
|
|
345
|
+
return { name: "HOME" };
|
|
346
|
+
case "TEST_LIST":
|
|
347
|
+
return { name: "TEST_LIST" };
|
|
348
|
+
case "MESSAGE_LIST":
|
|
349
|
+
return { name: "MESSAGE_LIST" };
|
|
350
|
+
case "COMPOSE_MESSAGE":
|
|
351
|
+
return { name: "COMPOSE_MESSAGE" };
|
|
352
|
+
case "PROFILE":
|
|
353
|
+
return { name: "PROFILE" };
|
|
354
|
+
case "REPORT_SUCCESS":
|
|
355
|
+
return { name: "REPORT_SUCCESS" };
|
|
356
|
+
case "TEST_DETAIL":
|
|
357
|
+
return { name: "TEST_DETAIL", testId: parsed.testId };
|
|
358
|
+
case "REPORT":
|
|
359
|
+
return { name: "REPORT" };
|
|
360
|
+
case "ISSUE_LIST":
|
|
361
|
+
return { name: "ISSUE_LIST", category: parsed.category || "open" };
|
|
362
|
+
default:
|
|
363
|
+
return null;
|
|
364
|
+
}
|
|
365
|
+
} catch {
|
|
366
|
+
return null;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
function getInitialScreen() {
|
|
370
|
+
if (typeof window === "undefined") return { name: "HOME" };
|
|
371
|
+
try {
|
|
372
|
+
const saved = localStorage.getItem(NAV_STORAGE_KEY);
|
|
373
|
+
if (saved) {
|
|
374
|
+
const screen = deserializeScreen(saved);
|
|
375
|
+
if (screen) return screen;
|
|
376
|
+
}
|
|
377
|
+
} catch {
|
|
378
|
+
}
|
|
379
|
+
return { name: "HOME" };
|
|
380
|
+
}
|
|
312
381
|
function navReducer(state, action) {
|
|
313
382
|
switch (action.type) {
|
|
314
383
|
case "PUSH":
|
|
@@ -324,9 +393,21 @@ function navReducer(state, action) {
|
|
|
324
393
|
}
|
|
325
394
|
}
|
|
326
395
|
function useNavigation() {
|
|
327
|
-
const
|
|
396
|
+
const initialScreen = useRef2(getInitialScreen());
|
|
397
|
+
const [state, dispatch] = useReducer(navReducer, { stack: [initialScreen.current] });
|
|
398
|
+
const currentScreen = state.stack[state.stack.length - 1];
|
|
399
|
+
useEffect2(() => {
|
|
400
|
+
if (typeof window === "undefined") return;
|
|
401
|
+
try {
|
|
402
|
+
const serialized = serializeScreen(currentScreen);
|
|
403
|
+
if (serialized) {
|
|
404
|
+
localStorage.setItem(NAV_STORAGE_KEY, serialized);
|
|
405
|
+
}
|
|
406
|
+
} catch {
|
|
407
|
+
}
|
|
408
|
+
}, [currentScreen]);
|
|
328
409
|
return {
|
|
329
|
-
currentScreen
|
|
410
|
+
currentScreen,
|
|
330
411
|
canGoBack: state.stack.length > 1,
|
|
331
412
|
push: (screen) => dispatch({ type: "PUSH", screen }),
|
|
332
413
|
pop: () => dispatch({ type: "POP" }),
|
|
@@ -420,8 +501,38 @@ function getThreadTypeIcon(type) {
|
|
|
420
501
|
}
|
|
421
502
|
}
|
|
422
503
|
|
|
504
|
+
// src/widget/useScreenCapture.ts
|
|
505
|
+
import { domToBlob } from "modern-screenshot";
|
|
506
|
+
async function capturePageScreenshot(options = {}) {
|
|
507
|
+
const { excludeElement, timeout = 3e3 } = options;
|
|
508
|
+
try {
|
|
509
|
+
const blob = await Promise.race([
|
|
510
|
+
domToBlob(document.documentElement, {
|
|
511
|
+
filter: (node) => {
|
|
512
|
+
if (excludeElement && node === excludeElement) return false;
|
|
513
|
+
return true;
|
|
514
|
+
},
|
|
515
|
+
scale: 1,
|
|
516
|
+
quality: 0.85
|
|
517
|
+
}),
|
|
518
|
+
new Promise(
|
|
519
|
+
(_, reject) => setTimeout(() => reject(new Error("Screenshot capture timed out")), timeout)
|
|
520
|
+
)
|
|
521
|
+
]);
|
|
522
|
+
if (!blob) return null;
|
|
523
|
+
return new File(
|
|
524
|
+
[blob],
|
|
525
|
+
`screenshot-auto-${Date.now()}.png`,
|
|
526
|
+
{ type: "image/png" }
|
|
527
|
+
);
|
|
528
|
+
} catch (err) {
|
|
529
|
+
console.warn("BugBear: Auto-capture failed, user can attach manually", err);
|
|
530
|
+
return null;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
423
534
|
// src/widget/screens/HomeScreen.tsx
|
|
424
|
-
import { useEffect as
|
|
535
|
+
import { useEffect as useEffect3 } from "react";
|
|
425
536
|
|
|
426
537
|
// src/widget/Skeleton.tsx
|
|
427
538
|
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
@@ -562,9 +673,9 @@ function MessageListScreenSkeleton() {
|
|
|
562
673
|
|
|
563
674
|
// src/widget/screens/HomeScreen.tsx
|
|
564
675
|
import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
565
|
-
function HomeScreen({ nav }) {
|
|
566
|
-
const { assignments, unreadCount, threads, refreshAssignments, refreshThreads, issueCounts, refreshIssueCounts, isLoading } = useBugBear();
|
|
567
|
-
|
|
676
|
+
function HomeScreen({ nav, onReportBug }) {
|
|
677
|
+
const { assignments, unreadCount, threads, refreshAssignments, refreshThreads, issueCounts, refreshIssueCounts, dashboardUrl, isLoading } = useBugBear();
|
|
678
|
+
useEffect3(() => {
|
|
568
679
|
refreshAssignments();
|
|
569
680
|
refreshThreads();
|
|
570
681
|
refreshIssueCounts();
|
|
@@ -751,9 +862,11 @@ function HomeScreen({ nav }) {
|
|
|
751
862
|
{
|
|
752
863
|
role: "button",
|
|
753
864
|
tabIndex: 0,
|
|
754
|
-
onClick: () => nav.push({ name: "REPORT", prefill: { type: "bug" } }),
|
|
865
|
+
onClick: () => onReportBug ? onReportBug() : nav.push({ name: "REPORT", prefill: { type: "bug" } }),
|
|
755
866
|
onKeyDown: (e) => {
|
|
756
|
-
if (e.key === "Enter" || e.key === " ")
|
|
867
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
868
|
+
onReportBug ? onReportBug() : nav.push({ name: "REPORT", prefill: { type: "bug" } });
|
|
869
|
+
}
|
|
757
870
|
},
|
|
758
871
|
style: {
|
|
759
872
|
backgroundColor: colors.card,
|
|
@@ -968,6 +1081,33 @@ function HomeScreen({ nav }) {
|
|
|
968
1081
|
" tests completed"
|
|
969
1082
|
] })
|
|
970
1083
|
] }),
|
|
1084
|
+
dashboardUrl && /* @__PURE__ */ jsxs2(
|
|
1085
|
+
"a",
|
|
1086
|
+
{
|
|
1087
|
+
href: dashboardUrl,
|
|
1088
|
+
target: "_blank",
|
|
1089
|
+
rel: "noopener noreferrer",
|
|
1090
|
+
style: {
|
|
1091
|
+
display: "flex",
|
|
1092
|
+
alignItems: "center",
|
|
1093
|
+
backgroundColor: colors.card,
|
|
1094
|
+
border: `1px solid ${colors.border}`,
|
|
1095
|
+
borderRadius: 12,
|
|
1096
|
+
padding: 14,
|
|
1097
|
+
marginBottom: 16,
|
|
1098
|
+
textDecoration: "none",
|
|
1099
|
+
cursor: "pointer"
|
|
1100
|
+
},
|
|
1101
|
+
children: [
|
|
1102
|
+
/* @__PURE__ */ jsx3("span", { style: { fontSize: 22, marginRight: 12 }, children: "\u{1F310}" }),
|
|
1103
|
+
/* @__PURE__ */ jsxs2("span", { style: { flex: 1 }, children: [
|
|
1104
|
+
/* @__PURE__ */ jsx3("span", { style: { display: "block", fontSize: 14, fontWeight: 600, color: colors.textPrimary }, children: "Open Dashboard" }),
|
|
1105
|
+
/* @__PURE__ */ jsx3("span", { style: { display: "block", fontSize: 12, color: colors.textMuted, marginTop: 2 }, children: "View analytics, history & more" })
|
|
1106
|
+
] }),
|
|
1107
|
+
/* @__PURE__ */ jsx3("span", { style: { fontSize: 16, color: colors.textMuted, marginLeft: 8 }, children: "\u2192" })
|
|
1108
|
+
]
|
|
1109
|
+
}
|
|
1110
|
+
),
|
|
971
1111
|
/* @__PURE__ */ jsx3("div", { style: { display: "flex", justifyContent: "center", padding: "8px 0" }, children: /* @__PURE__ */ jsx3(
|
|
972
1112
|
"button",
|
|
973
1113
|
{
|
|
@@ -992,7 +1132,7 @@ function HomeScreen({ nav }) {
|
|
|
992
1132
|
}
|
|
993
1133
|
|
|
994
1134
|
// src/widget/screens/TestDetailScreen.tsx
|
|
995
|
-
import { useState as useState2, useEffect as
|
|
1135
|
+
import { useState as useState2, useEffect as useEffect4, useCallback as useCallback2 } from "react";
|
|
996
1136
|
import { Fragment, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
997
1137
|
function TestDetailScreen({ testId, nav }) {
|
|
998
1138
|
const { client, assignments, currentAssignment, refreshAssignments, onNavigate } = useBugBear();
|
|
@@ -1006,12 +1146,12 @@ function TestDetailScreen({ testId, nav }) {
|
|
|
1006
1146
|
const [skipNotes, setSkipNotes] = useState2("");
|
|
1007
1147
|
const [skipping, setSkipping] = useState2(false);
|
|
1008
1148
|
const [isSubmitting, setIsSubmitting] = useState2(false);
|
|
1009
|
-
|
|
1149
|
+
useEffect4(() => {
|
|
1010
1150
|
setCriteriaResults({});
|
|
1011
1151
|
setShowSteps(true);
|
|
1012
1152
|
setShowDetails(false);
|
|
1013
1153
|
}, [displayedAssignment?.id]);
|
|
1014
|
-
|
|
1154
|
+
useEffect4(() => {
|
|
1015
1155
|
const active = displayedAssignment?.status === "in_progress" ? displayedAssignment : null;
|
|
1016
1156
|
if (!active?.startedAt) {
|
|
1017
1157
|
setAssignmentElapsedTime(0);
|
|
@@ -1058,6 +1198,37 @@ function TestDetailScreen({ testId, nav }) {
|
|
|
1058
1198
|
setIsSubmitting(false);
|
|
1059
1199
|
}
|
|
1060
1200
|
}, [client, displayedAssignment, refreshAssignments, nav, isSubmitting]);
|
|
1201
|
+
const handleReopen = useCallback2(async () => {
|
|
1202
|
+
if (!client || !displayedAssignment || isSubmitting) return;
|
|
1203
|
+
setIsSubmitting(true);
|
|
1204
|
+
try {
|
|
1205
|
+
await client.reopenAssignment(displayedAssignment.id);
|
|
1206
|
+
await refreshAssignments();
|
|
1207
|
+
} finally {
|
|
1208
|
+
setIsSubmitting(false);
|
|
1209
|
+
}
|
|
1210
|
+
}, [client, displayedAssignment, refreshAssignments, isSubmitting]);
|
|
1211
|
+
const handleChangeResult = useCallback2(async (newStatus) => {
|
|
1212
|
+
if (!client || !displayedAssignment || isSubmitting) return;
|
|
1213
|
+
setIsSubmitting(true);
|
|
1214
|
+
try {
|
|
1215
|
+
await client.reopenAssignment(displayedAssignment.id);
|
|
1216
|
+
await client.updateAssignmentStatus(displayedAssignment.id, newStatus);
|
|
1217
|
+
await refreshAssignments();
|
|
1218
|
+
if (newStatus === "failed") {
|
|
1219
|
+
nav.replace({
|
|
1220
|
+
name: "REPORT",
|
|
1221
|
+
prefill: {
|
|
1222
|
+
type: "test_fail",
|
|
1223
|
+
assignmentId: displayedAssignment.id,
|
|
1224
|
+
testCaseId: displayedAssignment.testCase.id
|
|
1225
|
+
}
|
|
1226
|
+
});
|
|
1227
|
+
}
|
|
1228
|
+
} finally {
|
|
1229
|
+
setIsSubmitting(false);
|
|
1230
|
+
}
|
|
1231
|
+
}, [client, displayedAssignment, refreshAssignments, nav, isSubmitting]);
|
|
1061
1232
|
const handleSkip = useCallback2(async () => {
|
|
1062
1233
|
if (!client || !displayedAssignment || !selectedSkipReason) return;
|
|
1063
1234
|
setSkipping(true);
|
|
@@ -1565,6 +1736,11 @@ function TestDetailScreen({ testId, nav }) {
|
|
|
1565
1736
|
]
|
|
1566
1737
|
}
|
|
1567
1738
|
),
|
|
1739
|
+
testCase.track && /* @__PURE__ */ jsxs3("div", { style: { fontSize: 12, color: colors.textSecondary, marginBottom: 6 }, children: [
|
|
1740
|
+
testCase.track.icon,
|
|
1741
|
+
" ",
|
|
1742
|
+
testCase.track.name
|
|
1743
|
+
] }),
|
|
1568
1744
|
testCase.description && /* @__PURE__ */ jsx4("div", { style: { fontSize: 13, color: colors.textSecondary, lineHeight: "18px" }, children: testCase.description }),
|
|
1569
1745
|
testCase.group && /* @__PURE__ */ jsx4("div", { style: { marginTop: 8 }, children: /* @__PURE__ */ jsxs3("span", { style: { fontSize: 12, color: colors.textMuted }, children: [
|
|
1570
1746
|
"\u{1F4C1} ",
|
|
@@ -1573,7 +1749,113 @@ function TestDetailScreen({ testId, nav }) {
|
|
|
1573
1749
|
]
|
|
1574
1750
|
}
|
|
1575
1751
|
),
|
|
1576
|
-
|
|
1752
|
+
displayedAssignment.status === "passed" || displayedAssignment.status === "failed" || displayedAssignment.status === "skipped" || displayedAssignment.status === "blocked" ? /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
1753
|
+
/* @__PURE__ */ jsxs3(
|
|
1754
|
+
"div",
|
|
1755
|
+
{
|
|
1756
|
+
style: {
|
|
1757
|
+
display: "flex",
|
|
1758
|
+
alignItems: "center",
|
|
1759
|
+
justifyContent: "center",
|
|
1760
|
+
gap: 6,
|
|
1761
|
+
padding: "8px 12px",
|
|
1762
|
+
borderRadius: 8,
|
|
1763
|
+
marginTop: 8,
|
|
1764
|
+
marginBottom: 8,
|
|
1765
|
+
backgroundColor: displayedAssignment.status === "passed" ? colors.greenDark : displayedAssignment.status === "failed" ? colors.redDark : colors.card,
|
|
1766
|
+
border: `1px solid ${displayedAssignment.status === "passed" ? colors.green : displayedAssignment.status === "failed" ? colors.red : colors.border}`
|
|
1767
|
+
},
|
|
1768
|
+
children: [
|
|
1769
|
+
/* @__PURE__ */ jsx4("span", { style: { fontSize: 14 }, children: displayedAssignment.status === "passed" ? "\u2705" : displayedAssignment.status === "failed" ? "\u274C" : displayedAssignment.status === "skipped" ? "\u23ED" : "\u{1F6AB}" }),
|
|
1770
|
+
/* @__PURE__ */ jsxs3(
|
|
1771
|
+
"span",
|
|
1772
|
+
{
|
|
1773
|
+
style: {
|
|
1774
|
+
fontSize: 13,
|
|
1775
|
+
fontWeight: 600,
|
|
1776
|
+
color: displayedAssignment.status === "passed" ? colors.green : displayedAssignment.status === "failed" ? "#fca5a5" : colors.textSecondary
|
|
1777
|
+
},
|
|
1778
|
+
children: [
|
|
1779
|
+
"Marked as ",
|
|
1780
|
+
displayedAssignment.status.charAt(0).toUpperCase() + displayedAssignment.status.slice(1)
|
|
1781
|
+
]
|
|
1782
|
+
}
|
|
1783
|
+
)
|
|
1784
|
+
]
|
|
1785
|
+
}
|
|
1786
|
+
),
|
|
1787
|
+
/* @__PURE__ */ jsxs3("div", { style: { display: "flex", gap: 10, marginTop: 4 }, children: [
|
|
1788
|
+
/* @__PURE__ */ jsx4(
|
|
1789
|
+
"button",
|
|
1790
|
+
{
|
|
1791
|
+
type: "button",
|
|
1792
|
+
onClick: handleReopen,
|
|
1793
|
+
disabled: isSubmitting,
|
|
1794
|
+
style: {
|
|
1795
|
+
flex: 1,
|
|
1796
|
+
paddingTop: 14,
|
|
1797
|
+
paddingBottom: 14,
|
|
1798
|
+
borderRadius: 12,
|
|
1799
|
+
textAlign: "center",
|
|
1800
|
+
backgroundColor: colors.card,
|
|
1801
|
+
border: `1px solid ${colors.blue}`,
|
|
1802
|
+
cursor: isSubmitting ? "not-allowed" : "pointer",
|
|
1803
|
+
fontSize: 14,
|
|
1804
|
+
fontWeight: 600,
|
|
1805
|
+
color: colors.blue,
|
|
1806
|
+
opacity: isSubmitting ? 0.5 : 1
|
|
1807
|
+
},
|
|
1808
|
+
children: isSubmitting ? "Reopening..." : "\u{1F504} Reopen Test"
|
|
1809
|
+
}
|
|
1810
|
+
),
|
|
1811
|
+
displayedAssignment.status === "passed" && /* @__PURE__ */ jsx4(
|
|
1812
|
+
"button",
|
|
1813
|
+
{
|
|
1814
|
+
type: "button",
|
|
1815
|
+
onClick: () => handleChangeResult("failed"),
|
|
1816
|
+
disabled: isSubmitting,
|
|
1817
|
+
style: {
|
|
1818
|
+
flex: 1,
|
|
1819
|
+
paddingTop: 14,
|
|
1820
|
+
paddingBottom: 14,
|
|
1821
|
+
borderRadius: 12,
|
|
1822
|
+
textAlign: "center",
|
|
1823
|
+
backgroundColor: colors.redDark,
|
|
1824
|
+
border: `1px solid ${colors.red}`,
|
|
1825
|
+
cursor: isSubmitting ? "not-allowed" : "pointer",
|
|
1826
|
+
fontSize: 14,
|
|
1827
|
+
fontWeight: 600,
|
|
1828
|
+
color: "#fca5a5",
|
|
1829
|
+
opacity: isSubmitting ? 0.5 : 1
|
|
1830
|
+
},
|
|
1831
|
+
children: "Change to Fail"
|
|
1832
|
+
}
|
|
1833
|
+
),
|
|
1834
|
+
displayedAssignment.status === "failed" && /* @__PURE__ */ jsx4(
|
|
1835
|
+
"button",
|
|
1836
|
+
{
|
|
1837
|
+
type: "button",
|
|
1838
|
+
onClick: () => handleChangeResult("passed"),
|
|
1839
|
+
disabled: isSubmitting,
|
|
1840
|
+
style: {
|
|
1841
|
+
flex: 1,
|
|
1842
|
+
paddingTop: 14,
|
|
1843
|
+
paddingBottom: 14,
|
|
1844
|
+
borderRadius: 12,
|
|
1845
|
+
textAlign: "center",
|
|
1846
|
+
backgroundColor: colors.greenDark,
|
|
1847
|
+
border: `1px solid ${colors.green}`,
|
|
1848
|
+
cursor: isSubmitting ? "not-allowed" : "pointer",
|
|
1849
|
+
fontSize: 14,
|
|
1850
|
+
fontWeight: 600,
|
|
1851
|
+
color: "#86efac",
|
|
1852
|
+
opacity: isSubmitting ? 0.5 : 1
|
|
1853
|
+
},
|
|
1854
|
+
children: "Change to Pass"
|
|
1855
|
+
}
|
|
1856
|
+
)
|
|
1857
|
+
] })
|
|
1858
|
+
] }) : /* @__PURE__ */ jsxs3("div", { style: { display: "flex", gap: 10, marginTop: 8 }, children: [
|
|
1577
1859
|
/* @__PURE__ */ jsx4(
|
|
1578
1860
|
"button",
|
|
1579
1861
|
{
|
|
@@ -1792,10 +2074,11 @@ function TestDetailScreen({ testId, nav }) {
|
|
|
1792
2074
|
import { useState as useState3, useMemo, useCallback as useCallback3 } from "react";
|
|
1793
2075
|
import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1794
2076
|
function TestListScreen({ nav }) {
|
|
1795
|
-
const { assignments, currentAssignment, refreshAssignments, isLoading } = useBugBear();
|
|
2077
|
+
const { assignments, currentAssignment, refreshAssignments, dashboardUrl, isLoading } = useBugBear();
|
|
1796
2078
|
const [filter, setFilter] = useState3("all");
|
|
1797
2079
|
const [roleFilter, setRoleFilter] = useState3(null);
|
|
1798
2080
|
const [trackFilter, setTrackFilter] = useState3(null);
|
|
2081
|
+
const [platformFilter, setPlatformFilter] = useState3("web");
|
|
1799
2082
|
const [searchQuery, setSearchQuery] = useState3("");
|
|
1800
2083
|
const [sortMode, setSortMode] = useState3("priority");
|
|
1801
2084
|
const [collapsedFolders, setCollapsedFolders] = useState3(/* @__PURE__ */ new Set());
|
|
@@ -1873,6 +2156,7 @@ function TestListScreen({ nav }) {
|
|
|
1873
2156
|
});
|
|
1874
2157
|
}, []);
|
|
1875
2158
|
const filterAssignment = useCallback3((a) => {
|
|
2159
|
+
if (platformFilter && a.testCase.platforms && !a.testCase.platforms.includes(platformFilter)) return false;
|
|
1876
2160
|
if (roleFilter && a.testCase.role?.id !== roleFilter) return false;
|
|
1877
2161
|
if (trackFilter && a.testCase.track?.id !== trackFilter) return false;
|
|
1878
2162
|
if (searchQuery) {
|
|
@@ -1885,7 +2169,7 @@ function TestListScreen({ nav }) {
|
|
|
1885
2169
|
if (filter === "done") return a.status === "passed";
|
|
1886
2170
|
if (filter === "reopened") return a.status === "failed";
|
|
1887
2171
|
return true;
|
|
1888
|
-
}, [roleFilter, trackFilter, searchQuery, filter]);
|
|
2172
|
+
}, [platformFilter, roleFilter, trackFilter, searchQuery, filter]);
|
|
1889
2173
|
const pendingCount = assignments.filter(
|
|
1890
2174
|
(a) => a.status === "pending" || a.status === "in_progress"
|
|
1891
2175
|
).length;
|
|
@@ -2019,6 +2303,61 @@ function TestListScreen({ nav }) {
|
|
|
2019
2303
|
}
|
|
2020
2304
|
}
|
|
2021
2305
|
) }),
|
|
2306
|
+
/* @__PURE__ */ jsxs4("div", { style: { display: "flex", gap: 4, marginBottom: 8 }, children: [
|
|
2307
|
+
/* @__PURE__ */ jsx5(
|
|
2308
|
+
"button",
|
|
2309
|
+
{
|
|
2310
|
+
type: "button",
|
|
2311
|
+
onClick: () => setPlatformFilter(null),
|
|
2312
|
+
style: {
|
|
2313
|
+
padding: "3px 8px",
|
|
2314
|
+
borderRadius: 6,
|
|
2315
|
+
backgroundColor: !platformFilter ? colors.card : "transparent",
|
|
2316
|
+
border: !platformFilter ? `1px solid ${colors.border}` : "1px solid transparent",
|
|
2317
|
+
cursor: "pointer",
|
|
2318
|
+
fontSize: 11,
|
|
2319
|
+
color: !platformFilter ? colors.textPrimary : colors.textMuted,
|
|
2320
|
+
fontWeight: !platformFilter ? 600 : 400,
|
|
2321
|
+
whiteSpace: "nowrap"
|
|
2322
|
+
},
|
|
2323
|
+
children: "All"
|
|
2324
|
+
}
|
|
2325
|
+
),
|
|
2326
|
+
[
|
|
2327
|
+
{ key: "web", label: "Web", icon: "\u{1F310}" },
|
|
2328
|
+
{ key: "ios", label: "iOS", icon: "\u{1F4F1}" },
|
|
2329
|
+
{ key: "android", label: "Android", icon: "\u{1F916}" }
|
|
2330
|
+
].map((p) => {
|
|
2331
|
+
const isActive = platformFilter === p.key;
|
|
2332
|
+
return /* @__PURE__ */ jsxs4(
|
|
2333
|
+
"button",
|
|
2334
|
+
{
|
|
2335
|
+
type: "button",
|
|
2336
|
+
onClick: () => setPlatformFilter(isActive ? null : p.key),
|
|
2337
|
+
style: {
|
|
2338
|
+
display: "flex",
|
|
2339
|
+
alignItems: "center",
|
|
2340
|
+
gap: 4,
|
|
2341
|
+
padding: "3px 8px",
|
|
2342
|
+
borderRadius: 6,
|
|
2343
|
+
backgroundColor: isActive ? colors.blue + "20" : "transparent",
|
|
2344
|
+
border: isActive ? `1px solid ${colors.blue}60` : "1px solid transparent",
|
|
2345
|
+
cursor: "pointer",
|
|
2346
|
+
fontSize: 11,
|
|
2347
|
+
color: isActive ? colors.blue : colors.textMuted,
|
|
2348
|
+
fontWeight: isActive ? 600 : 400,
|
|
2349
|
+
whiteSpace: "nowrap"
|
|
2350
|
+
},
|
|
2351
|
+
children: [
|
|
2352
|
+
p.icon,
|
|
2353
|
+
" ",
|
|
2354
|
+
p.label
|
|
2355
|
+
]
|
|
2356
|
+
},
|
|
2357
|
+
p.key
|
|
2358
|
+
);
|
|
2359
|
+
})
|
|
2360
|
+
] }),
|
|
2022
2361
|
(availableTracks.length >= 2 || true) && /* @__PURE__ */ jsxs4("div", { style: { display: "flex", alignItems: "center", gap: 6, marginBottom: 12 }, children: [
|
|
2023
2362
|
availableTracks.length >= 2 && /* @__PURE__ */ jsxs4("div", { style: { display: "flex", gap: 4, flex: 1, overflow: "auto" }, children: [
|
|
2024
2363
|
/* @__PURE__ */ jsx5(
|
|
@@ -2332,7 +2671,27 @@ function TestListScreen({ nav }) {
|
|
|
2332
2671
|
]
|
|
2333
2672
|
}
|
|
2334
2673
|
),
|
|
2335
|
-
|
|
2674
|
+
dashboardUrl && /* @__PURE__ */ jsx5(
|
|
2675
|
+
"a",
|
|
2676
|
+
{
|
|
2677
|
+
href: `${dashboardUrl}/test-cases`,
|
|
2678
|
+
target: "_blank",
|
|
2679
|
+
rel: "noopener noreferrer",
|
|
2680
|
+
style: {
|
|
2681
|
+
display: "flex",
|
|
2682
|
+
alignItems: "center",
|
|
2683
|
+
justifyContent: "center",
|
|
2684
|
+
gap: 6,
|
|
2685
|
+
paddingTop: 12,
|
|
2686
|
+
fontSize: 13,
|
|
2687
|
+
fontWeight: 500,
|
|
2688
|
+
color: colors.blue,
|
|
2689
|
+
textDecoration: "none"
|
|
2690
|
+
},
|
|
2691
|
+
children: "\u{1F310} Manage on Dashboard \u2192"
|
|
2692
|
+
}
|
|
2693
|
+
),
|
|
2694
|
+
/* @__PURE__ */ jsx5("div", { style: { display: "flex", justifyContent: "center", paddingTop: 8, paddingBottom: 8 }, children: /* @__PURE__ */ jsx5(
|
|
2336
2695
|
"button",
|
|
2337
2696
|
{
|
|
2338
2697
|
type: "button",
|
|
@@ -2403,6 +2762,25 @@ function useImageAttachments(uploadFn, maxImages, bucket = "screenshots") {
|
|
|
2403
2762
|
const pickFromCamera = useCallback4(() => {
|
|
2404
2763
|
triggerFilePicker("environment");
|
|
2405
2764
|
}, [triggerFilePicker]);
|
|
2765
|
+
const addFile = useCallback4((file) => {
|
|
2766
|
+
const id = `img-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
|
|
2767
|
+
const localUri = URL.createObjectURL(file);
|
|
2768
|
+
const name = file.name || `image-${id}.png`;
|
|
2769
|
+
setImages((prev) => {
|
|
2770
|
+
if (prev.length >= maxImages) return prev;
|
|
2771
|
+
return [...prev, { id, localUri, remoteUrl: null, name, status: "uploading" }];
|
|
2772
|
+
});
|
|
2773
|
+
uploadFn(file, bucket).then((url) => {
|
|
2774
|
+
setImages((prev) => prev.map(
|
|
2775
|
+
(img) => img.id === id ? { ...img, remoteUrl: url, status: url ? "done" : "error" } : img
|
|
2776
|
+
));
|
|
2777
|
+
}).catch((err) => {
|
|
2778
|
+
console.error("BugBear: Image upload failed", err);
|
|
2779
|
+
setImages((prev) => prev.map(
|
|
2780
|
+
(img) => img.id === id ? { ...img, status: "error" } : img
|
|
2781
|
+
));
|
|
2782
|
+
});
|
|
2783
|
+
}, [maxImages, uploadFn, bucket]);
|
|
2406
2784
|
const removeImage = useCallback4((id) => {
|
|
2407
2785
|
setImages((prev) => {
|
|
2408
2786
|
const img = prev.find((i) => i.id === id);
|
|
@@ -2424,7 +2802,7 @@ function useImageAttachments(uploadFn, maxImages, bucket = "screenshots") {
|
|
|
2424
2802
|
const getScreenshotUrls = useCallback4(() => {
|
|
2425
2803
|
return images.filter((img) => img.status === "done" && img.remoteUrl).map((img) => img.remoteUrl);
|
|
2426
2804
|
}, [images]);
|
|
2427
|
-
return { images, pickFromGallery, pickFromCamera, removeImage, clear, isUploading, hasError, getAttachments, getScreenshotUrls };
|
|
2805
|
+
return { images, pickFromGallery, pickFromCamera, addFile, removeImage, clear, isUploading, hasError, getAttachments, getScreenshotUrls };
|
|
2428
2806
|
}
|
|
2429
2807
|
|
|
2430
2808
|
// src/widget/ImagePreviewStrip.tsx
|
|
@@ -2545,7 +2923,12 @@ function ImagePickerButtons({ images, maxImages, onPickGallery, onPickCamera, on
|
|
|
2545
2923
|
maxImages
|
|
2546
2924
|
] })
|
|
2547
2925
|
] }),
|
|
2548
|
-
/* @__PURE__ */ jsx7(ImagePreviewStrip, { images, onRemove })
|
|
2926
|
+
/* @__PURE__ */ jsx7(ImagePreviewStrip, { images, onRemove }),
|
|
2927
|
+
typeof navigator !== "undefined" && /* @__PURE__ */ jsxs6("span", { style: { fontSize: 11, color: colors.textDim, display: "block", marginTop: 4 }, children: [
|
|
2928
|
+
"Paste images with ",
|
|
2929
|
+
typeof navigator !== "undefined" && navigator.platform?.includes("Mac") ? "\u2318" : "Ctrl",
|
|
2930
|
+
"+V"
|
|
2931
|
+
] })
|
|
2549
2932
|
] });
|
|
2550
2933
|
}
|
|
2551
2934
|
|
|
@@ -2843,7 +3226,7 @@ var styles = {
|
|
|
2843
3226
|
};
|
|
2844
3227
|
|
|
2845
3228
|
// src/widget/screens/ReportScreen.tsx
|
|
2846
|
-
import React6, { useState as useState6, useRef as
|
|
3229
|
+
import React6, { useState as useState6, useRef as useRef3, useEffect as useEffect6 } from "react";
|
|
2847
3230
|
|
|
2848
3231
|
// src/widget/CategoryDropdown.tsx
|
|
2849
3232
|
import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
@@ -2889,11 +3272,63 @@ function CategoryDropdown({ value, onChange, optional = true, disabled = false }
|
|
|
2889
3272
|
);
|
|
2890
3273
|
}
|
|
2891
3274
|
|
|
3275
|
+
// src/widget/useClipboardPaste.ts
|
|
3276
|
+
import { useEffect as useEffect5, useCallback as useCallback5 } from "react";
|
|
3277
|
+
function useClipboardPaste({
|
|
3278
|
+
containerRef,
|
|
3279
|
+
enabled,
|
|
3280
|
+
currentCount,
|
|
3281
|
+
maxImages,
|
|
3282
|
+
addFile
|
|
3283
|
+
}) {
|
|
3284
|
+
const handlePaste = useCallback5((event) => {
|
|
3285
|
+
if (!enabled || currentCount >= maxImages) return;
|
|
3286
|
+
const items = event.clipboardData?.items;
|
|
3287
|
+
if (!items) return;
|
|
3288
|
+
const imageFiles = [];
|
|
3289
|
+
for (let i = 0; i < items.length; i++) {
|
|
3290
|
+
const item = items[i];
|
|
3291
|
+
if (item.type.startsWith("image/")) {
|
|
3292
|
+
const file = item.getAsFile();
|
|
3293
|
+
if (file) imageFiles.push(file);
|
|
3294
|
+
}
|
|
3295
|
+
}
|
|
3296
|
+
if (imageFiles.length === 0) return;
|
|
3297
|
+
event.preventDefault();
|
|
3298
|
+
const remaining = maxImages - currentCount;
|
|
3299
|
+
for (const file of imageFiles.slice(0, remaining)) {
|
|
3300
|
+
addFile(file);
|
|
3301
|
+
}
|
|
3302
|
+
}, [enabled, currentCount, maxImages, addFile]);
|
|
3303
|
+
useEffect5(() => {
|
|
3304
|
+
const container = containerRef.current;
|
|
3305
|
+
if (!container || !enabled) return;
|
|
3306
|
+
container.addEventListener("paste", handlePaste);
|
|
3307
|
+
return () => container.removeEventListener("paste", handlePaste);
|
|
3308
|
+
}, [containerRef, enabled, handlePaste]);
|
|
3309
|
+
}
|
|
3310
|
+
|
|
2892
3311
|
// src/widget/screens/ReportScreen.tsx
|
|
2893
3312
|
import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2894
|
-
function ReportScreen({ nav, prefill }) {
|
|
3313
|
+
function ReportScreen({ nav, prefill, autoCapture, onAutoCaptureConsumed }) {
|
|
2895
3314
|
const { client, refreshAssignments, uploadImage } = useBugBear();
|
|
2896
3315
|
const images = useImageAttachments(uploadImage, 5, "screenshots");
|
|
3316
|
+
const formRef = useRef3(null);
|
|
3317
|
+
const hasConsumedCapture = useRef3(false);
|
|
3318
|
+
useEffect6(() => {
|
|
3319
|
+
if (autoCapture && !hasConsumedCapture.current) {
|
|
3320
|
+
hasConsumedCapture.current = true;
|
|
3321
|
+
images.addFile(autoCapture);
|
|
3322
|
+
onAutoCaptureConsumed?.();
|
|
3323
|
+
}
|
|
3324
|
+
}, [autoCapture, onAutoCaptureConsumed]);
|
|
3325
|
+
useClipboardPaste({
|
|
3326
|
+
containerRef: formRef,
|
|
3327
|
+
enabled: true,
|
|
3328
|
+
currentCount: images.images.length,
|
|
3329
|
+
maxImages: 5,
|
|
3330
|
+
addFile: images.addFile
|
|
3331
|
+
});
|
|
2897
3332
|
const [reportType, setReportType] = useState6(prefill?.type || "bug");
|
|
2898
3333
|
const [severity, setSeverity] = useState6("medium");
|
|
2899
3334
|
const [category, setCategory] = useState6(null);
|
|
@@ -2901,7 +3336,7 @@ function ReportScreen({ nav, prefill }) {
|
|
|
2901
3336
|
const [affectedRoute, setAffectedRoute] = useState6("");
|
|
2902
3337
|
const [submitting, setSubmitting] = useState6(false);
|
|
2903
3338
|
const [error, setError] = useState6(null);
|
|
2904
|
-
const submittingRef =
|
|
3339
|
+
const submittingRef = useRef3(false);
|
|
2905
3340
|
React6.useEffect(() => {
|
|
2906
3341
|
if (reportType === "feedback" || reportType === "suggestion") {
|
|
2907
3342
|
setCategory("other");
|
|
@@ -2909,7 +3344,7 @@ function ReportScreen({ nav, prefill }) {
|
|
|
2909
3344
|
setCategory(null);
|
|
2910
3345
|
}
|
|
2911
3346
|
}, [reportType]);
|
|
2912
|
-
const observedRoute =
|
|
3347
|
+
const observedRoute = useRef3(
|
|
2913
3348
|
typeof window !== "undefined" ? window.location.pathname : "unknown"
|
|
2914
3349
|
);
|
|
2915
3350
|
const isRetestFailure = prefill?.type === "test_fail";
|
|
@@ -2965,7 +3400,7 @@ function ReportScreen({ nav, prefill }) {
|
|
|
2965
3400
|
{ sev: "medium", color: "#eab308" },
|
|
2966
3401
|
{ sev: "low", color: "#6b7280" }
|
|
2967
3402
|
];
|
|
2968
|
-
return /* @__PURE__ */ jsx10("div", { children: isRetestFailure ? /* @__PURE__ */ jsxs9(Fragment3, { children: [
|
|
3403
|
+
return /* @__PURE__ */ jsx10("div", { ref: formRef, tabIndex: -1, style: { outline: "none" }, children: isRetestFailure ? /* @__PURE__ */ jsxs9(Fragment3, { children: [
|
|
2969
3404
|
/* @__PURE__ */ jsxs9("div", { style: styles2.retestBanner, children: [
|
|
2970
3405
|
/* @__PURE__ */ jsx10("span", { style: { fontSize: 16 }, children: "\u{1F504}" }),
|
|
2971
3406
|
/* @__PURE__ */ jsxs9("div", { children: [
|
|
@@ -3293,10 +3728,10 @@ var styles2 = {
|
|
|
3293
3728
|
};
|
|
3294
3729
|
|
|
3295
3730
|
// src/widget/screens/ReportSuccessScreen.tsx
|
|
3296
|
-
import { useEffect as
|
|
3731
|
+
import { useEffect as useEffect7 } from "react";
|
|
3297
3732
|
import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
3298
3733
|
function ReportSuccessScreen({ nav }) {
|
|
3299
|
-
|
|
3734
|
+
useEffect7(() => {
|
|
3300
3735
|
const timer = setTimeout(() => nav.reset(), 2e3);
|
|
3301
3736
|
return () => clearTimeout(timer);
|
|
3302
3737
|
}, [nav]);
|
|
@@ -3334,7 +3769,7 @@ var styles3 = {
|
|
|
3334
3769
|
// src/widget/screens/MessageListScreen.tsx
|
|
3335
3770
|
import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
3336
3771
|
function MessageListScreen({ nav }) {
|
|
3337
|
-
const { threads, unreadCount, refreshThreads, isLoading } = useBugBear();
|
|
3772
|
+
const { threads, unreadCount, refreshThreads, dashboardUrl, isLoading } = useBugBear();
|
|
3338
3773
|
if (isLoading) return /* @__PURE__ */ jsx12(MessageListScreenSkeleton, {});
|
|
3339
3774
|
return /* @__PURE__ */ jsxs11("div", { children: [
|
|
3340
3775
|
/* @__PURE__ */ jsx12(
|
|
@@ -3523,6 +3958,26 @@ function MessageListScreen({ nav }) {
|
|
|
3523
3958
|
},
|
|
3524
3959
|
thread.id
|
|
3525
3960
|
)) }),
|
|
3961
|
+
dashboardUrl && /* @__PURE__ */ jsx12(
|
|
3962
|
+
"a",
|
|
3963
|
+
{
|
|
3964
|
+
href: `${dashboardUrl}/discussions`,
|
|
3965
|
+
target: "_blank",
|
|
3966
|
+
rel: "noopener noreferrer",
|
|
3967
|
+
style: {
|
|
3968
|
+
display: "flex",
|
|
3969
|
+
alignItems: "center",
|
|
3970
|
+
justifyContent: "center",
|
|
3971
|
+
gap: 6,
|
|
3972
|
+
paddingTop: 12,
|
|
3973
|
+
fontSize: 13,
|
|
3974
|
+
fontWeight: 500,
|
|
3975
|
+
color: colors.blue,
|
|
3976
|
+
textDecoration: "none"
|
|
3977
|
+
},
|
|
3978
|
+
children: "\u{1F310} View on Dashboard \u2192"
|
|
3979
|
+
}
|
|
3980
|
+
),
|
|
3526
3981
|
/* @__PURE__ */ jsxs11(
|
|
3527
3982
|
"div",
|
|
3528
3983
|
{
|
|
@@ -3530,7 +3985,7 @@ function MessageListScreen({ nav }) {
|
|
|
3530
3985
|
display: "flex",
|
|
3531
3986
|
justifyContent: "space-between",
|
|
3532
3987
|
alignItems: "center",
|
|
3533
|
-
paddingTop:
|
|
3988
|
+
paddingTop: 8,
|
|
3534
3989
|
paddingLeft: 4,
|
|
3535
3990
|
paddingRight: 4
|
|
3536
3991
|
},
|
|
@@ -3566,7 +4021,7 @@ function MessageListScreen({ nav }) {
|
|
|
3566
4021
|
}
|
|
3567
4022
|
|
|
3568
4023
|
// src/widget/screens/ThreadDetailScreen.tsx
|
|
3569
|
-
import { useState as useState7, useEffect as
|
|
4024
|
+
import { useState as useState7, useEffect as useEffect8 } from "react";
|
|
3570
4025
|
import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
3571
4026
|
var inputStyle = {
|
|
3572
4027
|
backgroundColor: "#27272a",
|
|
@@ -3588,7 +4043,7 @@ function ThreadDetailScreen({
|
|
|
3588
4043
|
const [replyText, setReplyText] = useState7("");
|
|
3589
4044
|
const [sending, setSending] = useState7(false);
|
|
3590
4045
|
const [sendError, setSendError] = useState7(false);
|
|
3591
|
-
|
|
4046
|
+
useEffect8(() => {
|
|
3592
4047
|
let cancelled = false;
|
|
3593
4048
|
setLoading(true);
|
|
3594
4049
|
(async () => {
|
|
@@ -4013,7 +4468,7 @@ function ComposeMessageScreen({ nav }) {
|
|
|
4013
4468
|
}
|
|
4014
4469
|
|
|
4015
4470
|
// src/widget/screens/ProfileScreen.tsx
|
|
4016
|
-
import { useState as useState9, useEffect as
|
|
4471
|
+
import { useState as useState9, useEffect as useEffect9 } from "react";
|
|
4017
4472
|
import { jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
4018
4473
|
function ProfileScreen({ nav }) {
|
|
4019
4474
|
const { testerInfo, assignments, updateTesterProfile, refreshTesterInfo } = useBugBear();
|
|
@@ -4026,7 +4481,7 @@ function ProfileScreen({ nav }) {
|
|
|
4026
4481
|
const [saved, setSaved] = useState9(false);
|
|
4027
4482
|
const [showDetails, setShowDetails] = useState9(false);
|
|
4028
4483
|
const completedCount = assignments.filter((a) => a.status === "passed" || a.status === "failed").length;
|
|
4029
|
-
|
|
4484
|
+
useEffect9(() => {
|
|
4030
4485
|
if (testerInfo) {
|
|
4031
4486
|
setName(testerInfo.name);
|
|
4032
4487
|
setAdditionalEmails(testerInfo.additionalEmails || []);
|
|
@@ -4490,7 +4945,7 @@ var styles4 = {
|
|
|
4490
4945
|
};
|
|
4491
4946
|
|
|
4492
4947
|
// src/widget/screens/IssueListScreen.tsx
|
|
4493
|
-
import { useState as useState10, useEffect as
|
|
4948
|
+
import { useState as useState10, useEffect as useEffect10 } from "react";
|
|
4494
4949
|
import { jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
4495
4950
|
var CATEGORY_CONFIG = {
|
|
4496
4951
|
open: { label: "Open Issues", accent: "#f97316", emptyIcon: "\u2705", emptyText: "No open issues" },
|
|
@@ -4508,7 +4963,7 @@ function IssueListScreen({ nav, category }) {
|
|
|
4508
4963
|
const [issues, setIssues] = useState10([]);
|
|
4509
4964
|
const [loading, setLoading] = useState10(true);
|
|
4510
4965
|
const config = CATEGORY_CONFIG[category];
|
|
4511
|
-
|
|
4966
|
+
useEffect10(() => {
|
|
4512
4967
|
let cancelled = false;
|
|
4513
4968
|
setLoading(true);
|
|
4514
4969
|
(async () => {
|
|
@@ -4654,6 +5109,7 @@ var SEVERITY_CONFIG = {
|
|
|
4654
5109
|
low: { label: "Low", color: "#71717a", bg: "#27272a" }
|
|
4655
5110
|
};
|
|
4656
5111
|
function IssueDetailScreen({ nav, issue }) {
|
|
5112
|
+
const { dashboardUrl } = useBugBear();
|
|
4657
5113
|
const statusConfig = STATUS_LABELS[issue.status] || { label: issue.status, bg: "#27272a", color: "#a1a1aa" };
|
|
4658
5114
|
const severityConfig = issue.severity ? SEVERITY_CONFIG[issue.severity] : null;
|
|
4659
5115
|
return /* @__PURE__ */ jsxs16("div", { children: [
|
|
@@ -4771,7 +5227,29 @@ function IssueDetailScreen({ nav, issue }) {
|
|
|
4771
5227
|
" \xB7 Updated ",
|
|
4772
5228
|
formatRelativeTime(issue.updatedAt)
|
|
4773
5229
|
] })
|
|
4774
|
-
] })
|
|
5230
|
+
] }),
|
|
5231
|
+
dashboardUrl && /* @__PURE__ */ jsx17(
|
|
5232
|
+
"a",
|
|
5233
|
+
{
|
|
5234
|
+
href: `${dashboardUrl}/reports`,
|
|
5235
|
+
target: "_blank",
|
|
5236
|
+
rel: "noopener noreferrer",
|
|
5237
|
+
style: {
|
|
5238
|
+
display: "flex",
|
|
5239
|
+
alignItems: "center",
|
|
5240
|
+
justifyContent: "center",
|
|
5241
|
+
gap: 6,
|
|
5242
|
+
marginTop: 12,
|
|
5243
|
+
padding: "10px 0",
|
|
5244
|
+
fontSize: 13,
|
|
5245
|
+
fontWeight: 500,
|
|
5246
|
+
color: colors.blue,
|
|
5247
|
+
textDecoration: "none",
|
|
5248
|
+
borderTop: `1px solid ${colors.border}`
|
|
5249
|
+
},
|
|
5250
|
+
children: "\u{1F310} View on Dashboard \u2192"
|
|
5251
|
+
}
|
|
5252
|
+
)
|
|
4775
5253
|
] });
|
|
4776
5254
|
}
|
|
4777
5255
|
|
|
@@ -4827,11 +5305,12 @@ function BugBearPanel({
|
|
|
4827
5305
|
const { shouldShowWidget, testerInfo, assignments, isLoading, unreadCount } = useBugBear();
|
|
4828
5306
|
const { currentScreen, canGoBack, push, pop, replace, reset } = useNavigation();
|
|
4829
5307
|
const [collapsed, setCollapsed] = useState11(defaultCollapsed);
|
|
5308
|
+
const autoCaptureRef = useRef4(null);
|
|
4830
5309
|
const [panelPosition, setPanelPosition] = useState11(null);
|
|
4831
5310
|
const [isDragging, setIsDragging] = useState11(false);
|
|
4832
|
-
const dragStartRef =
|
|
4833
|
-
const panelRef =
|
|
4834
|
-
|
|
5311
|
+
const dragStartRef = useRef4(null);
|
|
5312
|
+
const panelRef = useRef4(null);
|
|
5313
|
+
useEffect11(() => {
|
|
4835
5314
|
if (typeof window === "undefined") return;
|
|
4836
5315
|
try {
|
|
4837
5316
|
const saved = localStorage.getItem(STORAGE_KEY);
|
|
@@ -4845,7 +5324,7 @@ function BugBearPanel({
|
|
|
4845
5324
|
setPanelPosition(getDefaultPosition(position));
|
|
4846
5325
|
}
|
|
4847
5326
|
}, [position]);
|
|
4848
|
-
|
|
5327
|
+
useEffect11(() => {
|
|
4849
5328
|
if (panelPosition && typeof window !== "undefined") {
|
|
4850
5329
|
try {
|
|
4851
5330
|
localStorage.setItem(STORAGE_KEY, JSON.stringify(panelPosition));
|
|
@@ -4853,7 +5332,7 @@ function BugBearPanel({
|
|
|
4853
5332
|
}
|
|
4854
5333
|
}
|
|
4855
5334
|
}, [panelPosition]);
|
|
4856
|
-
|
|
5335
|
+
useEffect11(() => {
|
|
4857
5336
|
if (typeof window === "undefined") return;
|
|
4858
5337
|
const handleResize = () => {
|
|
4859
5338
|
setPanelPosition((prev) => prev ? clampPosition(prev) : getDefaultPosition(position));
|
|
@@ -4861,7 +5340,7 @@ function BugBearPanel({
|
|
|
4861
5340
|
window.addEventListener("resize", handleResize);
|
|
4862
5341
|
return () => window.removeEventListener("resize", handleResize);
|
|
4863
5342
|
}, [position]);
|
|
4864
|
-
const handleMouseDown =
|
|
5343
|
+
const handleMouseDown = useCallback6((e) => {
|
|
4865
5344
|
if (!draggable || !panelPosition) return;
|
|
4866
5345
|
const target = e.target;
|
|
4867
5346
|
if (!target.closest("[data-drag-handle]")) return;
|
|
@@ -4874,7 +5353,7 @@ function BugBearPanel({
|
|
|
4874
5353
|
panelY: panelPosition.y
|
|
4875
5354
|
};
|
|
4876
5355
|
}, [draggable, panelPosition]);
|
|
4877
|
-
|
|
5356
|
+
useEffect11(() => {
|
|
4878
5357
|
if (!isDragging) return;
|
|
4879
5358
|
const handleMouseMove = (e) => {
|
|
4880
5359
|
if (!dragStartRef.current) return;
|
|
@@ -4896,7 +5375,7 @@ function BugBearPanel({
|
|
|
4896
5375
|
document.removeEventListener("mouseup", handleMouseUp);
|
|
4897
5376
|
};
|
|
4898
5377
|
}, [isDragging]);
|
|
4899
|
-
const handleDoubleClick =
|
|
5378
|
+
const handleDoubleClick = useCallback6(() => {
|
|
4900
5379
|
if (!draggable) return;
|
|
4901
5380
|
setPanelPosition(getDefaultPosition(position));
|
|
4902
5381
|
try {
|
|
@@ -4904,6 +5383,14 @@ function BugBearPanel({
|
|
|
4904
5383
|
} catch {
|
|
4905
5384
|
}
|
|
4906
5385
|
}, [draggable, position]);
|
|
5386
|
+
const handleReportBug = useCallback6(async () => {
|
|
5387
|
+
const file = await capturePageScreenshot({
|
|
5388
|
+
excludeElement: panelRef.current,
|
|
5389
|
+
timeout: 3e3
|
|
5390
|
+
});
|
|
5391
|
+
autoCaptureRef.current = file;
|
|
5392
|
+
push({ name: "REPORT", prefill: { type: "bug" } });
|
|
5393
|
+
}, [push]);
|
|
4907
5394
|
if (isLoading || !shouldShowWidget) return null;
|
|
4908
5395
|
if (!panelPosition) return null;
|
|
4909
5396
|
const pendingCount = assignments.filter((a) => a.status === "pending" || a.status === "in_progress").length;
|
|
@@ -4940,13 +5427,12 @@ function BugBearPanel({
|
|
|
4940
5427
|
};
|
|
4941
5428
|
const handleClose = () => {
|
|
4942
5429
|
setCollapsed(true);
|
|
4943
|
-
reset();
|
|
4944
5430
|
};
|
|
4945
5431
|
const nav = { push, pop, replace, reset, canGoBack };
|
|
4946
5432
|
const renderScreen = () => {
|
|
4947
5433
|
switch (currentScreen.name) {
|
|
4948
5434
|
case "HOME":
|
|
4949
|
-
return /* @__PURE__ */ jsx18(HomeScreen, { nav });
|
|
5435
|
+
return /* @__PURE__ */ jsx18(HomeScreen, { nav, onReportBug: handleReportBug });
|
|
4950
5436
|
case "TEST_DETAIL":
|
|
4951
5437
|
return /* @__PURE__ */ jsx18(TestDetailScreen, { testId: currentScreen.testId, nav });
|
|
4952
5438
|
case "TEST_LIST":
|
|
@@ -4954,7 +5440,17 @@ function BugBearPanel({
|
|
|
4954
5440
|
case "TEST_FEEDBACK":
|
|
4955
5441
|
return /* @__PURE__ */ jsx18(TestFeedbackScreen, { status: currentScreen.status, assignmentId: currentScreen.assignmentId, nav });
|
|
4956
5442
|
case "REPORT":
|
|
4957
|
-
return /* @__PURE__ */ jsx18(
|
|
5443
|
+
return /* @__PURE__ */ jsx18(
|
|
5444
|
+
ReportScreen,
|
|
5445
|
+
{
|
|
5446
|
+
nav,
|
|
5447
|
+
prefill: currentScreen.prefill,
|
|
5448
|
+
autoCapture: autoCaptureRef.current,
|
|
5449
|
+
onAutoCaptureConsumed: () => {
|
|
5450
|
+
autoCaptureRef.current = null;
|
|
5451
|
+
}
|
|
5452
|
+
}
|
|
5453
|
+
);
|
|
4958
5454
|
case "REPORT_SUCCESS":
|
|
4959
5455
|
return /* @__PURE__ */ jsx18(ReportSuccessScreen, { nav });
|
|
4960
5456
|
case "MESSAGE_LIST":
|