@bbearai/react-native 0.6.0 → 0.6.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 +233 -27
- package/dist/index.mjs +238 -32
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -12007,8 +12007,8 @@ var BugBearClient = class {
|
|
|
12007
12007
|
return { success: false, error: rateLimit.error };
|
|
12008
12008
|
}
|
|
12009
12009
|
if (!userInfo) {
|
|
12010
|
-
console.error("BugBear: No user info available, cannot submit report");
|
|
12011
|
-
return { success: false, error: "
|
|
12010
|
+
console.error("BugBear: No user info available, cannot submit report. Ensure your BugBear config provides a getCurrentUser() callback that returns { id, email }.");
|
|
12011
|
+
return { success: false, error: "Unable to identify user. Check that your app passes a getCurrentUser callback to BugBear config." };
|
|
12012
12012
|
}
|
|
12013
12013
|
const testerInfo = await this.getTesterInfo();
|
|
12014
12014
|
fullReport = {
|
|
@@ -12071,10 +12071,11 @@ var BugBearClient = class {
|
|
|
12071
12071
|
const pageSize = Math.min(options?.pageSize ?? 100, 100);
|
|
12072
12072
|
const from = (options?.page ?? 0) * pageSize;
|
|
12073
12073
|
const to = from + pageSize - 1;
|
|
12074
|
-
const
|
|
12074
|
+
const selectFields = `
|
|
12075
12075
|
id,
|
|
12076
12076
|
status,
|
|
12077
12077
|
started_at,
|
|
12078
|
+
completed_at,
|
|
12078
12079
|
skip_reason,
|
|
12079
12080
|
is_verification,
|
|
12080
12081
|
original_report_id,
|
|
@@ -12109,20 +12110,23 @@ var BugBearClient = class {
|
|
|
12109
12110
|
color,
|
|
12110
12111
|
description,
|
|
12111
12112
|
login_hint
|
|
12112
|
-
)
|
|
12113
|
+
),
|
|
12114
|
+
platforms
|
|
12113
12115
|
)
|
|
12114
|
-
|
|
12115
|
-
|
|
12116
|
-
|
|
12116
|
+
`;
|
|
12117
|
+
const [pendingResult, completedResult] = await Promise.all([
|
|
12118
|
+
this.supabase.from("test_assignments").select(selectFields).eq("project_id", this.config.projectId).eq("tester_id", testerInfo.id).in("status", ["pending", "in_progress"]).order("created_at", { ascending: true }).range(from, to),
|
|
12119
|
+
this.supabase.from("test_assignments").select(selectFields).eq("project_id", this.config.projectId).eq("tester_id", testerInfo.id).in("status", ["passed", "failed", "skipped", "blocked"]).order("completed_at", { ascending: false }).limit(100)
|
|
12120
|
+
]);
|
|
12121
|
+
if (pendingResult.error) {
|
|
12122
|
+
console.error("BugBear: Failed to fetch assignments", formatPgError(pendingResult.error));
|
|
12117
12123
|
return [];
|
|
12118
12124
|
}
|
|
12119
|
-
const
|
|
12120
|
-
|
|
12121
|
-
|
|
12122
|
-
|
|
12123
|
-
|
|
12124
|
-
return true;
|
|
12125
|
-
}).map((item) => ({
|
|
12125
|
+
const allData = [
|
|
12126
|
+
...pendingResult.data || [],
|
|
12127
|
+
...completedResult.data || []
|
|
12128
|
+
];
|
|
12129
|
+
const mapItem = (item) => ({
|
|
12126
12130
|
id: item.id,
|
|
12127
12131
|
status: item.status,
|
|
12128
12132
|
startedAt: item.started_at,
|
|
@@ -12160,12 +12164,24 @@ var BugBearClient = class {
|
|
|
12160
12164
|
color: item.test_case.role.color,
|
|
12161
12165
|
description: item.test_case.role.description,
|
|
12162
12166
|
loginHint: item.test_case.role.login_hint
|
|
12163
|
-
} : void 0
|
|
12167
|
+
} : void 0,
|
|
12168
|
+
platforms: item.test_case.platforms || void 0
|
|
12164
12169
|
}
|
|
12165
|
-
})
|
|
12170
|
+
});
|
|
12171
|
+
const mapped = allData.filter((item) => {
|
|
12172
|
+
if (!item.test_case) {
|
|
12173
|
+
console.warn("BugBear: Assignment returned without test_case", { id: item.id });
|
|
12174
|
+
return false;
|
|
12175
|
+
}
|
|
12176
|
+
return true;
|
|
12177
|
+
}).map(mapItem);
|
|
12166
12178
|
mapped.sort((a, b) => {
|
|
12167
12179
|
if (a.isVerification && !b.isVerification) return -1;
|
|
12168
12180
|
if (!a.isVerification && b.isVerification) return 1;
|
|
12181
|
+
const aActive = a.status === "pending" || a.status === "in_progress";
|
|
12182
|
+
const bActive = b.status === "pending" || b.status === "in_progress";
|
|
12183
|
+
if (aActive && !bActive) return -1;
|
|
12184
|
+
if (!aActive && bActive) return 1;
|
|
12169
12185
|
return 0;
|
|
12170
12186
|
});
|
|
12171
12187
|
return mapped;
|
|
@@ -12330,6 +12346,36 @@ var BugBearClient = class {
|
|
|
12330
12346
|
async failAssignment(assignmentId) {
|
|
12331
12347
|
return this.updateAssignmentStatus(assignmentId, "failed");
|
|
12332
12348
|
}
|
|
12349
|
+
/**
|
|
12350
|
+
* Reopen a completed assignment — sets it back to in_progress with a fresh timer.
|
|
12351
|
+
* Clears completed_at and duration_seconds so it can be re-evaluated.
|
|
12352
|
+
*/
|
|
12353
|
+
async reopenAssignment(assignmentId) {
|
|
12354
|
+
try {
|
|
12355
|
+
const { data: current, error: fetchError } = await this.supabase.from("test_assignments").select("status").eq("id", assignmentId).single();
|
|
12356
|
+
if (fetchError || !current) {
|
|
12357
|
+
return { success: false, error: "Assignment not found" };
|
|
12358
|
+
}
|
|
12359
|
+
if (current.status === "pending" || current.status === "in_progress") {
|
|
12360
|
+
return { success: true };
|
|
12361
|
+
}
|
|
12362
|
+
const { error } = await this.supabase.from("test_assignments").update({
|
|
12363
|
+
status: "in_progress",
|
|
12364
|
+
started_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12365
|
+
completed_at: null,
|
|
12366
|
+
duration_seconds: null,
|
|
12367
|
+
skip_reason: null
|
|
12368
|
+
}).eq("id", assignmentId).eq("status", current.status);
|
|
12369
|
+
if (error) {
|
|
12370
|
+
console.error("BugBear: Failed to reopen assignment", error);
|
|
12371
|
+
return { success: false, error: error.message };
|
|
12372
|
+
}
|
|
12373
|
+
return { success: true };
|
|
12374
|
+
} catch (err) {
|
|
12375
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
12376
|
+
return { success: false, error: message };
|
|
12377
|
+
}
|
|
12378
|
+
}
|
|
12333
12379
|
/**
|
|
12334
12380
|
* Skip a test assignment with a required reason
|
|
12335
12381
|
* Marks the assignment as 'skipped' and records why it was skipped
|
|
@@ -14470,6 +14516,39 @@ function TestDetailScreen({ testId, nav }) {
|
|
|
14470
14516
|
setIsSubmitting(false);
|
|
14471
14517
|
}
|
|
14472
14518
|
}, [client, displayedAssignment, refreshAssignments, nav, isSubmitting]);
|
|
14519
|
+
const handleReopen = (0, import_react5.useCallback)(async () => {
|
|
14520
|
+
if (!client || !displayedAssignment || isSubmitting) return;
|
|
14521
|
+
import_react_native5.Keyboard.dismiss();
|
|
14522
|
+
setIsSubmitting(true);
|
|
14523
|
+
try {
|
|
14524
|
+
await client.reopenAssignment(displayedAssignment.id);
|
|
14525
|
+
await refreshAssignments();
|
|
14526
|
+
} finally {
|
|
14527
|
+
setIsSubmitting(false);
|
|
14528
|
+
}
|
|
14529
|
+
}, [client, displayedAssignment, refreshAssignments, isSubmitting]);
|
|
14530
|
+
const handleChangeResult = (0, import_react5.useCallback)(async (newStatus) => {
|
|
14531
|
+
if (!client || !displayedAssignment || isSubmitting) return;
|
|
14532
|
+
import_react_native5.Keyboard.dismiss();
|
|
14533
|
+
setIsSubmitting(true);
|
|
14534
|
+
try {
|
|
14535
|
+
await client.reopenAssignment(displayedAssignment.id);
|
|
14536
|
+
await client.updateAssignmentStatus(displayedAssignment.id, newStatus);
|
|
14537
|
+
await refreshAssignments();
|
|
14538
|
+
if (newStatus === "failed") {
|
|
14539
|
+
nav.replace({
|
|
14540
|
+
name: "REPORT",
|
|
14541
|
+
prefill: {
|
|
14542
|
+
type: "test_fail",
|
|
14543
|
+
assignmentId: displayedAssignment.id,
|
|
14544
|
+
testCaseId: displayedAssignment.testCase.id
|
|
14545
|
+
}
|
|
14546
|
+
});
|
|
14547
|
+
}
|
|
14548
|
+
} finally {
|
|
14549
|
+
setIsSubmitting(false);
|
|
14550
|
+
}
|
|
14551
|
+
}, [client, displayedAssignment, refreshAssignments, nav, isSubmitting]);
|
|
14473
14552
|
const handleSkip = (0, import_react5.useCallback)(async () => {
|
|
14474
14553
|
if (!client || !displayedAssignment || !selectedSkipReason) return;
|
|
14475
14554
|
import_react_native5.Keyboard.dismiss();
|
|
@@ -14550,7 +14629,39 @@ function TestDetailScreen({ testId, nav }) {
|
|
|
14550
14629
|
}
|
|
14551
14630
|
},
|
|
14552
14631
|
/* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.navigateText }, "\u{1F9ED} Go to test location")
|
|
14553
|
-
), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.TouchableOpacity, { onPress: () => setShowDetails(!showDetails), style: styles2.detailsToggle }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.detailsToggleText }, showDetails ? "\u25BC" : "\u25B6", " Details")), showDetails && /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles2.detailsSection }, testCase.key && /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.detailMeta }, testCase.key, " \xB7 ", testCase.priority, " \xB7 ", info.name), testCase.
|
|
14632
|
+
), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.TouchableOpacity, { onPress: () => setShowDetails(!showDetails), style: styles2.detailsToggle }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.detailsToggleText }, showDetails ? "\u25BC" : "\u25B6", " Details")), showDetails && /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles2.detailsSection }, testCase.key && /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.detailMeta }, testCase.key, " \xB7 ", testCase.priority, " \xB7 ", info.name), testCase.track && /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.detailMeta }, testCase.track.icon, " ", testCase.track.name), testCase.description && /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.detailDesc }, testCase.description), testCase.group && /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles2.folderProgress }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.folderName }, "\u{1F4C1} ", testCase.group.name))), displayedAssignment.status === "passed" || displayedAssignment.status === "failed" || displayedAssignment.status === "skipped" || displayedAssignment.status === "blocked" ? /* @__PURE__ */ import_react5.default.createElement(import_react5.default.Fragment, null, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: [
|
|
14633
|
+
styles2.completedBanner,
|
|
14634
|
+
displayedAssignment.status === "passed" && styles2.completedBannerPass,
|
|
14635
|
+
displayedAssignment.status === "failed" && styles2.completedBannerFail
|
|
14636
|
+
] }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.completedIcon }, displayedAssignment.status === "passed" ? "\u2705" : displayedAssignment.status === "failed" ? "\u274C" : displayedAssignment.status === "skipped" ? "\u23ED" : "\u{1F6AB}"), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: [
|
|
14637
|
+
styles2.completedLabel,
|
|
14638
|
+
displayedAssignment.status === "passed" && { color: colors.green },
|
|
14639
|
+
displayedAssignment.status === "failed" && { color: "#fca5a5" }
|
|
14640
|
+
] }, "Marked as ", displayedAssignment.status.charAt(0).toUpperCase() + displayedAssignment.status.slice(1))), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: [styles2.actionButtons, { marginTop: 4 }] }, /* @__PURE__ */ import_react5.default.createElement(
|
|
14641
|
+
import_react_native5.TouchableOpacity,
|
|
14642
|
+
{
|
|
14643
|
+
style: [styles2.actionBtn, styles2.reopenBtn, isSubmitting && { opacity: 0.5 }],
|
|
14644
|
+
onPress: handleReopen,
|
|
14645
|
+
disabled: isSubmitting
|
|
14646
|
+
},
|
|
14647
|
+
/* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.reopenBtnText }, isSubmitting ? "Reopening..." : "\u{1F504} Reopen Test")
|
|
14648
|
+
), displayedAssignment.status === "passed" && /* @__PURE__ */ import_react5.default.createElement(
|
|
14649
|
+
import_react_native5.TouchableOpacity,
|
|
14650
|
+
{
|
|
14651
|
+
style: [styles2.actionBtn, styles2.failBtn, isSubmitting && { opacity: 0.5 }],
|
|
14652
|
+
onPress: () => handleChangeResult("failed"),
|
|
14653
|
+
disabled: isSubmitting
|
|
14654
|
+
},
|
|
14655
|
+
/* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.failBtnText }, "Change to Fail")
|
|
14656
|
+
), displayedAssignment.status === "failed" && /* @__PURE__ */ import_react5.default.createElement(
|
|
14657
|
+
import_react_native5.TouchableOpacity,
|
|
14658
|
+
{
|
|
14659
|
+
style: [styles2.actionBtn, styles2.passBtn, isSubmitting && { opacity: 0.5 }],
|
|
14660
|
+
onPress: () => handleChangeResult("passed"),
|
|
14661
|
+
disabled: isSubmitting
|
|
14662
|
+
},
|
|
14663
|
+
/* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.passBtnText }, "Change to Pass")
|
|
14664
|
+
))) : /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles2.actionButtons }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.TouchableOpacity, { style: [styles2.actionBtn, styles2.failBtn, isSubmitting && { opacity: 0.5 }], onPress: handleFail, disabled: isSubmitting }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.failBtnText }, isSubmitting ? "Failing..." : "Fail")), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.TouchableOpacity, { style: [styles2.actionBtn, styles2.skipBtn, isSubmitting && { opacity: 0.5 }], onPress: () => setShowSkipModal(true), disabled: isSubmitting }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.skipBtnText }, "Skip")), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.TouchableOpacity, { style: [styles2.actionBtn, styles2.passBtn, isSubmitting && { opacity: 0.5 }], onPress: handlePass, disabled: isSubmitting }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.passBtnText }, isSubmitting ? "Passing..." : "Pass"))), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Modal, { visible: showSkipModal, transparent: true, animationType: "fade" }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles2.modalOverlay }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles2.modalContent }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.modalTitle }, "Skip this test?"), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.modalSubtitle }, "Select a reason:"), [
|
|
14554
14665
|
{ reason: "blocked", label: "\u{1F6AB} Blocked by a bug" },
|
|
14555
14666
|
{ reason: "not_ready", label: "\u{1F6A7} Feature not ready" },
|
|
14556
14667
|
{ reason: "dependency", label: "\u{1F517} Needs another test first" },
|
|
@@ -14655,6 +14766,14 @@ var styles2 = import_react_native5.StyleSheet.create({
|
|
|
14655
14766
|
detailDesc: { fontSize: 13, color: colors.textSecondary, lineHeight: 18 },
|
|
14656
14767
|
folderProgress: { marginTop: 8 },
|
|
14657
14768
|
folderName: { fontSize: 12, color: colors.textMuted },
|
|
14769
|
+
// Completed state
|
|
14770
|
+
completedBanner: { flexDirection: "row", alignItems: "center", justifyContent: "center", gap: 6, paddingVertical: 8, paddingHorizontal: 12, borderRadius: 8, marginTop: 8, marginBottom: 8, backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border },
|
|
14771
|
+
completedBannerPass: { backgroundColor: colors.greenDark, borderColor: colors.green },
|
|
14772
|
+
completedBannerFail: { backgroundColor: colors.redDark, borderColor: colors.red },
|
|
14773
|
+
completedIcon: { fontSize: 14 },
|
|
14774
|
+
completedLabel: { fontSize: 13, fontWeight: "600", color: colors.textSecondary },
|
|
14775
|
+
reopenBtn: { backgroundColor: colors.card, borderWidth: 1, borderColor: colors.blue },
|
|
14776
|
+
reopenBtnText: { fontSize: 14, fontWeight: "600", color: colors.blue },
|
|
14658
14777
|
// Action buttons
|
|
14659
14778
|
actionButtons: { flexDirection: "row", gap: 10, marginTop: 8 },
|
|
14660
14779
|
actionBtn: { flex: 1, paddingVertical: 14, borderRadius: 12, alignItems: "center" },
|
|
@@ -14685,10 +14804,11 @@ var styles2 = import_react_native5.StyleSheet.create({
|
|
|
14685
14804
|
var import_react6 = __toESM(require("react"));
|
|
14686
14805
|
var import_react_native6 = require("react-native");
|
|
14687
14806
|
function TestListScreen({ nav }) {
|
|
14688
|
-
const { assignments, currentAssignment, refreshAssignments, isLoading } = useBugBear();
|
|
14807
|
+
const { assignments, currentAssignment, refreshAssignments, dashboardUrl, isLoading } = useBugBear();
|
|
14689
14808
|
const [filter, setFilter] = (0, import_react6.useState)("all");
|
|
14690
14809
|
const [roleFilter, setRoleFilter] = (0, import_react6.useState)(null);
|
|
14691
14810
|
const [trackFilter, setTrackFilter] = (0, import_react6.useState)(null);
|
|
14811
|
+
const [platformFilter, setPlatformFilter] = (0, import_react6.useState)(import_react_native6.Platform.OS === "android" ? "android" : "ios");
|
|
14692
14812
|
const [searchQuery, setSearchQuery] = (0, import_react6.useState)("");
|
|
14693
14813
|
const [sortMode, setSortMode] = (0, import_react6.useState)("priority");
|
|
14694
14814
|
const [collapsedFolders, setCollapsedFolders] = (0, import_react6.useState)(/* @__PURE__ */ new Set());
|
|
@@ -14755,6 +14875,7 @@ function TestListScreen({ nav }) {
|
|
|
14755
14875
|
});
|
|
14756
14876
|
}, []);
|
|
14757
14877
|
const filterAssignment = (0, import_react6.useCallback)((a) => {
|
|
14878
|
+
if (platformFilter && a.testCase.platforms && !a.testCase.platforms.includes(platformFilter)) return false;
|
|
14758
14879
|
if (roleFilter && a.testCase.role?.id !== roleFilter) return false;
|
|
14759
14880
|
if (trackFilter && a.testCase.track?.id !== trackFilter) return false;
|
|
14760
14881
|
if (searchQuery) {
|
|
@@ -14767,7 +14888,7 @@ function TestListScreen({ nav }) {
|
|
|
14767
14888
|
if (filter === "done") return a.status === "passed";
|
|
14768
14889
|
if (filter === "reopened") return a.status === "failed";
|
|
14769
14890
|
return true;
|
|
14770
|
-
}, [roleFilter, trackFilter, searchQuery, filter]);
|
|
14891
|
+
}, [platformFilter, roleFilter, trackFilter, searchQuery, filter]);
|
|
14771
14892
|
if (isLoading) return /* @__PURE__ */ import_react6.default.createElement(TestListScreenSkeleton, null);
|
|
14772
14893
|
return /* @__PURE__ */ import_react6.default.createElement(import_react_native6.View, null, /* @__PURE__ */ import_react6.default.createElement(import_react_native6.View, { style: styles3.filterBar }, [
|
|
14773
14894
|
{ key: "all", label: "All", count: assignments.length },
|
|
@@ -14805,7 +14926,32 @@ function TestListScreen({ nav }) {
|
|
|
14805
14926
|
placeholderTextColor: colors.textMuted,
|
|
14806
14927
|
style: styles3.searchInput
|
|
14807
14928
|
}
|
|
14808
|
-
)), /* @__PURE__ */ import_react6.default.createElement(import_react_native6.
|
|
14929
|
+
)), /* @__PURE__ */ import_react6.default.createElement(import_react_native6.ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, style: styles3.platformBar }, /* @__PURE__ */ import_react6.default.createElement(
|
|
14930
|
+
import_react_native6.TouchableOpacity,
|
|
14931
|
+
{
|
|
14932
|
+
style: [styles3.platformBtn, !platformFilter && styles3.platformBtnActive],
|
|
14933
|
+
onPress: () => setPlatformFilter(null)
|
|
14934
|
+
},
|
|
14935
|
+
/* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: [styles3.platformBtnText, !platformFilter && styles3.platformBtnTextActive] }, "All")
|
|
14936
|
+
), [
|
|
14937
|
+
{ key: "web", label: "Web", icon: "\u{1F310}" },
|
|
14938
|
+
{ key: "ios", label: "iOS", icon: "\u{1F4F1}" },
|
|
14939
|
+
{ key: "android", label: "Android", icon: "\u{1F916}" }
|
|
14940
|
+
].map((p) => {
|
|
14941
|
+
const isActive = platformFilter === p.key;
|
|
14942
|
+
return /* @__PURE__ */ import_react6.default.createElement(
|
|
14943
|
+
import_react_native6.TouchableOpacity,
|
|
14944
|
+
{
|
|
14945
|
+
key: p.key,
|
|
14946
|
+
style: [
|
|
14947
|
+
styles3.platformBtn,
|
|
14948
|
+
isActive && { backgroundColor: colors.blue + "20", borderColor: colors.blue + "60", borderWidth: 1 }
|
|
14949
|
+
],
|
|
14950
|
+
onPress: () => setPlatformFilter(isActive ? null : p.key)
|
|
14951
|
+
},
|
|
14952
|
+
/* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: [styles3.platformBtnText, isActive && { color: colors.blue, fontWeight: "600" }] }, p.icon, " ", p.label)
|
|
14953
|
+
);
|
|
14954
|
+
})), /* @__PURE__ */ import_react6.default.createElement(import_react_native6.View, { style: styles3.trackSortRow }, availableTracks.length >= 2 && /* @__PURE__ */ import_react6.default.createElement(import_react_native6.ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, style: { flex: 1 } }, /* @__PURE__ */ import_react6.default.createElement(
|
|
14809
14955
|
import_react_native6.TouchableOpacity,
|
|
14810
14956
|
{
|
|
14811
14957
|
style: [styles3.trackBtn, !trackFilter && styles3.trackBtnActive],
|
|
@@ -14866,7 +15012,15 @@ function TestListScreen({ nav }) {
|
|
|
14866
15012
|
] }, badge.label))
|
|
14867
15013
|
);
|
|
14868
15014
|
}));
|
|
14869
|
-
}),
|
|
15015
|
+
}), dashboardUrl && /* @__PURE__ */ import_react6.default.createElement(
|
|
15016
|
+
import_react_native6.TouchableOpacity,
|
|
15017
|
+
{
|
|
15018
|
+
style: styles3.dashboardLink,
|
|
15019
|
+
onPress: () => import_react_native6.Linking.openURL(`${dashboardUrl}/test-cases`),
|
|
15020
|
+
activeOpacity: 0.7
|
|
15021
|
+
},
|
|
15022
|
+
/* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: styles3.dashboardLinkText }, "\u{1F310}", " Manage on Dashboard ", "\u2192")
|
|
15023
|
+
), /* @__PURE__ */ import_react6.default.createElement(import_react_native6.TouchableOpacity, { style: styles3.refreshBtn, onPress: refreshAssignments }, /* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: styles3.refreshText }, "\u21BB", " Refresh")));
|
|
14870
15024
|
}
|
|
14871
15025
|
var styles3 = import_react_native6.StyleSheet.create({
|
|
14872
15026
|
filterBar: { flexDirection: "row", gap: 8, marginBottom: 8 },
|
|
@@ -14905,6 +15059,11 @@ var styles3 = import_react_native6.StyleSheet.create({
|
|
|
14905
15059
|
statusPillText: { fontSize: 10, fontWeight: "600" },
|
|
14906
15060
|
searchContainer: { marginBottom: 8 },
|
|
14907
15061
|
searchInput: { backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border, borderRadius: 8, paddingHorizontal: 12, paddingVertical: 8, fontSize: 13, color: colors.textPrimary },
|
|
15062
|
+
platformBar: { flexDirection: "row", marginBottom: 8 },
|
|
15063
|
+
platformBtn: { flexDirection: "row", alignItems: "center", gap: 4, paddingHorizontal: 10, paddingVertical: 4, borderRadius: 6, marginRight: 6, borderWidth: 1, borderColor: "transparent" },
|
|
15064
|
+
platformBtnActive: { backgroundColor: colors.card, borderColor: colors.border },
|
|
15065
|
+
platformBtnText: { fontSize: 11, color: colors.textMuted },
|
|
15066
|
+
platformBtnTextActive: { color: colors.textPrimary, fontWeight: "600" },
|
|
14908
15067
|
trackSortRow: { flexDirection: "row", alignItems: "center", marginBottom: 10, gap: 8 },
|
|
14909
15068
|
trackBtn: { flexDirection: "row", alignItems: "center", gap: 4, paddingHorizontal: 10, paddingVertical: 4, borderRadius: 6, marginRight: 6, borderWidth: 1, borderColor: "transparent" },
|
|
14910
15069
|
trackBtnActive: { backgroundColor: colors.card, borderColor: colors.border },
|
|
@@ -14915,7 +15074,9 @@ var styles3 = import_react_native6.StyleSheet.create({
|
|
|
14915
15074
|
sortBtnActive: { backgroundColor: colors.card, borderColor: colors.border },
|
|
14916
15075
|
sortBtnText: { fontSize: 11, color: colors.textMuted },
|
|
14917
15076
|
sortBtnTextActive: { color: colors.textPrimary, fontWeight: "600" },
|
|
14918
|
-
|
|
15077
|
+
dashboardLink: { alignItems: "center", paddingTop: 12 },
|
|
15078
|
+
dashboardLinkText: { fontSize: 13, fontWeight: "500", color: colors.blue },
|
|
15079
|
+
refreshBtn: { alignItems: "center", paddingVertical: 8 },
|
|
14919
15080
|
refreshText: { fontSize: 13, color: colors.blue }
|
|
14920
15081
|
});
|
|
14921
15082
|
|
|
@@ -15084,7 +15245,9 @@ var styles4 = import_react_native7.StyleSheet.create({
|
|
|
15084
15245
|
|
|
15085
15246
|
// src/widget/ImagePickerButtons.tsx
|
|
15086
15247
|
function ImagePickerButtons({ images, maxImages, onPickGallery, onPickCamera, onRemove, label }) {
|
|
15087
|
-
if (!IMAGE_PICKER_AVAILABLE)
|
|
15248
|
+
if (!IMAGE_PICKER_AVAILABLE) {
|
|
15249
|
+
return /* @__PURE__ */ import_react9.default.createElement(import_react_native8.View, { style: styles5.section }, label && /* @__PURE__ */ import_react9.default.createElement(import_react_native8.Text, { style: styles5.label }, label), /* @__PURE__ */ import_react9.default.createElement(import_react_native8.View, { style: styles5.unavailableRow }, /* @__PURE__ */ import_react9.default.createElement(import_react_native8.Text, { style: styles5.unavailableText }, "Install react-native-image-picker to enable photo attachments")));
|
|
15250
|
+
}
|
|
15088
15251
|
return /* @__PURE__ */ import_react9.default.createElement(import_react_native8.View, { style: styles5.section }, label && /* @__PURE__ */ import_react9.default.createElement(import_react_native8.Text, { style: styles5.label }, label), /* @__PURE__ */ import_react9.default.createElement(import_react_native8.View, { style: styles5.buttonRow }, /* @__PURE__ */ import_react9.default.createElement(
|
|
15089
15252
|
import_react_native8.TouchableOpacity,
|
|
15090
15253
|
{
|
|
@@ -15139,6 +15302,18 @@ var styles5 = import_react_native8.StyleSheet.create({
|
|
|
15139
15302
|
fontSize: 12,
|
|
15140
15303
|
color: colors.textDim,
|
|
15141
15304
|
marginLeft: 4
|
|
15305
|
+
},
|
|
15306
|
+
unavailableRow: {
|
|
15307
|
+
backgroundColor: colors.card,
|
|
15308
|
+
borderWidth: 1,
|
|
15309
|
+
borderColor: colors.border,
|
|
15310
|
+
borderRadius: 8,
|
|
15311
|
+
padding: 12
|
|
15312
|
+
},
|
|
15313
|
+
unavailableText: {
|
|
15314
|
+
fontSize: 12,
|
|
15315
|
+
color: colors.textDim,
|
|
15316
|
+
fontStyle: "italic"
|
|
15142
15317
|
}
|
|
15143
15318
|
});
|
|
15144
15319
|
|
|
@@ -15639,7 +15814,7 @@ var styles9 = import_react_native12.StyleSheet.create({
|
|
|
15639
15814
|
var import_react14 = __toESM(require("react"));
|
|
15640
15815
|
var import_react_native13 = require("react-native");
|
|
15641
15816
|
function MessageListScreen({ nav }) {
|
|
15642
|
-
const { threads, unreadCount, refreshThreads, isLoading } = useBugBear();
|
|
15817
|
+
const { threads, unreadCount, refreshThreads, dashboardUrl, isLoading } = useBugBear();
|
|
15643
15818
|
if (isLoading) return /* @__PURE__ */ import_react14.default.createElement(MessageListScreenSkeleton, null);
|
|
15644
15819
|
return /* @__PURE__ */ import_react14.default.createElement(import_react_native13.View, null, /* @__PURE__ */ import_react14.default.createElement(
|
|
15645
15820
|
import_react_native13.TouchableOpacity,
|
|
@@ -15657,7 +15832,15 @@ function MessageListScreen({ nav }) {
|
|
|
15657
15832
|
},
|
|
15658
15833
|
/* @__PURE__ */ import_react14.default.createElement(import_react_native13.View, { style: styles10.threadLeft }, /* @__PURE__ */ import_react14.default.createElement(import_react_native13.Text, { style: styles10.threadIcon }, getThreadTypeIcon(thread.threadType)), /* @__PURE__ */ import_react14.default.createElement(import_react_native13.View, { style: styles10.threadInfo }, /* @__PURE__ */ import_react14.default.createElement(import_react_native13.View, { style: styles10.threadTitleRow }, thread.isPinned && /* @__PURE__ */ import_react14.default.createElement(import_react_native13.Text, { style: styles10.pinIcon }, "\u{1F4CC}"), /* @__PURE__ */ import_react14.default.createElement(import_react_native13.Text, { style: styles10.threadSubject, numberOfLines: 1 }, thread.subject || "No subject")), thread.lastMessage && /* @__PURE__ */ import_react14.default.createElement(import_react_native13.Text, { style: styles10.threadPreview, numberOfLines: 1 }, thread.lastMessage.senderName, ": ", thread.lastMessage.content))),
|
|
15659
15834
|
/* @__PURE__ */ import_react14.default.createElement(import_react_native13.View, { style: styles10.threadRight }, /* @__PURE__ */ import_react14.default.createElement(import_react_native13.Text, { style: styles10.threadTime }, formatRelativeTime(thread.lastMessageAt)), thread.unreadCount > 0 && /* @__PURE__ */ import_react14.default.createElement(import_react_native13.View, { style: styles10.unreadBadge }, /* @__PURE__ */ import_react14.default.createElement(import_react_native13.Text, { style: styles10.unreadText }, thread.unreadCount)), thread.priority !== "normal" && /* @__PURE__ */ import_react14.default.createElement(import_react_native13.View, { style: [styles10.priorityDot, { backgroundColor: getPriorityColor(thread.priority) }] }))
|
|
15660
|
-
))),
|
|
15835
|
+
))), dashboardUrl && /* @__PURE__ */ import_react14.default.createElement(
|
|
15836
|
+
import_react_native13.TouchableOpacity,
|
|
15837
|
+
{
|
|
15838
|
+
style: styles10.dashboardLink,
|
|
15839
|
+
onPress: () => import_react_native13.Linking.openURL(`${dashboardUrl}/discussions`),
|
|
15840
|
+
activeOpacity: 0.7
|
|
15841
|
+
},
|
|
15842
|
+
/* @__PURE__ */ import_react14.default.createElement(import_react_native13.Text, { style: styles10.dashboardLinkText }, "\u{1F310}", " View on Dashboard ", "\u2192")
|
|
15843
|
+
), /* @__PURE__ */ import_react14.default.createElement(import_react_native13.View, { style: styles10.footer }, /* @__PURE__ */ import_react14.default.createElement(import_react_native13.Text, { style: styles10.footerText }, threads.length, " thread", threads.length !== 1 ? "s" : "", " \xB7 ", unreadCount, " unread"), /* @__PURE__ */ import_react14.default.createElement(import_react_native13.TouchableOpacity, { onPress: refreshThreads }, /* @__PURE__ */ import_react14.default.createElement(import_react_native13.Text, { style: styles10.refreshText }, "\u21BB Refresh"))));
|
|
15661
15844
|
}
|
|
15662
15845
|
var styles10 = import_react_native13.StyleSheet.create({
|
|
15663
15846
|
newMsgButton: { backgroundColor: colors.blue, paddingVertical: 12, borderRadius: 12, alignItems: "center", marginBottom: 16 },
|
|
@@ -15676,7 +15859,9 @@ var styles10 = import_react_native13.StyleSheet.create({
|
|
|
15676
15859
|
unreadBadge: { backgroundColor: colors.blue, borderRadius: 10, minWidth: 20, height: 20, justifyContent: "center", alignItems: "center", paddingHorizontal: 6 },
|
|
15677
15860
|
unreadText: { fontSize: 11, fontWeight: "bold", color: "#fff" },
|
|
15678
15861
|
priorityDot: { width: 8, height: 8, borderRadius: 4 },
|
|
15679
|
-
|
|
15862
|
+
dashboardLink: { alignItems: "center", paddingTop: 12 },
|
|
15863
|
+
dashboardLinkText: { fontSize: 13, fontWeight: "500", color: colors.blue },
|
|
15864
|
+
footer: { flexDirection: "row", justifyContent: "space-between", alignItems: "center", paddingTop: 8, paddingHorizontal: 4 },
|
|
15680
15865
|
footerText: { fontSize: 12, color: colors.textMuted },
|
|
15681
15866
|
refreshText: { fontSize: 13, color: colors.blue }
|
|
15682
15867
|
});
|
|
@@ -16179,9 +16364,18 @@ var SEVERITY_CONFIG = {
|
|
|
16179
16364
|
low: { label: "Low", color: "#71717a", bg: "#27272a" }
|
|
16180
16365
|
};
|
|
16181
16366
|
function IssueDetailScreen({ nav, issue }) {
|
|
16367
|
+
const { dashboardUrl } = useBugBear();
|
|
16182
16368
|
const statusConfig = STATUS_LABELS[issue.status] || { label: issue.status, bg: "#27272a", color: "#a1a1aa" };
|
|
16183
16369
|
const severityConfig = issue.severity ? SEVERITY_CONFIG[issue.severity] : null;
|
|
16184
|
-
return /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, null, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: styles15.badgeRow }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: [styles15.badge, { backgroundColor: statusConfig.bg }] }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: [styles15.badgeText, { color: statusConfig.color }] }, statusConfig.label)), severityConfig && /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: [styles15.badge, { backgroundColor: severityConfig.bg }] }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: [styles15.badgeText, { color: severityConfig.color }] }, severityConfig.label))), /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.title }, issue.title), issue.route && /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.route }, issue.route), issue.description && /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: styles15.descriptionCard }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.descriptionText }, issue.description)), issue.verifiedByName && /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: styles15.verifiedCard }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: styles15.verifiedHeader }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.verifiedIcon }, "\u2705"), /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.verifiedTitle }, "Retesting Proof")), /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.verifiedBody }, "Verified by ", issue.verifiedByName, issue.verifiedAt && ` on ${new Date(issue.verifiedAt).toLocaleDateString(void 0, { month: "short", day: "numeric", year: "numeric" })}`)), issue.originalBugTitle && /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: styles15.originalBugCard }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: styles15.originalBugHeader }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.originalBugIcon }, "\u{1F504}"), /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.originalBugTitle }, "Original Bug")), /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.originalBugBody }, "Retest of: ", issue.originalBugTitle)), issue.screenshotUrls && issue.screenshotUrls.length > 0 && /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: styles15.screenshotSection }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.screenshotLabel }, "Screenshots (", issue.screenshotUrls.length, ")"), /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: styles15.screenshotRow }, issue.screenshotUrls.map((url, i) => /* @__PURE__ */ import_react19.default.createElement(import_react_native18.TouchableOpacity, { key: i, onPress: () => import_react_native18.Linking.openURL(url), activeOpacity: 0.7 }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Image, { source: { uri: url }, style: styles15.screenshotThumb }))))), /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: styles15.metaSection }, issue.reporterName && /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.metaText }, "Reported by ", issue.reporterName), /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.metaTextSmall }, "Created ", formatRelativeTime(issue.createdAt), " ", "\xB7", " Updated ", formatRelativeTime(issue.updatedAt)))
|
|
16370
|
+
return /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, null, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: styles15.badgeRow }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: [styles15.badge, { backgroundColor: statusConfig.bg }] }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: [styles15.badgeText, { color: statusConfig.color }] }, statusConfig.label)), severityConfig && /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: [styles15.badge, { backgroundColor: severityConfig.bg }] }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: [styles15.badgeText, { color: severityConfig.color }] }, severityConfig.label))), /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.title }, issue.title), issue.route && /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.route }, issue.route), issue.description && /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: styles15.descriptionCard }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.descriptionText }, issue.description)), issue.verifiedByName && /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: styles15.verifiedCard }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: styles15.verifiedHeader }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.verifiedIcon }, "\u2705"), /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.verifiedTitle }, "Retesting Proof")), /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.verifiedBody }, "Verified by ", issue.verifiedByName, issue.verifiedAt && ` on ${new Date(issue.verifiedAt).toLocaleDateString(void 0, { month: "short", day: "numeric", year: "numeric" })}`)), issue.originalBugTitle && /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: styles15.originalBugCard }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: styles15.originalBugHeader }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.originalBugIcon }, "\u{1F504}"), /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.originalBugTitle }, "Original Bug")), /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.originalBugBody }, "Retest of: ", issue.originalBugTitle)), issue.screenshotUrls && issue.screenshotUrls.length > 0 && /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: styles15.screenshotSection }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.screenshotLabel }, "Screenshots (", issue.screenshotUrls.length, ")"), /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: styles15.screenshotRow }, issue.screenshotUrls.map((url, i) => /* @__PURE__ */ import_react19.default.createElement(import_react_native18.TouchableOpacity, { key: i, onPress: () => import_react_native18.Linking.openURL(url), activeOpacity: 0.7 }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Image, { source: { uri: url }, style: styles15.screenshotThumb }))))), /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: styles15.metaSection }, issue.reporterName && /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.metaText }, "Reported by ", issue.reporterName), /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.metaTextSmall }, "Created ", formatRelativeTime(issue.createdAt), " ", "\xB7", " Updated ", formatRelativeTime(issue.updatedAt))), dashboardUrl && /* @__PURE__ */ import_react19.default.createElement(
|
|
16371
|
+
import_react_native18.TouchableOpacity,
|
|
16372
|
+
{
|
|
16373
|
+
style: styles15.dashboardLink,
|
|
16374
|
+
onPress: () => import_react_native18.Linking.openURL(`${dashboardUrl}/reports`),
|
|
16375
|
+
activeOpacity: 0.7
|
|
16376
|
+
},
|
|
16377
|
+
/* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.dashboardLinkText }, "\u{1F310}", " View on Dashboard ", "\u2192")
|
|
16378
|
+
));
|
|
16185
16379
|
}
|
|
16186
16380
|
var styles15 = import_react_native18.StyleSheet.create({
|
|
16187
16381
|
badgeRow: {
|
|
@@ -16310,6 +16504,18 @@ var styles15 = import_react_native18.StyleSheet.create({
|
|
|
16310
16504
|
metaTextSmall: {
|
|
16311
16505
|
fontSize: 11,
|
|
16312
16506
|
color: colors.textDim
|
|
16507
|
+
},
|
|
16508
|
+
dashboardLink: {
|
|
16509
|
+
alignItems: "center",
|
|
16510
|
+
paddingVertical: 10,
|
|
16511
|
+
marginTop: 12,
|
|
16512
|
+
borderTopWidth: 1,
|
|
16513
|
+
borderTopColor: colors.border
|
|
16514
|
+
},
|
|
16515
|
+
dashboardLinkText: {
|
|
16516
|
+
fontSize: 13,
|
|
16517
|
+
fontWeight: "500",
|
|
16518
|
+
color: colors.blue
|
|
16313
16519
|
}
|
|
16314
16520
|
});
|
|
16315
16521
|
|
package/dist/index.mjs
CHANGED
|
@@ -11974,8 +11974,8 @@ var BugBearClient = class {
|
|
|
11974
11974
|
return { success: false, error: rateLimit.error };
|
|
11975
11975
|
}
|
|
11976
11976
|
if (!userInfo) {
|
|
11977
|
-
console.error("BugBear: No user info available, cannot submit report");
|
|
11978
|
-
return { success: false, error: "
|
|
11977
|
+
console.error("BugBear: No user info available, cannot submit report. Ensure your BugBear config provides a getCurrentUser() callback that returns { id, email }.");
|
|
11978
|
+
return { success: false, error: "Unable to identify user. Check that your app passes a getCurrentUser callback to BugBear config." };
|
|
11979
11979
|
}
|
|
11980
11980
|
const testerInfo = await this.getTesterInfo();
|
|
11981
11981
|
fullReport = {
|
|
@@ -12038,10 +12038,11 @@ var BugBearClient = class {
|
|
|
12038
12038
|
const pageSize = Math.min(options?.pageSize ?? 100, 100);
|
|
12039
12039
|
const from = (options?.page ?? 0) * pageSize;
|
|
12040
12040
|
const to = from + pageSize - 1;
|
|
12041
|
-
const
|
|
12041
|
+
const selectFields = `
|
|
12042
12042
|
id,
|
|
12043
12043
|
status,
|
|
12044
12044
|
started_at,
|
|
12045
|
+
completed_at,
|
|
12045
12046
|
skip_reason,
|
|
12046
12047
|
is_verification,
|
|
12047
12048
|
original_report_id,
|
|
@@ -12076,20 +12077,23 @@ var BugBearClient = class {
|
|
|
12076
12077
|
color,
|
|
12077
12078
|
description,
|
|
12078
12079
|
login_hint
|
|
12079
|
-
)
|
|
12080
|
+
),
|
|
12081
|
+
platforms
|
|
12080
12082
|
)
|
|
12081
|
-
|
|
12082
|
-
|
|
12083
|
-
|
|
12083
|
+
`;
|
|
12084
|
+
const [pendingResult, completedResult] = await Promise.all([
|
|
12085
|
+
this.supabase.from("test_assignments").select(selectFields).eq("project_id", this.config.projectId).eq("tester_id", testerInfo.id).in("status", ["pending", "in_progress"]).order("created_at", { ascending: true }).range(from, to),
|
|
12086
|
+
this.supabase.from("test_assignments").select(selectFields).eq("project_id", this.config.projectId).eq("tester_id", testerInfo.id).in("status", ["passed", "failed", "skipped", "blocked"]).order("completed_at", { ascending: false }).limit(100)
|
|
12087
|
+
]);
|
|
12088
|
+
if (pendingResult.error) {
|
|
12089
|
+
console.error("BugBear: Failed to fetch assignments", formatPgError(pendingResult.error));
|
|
12084
12090
|
return [];
|
|
12085
12091
|
}
|
|
12086
|
-
const
|
|
12087
|
-
|
|
12088
|
-
|
|
12089
|
-
|
|
12090
|
-
|
|
12091
|
-
return true;
|
|
12092
|
-
}).map((item) => ({
|
|
12092
|
+
const allData = [
|
|
12093
|
+
...pendingResult.data || [],
|
|
12094
|
+
...completedResult.data || []
|
|
12095
|
+
];
|
|
12096
|
+
const mapItem = (item) => ({
|
|
12093
12097
|
id: item.id,
|
|
12094
12098
|
status: item.status,
|
|
12095
12099
|
startedAt: item.started_at,
|
|
@@ -12127,12 +12131,24 @@ var BugBearClient = class {
|
|
|
12127
12131
|
color: item.test_case.role.color,
|
|
12128
12132
|
description: item.test_case.role.description,
|
|
12129
12133
|
loginHint: item.test_case.role.login_hint
|
|
12130
|
-
} : void 0
|
|
12134
|
+
} : void 0,
|
|
12135
|
+
platforms: item.test_case.platforms || void 0
|
|
12131
12136
|
}
|
|
12132
|
-
})
|
|
12137
|
+
});
|
|
12138
|
+
const mapped = allData.filter((item) => {
|
|
12139
|
+
if (!item.test_case) {
|
|
12140
|
+
console.warn("BugBear: Assignment returned without test_case", { id: item.id });
|
|
12141
|
+
return false;
|
|
12142
|
+
}
|
|
12143
|
+
return true;
|
|
12144
|
+
}).map(mapItem);
|
|
12133
12145
|
mapped.sort((a, b) => {
|
|
12134
12146
|
if (a.isVerification && !b.isVerification) return -1;
|
|
12135
12147
|
if (!a.isVerification && b.isVerification) return 1;
|
|
12148
|
+
const aActive = a.status === "pending" || a.status === "in_progress";
|
|
12149
|
+
const bActive = b.status === "pending" || b.status === "in_progress";
|
|
12150
|
+
if (aActive && !bActive) return -1;
|
|
12151
|
+
if (!aActive && bActive) return 1;
|
|
12136
12152
|
return 0;
|
|
12137
12153
|
});
|
|
12138
12154
|
return mapped;
|
|
@@ -12297,6 +12313,36 @@ var BugBearClient = class {
|
|
|
12297
12313
|
async failAssignment(assignmentId) {
|
|
12298
12314
|
return this.updateAssignmentStatus(assignmentId, "failed");
|
|
12299
12315
|
}
|
|
12316
|
+
/**
|
|
12317
|
+
* Reopen a completed assignment — sets it back to in_progress with a fresh timer.
|
|
12318
|
+
* Clears completed_at and duration_seconds so it can be re-evaluated.
|
|
12319
|
+
*/
|
|
12320
|
+
async reopenAssignment(assignmentId) {
|
|
12321
|
+
try {
|
|
12322
|
+
const { data: current, error: fetchError } = await this.supabase.from("test_assignments").select("status").eq("id", assignmentId).single();
|
|
12323
|
+
if (fetchError || !current) {
|
|
12324
|
+
return { success: false, error: "Assignment not found" };
|
|
12325
|
+
}
|
|
12326
|
+
if (current.status === "pending" || current.status === "in_progress") {
|
|
12327
|
+
return { success: true };
|
|
12328
|
+
}
|
|
12329
|
+
const { error } = await this.supabase.from("test_assignments").update({
|
|
12330
|
+
status: "in_progress",
|
|
12331
|
+
started_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12332
|
+
completed_at: null,
|
|
12333
|
+
duration_seconds: null,
|
|
12334
|
+
skip_reason: null
|
|
12335
|
+
}).eq("id", assignmentId).eq("status", current.status);
|
|
12336
|
+
if (error) {
|
|
12337
|
+
console.error("BugBear: Failed to reopen assignment", error);
|
|
12338
|
+
return { success: false, error: error.message };
|
|
12339
|
+
}
|
|
12340
|
+
return { success: true };
|
|
12341
|
+
} catch (err) {
|
|
12342
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
12343
|
+
return { success: false, error: message };
|
|
12344
|
+
}
|
|
12345
|
+
}
|
|
12300
12346
|
/**
|
|
12301
12347
|
* Skip a test assignment with a required reason
|
|
12302
12348
|
* Marks the assignment as 'skipped' and records why it was skipped
|
|
@@ -13733,7 +13779,7 @@ import {
|
|
|
13733
13779
|
StyleSheet as StyleSheet18,
|
|
13734
13780
|
Dimensions as Dimensions2,
|
|
13735
13781
|
KeyboardAvoidingView,
|
|
13736
|
-
Platform as
|
|
13782
|
+
Platform as Platform5,
|
|
13737
13783
|
PanResponder,
|
|
13738
13784
|
Animated as Animated2,
|
|
13739
13785
|
ActivityIndicator as ActivityIndicator2,
|
|
@@ -14452,6 +14498,39 @@ function TestDetailScreen({ testId, nav }) {
|
|
|
14452
14498
|
setIsSubmitting(false);
|
|
14453
14499
|
}
|
|
14454
14500
|
}, [client, displayedAssignment, refreshAssignments, nav, isSubmitting]);
|
|
14501
|
+
const handleReopen = useCallback2(async () => {
|
|
14502
|
+
if (!client || !displayedAssignment || isSubmitting) return;
|
|
14503
|
+
Keyboard.dismiss();
|
|
14504
|
+
setIsSubmitting(true);
|
|
14505
|
+
try {
|
|
14506
|
+
await client.reopenAssignment(displayedAssignment.id);
|
|
14507
|
+
await refreshAssignments();
|
|
14508
|
+
} finally {
|
|
14509
|
+
setIsSubmitting(false);
|
|
14510
|
+
}
|
|
14511
|
+
}, [client, displayedAssignment, refreshAssignments, isSubmitting]);
|
|
14512
|
+
const handleChangeResult = useCallback2(async (newStatus) => {
|
|
14513
|
+
if (!client || !displayedAssignment || isSubmitting) return;
|
|
14514
|
+
Keyboard.dismiss();
|
|
14515
|
+
setIsSubmitting(true);
|
|
14516
|
+
try {
|
|
14517
|
+
await client.reopenAssignment(displayedAssignment.id);
|
|
14518
|
+
await client.updateAssignmentStatus(displayedAssignment.id, newStatus);
|
|
14519
|
+
await refreshAssignments();
|
|
14520
|
+
if (newStatus === "failed") {
|
|
14521
|
+
nav.replace({
|
|
14522
|
+
name: "REPORT",
|
|
14523
|
+
prefill: {
|
|
14524
|
+
type: "test_fail",
|
|
14525
|
+
assignmentId: displayedAssignment.id,
|
|
14526
|
+
testCaseId: displayedAssignment.testCase.id
|
|
14527
|
+
}
|
|
14528
|
+
});
|
|
14529
|
+
}
|
|
14530
|
+
} finally {
|
|
14531
|
+
setIsSubmitting(false);
|
|
14532
|
+
}
|
|
14533
|
+
}, [client, displayedAssignment, refreshAssignments, nav, isSubmitting]);
|
|
14455
14534
|
const handleSkip = useCallback2(async () => {
|
|
14456
14535
|
if (!client || !displayedAssignment || !selectedSkipReason) return;
|
|
14457
14536
|
Keyboard.dismiss();
|
|
@@ -14532,7 +14611,39 @@ function TestDetailScreen({ testId, nav }) {
|
|
|
14532
14611
|
}
|
|
14533
14612
|
},
|
|
14534
14613
|
/* @__PURE__ */ React4.createElement(Text2, { style: styles2.navigateText }, "\u{1F9ED} Go to test location")
|
|
14535
|
-
), /* @__PURE__ */ React4.createElement(TouchableOpacity2, { onPress: () => setShowDetails(!showDetails), style: styles2.detailsToggle }, /* @__PURE__ */ React4.createElement(Text2, { style: styles2.detailsToggleText }, showDetails ? "\u25BC" : "\u25B6", " Details")), showDetails && /* @__PURE__ */ React4.createElement(View3, { style: styles2.detailsSection }, testCase.key && /* @__PURE__ */ React4.createElement(Text2, { style: styles2.detailMeta }, testCase.key, " \xB7 ", testCase.priority, " \xB7 ", info.name), testCase.
|
|
14614
|
+
), /* @__PURE__ */ React4.createElement(TouchableOpacity2, { onPress: () => setShowDetails(!showDetails), style: styles2.detailsToggle }, /* @__PURE__ */ React4.createElement(Text2, { style: styles2.detailsToggleText }, showDetails ? "\u25BC" : "\u25B6", " Details")), showDetails && /* @__PURE__ */ React4.createElement(View3, { style: styles2.detailsSection }, testCase.key && /* @__PURE__ */ React4.createElement(Text2, { style: styles2.detailMeta }, testCase.key, " \xB7 ", testCase.priority, " \xB7 ", info.name), testCase.track && /* @__PURE__ */ React4.createElement(Text2, { style: styles2.detailMeta }, testCase.track.icon, " ", testCase.track.name), testCase.description && /* @__PURE__ */ React4.createElement(Text2, { style: styles2.detailDesc }, testCase.description), testCase.group && /* @__PURE__ */ React4.createElement(View3, { style: styles2.folderProgress }, /* @__PURE__ */ React4.createElement(Text2, { style: styles2.folderName }, "\u{1F4C1} ", testCase.group.name))), displayedAssignment.status === "passed" || displayedAssignment.status === "failed" || displayedAssignment.status === "skipped" || displayedAssignment.status === "blocked" ? /* @__PURE__ */ React4.createElement(React4.Fragment, null, /* @__PURE__ */ React4.createElement(View3, { style: [
|
|
14615
|
+
styles2.completedBanner,
|
|
14616
|
+
displayedAssignment.status === "passed" && styles2.completedBannerPass,
|
|
14617
|
+
displayedAssignment.status === "failed" && styles2.completedBannerFail
|
|
14618
|
+
] }, /* @__PURE__ */ React4.createElement(Text2, { style: styles2.completedIcon }, displayedAssignment.status === "passed" ? "\u2705" : displayedAssignment.status === "failed" ? "\u274C" : displayedAssignment.status === "skipped" ? "\u23ED" : "\u{1F6AB}"), /* @__PURE__ */ React4.createElement(Text2, { style: [
|
|
14619
|
+
styles2.completedLabel,
|
|
14620
|
+
displayedAssignment.status === "passed" && { color: colors.green },
|
|
14621
|
+
displayedAssignment.status === "failed" && { color: "#fca5a5" }
|
|
14622
|
+
] }, "Marked as ", displayedAssignment.status.charAt(0).toUpperCase() + displayedAssignment.status.slice(1))), /* @__PURE__ */ React4.createElement(View3, { style: [styles2.actionButtons, { marginTop: 4 }] }, /* @__PURE__ */ React4.createElement(
|
|
14623
|
+
TouchableOpacity2,
|
|
14624
|
+
{
|
|
14625
|
+
style: [styles2.actionBtn, styles2.reopenBtn, isSubmitting && { opacity: 0.5 }],
|
|
14626
|
+
onPress: handleReopen,
|
|
14627
|
+
disabled: isSubmitting
|
|
14628
|
+
},
|
|
14629
|
+
/* @__PURE__ */ React4.createElement(Text2, { style: styles2.reopenBtnText }, isSubmitting ? "Reopening..." : "\u{1F504} Reopen Test")
|
|
14630
|
+
), displayedAssignment.status === "passed" && /* @__PURE__ */ React4.createElement(
|
|
14631
|
+
TouchableOpacity2,
|
|
14632
|
+
{
|
|
14633
|
+
style: [styles2.actionBtn, styles2.failBtn, isSubmitting && { opacity: 0.5 }],
|
|
14634
|
+
onPress: () => handleChangeResult("failed"),
|
|
14635
|
+
disabled: isSubmitting
|
|
14636
|
+
},
|
|
14637
|
+
/* @__PURE__ */ React4.createElement(Text2, { style: styles2.failBtnText }, "Change to Fail")
|
|
14638
|
+
), displayedAssignment.status === "failed" && /* @__PURE__ */ React4.createElement(
|
|
14639
|
+
TouchableOpacity2,
|
|
14640
|
+
{
|
|
14641
|
+
style: [styles2.actionBtn, styles2.passBtn, isSubmitting && { opacity: 0.5 }],
|
|
14642
|
+
onPress: () => handleChangeResult("passed"),
|
|
14643
|
+
disabled: isSubmitting
|
|
14644
|
+
},
|
|
14645
|
+
/* @__PURE__ */ React4.createElement(Text2, { style: styles2.passBtnText }, "Change to Pass")
|
|
14646
|
+
))) : /* @__PURE__ */ React4.createElement(View3, { style: styles2.actionButtons }, /* @__PURE__ */ React4.createElement(TouchableOpacity2, { style: [styles2.actionBtn, styles2.failBtn, isSubmitting && { opacity: 0.5 }], onPress: handleFail, disabled: isSubmitting }, /* @__PURE__ */ React4.createElement(Text2, { style: styles2.failBtnText }, isSubmitting ? "Failing..." : "Fail")), /* @__PURE__ */ React4.createElement(TouchableOpacity2, { style: [styles2.actionBtn, styles2.skipBtn, isSubmitting && { opacity: 0.5 }], onPress: () => setShowSkipModal(true), disabled: isSubmitting }, /* @__PURE__ */ React4.createElement(Text2, { style: styles2.skipBtnText }, "Skip")), /* @__PURE__ */ React4.createElement(TouchableOpacity2, { style: [styles2.actionBtn, styles2.passBtn, isSubmitting && { opacity: 0.5 }], onPress: handlePass, disabled: isSubmitting }, /* @__PURE__ */ React4.createElement(Text2, { style: styles2.passBtnText }, isSubmitting ? "Passing..." : "Pass"))), /* @__PURE__ */ React4.createElement(Modal, { visible: showSkipModal, transparent: true, animationType: "fade" }, /* @__PURE__ */ React4.createElement(View3, { style: styles2.modalOverlay }, /* @__PURE__ */ React4.createElement(View3, { style: styles2.modalContent }, /* @__PURE__ */ React4.createElement(Text2, { style: styles2.modalTitle }, "Skip this test?"), /* @__PURE__ */ React4.createElement(Text2, { style: styles2.modalSubtitle }, "Select a reason:"), [
|
|
14536
14647
|
{ reason: "blocked", label: "\u{1F6AB} Blocked by a bug" },
|
|
14537
14648
|
{ reason: "not_ready", label: "\u{1F6A7} Feature not ready" },
|
|
14538
14649
|
{ reason: "dependency", label: "\u{1F517} Needs another test first" },
|
|
@@ -14637,6 +14748,14 @@ var styles2 = StyleSheet4.create({
|
|
|
14637
14748
|
detailDesc: { fontSize: 13, color: colors.textSecondary, lineHeight: 18 },
|
|
14638
14749
|
folderProgress: { marginTop: 8 },
|
|
14639
14750
|
folderName: { fontSize: 12, color: colors.textMuted },
|
|
14751
|
+
// Completed state
|
|
14752
|
+
completedBanner: { flexDirection: "row", alignItems: "center", justifyContent: "center", gap: 6, paddingVertical: 8, paddingHorizontal: 12, borderRadius: 8, marginTop: 8, marginBottom: 8, backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border },
|
|
14753
|
+
completedBannerPass: { backgroundColor: colors.greenDark, borderColor: colors.green },
|
|
14754
|
+
completedBannerFail: { backgroundColor: colors.redDark, borderColor: colors.red },
|
|
14755
|
+
completedIcon: { fontSize: 14 },
|
|
14756
|
+
completedLabel: { fontSize: 13, fontWeight: "600", color: colors.textSecondary },
|
|
14757
|
+
reopenBtn: { backgroundColor: colors.card, borderWidth: 1, borderColor: colors.blue },
|
|
14758
|
+
reopenBtnText: { fontSize: 14, fontWeight: "600", color: colors.blue },
|
|
14640
14759
|
// Action buttons
|
|
14641
14760
|
actionButtons: { flexDirection: "row", gap: 10, marginTop: 8 },
|
|
14642
14761
|
actionBtn: { flex: 1, paddingVertical: 14, borderRadius: 12, alignItems: "center" },
|
|
@@ -14665,12 +14784,13 @@ var styles2 = StyleSheet4.create({
|
|
|
14665
14784
|
|
|
14666
14785
|
// src/widget/screens/TestListScreen.tsx
|
|
14667
14786
|
import React5, { useState as useState3, useMemo as useMemo2, useCallback as useCallback3, useEffect as useEffect5 } from "react";
|
|
14668
|
-
import { View as View4, Text as Text3, TouchableOpacity as TouchableOpacity3, StyleSheet as StyleSheet5, ScrollView, TextInput as TextInput2 } from "react-native";
|
|
14787
|
+
import { View as View4, Text as Text3, TouchableOpacity as TouchableOpacity3, StyleSheet as StyleSheet5, ScrollView, TextInput as TextInput2, Platform as Platform3, Linking as Linking2 } from "react-native";
|
|
14669
14788
|
function TestListScreen({ nav }) {
|
|
14670
|
-
const { assignments, currentAssignment, refreshAssignments, isLoading } = useBugBear();
|
|
14789
|
+
const { assignments, currentAssignment, refreshAssignments, dashboardUrl, isLoading } = useBugBear();
|
|
14671
14790
|
const [filter, setFilter] = useState3("all");
|
|
14672
14791
|
const [roleFilter, setRoleFilter] = useState3(null);
|
|
14673
14792
|
const [trackFilter, setTrackFilter] = useState3(null);
|
|
14793
|
+
const [platformFilter, setPlatformFilter] = useState3(Platform3.OS === "android" ? "android" : "ios");
|
|
14674
14794
|
const [searchQuery, setSearchQuery] = useState3("");
|
|
14675
14795
|
const [sortMode, setSortMode] = useState3("priority");
|
|
14676
14796
|
const [collapsedFolders, setCollapsedFolders] = useState3(/* @__PURE__ */ new Set());
|
|
@@ -14737,6 +14857,7 @@ function TestListScreen({ nav }) {
|
|
|
14737
14857
|
});
|
|
14738
14858
|
}, []);
|
|
14739
14859
|
const filterAssignment = useCallback3((a) => {
|
|
14860
|
+
if (platformFilter && a.testCase.platforms && !a.testCase.platforms.includes(platformFilter)) return false;
|
|
14740
14861
|
if (roleFilter && a.testCase.role?.id !== roleFilter) return false;
|
|
14741
14862
|
if (trackFilter && a.testCase.track?.id !== trackFilter) return false;
|
|
14742
14863
|
if (searchQuery) {
|
|
@@ -14749,7 +14870,7 @@ function TestListScreen({ nav }) {
|
|
|
14749
14870
|
if (filter === "done") return a.status === "passed";
|
|
14750
14871
|
if (filter === "reopened") return a.status === "failed";
|
|
14751
14872
|
return true;
|
|
14752
|
-
}, [roleFilter, trackFilter, searchQuery, filter]);
|
|
14873
|
+
}, [platformFilter, roleFilter, trackFilter, searchQuery, filter]);
|
|
14753
14874
|
if (isLoading) return /* @__PURE__ */ React5.createElement(TestListScreenSkeleton, null);
|
|
14754
14875
|
return /* @__PURE__ */ React5.createElement(View4, null, /* @__PURE__ */ React5.createElement(View4, { style: styles3.filterBar }, [
|
|
14755
14876
|
{ key: "all", label: "All", count: assignments.length },
|
|
@@ -14787,7 +14908,32 @@ function TestListScreen({ nav }) {
|
|
|
14787
14908
|
placeholderTextColor: colors.textMuted,
|
|
14788
14909
|
style: styles3.searchInput
|
|
14789
14910
|
}
|
|
14790
|
-
)), /* @__PURE__ */ React5.createElement(
|
|
14911
|
+
)), /* @__PURE__ */ React5.createElement(ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, style: styles3.platformBar }, /* @__PURE__ */ React5.createElement(
|
|
14912
|
+
TouchableOpacity3,
|
|
14913
|
+
{
|
|
14914
|
+
style: [styles3.platformBtn, !platformFilter && styles3.platformBtnActive],
|
|
14915
|
+
onPress: () => setPlatformFilter(null)
|
|
14916
|
+
},
|
|
14917
|
+
/* @__PURE__ */ React5.createElement(Text3, { style: [styles3.platformBtnText, !platformFilter && styles3.platformBtnTextActive] }, "All")
|
|
14918
|
+
), [
|
|
14919
|
+
{ key: "web", label: "Web", icon: "\u{1F310}" },
|
|
14920
|
+
{ key: "ios", label: "iOS", icon: "\u{1F4F1}" },
|
|
14921
|
+
{ key: "android", label: "Android", icon: "\u{1F916}" }
|
|
14922
|
+
].map((p) => {
|
|
14923
|
+
const isActive = platformFilter === p.key;
|
|
14924
|
+
return /* @__PURE__ */ React5.createElement(
|
|
14925
|
+
TouchableOpacity3,
|
|
14926
|
+
{
|
|
14927
|
+
key: p.key,
|
|
14928
|
+
style: [
|
|
14929
|
+
styles3.platformBtn,
|
|
14930
|
+
isActive && { backgroundColor: colors.blue + "20", borderColor: colors.blue + "60", borderWidth: 1 }
|
|
14931
|
+
],
|
|
14932
|
+
onPress: () => setPlatformFilter(isActive ? null : p.key)
|
|
14933
|
+
},
|
|
14934
|
+
/* @__PURE__ */ React5.createElement(Text3, { style: [styles3.platformBtnText, isActive && { color: colors.blue, fontWeight: "600" }] }, p.icon, " ", p.label)
|
|
14935
|
+
);
|
|
14936
|
+
})), /* @__PURE__ */ React5.createElement(View4, { style: styles3.trackSortRow }, availableTracks.length >= 2 && /* @__PURE__ */ React5.createElement(ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, style: { flex: 1 } }, /* @__PURE__ */ React5.createElement(
|
|
14791
14937
|
TouchableOpacity3,
|
|
14792
14938
|
{
|
|
14793
14939
|
style: [styles3.trackBtn, !trackFilter && styles3.trackBtnActive],
|
|
@@ -14848,7 +14994,15 @@ function TestListScreen({ nav }) {
|
|
|
14848
14994
|
] }, badge.label))
|
|
14849
14995
|
);
|
|
14850
14996
|
}));
|
|
14851
|
-
}),
|
|
14997
|
+
}), dashboardUrl && /* @__PURE__ */ React5.createElement(
|
|
14998
|
+
TouchableOpacity3,
|
|
14999
|
+
{
|
|
15000
|
+
style: styles3.dashboardLink,
|
|
15001
|
+
onPress: () => Linking2.openURL(`${dashboardUrl}/test-cases`),
|
|
15002
|
+
activeOpacity: 0.7
|
|
15003
|
+
},
|
|
15004
|
+
/* @__PURE__ */ React5.createElement(Text3, { style: styles3.dashboardLinkText }, "\u{1F310}", " Manage on Dashboard ", "\u2192")
|
|
15005
|
+
), /* @__PURE__ */ React5.createElement(TouchableOpacity3, { style: styles3.refreshBtn, onPress: refreshAssignments }, /* @__PURE__ */ React5.createElement(Text3, { style: styles3.refreshText }, "\u21BB", " Refresh")));
|
|
14852
15006
|
}
|
|
14853
15007
|
var styles3 = StyleSheet5.create({
|
|
14854
15008
|
filterBar: { flexDirection: "row", gap: 8, marginBottom: 8 },
|
|
@@ -14887,6 +15041,11 @@ var styles3 = StyleSheet5.create({
|
|
|
14887
15041
|
statusPillText: { fontSize: 10, fontWeight: "600" },
|
|
14888
15042
|
searchContainer: { marginBottom: 8 },
|
|
14889
15043
|
searchInput: { backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border, borderRadius: 8, paddingHorizontal: 12, paddingVertical: 8, fontSize: 13, color: colors.textPrimary },
|
|
15044
|
+
platformBar: { flexDirection: "row", marginBottom: 8 },
|
|
15045
|
+
platformBtn: { flexDirection: "row", alignItems: "center", gap: 4, paddingHorizontal: 10, paddingVertical: 4, borderRadius: 6, marginRight: 6, borderWidth: 1, borderColor: "transparent" },
|
|
15046
|
+
platformBtnActive: { backgroundColor: colors.card, borderColor: colors.border },
|
|
15047
|
+
platformBtnText: { fontSize: 11, color: colors.textMuted },
|
|
15048
|
+
platformBtnTextActive: { color: colors.textPrimary, fontWeight: "600" },
|
|
14890
15049
|
trackSortRow: { flexDirection: "row", alignItems: "center", marginBottom: 10, gap: 8 },
|
|
14891
15050
|
trackBtn: { flexDirection: "row", alignItems: "center", gap: 4, paddingHorizontal: 10, paddingVertical: 4, borderRadius: 6, marginRight: 6, borderWidth: 1, borderColor: "transparent" },
|
|
14892
15051
|
trackBtnActive: { backgroundColor: colors.card, borderColor: colors.border },
|
|
@@ -14897,7 +15056,9 @@ var styles3 = StyleSheet5.create({
|
|
|
14897
15056
|
sortBtnActive: { backgroundColor: colors.card, borderColor: colors.border },
|
|
14898
15057
|
sortBtnText: { fontSize: 11, color: colors.textMuted },
|
|
14899
15058
|
sortBtnTextActive: { color: colors.textPrimary, fontWeight: "600" },
|
|
14900
|
-
|
|
15059
|
+
dashboardLink: { alignItems: "center", paddingTop: 12 },
|
|
15060
|
+
dashboardLinkText: { fontSize: 13, fontWeight: "500", color: colors.blue },
|
|
15061
|
+
refreshBtn: { alignItems: "center", paddingVertical: 8 },
|
|
14901
15062
|
refreshText: { fontSize: 13, color: colors.blue }
|
|
14902
15063
|
});
|
|
14903
15064
|
|
|
@@ -15066,7 +15227,9 @@ var styles4 = StyleSheet6.create({
|
|
|
15066
15227
|
|
|
15067
15228
|
// src/widget/ImagePickerButtons.tsx
|
|
15068
15229
|
function ImagePickerButtons({ images, maxImages, onPickGallery, onPickCamera, onRemove, label }) {
|
|
15069
|
-
if (!IMAGE_PICKER_AVAILABLE)
|
|
15230
|
+
if (!IMAGE_PICKER_AVAILABLE) {
|
|
15231
|
+
return /* @__PURE__ */ React7.createElement(View6, { style: styles5.section }, label && /* @__PURE__ */ React7.createElement(Text5, { style: styles5.label }, label), /* @__PURE__ */ React7.createElement(View6, { style: styles5.unavailableRow }, /* @__PURE__ */ React7.createElement(Text5, { style: styles5.unavailableText }, "Install react-native-image-picker to enable photo attachments")));
|
|
15232
|
+
}
|
|
15070
15233
|
return /* @__PURE__ */ React7.createElement(View6, { style: styles5.section }, label && /* @__PURE__ */ React7.createElement(Text5, { style: styles5.label }, label), /* @__PURE__ */ React7.createElement(View6, { style: styles5.buttonRow }, /* @__PURE__ */ React7.createElement(
|
|
15071
15234
|
TouchableOpacity5,
|
|
15072
15235
|
{
|
|
@@ -15121,6 +15284,18 @@ var styles5 = StyleSheet7.create({
|
|
|
15121
15284
|
fontSize: 12,
|
|
15122
15285
|
color: colors.textDim,
|
|
15123
15286
|
marginLeft: 4
|
|
15287
|
+
},
|
|
15288
|
+
unavailableRow: {
|
|
15289
|
+
backgroundColor: colors.card,
|
|
15290
|
+
borderWidth: 1,
|
|
15291
|
+
borderColor: colors.border,
|
|
15292
|
+
borderRadius: 8,
|
|
15293
|
+
padding: 12
|
|
15294
|
+
},
|
|
15295
|
+
unavailableText: {
|
|
15296
|
+
fontSize: 12,
|
|
15297
|
+
color: colors.textDim,
|
|
15298
|
+
fontStyle: "italic"
|
|
15124
15299
|
}
|
|
15125
15300
|
});
|
|
15126
15301
|
|
|
@@ -15619,9 +15794,9 @@ var styles9 = StyleSheet11.create({
|
|
|
15619
15794
|
|
|
15620
15795
|
// src/widget/screens/MessageListScreen.tsx
|
|
15621
15796
|
import React12 from "react";
|
|
15622
|
-
import { View as View11, Text as Text10, TouchableOpacity as TouchableOpacity9, StyleSheet as StyleSheet12 } from "react-native";
|
|
15797
|
+
import { View as View11, Text as Text10, TouchableOpacity as TouchableOpacity9, StyleSheet as StyleSheet12, Linking as Linking3 } from "react-native";
|
|
15623
15798
|
function MessageListScreen({ nav }) {
|
|
15624
|
-
const { threads, unreadCount, refreshThreads, isLoading } = useBugBear();
|
|
15799
|
+
const { threads, unreadCount, refreshThreads, dashboardUrl, isLoading } = useBugBear();
|
|
15625
15800
|
if (isLoading) return /* @__PURE__ */ React12.createElement(MessageListScreenSkeleton, null);
|
|
15626
15801
|
return /* @__PURE__ */ React12.createElement(View11, null, /* @__PURE__ */ React12.createElement(
|
|
15627
15802
|
TouchableOpacity9,
|
|
@@ -15639,7 +15814,15 @@ function MessageListScreen({ nav }) {
|
|
|
15639
15814
|
},
|
|
15640
15815
|
/* @__PURE__ */ React12.createElement(View11, { style: styles10.threadLeft }, /* @__PURE__ */ React12.createElement(Text10, { style: styles10.threadIcon }, getThreadTypeIcon(thread.threadType)), /* @__PURE__ */ React12.createElement(View11, { style: styles10.threadInfo }, /* @__PURE__ */ React12.createElement(View11, { style: styles10.threadTitleRow }, thread.isPinned && /* @__PURE__ */ React12.createElement(Text10, { style: styles10.pinIcon }, "\u{1F4CC}"), /* @__PURE__ */ React12.createElement(Text10, { style: styles10.threadSubject, numberOfLines: 1 }, thread.subject || "No subject")), thread.lastMessage && /* @__PURE__ */ React12.createElement(Text10, { style: styles10.threadPreview, numberOfLines: 1 }, thread.lastMessage.senderName, ": ", thread.lastMessage.content))),
|
|
15641
15816
|
/* @__PURE__ */ React12.createElement(View11, { style: styles10.threadRight }, /* @__PURE__ */ React12.createElement(Text10, { style: styles10.threadTime }, formatRelativeTime(thread.lastMessageAt)), thread.unreadCount > 0 && /* @__PURE__ */ React12.createElement(View11, { style: styles10.unreadBadge }, /* @__PURE__ */ React12.createElement(Text10, { style: styles10.unreadText }, thread.unreadCount)), thread.priority !== "normal" && /* @__PURE__ */ React12.createElement(View11, { style: [styles10.priorityDot, { backgroundColor: getPriorityColor(thread.priority) }] }))
|
|
15642
|
-
))),
|
|
15817
|
+
))), dashboardUrl && /* @__PURE__ */ React12.createElement(
|
|
15818
|
+
TouchableOpacity9,
|
|
15819
|
+
{
|
|
15820
|
+
style: styles10.dashboardLink,
|
|
15821
|
+
onPress: () => Linking3.openURL(`${dashboardUrl}/discussions`),
|
|
15822
|
+
activeOpacity: 0.7
|
|
15823
|
+
},
|
|
15824
|
+
/* @__PURE__ */ React12.createElement(Text10, { style: styles10.dashboardLinkText }, "\u{1F310}", " View on Dashboard ", "\u2192")
|
|
15825
|
+
), /* @__PURE__ */ React12.createElement(View11, { style: styles10.footer }, /* @__PURE__ */ React12.createElement(Text10, { style: styles10.footerText }, threads.length, " thread", threads.length !== 1 ? "s" : "", " \xB7 ", unreadCount, " unread"), /* @__PURE__ */ React12.createElement(TouchableOpacity9, { onPress: refreshThreads }, /* @__PURE__ */ React12.createElement(Text10, { style: styles10.refreshText }, "\u21BB Refresh"))));
|
|
15643
15826
|
}
|
|
15644
15827
|
var styles10 = StyleSheet12.create({
|
|
15645
15828
|
newMsgButton: { backgroundColor: colors.blue, paddingVertical: 12, borderRadius: 12, alignItems: "center", marginBottom: 16 },
|
|
@@ -15658,7 +15841,9 @@ var styles10 = StyleSheet12.create({
|
|
|
15658
15841
|
unreadBadge: { backgroundColor: colors.blue, borderRadius: 10, minWidth: 20, height: 20, justifyContent: "center", alignItems: "center", paddingHorizontal: 6 },
|
|
15659
15842
|
unreadText: { fontSize: 11, fontWeight: "bold", color: "#fff" },
|
|
15660
15843
|
priorityDot: { width: 8, height: 8, borderRadius: 4 },
|
|
15661
|
-
|
|
15844
|
+
dashboardLink: { alignItems: "center", paddingTop: 12 },
|
|
15845
|
+
dashboardLinkText: { fontSize: 13, fontWeight: "500", color: colors.blue },
|
|
15846
|
+
footer: { flexDirection: "row", justifyContent: "space-between", alignItems: "center", paddingTop: 8, paddingHorizontal: 4 },
|
|
15662
15847
|
footerText: { fontSize: 12, color: colors.textMuted },
|
|
15663
15848
|
refreshText: { fontSize: 13, color: colors.blue }
|
|
15664
15849
|
});
|
|
@@ -16139,7 +16324,7 @@ var styles14 = StyleSheet16.create({
|
|
|
16139
16324
|
|
|
16140
16325
|
// src/widget/screens/IssueDetailScreen.tsx
|
|
16141
16326
|
import React17 from "react";
|
|
16142
|
-
import { View as View16, Text as Text15, Image as Image3, StyleSheet as StyleSheet17, Linking as
|
|
16327
|
+
import { View as View16, Text as Text15, Image as Image3, StyleSheet as StyleSheet17, Linking as Linking4, TouchableOpacity as TouchableOpacity14 } from "react-native";
|
|
16143
16328
|
var STATUS_LABELS = {
|
|
16144
16329
|
new: { label: "New", bg: "#1e3a5f", color: "#60a5fa" },
|
|
16145
16330
|
triaging: { label: "Triaging", bg: "#1e3a5f", color: "#60a5fa" },
|
|
@@ -16161,9 +16346,18 @@ var SEVERITY_CONFIG = {
|
|
|
16161
16346
|
low: { label: "Low", color: "#71717a", bg: "#27272a" }
|
|
16162
16347
|
};
|
|
16163
16348
|
function IssueDetailScreen({ nav, issue }) {
|
|
16349
|
+
const { dashboardUrl } = useBugBear();
|
|
16164
16350
|
const statusConfig = STATUS_LABELS[issue.status] || { label: issue.status, bg: "#27272a", color: "#a1a1aa" };
|
|
16165
16351
|
const severityConfig = issue.severity ? SEVERITY_CONFIG[issue.severity] : null;
|
|
16166
|
-
return /* @__PURE__ */ React17.createElement(View16, null, /* @__PURE__ */ React17.createElement(View16, { style: styles15.badgeRow }, /* @__PURE__ */ React17.createElement(View16, { style: [styles15.badge, { backgroundColor: statusConfig.bg }] }, /* @__PURE__ */ React17.createElement(Text15, { style: [styles15.badgeText, { color: statusConfig.color }] }, statusConfig.label)), severityConfig && /* @__PURE__ */ React17.createElement(View16, { style: [styles15.badge, { backgroundColor: severityConfig.bg }] }, /* @__PURE__ */ React17.createElement(Text15, { style: [styles15.badgeText, { color: severityConfig.color }] }, severityConfig.label))), /* @__PURE__ */ React17.createElement(Text15, { style: styles15.title }, issue.title), issue.route && /* @__PURE__ */ React17.createElement(Text15, { style: styles15.route }, issue.route), issue.description && /* @__PURE__ */ React17.createElement(View16, { style: styles15.descriptionCard }, /* @__PURE__ */ React17.createElement(Text15, { style: styles15.descriptionText }, issue.description)), issue.verifiedByName && /* @__PURE__ */ React17.createElement(View16, { style: styles15.verifiedCard }, /* @__PURE__ */ React17.createElement(View16, { style: styles15.verifiedHeader }, /* @__PURE__ */ React17.createElement(Text15, { style: styles15.verifiedIcon }, "\u2705"), /* @__PURE__ */ React17.createElement(Text15, { style: styles15.verifiedTitle }, "Retesting Proof")), /* @__PURE__ */ React17.createElement(Text15, { style: styles15.verifiedBody }, "Verified by ", issue.verifiedByName, issue.verifiedAt && ` on ${new Date(issue.verifiedAt).toLocaleDateString(void 0, { month: "short", day: "numeric", year: "numeric" })}`)), issue.originalBugTitle && /* @__PURE__ */ React17.createElement(View16, { style: styles15.originalBugCard }, /* @__PURE__ */ React17.createElement(View16, { style: styles15.originalBugHeader }, /* @__PURE__ */ React17.createElement(Text15, { style: styles15.originalBugIcon }, "\u{1F504}"), /* @__PURE__ */ React17.createElement(Text15, { style: styles15.originalBugTitle }, "Original Bug")), /* @__PURE__ */ React17.createElement(Text15, { style: styles15.originalBugBody }, "Retest of: ", issue.originalBugTitle)), issue.screenshotUrls && issue.screenshotUrls.length > 0 && /* @__PURE__ */ React17.createElement(View16, { style: styles15.screenshotSection }, /* @__PURE__ */ React17.createElement(Text15, { style: styles15.screenshotLabel }, "Screenshots (", issue.screenshotUrls.length, ")"), /* @__PURE__ */ React17.createElement(View16, { style: styles15.screenshotRow }, issue.screenshotUrls.map((url, i) => /* @__PURE__ */ React17.createElement(TouchableOpacity14, { key: i, onPress: () =>
|
|
16352
|
+
return /* @__PURE__ */ React17.createElement(View16, null, /* @__PURE__ */ React17.createElement(View16, { style: styles15.badgeRow }, /* @__PURE__ */ React17.createElement(View16, { style: [styles15.badge, { backgroundColor: statusConfig.bg }] }, /* @__PURE__ */ React17.createElement(Text15, { style: [styles15.badgeText, { color: statusConfig.color }] }, statusConfig.label)), severityConfig && /* @__PURE__ */ React17.createElement(View16, { style: [styles15.badge, { backgroundColor: severityConfig.bg }] }, /* @__PURE__ */ React17.createElement(Text15, { style: [styles15.badgeText, { color: severityConfig.color }] }, severityConfig.label))), /* @__PURE__ */ React17.createElement(Text15, { style: styles15.title }, issue.title), issue.route && /* @__PURE__ */ React17.createElement(Text15, { style: styles15.route }, issue.route), issue.description && /* @__PURE__ */ React17.createElement(View16, { style: styles15.descriptionCard }, /* @__PURE__ */ React17.createElement(Text15, { style: styles15.descriptionText }, issue.description)), issue.verifiedByName && /* @__PURE__ */ React17.createElement(View16, { style: styles15.verifiedCard }, /* @__PURE__ */ React17.createElement(View16, { style: styles15.verifiedHeader }, /* @__PURE__ */ React17.createElement(Text15, { style: styles15.verifiedIcon }, "\u2705"), /* @__PURE__ */ React17.createElement(Text15, { style: styles15.verifiedTitle }, "Retesting Proof")), /* @__PURE__ */ React17.createElement(Text15, { style: styles15.verifiedBody }, "Verified by ", issue.verifiedByName, issue.verifiedAt && ` on ${new Date(issue.verifiedAt).toLocaleDateString(void 0, { month: "short", day: "numeric", year: "numeric" })}`)), issue.originalBugTitle && /* @__PURE__ */ React17.createElement(View16, { style: styles15.originalBugCard }, /* @__PURE__ */ React17.createElement(View16, { style: styles15.originalBugHeader }, /* @__PURE__ */ React17.createElement(Text15, { style: styles15.originalBugIcon }, "\u{1F504}"), /* @__PURE__ */ React17.createElement(Text15, { style: styles15.originalBugTitle }, "Original Bug")), /* @__PURE__ */ React17.createElement(Text15, { style: styles15.originalBugBody }, "Retest of: ", issue.originalBugTitle)), issue.screenshotUrls && issue.screenshotUrls.length > 0 && /* @__PURE__ */ React17.createElement(View16, { style: styles15.screenshotSection }, /* @__PURE__ */ React17.createElement(Text15, { style: styles15.screenshotLabel }, "Screenshots (", issue.screenshotUrls.length, ")"), /* @__PURE__ */ React17.createElement(View16, { style: styles15.screenshotRow }, issue.screenshotUrls.map((url, i) => /* @__PURE__ */ React17.createElement(TouchableOpacity14, { key: i, onPress: () => Linking4.openURL(url), activeOpacity: 0.7 }, /* @__PURE__ */ React17.createElement(Image3, { source: { uri: url }, style: styles15.screenshotThumb }))))), /* @__PURE__ */ React17.createElement(View16, { style: styles15.metaSection }, issue.reporterName && /* @__PURE__ */ React17.createElement(Text15, { style: styles15.metaText }, "Reported by ", issue.reporterName), /* @__PURE__ */ React17.createElement(Text15, { style: styles15.metaTextSmall }, "Created ", formatRelativeTime(issue.createdAt), " ", "\xB7", " Updated ", formatRelativeTime(issue.updatedAt))), dashboardUrl && /* @__PURE__ */ React17.createElement(
|
|
16353
|
+
TouchableOpacity14,
|
|
16354
|
+
{
|
|
16355
|
+
style: styles15.dashboardLink,
|
|
16356
|
+
onPress: () => Linking4.openURL(`${dashboardUrl}/reports`),
|
|
16357
|
+
activeOpacity: 0.7
|
|
16358
|
+
},
|
|
16359
|
+
/* @__PURE__ */ React17.createElement(Text15, { style: styles15.dashboardLinkText }, "\u{1F310}", " View on Dashboard ", "\u2192")
|
|
16360
|
+
));
|
|
16167
16361
|
}
|
|
16168
16362
|
var styles15 = StyleSheet17.create({
|
|
16169
16363
|
badgeRow: {
|
|
@@ -16292,6 +16486,18 @@ var styles15 = StyleSheet17.create({
|
|
|
16292
16486
|
metaTextSmall: {
|
|
16293
16487
|
fontSize: 11,
|
|
16294
16488
|
color: colors.textDim
|
|
16489
|
+
},
|
|
16490
|
+
dashboardLink: {
|
|
16491
|
+
alignItems: "center",
|
|
16492
|
+
paddingVertical: 10,
|
|
16493
|
+
marginTop: 12,
|
|
16494
|
+
borderTopWidth: 1,
|
|
16495
|
+
borderTopColor: colors.border
|
|
16496
|
+
},
|
|
16497
|
+
dashboardLinkText: {
|
|
16498
|
+
fontSize: 13,
|
|
16499
|
+
fontWeight: "500",
|
|
16500
|
+
color: colors.blue
|
|
16295
16501
|
}
|
|
16296
16502
|
});
|
|
16297
16503
|
|
|
@@ -16479,7 +16685,7 @@ function BugBearButton({
|
|
|
16479
16685
|
/* @__PURE__ */ React18.createElement(
|
|
16480
16686
|
KeyboardAvoidingView,
|
|
16481
16687
|
{
|
|
16482
|
-
behavior:
|
|
16688
|
+
behavior: Platform5.OS === "ios" ? "padding" : "height",
|
|
16483
16689
|
style: styles16.modalOverlay
|
|
16484
16690
|
},
|
|
16485
16691
|
/* @__PURE__ */ React18.createElement(View17, { style: styles16.modalContainer }, /* @__PURE__ */ React18.createElement(View17, { style: styles16.header }, /* @__PURE__ */ React18.createElement(View17, { style: styles16.headerLeft }, canGoBack ? /* @__PURE__ */ React18.createElement(View17, { style: styles16.headerNavRow }, /* @__PURE__ */ React18.createElement(TouchableOpacity15, { onPress: () => nav.pop(), style: styles16.backButton }, /* @__PURE__ */ React18.createElement(Text16, { style: styles16.backText }, "\u2190 Back")), /* @__PURE__ */ React18.createElement(TouchableOpacity15, { onPress: () => nav.reset(), style: styles16.homeButton }, /* @__PURE__ */ React18.createElement(Text16, { style: styles16.homeText }, "\u{1F3E0}"))) : /* @__PURE__ */ React18.createElement(View17, { style: styles16.headerTitleRow }, /* @__PURE__ */ React18.createElement(Text16, { style: styles16.headerTitle }, "BugBear"), testerInfo && /* @__PURE__ */ React18.createElement(TouchableOpacity15, { onPress: () => push({ name: "PROFILE" }) }, /* @__PURE__ */ React18.createElement(Text16, { style: styles16.headerName }, testerInfo.name, " \u270E")))), getHeaderTitle() ? /* @__PURE__ */ React18.createElement(Text16, { style: styles16.headerScreenTitle, numberOfLines: 1 }, getHeaderTitle()) : null, /* @__PURE__ */ React18.createElement(TouchableOpacity15, { onPress: handleClose, style: styles16.closeButton }, /* @__PURE__ */ React18.createElement(Text16, { style: styles16.closeText }, "\u2715"))), /* @__PURE__ */ React18.createElement(
|