@bbearai/react-native 0.6.0 → 0.6.1
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 +222 -24
- package/dist/index.mjs +227 -29
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -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,24 @@ 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 twentyFourHoursAgo = new Date(Date.now() - 24 * 60 * 60 * 1e3).toISOString();
|
|
12118
|
+
const [pendingResult, completedResult] = await Promise.all([
|
|
12119
|
+
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),
|
|
12120
|
+
this.supabase.from("test_assignments").select(selectFields).eq("project_id", this.config.projectId).eq("tester_id", testerInfo.id).in("status", ["passed", "failed", "skipped", "blocked"]).gte("completed_at", twentyFourHoursAgo).order("completed_at", { ascending: false }).limit(50)
|
|
12121
|
+
]);
|
|
12122
|
+
if (pendingResult.error) {
|
|
12123
|
+
console.error("BugBear: Failed to fetch assignments", formatPgError(pendingResult.error));
|
|
12117
12124
|
return [];
|
|
12118
12125
|
}
|
|
12119
|
-
const
|
|
12120
|
-
|
|
12121
|
-
|
|
12122
|
-
|
|
12123
|
-
|
|
12124
|
-
return true;
|
|
12125
|
-
}).map((item) => ({
|
|
12126
|
+
const allData = [
|
|
12127
|
+
...pendingResult.data || [],
|
|
12128
|
+
...completedResult.data || []
|
|
12129
|
+
];
|
|
12130
|
+
const mapItem = (item) => ({
|
|
12126
12131
|
id: item.id,
|
|
12127
12132
|
status: item.status,
|
|
12128
12133
|
startedAt: item.started_at,
|
|
@@ -12160,12 +12165,24 @@ var BugBearClient = class {
|
|
|
12160
12165
|
color: item.test_case.role.color,
|
|
12161
12166
|
description: item.test_case.role.description,
|
|
12162
12167
|
loginHint: item.test_case.role.login_hint
|
|
12163
|
-
} : void 0
|
|
12168
|
+
} : void 0,
|
|
12169
|
+
platforms: item.test_case.platforms || void 0
|
|
12164
12170
|
}
|
|
12165
|
-
})
|
|
12171
|
+
});
|
|
12172
|
+
const mapped = allData.filter((item) => {
|
|
12173
|
+
if (!item.test_case) {
|
|
12174
|
+
console.warn("BugBear: Assignment returned without test_case", { id: item.id });
|
|
12175
|
+
return false;
|
|
12176
|
+
}
|
|
12177
|
+
return true;
|
|
12178
|
+
}).map(mapItem);
|
|
12166
12179
|
mapped.sort((a, b) => {
|
|
12167
12180
|
if (a.isVerification && !b.isVerification) return -1;
|
|
12168
12181
|
if (!a.isVerification && b.isVerification) return 1;
|
|
12182
|
+
const aActive = a.status === "pending" || a.status === "in_progress";
|
|
12183
|
+
const bActive = b.status === "pending" || b.status === "in_progress";
|
|
12184
|
+
if (aActive && !bActive) return -1;
|
|
12185
|
+
if (!aActive && bActive) return 1;
|
|
12169
12186
|
return 0;
|
|
12170
12187
|
});
|
|
12171
12188
|
return mapped;
|
|
@@ -12330,6 +12347,36 @@ var BugBearClient = class {
|
|
|
12330
12347
|
async failAssignment(assignmentId) {
|
|
12331
12348
|
return this.updateAssignmentStatus(assignmentId, "failed");
|
|
12332
12349
|
}
|
|
12350
|
+
/**
|
|
12351
|
+
* Reopen a completed assignment — sets it back to in_progress with a fresh timer.
|
|
12352
|
+
* Clears completed_at and duration_seconds so it can be re-evaluated.
|
|
12353
|
+
*/
|
|
12354
|
+
async reopenAssignment(assignmentId) {
|
|
12355
|
+
try {
|
|
12356
|
+
const { data: current, error: fetchError } = await this.supabase.from("test_assignments").select("status").eq("id", assignmentId).single();
|
|
12357
|
+
if (fetchError || !current) {
|
|
12358
|
+
return { success: false, error: "Assignment not found" };
|
|
12359
|
+
}
|
|
12360
|
+
if (current.status === "pending" || current.status === "in_progress") {
|
|
12361
|
+
return { success: true };
|
|
12362
|
+
}
|
|
12363
|
+
const { error } = await this.supabase.from("test_assignments").update({
|
|
12364
|
+
status: "in_progress",
|
|
12365
|
+
started_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12366
|
+
completed_at: null,
|
|
12367
|
+
duration_seconds: null,
|
|
12368
|
+
skip_reason: null
|
|
12369
|
+
}).eq("id", assignmentId).eq("status", current.status);
|
|
12370
|
+
if (error) {
|
|
12371
|
+
console.error("BugBear: Failed to reopen assignment", error);
|
|
12372
|
+
return { success: false, error: error.message };
|
|
12373
|
+
}
|
|
12374
|
+
return { success: true };
|
|
12375
|
+
} catch (err) {
|
|
12376
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
12377
|
+
return { success: false, error: message };
|
|
12378
|
+
}
|
|
12379
|
+
}
|
|
12333
12380
|
/**
|
|
12334
12381
|
* Skip a test assignment with a required reason
|
|
12335
12382
|
* Marks the assignment as 'skipped' and records why it was skipped
|
|
@@ -14470,6 +14517,39 @@ function TestDetailScreen({ testId, nav }) {
|
|
|
14470
14517
|
setIsSubmitting(false);
|
|
14471
14518
|
}
|
|
14472
14519
|
}, [client, displayedAssignment, refreshAssignments, nav, isSubmitting]);
|
|
14520
|
+
const handleReopen = (0, import_react5.useCallback)(async () => {
|
|
14521
|
+
if (!client || !displayedAssignment || isSubmitting) return;
|
|
14522
|
+
import_react_native5.Keyboard.dismiss();
|
|
14523
|
+
setIsSubmitting(true);
|
|
14524
|
+
try {
|
|
14525
|
+
await client.reopenAssignment(displayedAssignment.id);
|
|
14526
|
+
await refreshAssignments();
|
|
14527
|
+
} finally {
|
|
14528
|
+
setIsSubmitting(false);
|
|
14529
|
+
}
|
|
14530
|
+
}, [client, displayedAssignment, refreshAssignments, isSubmitting]);
|
|
14531
|
+
const handleChangeResult = (0, import_react5.useCallback)(async (newStatus) => {
|
|
14532
|
+
if (!client || !displayedAssignment || isSubmitting) return;
|
|
14533
|
+
import_react_native5.Keyboard.dismiss();
|
|
14534
|
+
setIsSubmitting(true);
|
|
14535
|
+
try {
|
|
14536
|
+
await client.reopenAssignment(displayedAssignment.id);
|
|
14537
|
+
await client.updateAssignmentStatus(displayedAssignment.id, newStatus);
|
|
14538
|
+
await refreshAssignments();
|
|
14539
|
+
if (newStatus === "failed") {
|
|
14540
|
+
nav.replace({
|
|
14541
|
+
name: "REPORT",
|
|
14542
|
+
prefill: {
|
|
14543
|
+
type: "test_fail",
|
|
14544
|
+
assignmentId: displayedAssignment.id,
|
|
14545
|
+
testCaseId: displayedAssignment.testCase.id
|
|
14546
|
+
}
|
|
14547
|
+
});
|
|
14548
|
+
}
|
|
14549
|
+
} finally {
|
|
14550
|
+
setIsSubmitting(false);
|
|
14551
|
+
}
|
|
14552
|
+
}, [client, displayedAssignment, refreshAssignments, nav, isSubmitting]);
|
|
14473
14553
|
const handleSkip = (0, import_react5.useCallback)(async () => {
|
|
14474
14554
|
if (!client || !displayedAssignment || !selectedSkipReason) return;
|
|
14475
14555
|
import_react_native5.Keyboard.dismiss();
|
|
@@ -14550,7 +14630,39 @@ function TestDetailScreen({ testId, nav }) {
|
|
|
14550
14630
|
}
|
|
14551
14631
|
},
|
|
14552
14632
|
/* @__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.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))),
|
|
14633
|
+
), /* @__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.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: [
|
|
14634
|
+
styles2.completedBanner,
|
|
14635
|
+
displayedAssignment.status === "passed" && styles2.completedBannerPass,
|
|
14636
|
+
displayedAssignment.status === "failed" && styles2.completedBannerFail
|
|
14637
|
+
] }, /* @__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: [
|
|
14638
|
+
styles2.completedLabel,
|
|
14639
|
+
displayedAssignment.status === "passed" && { color: colors.green },
|
|
14640
|
+
displayedAssignment.status === "failed" && { color: "#fca5a5" }
|
|
14641
|
+
] }, "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(
|
|
14642
|
+
import_react_native5.TouchableOpacity,
|
|
14643
|
+
{
|
|
14644
|
+
style: [styles2.actionBtn, styles2.reopenBtn, isSubmitting && { opacity: 0.5 }],
|
|
14645
|
+
onPress: handleReopen,
|
|
14646
|
+
disabled: isSubmitting
|
|
14647
|
+
},
|
|
14648
|
+
/* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.reopenBtnText }, isSubmitting ? "Reopening..." : "\u{1F504} Reopen Test")
|
|
14649
|
+
), displayedAssignment.status === "passed" && /* @__PURE__ */ import_react5.default.createElement(
|
|
14650
|
+
import_react_native5.TouchableOpacity,
|
|
14651
|
+
{
|
|
14652
|
+
style: [styles2.actionBtn, styles2.failBtn, isSubmitting && { opacity: 0.5 }],
|
|
14653
|
+
onPress: () => handleChangeResult("failed"),
|
|
14654
|
+
disabled: isSubmitting
|
|
14655
|
+
},
|
|
14656
|
+
/* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.failBtnText }, "Change to Fail")
|
|
14657
|
+
), displayedAssignment.status === "failed" && /* @__PURE__ */ import_react5.default.createElement(
|
|
14658
|
+
import_react_native5.TouchableOpacity,
|
|
14659
|
+
{
|
|
14660
|
+
style: [styles2.actionBtn, styles2.passBtn, isSubmitting && { opacity: 0.5 }],
|
|
14661
|
+
onPress: () => handleChangeResult("passed"),
|
|
14662
|
+
disabled: isSubmitting
|
|
14663
|
+
},
|
|
14664
|
+
/* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.passBtnText }, "Change to Pass")
|
|
14665
|
+
))) : /* @__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
14666
|
{ reason: "blocked", label: "\u{1F6AB} Blocked by a bug" },
|
|
14555
14667
|
{ reason: "not_ready", label: "\u{1F6A7} Feature not ready" },
|
|
14556
14668
|
{ reason: "dependency", label: "\u{1F517} Needs another test first" },
|
|
@@ -14655,6 +14767,14 @@ var styles2 = import_react_native5.StyleSheet.create({
|
|
|
14655
14767
|
detailDesc: { fontSize: 13, color: colors.textSecondary, lineHeight: 18 },
|
|
14656
14768
|
folderProgress: { marginTop: 8 },
|
|
14657
14769
|
folderName: { fontSize: 12, color: colors.textMuted },
|
|
14770
|
+
// Completed state
|
|
14771
|
+
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 },
|
|
14772
|
+
completedBannerPass: { backgroundColor: colors.greenDark, borderColor: colors.green },
|
|
14773
|
+
completedBannerFail: { backgroundColor: colors.redDark, borderColor: colors.red },
|
|
14774
|
+
completedIcon: { fontSize: 14 },
|
|
14775
|
+
completedLabel: { fontSize: 13, fontWeight: "600", color: colors.textSecondary },
|
|
14776
|
+
reopenBtn: { backgroundColor: colors.card, borderWidth: 1, borderColor: colors.blue },
|
|
14777
|
+
reopenBtnText: { fontSize: 14, fontWeight: "600", color: colors.blue },
|
|
14658
14778
|
// Action buttons
|
|
14659
14779
|
actionButtons: { flexDirection: "row", gap: 10, marginTop: 8 },
|
|
14660
14780
|
actionBtn: { flex: 1, paddingVertical: 14, borderRadius: 12, alignItems: "center" },
|
|
@@ -14685,10 +14805,11 @@ var styles2 = import_react_native5.StyleSheet.create({
|
|
|
14685
14805
|
var import_react6 = __toESM(require("react"));
|
|
14686
14806
|
var import_react_native6 = require("react-native");
|
|
14687
14807
|
function TestListScreen({ nav }) {
|
|
14688
|
-
const { assignments, currentAssignment, refreshAssignments, isLoading } = useBugBear();
|
|
14808
|
+
const { assignments, currentAssignment, refreshAssignments, dashboardUrl, isLoading } = useBugBear();
|
|
14689
14809
|
const [filter, setFilter] = (0, import_react6.useState)("all");
|
|
14690
14810
|
const [roleFilter, setRoleFilter] = (0, import_react6.useState)(null);
|
|
14691
14811
|
const [trackFilter, setTrackFilter] = (0, import_react6.useState)(null);
|
|
14812
|
+
const [platformFilter, setPlatformFilter] = (0, import_react6.useState)(import_react_native6.Platform.OS === "android" ? "android" : "ios");
|
|
14692
14813
|
const [searchQuery, setSearchQuery] = (0, import_react6.useState)("");
|
|
14693
14814
|
const [sortMode, setSortMode] = (0, import_react6.useState)("priority");
|
|
14694
14815
|
const [collapsedFolders, setCollapsedFolders] = (0, import_react6.useState)(/* @__PURE__ */ new Set());
|
|
@@ -14709,6 +14830,13 @@ function TestListScreen({ nav }) {
|
|
|
14709
14830
|
}
|
|
14710
14831
|
return Array.from(trackMap.values());
|
|
14711
14832
|
}, [assignments]);
|
|
14833
|
+
const availablePlatforms = (0, import_react6.useMemo)(() => {
|
|
14834
|
+
const set = /* @__PURE__ */ new Set();
|
|
14835
|
+
for (const a of assignments) {
|
|
14836
|
+
if (a.testCase.platforms) a.testCase.platforms.forEach((p) => set.add(p));
|
|
14837
|
+
}
|
|
14838
|
+
return Array.from(set).sort();
|
|
14839
|
+
}, [assignments]);
|
|
14712
14840
|
const selectedRole = availableRoles.find((r) => r.id === roleFilter);
|
|
14713
14841
|
const groupedAssignments = (0, import_react6.useMemo)(() => {
|
|
14714
14842
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -14755,6 +14883,7 @@ function TestListScreen({ nav }) {
|
|
|
14755
14883
|
});
|
|
14756
14884
|
}, []);
|
|
14757
14885
|
const filterAssignment = (0, import_react6.useCallback)((a) => {
|
|
14886
|
+
if (platformFilter && a.testCase.platforms && !a.testCase.platforms.includes(platformFilter)) return false;
|
|
14758
14887
|
if (roleFilter && a.testCase.role?.id !== roleFilter) return false;
|
|
14759
14888
|
if (trackFilter && a.testCase.track?.id !== trackFilter) return false;
|
|
14760
14889
|
if (searchQuery) {
|
|
@@ -14767,7 +14896,7 @@ function TestListScreen({ nav }) {
|
|
|
14767
14896
|
if (filter === "done") return a.status === "passed";
|
|
14768
14897
|
if (filter === "reopened") return a.status === "failed";
|
|
14769
14898
|
return true;
|
|
14770
|
-
}, [roleFilter, trackFilter, searchQuery, filter]);
|
|
14899
|
+
}, [platformFilter, roleFilter, trackFilter, searchQuery, filter]);
|
|
14771
14900
|
if (isLoading) return /* @__PURE__ */ import_react6.default.createElement(TestListScreenSkeleton, null);
|
|
14772
14901
|
return /* @__PURE__ */ import_react6.default.createElement(import_react_native6.View, null, /* @__PURE__ */ import_react6.default.createElement(import_react_native6.View, { style: styles3.filterBar }, [
|
|
14773
14902
|
{ key: "all", label: "All", count: assignments.length },
|
|
@@ -14805,7 +14934,30 @@ function TestListScreen({ nav }) {
|
|
|
14805
14934
|
placeholderTextColor: colors.textMuted,
|
|
14806
14935
|
style: styles3.searchInput
|
|
14807
14936
|
}
|
|
14808
|
-
)),
|
|
14937
|
+
)), availablePlatforms.length >= 2 && /* @__PURE__ */ import_react6.default.createElement(import_react_native6.ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, style: styles3.platformBar }, /* @__PURE__ */ import_react6.default.createElement(
|
|
14938
|
+
import_react_native6.TouchableOpacity,
|
|
14939
|
+
{
|
|
14940
|
+
style: [styles3.platformBtn, !platformFilter && styles3.platformBtnActive],
|
|
14941
|
+
onPress: () => setPlatformFilter(null)
|
|
14942
|
+
},
|
|
14943
|
+
/* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: [styles3.platformBtnText, !platformFilter && styles3.platformBtnTextActive] }, "All Platforms")
|
|
14944
|
+
), availablePlatforms.map((p) => {
|
|
14945
|
+
const isActive = platformFilter === p;
|
|
14946
|
+
const label = p === "ios" ? "iOS" : p === "android" ? "Android" : p === "web" ? "Web" : p;
|
|
14947
|
+
const icon = p === "ios" ? "\u{1F4F1}" : p === "android" ? "\u{1F916}" : p === "web" ? "\u{1F310}" : "\u{1F4CB}";
|
|
14948
|
+
return /* @__PURE__ */ import_react6.default.createElement(
|
|
14949
|
+
import_react_native6.TouchableOpacity,
|
|
14950
|
+
{
|
|
14951
|
+
key: p,
|
|
14952
|
+
style: [
|
|
14953
|
+
styles3.platformBtn,
|
|
14954
|
+
isActive && { backgroundColor: colors.blue + "20", borderColor: colors.blue + "60", borderWidth: 1 }
|
|
14955
|
+
],
|
|
14956
|
+
onPress: () => setPlatformFilter(isActive ? null : p)
|
|
14957
|
+
},
|
|
14958
|
+
/* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: [styles3.platformBtnText, isActive && { color: colors.blue, fontWeight: "600" }] }, icon, " ", label)
|
|
14959
|
+
);
|
|
14960
|
+
})), /* @__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
14961
|
import_react_native6.TouchableOpacity,
|
|
14810
14962
|
{
|
|
14811
14963
|
style: [styles3.trackBtn, !trackFilter && styles3.trackBtnActive],
|
|
@@ -14866,7 +15018,15 @@ function TestListScreen({ nav }) {
|
|
|
14866
15018
|
] }, badge.label))
|
|
14867
15019
|
);
|
|
14868
15020
|
}));
|
|
14869
|
-
}),
|
|
15021
|
+
}), dashboardUrl && /* @__PURE__ */ import_react6.default.createElement(
|
|
15022
|
+
import_react_native6.TouchableOpacity,
|
|
15023
|
+
{
|
|
15024
|
+
style: styles3.dashboardLink,
|
|
15025
|
+
onPress: () => import_react_native6.Linking.openURL(`${dashboardUrl}/test-cases`),
|
|
15026
|
+
activeOpacity: 0.7
|
|
15027
|
+
},
|
|
15028
|
+
/* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: styles3.dashboardLinkText }, "\u{1F310}", " Manage on Dashboard ", "\u2192")
|
|
15029
|
+
), /* @__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
15030
|
}
|
|
14871
15031
|
var styles3 = import_react_native6.StyleSheet.create({
|
|
14872
15032
|
filterBar: { flexDirection: "row", gap: 8, marginBottom: 8 },
|
|
@@ -14905,6 +15065,11 @@ var styles3 = import_react_native6.StyleSheet.create({
|
|
|
14905
15065
|
statusPillText: { fontSize: 10, fontWeight: "600" },
|
|
14906
15066
|
searchContainer: { marginBottom: 8 },
|
|
14907
15067
|
searchInput: { backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border, borderRadius: 8, paddingHorizontal: 12, paddingVertical: 8, fontSize: 13, color: colors.textPrimary },
|
|
15068
|
+
platformBar: { flexDirection: "row", marginBottom: 8 },
|
|
15069
|
+
platformBtn: { flexDirection: "row", alignItems: "center", gap: 4, paddingHorizontal: 10, paddingVertical: 4, borderRadius: 6, marginRight: 6, borderWidth: 1, borderColor: "transparent" },
|
|
15070
|
+
platformBtnActive: { backgroundColor: colors.card, borderColor: colors.border },
|
|
15071
|
+
platformBtnText: { fontSize: 11, color: colors.textMuted },
|
|
15072
|
+
platformBtnTextActive: { color: colors.textPrimary, fontWeight: "600" },
|
|
14908
15073
|
trackSortRow: { flexDirection: "row", alignItems: "center", marginBottom: 10, gap: 8 },
|
|
14909
15074
|
trackBtn: { flexDirection: "row", alignItems: "center", gap: 4, paddingHorizontal: 10, paddingVertical: 4, borderRadius: 6, marginRight: 6, borderWidth: 1, borderColor: "transparent" },
|
|
14910
15075
|
trackBtnActive: { backgroundColor: colors.card, borderColor: colors.border },
|
|
@@ -14915,7 +15080,9 @@ var styles3 = import_react_native6.StyleSheet.create({
|
|
|
14915
15080
|
sortBtnActive: { backgroundColor: colors.card, borderColor: colors.border },
|
|
14916
15081
|
sortBtnText: { fontSize: 11, color: colors.textMuted },
|
|
14917
15082
|
sortBtnTextActive: { color: colors.textPrimary, fontWeight: "600" },
|
|
14918
|
-
|
|
15083
|
+
dashboardLink: { alignItems: "center", paddingTop: 12 },
|
|
15084
|
+
dashboardLinkText: { fontSize: 13, fontWeight: "500", color: colors.blue },
|
|
15085
|
+
refreshBtn: { alignItems: "center", paddingVertical: 8 },
|
|
14919
15086
|
refreshText: { fontSize: 13, color: colors.blue }
|
|
14920
15087
|
});
|
|
14921
15088
|
|
|
@@ -15639,7 +15806,7 @@ var styles9 = import_react_native12.StyleSheet.create({
|
|
|
15639
15806
|
var import_react14 = __toESM(require("react"));
|
|
15640
15807
|
var import_react_native13 = require("react-native");
|
|
15641
15808
|
function MessageListScreen({ nav }) {
|
|
15642
|
-
const { threads, unreadCount, refreshThreads, isLoading } = useBugBear();
|
|
15809
|
+
const { threads, unreadCount, refreshThreads, dashboardUrl, isLoading } = useBugBear();
|
|
15643
15810
|
if (isLoading) return /* @__PURE__ */ import_react14.default.createElement(MessageListScreenSkeleton, null);
|
|
15644
15811
|
return /* @__PURE__ */ import_react14.default.createElement(import_react_native13.View, null, /* @__PURE__ */ import_react14.default.createElement(
|
|
15645
15812
|
import_react_native13.TouchableOpacity,
|
|
@@ -15657,7 +15824,15 @@ function MessageListScreen({ nav }) {
|
|
|
15657
15824
|
},
|
|
15658
15825
|
/* @__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
15826
|
/* @__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
|
-
))),
|
|
15827
|
+
))), dashboardUrl && /* @__PURE__ */ import_react14.default.createElement(
|
|
15828
|
+
import_react_native13.TouchableOpacity,
|
|
15829
|
+
{
|
|
15830
|
+
style: styles10.dashboardLink,
|
|
15831
|
+
onPress: () => import_react_native13.Linking.openURL(`${dashboardUrl}/discussions`),
|
|
15832
|
+
activeOpacity: 0.7
|
|
15833
|
+
},
|
|
15834
|
+
/* @__PURE__ */ import_react14.default.createElement(import_react_native13.Text, { style: styles10.dashboardLinkText }, "\u{1F310}", " View on Dashboard ", "\u2192")
|
|
15835
|
+
), /* @__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
15836
|
}
|
|
15662
15837
|
var styles10 = import_react_native13.StyleSheet.create({
|
|
15663
15838
|
newMsgButton: { backgroundColor: colors.blue, paddingVertical: 12, borderRadius: 12, alignItems: "center", marginBottom: 16 },
|
|
@@ -15676,7 +15851,9 @@ var styles10 = import_react_native13.StyleSheet.create({
|
|
|
15676
15851
|
unreadBadge: { backgroundColor: colors.blue, borderRadius: 10, minWidth: 20, height: 20, justifyContent: "center", alignItems: "center", paddingHorizontal: 6 },
|
|
15677
15852
|
unreadText: { fontSize: 11, fontWeight: "bold", color: "#fff" },
|
|
15678
15853
|
priorityDot: { width: 8, height: 8, borderRadius: 4 },
|
|
15679
|
-
|
|
15854
|
+
dashboardLink: { alignItems: "center", paddingTop: 12 },
|
|
15855
|
+
dashboardLinkText: { fontSize: 13, fontWeight: "500", color: colors.blue },
|
|
15856
|
+
footer: { flexDirection: "row", justifyContent: "space-between", alignItems: "center", paddingTop: 8, paddingHorizontal: 4 },
|
|
15680
15857
|
footerText: { fontSize: 12, color: colors.textMuted },
|
|
15681
15858
|
refreshText: { fontSize: 13, color: colors.blue }
|
|
15682
15859
|
});
|
|
@@ -16179,9 +16356,18 @@ var SEVERITY_CONFIG = {
|
|
|
16179
16356
|
low: { label: "Low", color: "#71717a", bg: "#27272a" }
|
|
16180
16357
|
};
|
|
16181
16358
|
function IssueDetailScreen({ nav, issue }) {
|
|
16359
|
+
const { dashboardUrl } = useBugBear();
|
|
16182
16360
|
const statusConfig = STATUS_LABELS[issue.status] || { label: issue.status, bg: "#27272a", color: "#a1a1aa" };
|
|
16183
16361
|
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)))
|
|
16362
|
+
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(
|
|
16363
|
+
import_react_native18.TouchableOpacity,
|
|
16364
|
+
{
|
|
16365
|
+
style: styles15.dashboardLink,
|
|
16366
|
+
onPress: () => import_react_native18.Linking.openURL(`${dashboardUrl}/reports`),
|
|
16367
|
+
activeOpacity: 0.7
|
|
16368
|
+
},
|
|
16369
|
+
/* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.dashboardLinkText }, "\u{1F310}", " View on Dashboard ", "\u2192")
|
|
16370
|
+
));
|
|
16185
16371
|
}
|
|
16186
16372
|
var styles15 = import_react_native18.StyleSheet.create({
|
|
16187
16373
|
badgeRow: {
|
|
@@ -16310,6 +16496,18 @@ var styles15 = import_react_native18.StyleSheet.create({
|
|
|
16310
16496
|
metaTextSmall: {
|
|
16311
16497
|
fontSize: 11,
|
|
16312
16498
|
color: colors.textDim
|
|
16499
|
+
},
|
|
16500
|
+
dashboardLink: {
|
|
16501
|
+
alignItems: "center",
|
|
16502
|
+
paddingVertical: 10,
|
|
16503
|
+
marginTop: 12,
|
|
16504
|
+
borderTopWidth: 1,
|
|
16505
|
+
borderTopColor: colors.border
|
|
16506
|
+
},
|
|
16507
|
+
dashboardLinkText: {
|
|
16508
|
+
fontSize: 13,
|
|
16509
|
+
fontWeight: "500",
|
|
16510
|
+
color: colors.blue
|
|
16313
16511
|
}
|
|
16314
16512
|
});
|
|
16315
16513
|
|
package/dist/index.mjs
CHANGED
|
@@ -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,24 @@ 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 twentyFourHoursAgo = new Date(Date.now() - 24 * 60 * 60 * 1e3).toISOString();
|
|
12085
|
+
const [pendingResult, completedResult] = await Promise.all([
|
|
12086
|
+
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),
|
|
12087
|
+
this.supabase.from("test_assignments").select(selectFields).eq("project_id", this.config.projectId).eq("tester_id", testerInfo.id).in("status", ["passed", "failed", "skipped", "blocked"]).gte("completed_at", twentyFourHoursAgo).order("completed_at", { ascending: false }).limit(50)
|
|
12088
|
+
]);
|
|
12089
|
+
if (pendingResult.error) {
|
|
12090
|
+
console.error("BugBear: Failed to fetch assignments", formatPgError(pendingResult.error));
|
|
12084
12091
|
return [];
|
|
12085
12092
|
}
|
|
12086
|
-
const
|
|
12087
|
-
|
|
12088
|
-
|
|
12089
|
-
|
|
12090
|
-
|
|
12091
|
-
return true;
|
|
12092
|
-
}).map((item) => ({
|
|
12093
|
+
const allData = [
|
|
12094
|
+
...pendingResult.data || [],
|
|
12095
|
+
...completedResult.data || []
|
|
12096
|
+
];
|
|
12097
|
+
const mapItem = (item) => ({
|
|
12093
12098
|
id: item.id,
|
|
12094
12099
|
status: item.status,
|
|
12095
12100
|
startedAt: item.started_at,
|
|
@@ -12127,12 +12132,24 @@ var BugBearClient = class {
|
|
|
12127
12132
|
color: item.test_case.role.color,
|
|
12128
12133
|
description: item.test_case.role.description,
|
|
12129
12134
|
loginHint: item.test_case.role.login_hint
|
|
12130
|
-
} : void 0
|
|
12135
|
+
} : void 0,
|
|
12136
|
+
platforms: item.test_case.platforms || void 0
|
|
12131
12137
|
}
|
|
12132
|
-
})
|
|
12138
|
+
});
|
|
12139
|
+
const mapped = allData.filter((item) => {
|
|
12140
|
+
if (!item.test_case) {
|
|
12141
|
+
console.warn("BugBear: Assignment returned without test_case", { id: item.id });
|
|
12142
|
+
return false;
|
|
12143
|
+
}
|
|
12144
|
+
return true;
|
|
12145
|
+
}).map(mapItem);
|
|
12133
12146
|
mapped.sort((a, b) => {
|
|
12134
12147
|
if (a.isVerification && !b.isVerification) return -1;
|
|
12135
12148
|
if (!a.isVerification && b.isVerification) return 1;
|
|
12149
|
+
const aActive = a.status === "pending" || a.status === "in_progress";
|
|
12150
|
+
const bActive = b.status === "pending" || b.status === "in_progress";
|
|
12151
|
+
if (aActive && !bActive) return -1;
|
|
12152
|
+
if (!aActive && bActive) return 1;
|
|
12136
12153
|
return 0;
|
|
12137
12154
|
});
|
|
12138
12155
|
return mapped;
|
|
@@ -12297,6 +12314,36 @@ var BugBearClient = class {
|
|
|
12297
12314
|
async failAssignment(assignmentId) {
|
|
12298
12315
|
return this.updateAssignmentStatus(assignmentId, "failed");
|
|
12299
12316
|
}
|
|
12317
|
+
/**
|
|
12318
|
+
* Reopen a completed assignment — sets it back to in_progress with a fresh timer.
|
|
12319
|
+
* Clears completed_at and duration_seconds so it can be re-evaluated.
|
|
12320
|
+
*/
|
|
12321
|
+
async reopenAssignment(assignmentId) {
|
|
12322
|
+
try {
|
|
12323
|
+
const { data: current, error: fetchError } = await this.supabase.from("test_assignments").select("status").eq("id", assignmentId).single();
|
|
12324
|
+
if (fetchError || !current) {
|
|
12325
|
+
return { success: false, error: "Assignment not found" };
|
|
12326
|
+
}
|
|
12327
|
+
if (current.status === "pending" || current.status === "in_progress") {
|
|
12328
|
+
return { success: true };
|
|
12329
|
+
}
|
|
12330
|
+
const { error } = await this.supabase.from("test_assignments").update({
|
|
12331
|
+
status: "in_progress",
|
|
12332
|
+
started_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12333
|
+
completed_at: null,
|
|
12334
|
+
duration_seconds: null,
|
|
12335
|
+
skip_reason: null
|
|
12336
|
+
}).eq("id", assignmentId).eq("status", current.status);
|
|
12337
|
+
if (error) {
|
|
12338
|
+
console.error("BugBear: Failed to reopen assignment", error);
|
|
12339
|
+
return { success: false, error: error.message };
|
|
12340
|
+
}
|
|
12341
|
+
return { success: true };
|
|
12342
|
+
} catch (err) {
|
|
12343
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
12344
|
+
return { success: false, error: message };
|
|
12345
|
+
}
|
|
12346
|
+
}
|
|
12300
12347
|
/**
|
|
12301
12348
|
* Skip a test assignment with a required reason
|
|
12302
12349
|
* Marks the assignment as 'skipped' and records why it was skipped
|
|
@@ -13733,7 +13780,7 @@ import {
|
|
|
13733
13780
|
StyleSheet as StyleSheet18,
|
|
13734
13781
|
Dimensions as Dimensions2,
|
|
13735
13782
|
KeyboardAvoidingView,
|
|
13736
|
-
Platform as
|
|
13783
|
+
Platform as Platform5,
|
|
13737
13784
|
PanResponder,
|
|
13738
13785
|
Animated as Animated2,
|
|
13739
13786
|
ActivityIndicator as ActivityIndicator2,
|
|
@@ -14452,6 +14499,39 @@ function TestDetailScreen({ testId, nav }) {
|
|
|
14452
14499
|
setIsSubmitting(false);
|
|
14453
14500
|
}
|
|
14454
14501
|
}, [client, displayedAssignment, refreshAssignments, nav, isSubmitting]);
|
|
14502
|
+
const handleReopen = useCallback2(async () => {
|
|
14503
|
+
if (!client || !displayedAssignment || isSubmitting) return;
|
|
14504
|
+
Keyboard.dismiss();
|
|
14505
|
+
setIsSubmitting(true);
|
|
14506
|
+
try {
|
|
14507
|
+
await client.reopenAssignment(displayedAssignment.id);
|
|
14508
|
+
await refreshAssignments();
|
|
14509
|
+
} finally {
|
|
14510
|
+
setIsSubmitting(false);
|
|
14511
|
+
}
|
|
14512
|
+
}, [client, displayedAssignment, refreshAssignments, isSubmitting]);
|
|
14513
|
+
const handleChangeResult = useCallback2(async (newStatus) => {
|
|
14514
|
+
if (!client || !displayedAssignment || isSubmitting) return;
|
|
14515
|
+
Keyboard.dismiss();
|
|
14516
|
+
setIsSubmitting(true);
|
|
14517
|
+
try {
|
|
14518
|
+
await client.reopenAssignment(displayedAssignment.id);
|
|
14519
|
+
await client.updateAssignmentStatus(displayedAssignment.id, newStatus);
|
|
14520
|
+
await refreshAssignments();
|
|
14521
|
+
if (newStatus === "failed") {
|
|
14522
|
+
nav.replace({
|
|
14523
|
+
name: "REPORT",
|
|
14524
|
+
prefill: {
|
|
14525
|
+
type: "test_fail",
|
|
14526
|
+
assignmentId: displayedAssignment.id,
|
|
14527
|
+
testCaseId: displayedAssignment.testCase.id
|
|
14528
|
+
}
|
|
14529
|
+
});
|
|
14530
|
+
}
|
|
14531
|
+
} finally {
|
|
14532
|
+
setIsSubmitting(false);
|
|
14533
|
+
}
|
|
14534
|
+
}, [client, displayedAssignment, refreshAssignments, nav, isSubmitting]);
|
|
14455
14535
|
const handleSkip = useCallback2(async () => {
|
|
14456
14536
|
if (!client || !displayedAssignment || !selectedSkipReason) return;
|
|
14457
14537
|
Keyboard.dismiss();
|
|
@@ -14532,7 +14612,39 @@ function TestDetailScreen({ testId, nav }) {
|
|
|
14532
14612
|
}
|
|
14533
14613
|
},
|
|
14534
14614
|
/* @__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.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))),
|
|
14615
|
+
), /* @__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.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: [
|
|
14616
|
+
styles2.completedBanner,
|
|
14617
|
+
displayedAssignment.status === "passed" && styles2.completedBannerPass,
|
|
14618
|
+
displayedAssignment.status === "failed" && styles2.completedBannerFail
|
|
14619
|
+
] }, /* @__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: [
|
|
14620
|
+
styles2.completedLabel,
|
|
14621
|
+
displayedAssignment.status === "passed" && { color: colors.green },
|
|
14622
|
+
displayedAssignment.status === "failed" && { color: "#fca5a5" }
|
|
14623
|
+
] }, "Marked as ", displayedAssignment.status.charAt(0).toUpperCase() + displayedAssignment.status.slice(1))), /* @__PURE__ */ React4.createElement(View3, { style: [styles2.actionButtons, { marginTop: 4 }] }, /* @__PURE__ */ React4.createElement(
|
|
14624
|
+
TouchableOpacity2,
|
|
14625
|
+
{
|
|
14626
|
+
style: [styles2.actionBtn, styles2.reopenBtn, isSubmitting && { opacity: 0.5 }],
|
|
14627
|
+
onPress: handleReopen,
|
|
14628
|
+
disabled: isSubmitting
|
|
14629
|
+
},
|
|
14630
|
+
/* @__PURE__ */ React4.createElement(Text2, { style: styles2.reopenBtnText }, isSubmitting ? "Reopening..." : "\u{1F504} Reopen Test")
|
|
14631
|
+
), displayedAssignment.status === "passed" && /* @__PURE__ */ React4.createElement(
|
|
14632
|
+
TouchableOpacity2,
|
|
14633
|
+
{
|
|
14634
|
+
style: [styles2.actionBtn, styles2.failBtn, isSubmitting && { opacity: 0.5 }],
|
|
14635
|
+
onPress: () => handleChangeResult("failed"),
|
|
14636
|
+
disabled: isSubmitting
|
|
14637
|
+
},
|
|
14638
|
+
/* @__PURE__ */ React4.createElement(Text2, { style: styles2.failBtnText }, "Change to Fail")
|
|
14639
|
+
), displayedAssignment.status === "failed" && /* @__PURE__ */ React4.createElement(
|
|
14640
|
+
TouchableOpacity2,
|
|
14641
|
+
{
|
|
14642
|
+
style: [styles2.actionBtn, styles2.passBtn, isSubmitting && { opacity: 0.5 }],
|
|
14643
|
+
onPress: () => handleChangeResult("passed"),
|
|
14644
|
+
disabled: isSubmitting
|
|
14645
|
+
},
|
|
14646
|
+
/* @__PURE__ */ React4.createElement(Text2, { style: styles2.passBtnText }, "Change to Pass")
|
|
14647
|
+
))) : /* @__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
14648
|
{ reason: "blocked", label: "\u{1F6AB} Blocked by a bug" },
|
|
14537
14649
|
{ reason: "not_ready", label: "\u{1F6A7} Feature not ready" },
|
|
14538
14650
|
{ reason: "dependency", label: "\u{1F517} Needs another test first" },
|
|
@@ -14637,6 +14749,14 @@ var styles2 = StyleSheet4.create({
|
|
|
14637
14749
|
detailDesc: { fontSize: 13, color: colors.textSecondary, lineHeight: 18 },
|
|
14638
14750
|
folderProgress: { marginTop: 8 },
|
|
14639
14751
|
folderName: { fontSize: 12, color: colors.textMuted },
|
|
14752
|
+
// Completed state
|
|
14753
|
+
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 },
|
|
14754
|
+
completedBannerPass: { backgroundColor: colors.greenDark, borderColor: colors.green },
|
|
14755
|
+
completedBannerFail: { backgroundColor: colors.redDark, borderColor: colors.red },
|
|
14756
|
+
completedIcon: { fontSize: 14 },
|
|
14757
|
+
completedLabel: { fontSize: 13, fontWeight: "600", color: colors.textSecondary },
|
|
14758
|
+
reopenBtn: { backgroundColor: colors.card, borderWidth: 1, borderColor: colors.blue },
|
|
14759
|
+
reopenBtnText: { fontSize: 14, fontWeight: "600", color: colors.blue },
|
|
14640
14760
|
// Action buttons
|
|
14641
14761
|
actionButtons: { flexDirection: "row", gap: 10, marginTop: 8 },
|
|
14642
14762
|
actionBtn: { flex: 1, paddingVertical: 14, borderRadius: 12, alignItems: "center" },
|
|
@@ -14665,12 +14785,13 @@ var styles2 = StyleSheet4.create({
|
|
|
14665
14785
|
|
|
14666
14786
|
// src/widget/screens/TestListScreen.tsx
|
|
14667
14787
|
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";
|
|
14788
|
+
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
14789
|
function TestListScreen({ nav }) {
|
|
14670
|
-
const { assignments, currentAssignment, refreshAssignments, isLoading } = useBugBear();
|
|
14790
|
+
const { assignments, currentAssignment, refreshAssignments, dashboardUrl, isLoading } = useBugBear();
|
|
14671
14791
|
const [filter, setFilter] = useState3("all");
|
|
14672
14792
|
const [roleFilter, setRoleFilter] = useState3(null);
|
|
14673
14793
|
const [trackFilter, setTrackFilter] = useState3(null);
|
|
14794
|
+
const [platformFilter, setPlatformFilter] = useState3(Platform3.OS === "android" ? "android" : "ios");
|
|
14674
14795
|
const [searchQuery, setSearchQuery] = useState3("");
|
|
14675
14796
|
const [sortMode, setSortMode] = useState3("priority");
|
|
14676
14797
|
const [collapsedFolders, setCollapsedFolders] = useState3(/* @__PURE__ */ new Set());
|
|
@@ -14691,6 +14812,13 @@ function TestListScreen({ nav }) {
|
|
|
14691
14812
|
}
|
|
14692
14813
|
return Array.from(trackMap.values());
|
|
14693
14814
|
}, [assignments]);
|
|
14815
|
+
const availablePlatforms = useMemo2(() => {
|
|
14816
|
+
const set = /* @__PURE__ */ new Set();
|
|
14817
|
+
for (const a of assignments) {
|
|
14818
|
+
if (a.testCase.platforms) a.testCase.platforms.forEach((p) => set.add(p));
|
|
14819
|
+
}
|
|
14820
|
+
return Array.from(set).sort();
|
|
14821
|
+
}, [assignments]);
|
|
14694
14822
|
const selectedRole = availableRoles.find((r) => r.id === roleFilter);
|
|
14695
14823
|
const groupedAssignments = useMemo2(() => {
|
|
14696
14824
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -14737,6 +14865,7 @@ function TestListScreen({ nav }) {
|
|
|
14737
14865
|
});
|
|
14738
14866
|
}, []);
|
|
14739
14867
|
const filterAssignment = useCallback3((a) => {
|
|
14868
|
+
if (platformFilter && a.testCase.platforms && !a.testCase.platforms.includes(platformFilter)) return false;
|
|
14740
14869
|
if (roleFilter && a.testCase.role?.id !== roleFilter) return false;
|
|
14741
14870
|
if (trackFilter && a.testCase.track?.id !== trackFilter) return false;
|
|
14742
14871
|
if (searchQuery) {
|
|
@@ -14749,7 +14878,7 @@ function TestListScreen({ nav }) {
|
|
|
14749
14878
|
if (filter === "done") return a.status === "passed";
|
|
14750
14879
|
if (filter === "reopened") return a.status === "failed";
|
|
14751
14880
|
return true;
|
|
14752
|
-
}, [roleFilter, trackFilter, searchQuery, filter]);
|
|
14881
|
+
}, [platformFilter, roleFilter, trackFilter, searchQuery, filter]);
|
|
14753
14882
|
if (isLoading) return /* @__PURE__ */ React5.createElement(TestListScreenSkeleton, null);
|
|
14754
14883
|
return /* @__PURE__ */ React5.createElement(View4, null, /* @__PURE__ */ React5.createElement(View4, { style: styles3.filterBar }, [
|
|
14755
14884
|
{ key: "all", label: "All", count: assignments.length },
|
|
@@ -14787,7 +14916,30 @@ function TestListScreen({ nav }) {
|
|
|
14787
14916
|
placeholderTextColor: colors.textMuted,
|
|
14788
14917
|
style: styles3.searchInput
|
|
14789
14918
|
}
|
|
14790
|
-
)),
|
|
14919
|
+
)), availablePlatforms.length >= 2 && /* @__PURE__ */ React5.createElement(ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, style: styles3.platformBar }, /* @__PURE__ */ React5.createElement(
|
|
14920
|
+
TouchableOpacity3,
|
|
14921
|
+
{
|
|
14922
|
+
style: [styles3.platformBtn, !platformFilter && styles3.platformBtnActive],
|
|
14923
|
+
onPress: () => setPlatformFilter(null)
|
|
14924
|
+
},
|
|
14925
|
+
/* @__PURE__ */ React5.createElement(Text3, { style: [styles3.platformBtnText, !platformFilter && styles3.platformBtnTextActive] }, "All Platforms")
|
|
14926
|
+
), availablePlatforms.map((p) => {
|
|
14927
|
+
const isActive = platformFilter === p;
|
|
14928
|
+
const label = p === "ios" ? "iOS" : p === "android" ? "Android" : p === "web" ? "Web" : p;
|
|
14929
|
+
const icon = p === "ios" ? "\u{1F4F1}" : p === "android" ? "\u{1F916}" : p === "web" ? "\u{1F310}" : "\u{1F4CB}";
|
|
14930
|
+
return /* @__PURE__ */ React5.createElement(
|
|
14931
|
+
TouchableOpacity3,
|
|
14932
|
+
{
|
|
14933
|
+
key: p,
|
|
14934
|
+
style: [
|
|
14935
|
+
styles3.platformBtn,
|
|
14936
|
+
isActive && { backgroundColor: colors.blue + "20", borderColor: colors.blue + "60", borderWidth: 1 }
|
|
14937
|
+
],
|
|
14938
|
+
onPress: () => setPlatformFilter(isActive ? null : p)
|
|
14939
|
+
},
|
|
14940
|
+
/* @__PURE__ */ React5.createElement(Text3, { style: [styles3.platformBtnText, isActive && { color: colors.blue, fontWeight: "600" }] }, icon, " ", label)
|
|
14941
|
+
);
|
|
14942
|
+
})), /* @__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
14943
|
TouchableOpacity3,
|
|
14792
14944
|
{
|
|
14793
14945
|
style: [styles3.trackBtn, !trackFilter && styles3.trackBtnActive],
|
|
@@ -14848,7 +15000,15 @@ function TestListScreen({ nav }) {
|
|
|
14848
15000
|
] }, badge.label))
|
|
14849
15001
|
);
|
|
14850
15002
|
}));
|
|
14851
|
-
}),
|
|
15003
|
+
}), dashboardUrl && /* @__PURE__ */ React5.createElement(
|
|
15004
|
+
TouchableOpacity3,
|
|
15005
|
+
{
|
|
15006
|
+
style: styles3.dashboardLink,
|
|
15007
|
+
onPress: () => Linking2.openURL(`${dashboardUrl}/test-cases`),
|
|
15008
|
+
activeOpacity: 0.7
|
|
15009
|
+
},
|
|
15010
|
+
/* @__PURE__ */ React5.createElement(Text3, { style: styles3.dashboardLinkText }, "\u{1F310}", " Manage on Dashboard ", "\u2192")
|
|
15011
|
+
), /* @__PURE__ */ React5.createElement(TouchableOpacity3, { style: styles3.refreshBtn, onPress: refreshAssignments }, /* @__PURE__ */ React5.createElement(Text3, { style: styles3.refreshText }, "\u21BB", " Refresh")));
|
|
14852
15012
|
}
|
|
14853
15013
|
var styles3 = StyleSheet5.create({
|
|
14854
15014
|
filterBar: { flexDirection: "row", gap: 8, marginBottom: 8 },
|
|
@@ -14887,6 +15047,11 @@ var styles3 = StyleSheet5.create({
|
|
|
14887
15047
|
statusPillText: { fontSize: 10, fontWeight: "600" },
|
|
14888
15048
|
searchContainer: { marginBottom: 8 },
|
|
14889
15049
|
searchInput: { backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border, borderRadius: 8, paddingHorizontal: 12, paddingVertical: 8, fontSize: 13, color: colors.textPrimary },
|
|
15050
|
+
platformBar: { flexDirection: "row", marginBottom: 8 },
|
|
15051
|
+
platformBtn: { flexDirection: "row", alignItems: "center", gap: 4, paddingHorizontal: 10, paddingVertical: 4, borderRadius: 6, marginRight: 6, borderWidth: 1, borderColor: "transparent" },
|
|
15052
|
+
platformBtnActive: { backgroundColor: colors.card, borderColor: colors.border },
|
|
15053
|
+
platformBtnText: { fontSize: 11, color: colors.textMuted },
|
|
15054
|
+
platformBtnTextActive: { color: colors.textPrimary, fontWeight: "600" },
|
|
14890
15055
|
trackSortRow: { flexDirection: "row", alignItems: "center", marginBottom: 10, gap: 8 },
|
|
14891
15056
|
trackBtn: { flexDirection: "row", alignItems: "center", gap: 4, paddingHorizontal: 10, paddingVertical: 4, borderRadius: 6, marginRight: 6, borderWidth: 1, borderColor: "transparent" },
|
|
14892
15057
|
trackBtnActive: { backgroundColor: colors.card, borderColor: colors.border },
|
|
@@ -14897,7 +15062,9 @@ var styles3 = StyleSheet5.create({
|
|
|
14897
15062
|
sortBtnActive: { backgroundColor: colors.card, borderColor: colors.border },
|
|
14898
15063
|
sortBtnText: { fontSize: 11, color: colors.textMuted },
|
|
14899
15064
|
sortBtnTextActive: { color: colors.textPrimary, fontWeight: "600" },
|
|
14900
|
-
|
|
15065
|
+
dashboardLink: { alignItems: "center", paddingTop: 12 },
|
|
15066
|
+
dashboardLinkText: { fontSize: 13, fontWeight: "500", color: colors.blue },
|
|
15067
|
+
refreshBtn: { alignItems: "center", paddingVertical: 8 },
|
|
14901
15068
|
refreshText: { fontSize: 13, color: colors.blue }
|
|
14902
15069
|
});
|
|
14903
15070
|
|
|
@@ -15619,9 +15786,9 @@ var styles9 = StyleSheet11.create({
|
|
|
15619
15786
|
|
|
15620
15787
|
// src/widget/screens/MessageListScreen.tsx
|
|
15621
15788
|
import React12 from "react";
|
|
15622
|
-
import { View as View11, Text as Text10, TouchableOpacity as TouchableOpacity9, StyleSheet as StyleSheet12 } from "react-native";
|
|
15789
|
+
import { View as View11, Text as Text10, TouchableOpacity as TouchableOpacity9, StyleSheet as StyleSheet12, Linking as Linking3 } from "react-native";
|
|
15623
15790
|
function MessageListScreen({ nav }) {
|
|
15624
|
-
const { threads, unreadCount, refreshThreads, isLoading } = useBugBear();
|
|
15791
|
+
const { threads, unreadCount, refreshThreads, dashboardUrl, isLoading } = useBugBear();
|
|
15625
15792
|
if (isLoading) return /* @__PURE__ */ React12.createElement(MessageListScreenSkeleton, null);
|
|
15626
15793
|
return /* @__PURE__ */ React12.createElement(View11, null, /* @__PURE__ */ React12.createElement(
|
|
15627
15794
|
TouchableOpacity9,
|
|
@@ -15639,7 +15806,15 @@ function MessageListScreen({ nav }) {
|
|
|
15639
15806
|
},
|
|
15640
15807
|
/* @__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
15808
|
/* @__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
|
-
))),
|
|
15809
|
+
))), dashboardUrl && /* @__PURE__ */ React12.createElement(
|
|
15810
|
+
TouchableOpacity9,
|
|
15811
|
+
{
|
|
15812
|
+
style: styles10.dashboardLink,
|
|
15813
|
+
onPress: () => Linking3.openURL(`${dashboardUrl}/discussions`),
|
|
15814
|
+
activeOpacity: 0.7
|
|
15815
|
+
},
|
|
15816
|
+
/* @__PURE__ */ React12.createElement(Text10, { style: styles10.dashboardLinkText }, "\u{1F310}", " View on Dashboard ", "\u2192")
|
|
15817
|
+
), /* @__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
15818
|
}
|
|
15644
15819
|
var styles10 = StyleSheet12.create({
|
|
15645
15820
|
newMsgButton: { backgroundColor: colors.blue, paddingVertical: 12, borderRadius: 12, alignItems: "center", marginBottom: 16 },
|
|
@@ -15658,7 +15833,9 @@ var styles10 = StyleSheet12.create({
|
|
|
15658
15833
|
unreadBadge: { backgroundColor: colors.blue, borderRadius: 10, minWidth: 20, height: 20, justifyContent: "center", alignItems: "center", paddingHorizontal: 6 },
|
|
15659
15834
|
unreadText: { fontSize: 11, fontWeight: "bold", color: "#fff" },
|
|
15660
15835
|
priorityDot: { width: 8, height: 8, borderRadius: 4 },
|
|
15661
|
-
|
|
15836
|
+
dashboardLink: { alignItems: "center", paddingTop: 12 },
|
|
15837
|
+
dashboardLinkText: { fontSize: 13, fontWeight: "500", color: colors.blue },
|
|
15838
|
+
footer: { flexDirection: "row", justifyContent: "space-between", alignItems: "center", paddingTop: 8, paddingHorizontal: 4 },
|
|
15662
15839
|
footerText: { fontSize: 12, color: colors.textMuted },
|
|
15663
15840
|
refreshText: { fontSize: 13, color: colors.blue }
|
|
15664
15841
|
});
|
|
@@ -16139,7 +16316,7 @@ var styles14 = StyleSheet16.create({
|
|
|
16139
16316
|
|
|
16140
16317
|
// src/widget/screens/IssueDetailScreen.tsx
|
|
16141
16318
|
import React17 from "react";
|
|
16142
|
-
import { View as View16, Text as Text15, Image as Image3, StyleSheet as StyleSheet17, Linking as
|
|
16319
|
+
import { View as View16, Text as Text15, Image as Image3, StyleSheet as StyleSheet17, Linking as Linking4, TouchableOpacity as TouchableOpacity14 } from "react-native";
|
|
16143
16320
|
var STATUS_LABELS = {
|
|
16144
16321
|
new: { label: "New", bg: "#1e3a5f", color: "#60a5fa" },
|
|
16145
16322
|
triaging: { label: "Triaging", bg: "#1e3a5f", color: "#60a5fa" },
|
|
@@ -16161,9 +16338,18 @@ var SEVERITY_CONFIG = {
|
|
|
16161
16338
|
low: { label: "Low", color: "#71717a", bg: "#27272a" }
|
|
16162
16339
|
};
|
|
16163
16340
|
function IssueDetailScreen({ nav, issue }) {
|
|
16341
|
+
const { dashboardUrl } = useBugBear();
|
|
16164
16342
|
const statusConfig = STATUS_LABELS[issue.status] || { label: issue.status, bg: "#27272a", color: "#a1a1aa" };
|
|
16165
16343
|
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: () =>
|
|
16344
|
+
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(
|
|
16345
|
+
TouchableOpacity14,
|
|
16346
|
+
{
|
|
16347
|
+
style: styles15.dashboardLink,
|
|
16348
|
+
onPress: () => Linking4.openURL(`${dashboardUrl}/reports`),
|
|
16349
|
+
activeOpacity: 0.7
|
|
16350
|
+
},
|
|
16351
|
+
/* @__PURE__ */ React17.createElement(Text15, { style: styles15.dashboardLinkText }, "\u{1F310}", " View on Dashboard ", "\u2192")
|
|
16352
|
+
));
|
|
16167
16353
|
}
|
|
16168
16354
|
var styles15 = StyleSheet17.create({
|
|
16169
16355
|
badgeRow: {
|
|
@@ -16292,6 +16478,18 @@ var styles15 = StyleSheet17.create({
|
|
|
16292
16478
|
metaTextSmall: {
|
|
16293
16479
|
fontSize: 11,
|
|
16294
16480
|
color: colors.textDim
|
|
16481
|
+
},
|
|
16482
|
+
dashboardLink: {
|
|
16483
|
+
alignItems: "center",
|
|
16484
|
+
paddingVertical: 10,
|
|
16485
|
+
marginTop: 12,
|
|
16486
|
+
borderTopWidth: 1,
|
|
16487
|
+
borderTopColor: colors.border
|
|
16488
|
+
},
|
|
16489
|
+
dashboardLinkText: {
|
|
16490
|
+
fontSize: 13,
|
|
16491
|
+
fontWeight: "500",
|
|
16492
|
+
color: colors.blue
|
|
16295
16493
|
}
|
|
16296
16494
|
});
|
|
16297
16495
|
|
|
@@ -16479,7 +16677,7 @@ function BugBearButton({
|
|
|
16479
16677
|
/* @__PURE__ */ React18.createElement(
|
|
16480
16678
|
KeyboardAvoidingView,
|
|
16481
16679
|
{
|
|
16482
|
-
behavior:
|
|
16680
|
+
behavior: Platform5.OS === "ios" ? "padding" : "height",
|
|
16483
16681
|
style: styles16.modalOverlay
|
|
16484
16682
|
},
|
|
16485
16683
|
/* @__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(
|