@bbearai/react-native 0.3.3 → 0.3.4
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 +192 -138
- package/dist/index.mjs +187 -132
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -11659,6 +11659,11 @@ var BugBearClient = class {
|
|
|
11659
11659
|
try {
|
|
11660
11660
|
const { data: currentAssignment, error: fetchError } = await this.supabase.from("test_assignments").select("status, started_at").eq("id", assignmentId).single();
|
|
11661
11661
|
if (fetchError || !currentAssignment) {
|
|
11662
|
+
console.error("BugBear: Assignment not found", {
|
|
11663
|
+
message: fetchError?.message,
|
|
11664
|
+
code: fetchError?.code,
|
|
11665
|
+
assignmentId
|
|
11666
|
+
});
|
|
11662
11667
|
return { success: false, error: "Assignment not found" };
|
|
11663
11668
|
}
|
|
11664
11669
|
const updateData = { status };
|
|
@@ -11683,7 +11688,15 @@ var BugBearClient = class {
|
|
|
11683
11688
|
}
|
|
11684
11689
|
const { error } = await this.supabase.from("test_assignments").update(updateData).eq("id", assignmentId);
|
|
11685
11690
|
if (error) {
|
|
11686
|
-
console.error("BugBear: Failed to update assignment status",
|
|
11691
|
+
console.error("BugBear: Failed to update assignment status", {
|
|
11692
|
+
message: error.message,
|
|
11693
|
+
details: error.details,
|
|
11694
|
+
hint: error.hint,
|
|
11695
|
+
code: error.code,
|
|
11696
|
+
assignmentId,
|
|
11697
|
+
status,
|
|
11698
|
+
updateData
|
|
11699
|
+
});
|
|
11687
11700
|
return { success: false, error: error.message };
|
|
11688
11701
|
}
|
|
11689
11702
|
if (options?.feedback && ["passed", "failed", "blocked"].includes(status)) {
|
|
@@ -11745,7 +11758,7 @@ var BugBearClient = class {
|
|
|
11745
11758
|
if (!testerInfo) {
|
|
11746
11759
|
return { success: false, error: "Not authenticated as tester" };
|
|
11747
11760
|
}
|
|
11748
|
-
const { testCaseId, assignmentId, feedback, timeToCompleteSeconds } = options;
|
|
11761
|
+
const { testCaseId, assignmentId, feedback, timeToCompleteSeconds, screenshotUrls } = options;
|
|
11749
11762
|
if (feedback.rating < 1 || feedback.rating > 5) {
|
|
11750
11763
|
return { success: false, error: "Rating must be between 1 and 5" };
|
|
11751
11764
|
}
|
|
@@ -11765,7 +11778,8 @@ var BugBearClient = class {
|
|
|
11765
11778
|
steps_unclear: feedback.stepsUnclear || false,
|
|
11766
11779
|
expected_result_unclear: feedback.expectedResultUnclear || false,
|
|
11767
11780
|
platform: this.getDeviceInfo().platform,
|
|
11768
|
-
time_to_complete_seconds: timeToCompleteSeconds || null
|
|
11781
|
+
time_to_complete_seconds: timeToCompleteSeconds || null,
|
|
11782
|
+
screenshot_urls: screenshotUrls || []
|
|
11769
11783
|
});
|
|
11770
11784
|
if (feedbackError) {
|
|
11771
11785
|
console.error("BugBear: Failed to submit feedback", feedbackError);
|
|
@@ -12080,19 +12094,21 @@ var BugBearClient = class {
|
|
|
12080
12094
|
/**
|
|
12081
12095
|
* Upload a screenshot (web - uses File/Blob)
|
|
12082
12096
|
*/
|
|
12083
|
-
async uploadScreenshot(file, filename) {
|
|
12097
|
+
async uploadScreenshot(file, filename, bucket = "screenshots") {
|
|
12084
12098
|
try {
|
|
12085
|
-
const
|
|
12099
|
+
const contentType = file.type || "image/png";
|
|
12100
|
+
const ext = contentType.includes("png") ? "png" : "jpg";
|
|
12101
|
+
const name = filename || `screenshot-${Date.now()}.${ext}`;
|
|
12086
12102
|
const path = `${this.config.projectId}/${name}`;
|
|
12087
|
-
const { error } = await this.supabase.storage.from(
|
|
12088
|
-
contentType
|
|
12103
|
+
const { error } = await this.supabase.storage.from(bucket).upload(path, file, {
|
|
12104
|
+
contentType,
|
|
12089
12105
|
upsert: false
|
|
12090
12106
|
});
|
|
12091
12107
|
if (error) {
|
|
12092
12108
|
console.error("BugBear: Failed to upload screenshot", error);
|
|
12093
12109
|
return null;
|
|
12094
12110
|
}
|
|
12095
|
-
const { data: { publicUrl } } = this.supabase.storage.from(
|
|
12111
|
+
const { data: { publicUrl } } = this.supabase.storage.from(bucket).getPublicUrl(path);
|
|
12096
12112
|
return publicUrl;
|
|
12097
12113
|
} catch (err) {
|
|
12098
12114
|
console.error("BugBear: Error uploading screenshot", err);
|
|
@@ -13058,7 +13074,7 @@ function BugBearProvider({ config, children, appVersion, enabled = true }) {
|
|
|
13058
13074
|
(a) => a.status === "in_progress"
|
|
13059
13075
|
) || assignments.find(
|
|
13060
13076
|
(a) => a.status === "pending"
|
|
13061
|
-
) ||
|
|
13077
|
+
) || null;
|
|
13062
13078
|
const shouldShowWidget = isQAEnabled && isTester;
|
|
13063
13079
|
return /* @__PURE__ */ import_react.default.createElement(
|
|
13064
13080
|
BugBearContext.Provider,
|
|
@@ -13104,6 +13120,9 @@ function BugBearProvider({ config, children, appVersion, enabled = true }) {
|
|
|
13104
13120
|
var import_react16 = __toESM(require("react"));
|
|
13105
13121
|
var import_react_native15 = require("react-native");
|
|
13106
13122
|
|
|
13123
|
+
// src/widget/logo.ts
|
|
13124
|
+
var BUGBEAR_LOGO_BASE64 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAJhlWElmTU0AKgAAAAgABAEaAAUAAAABAAAAPgEbAAUAAAABAAAARgEoAAMAAAABAAIAAIdpAAQAAAABAAAATgAAAAAAAABIAAAAAQAAAEgAAAABAASQBAACAAAAFAAAAISgAQADAAAAAQABAACgAgAEAAAAAQAAAECgAwAEAAAAAQAAAEAAAAAAMjAyNjowMToyNCAxNjoyMTozOAAvO6cIAAAACXBIWXMAAAsTAAALEwEAmpwYAAACo2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNi4wLjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczpJcHRjNHhtcEV4dD0iaHR0cDovL2lwdGMub3JnL3N0ZC9JcHRjNHhtcEV4dC8yMDA4LTAyLTI5LyIKICAgICAgICAgICAgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIj4KICAgICAgICAgPElwdGM0eG1wRXh0OkRpZ2l0YWxTb3VyY2VUeXBlPmh0dHA6Ly9jdi5pcHRjLm9yZy9uZXdzY29kZXMvZGlnaXRhbHNvdXJjZXR5cGUvdHJhaW5lZEFsZ29yaXRobWljTWVkaWE8L0lwdGM0eG1wRXh0OkRpZ2l0YWxTb3VyY2VUeXBlPgogICAgICAgICA8SXB0YzR4bXBFeHQ6RGlnSW1hZ2VHVUlEPmZjNzJlN2Q2LTYyYTEtNDE1ZS04MjY5LWM2NjA4MjY0OWRiMDwvSXB0YzR4bXBFeHQ6RGlnSW1hZ2VHVUlEPgogICAgICAgICA8eG1wOkNyZWF0ZURhdGU+MjAyNi0wMS0yNFQxNjoyMTozODwveG1wOkNyZWF0ZURhdGU+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+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";
|
|
13125
|
+
|
|
13107
13126
|
// src/widget/navigation.ts
|
|
13108
13127
|
var import_react2 = require("react");
|
|
13109
13128
|
function navReducer(state, action) {
|
|
@@ -13608,7 +13627,14 @@ function TestDetailScreen({ testId, nav }) {
|
|
|
13608
13627
|
if (!client || !displayedAssignment) return;
|
|
13609
13628
|
await client.failAssignment(displayedAssignment.id);
|
|
13610
13629
|
await refreshAssignments();
|
|
13611
|
-
nav.replace({
|
|
13630
|
+
nav.replace({
|
|
13631
|
+
name: "REPORT",
|
|
13632
|
+
prefill: {
|
|
13633
|
+
type: "test_fail",
|
|
13634
|
+
assignmentId: displayedAssignment.id,
|
|
13635
|
+
testCaseId: displayedAssignment.testCase.id
|
|
13636
|
+
}
|
|
13637
|
+
});
|
|
13612
13638
|
}, [client, displayedAssignment, refreshAssignments, nav]);
|
|
13613
13639
|
const handleSkip = (0, import_react4.useCallback)(async () => {
|
|
13614
13640
|
if (!client || !displayedAssignment || !selectedSkipReason) return;
|
|
@@ -13622,9 +13648,11 @@ function TestDetailScreen({ testId, nav }) {
|
|
|
13622
13648
|
setSelectedSkipReason(null);
|
|
13623
13649
|
setSkipNotes("");
|
|
13624
13650
|
setSkipping(false);
|
|
13625
|
-
const remaining = assignments.filter(
|
|
13626
|
-
|
|
13627
|
-
|
|
13651
|
+
const remaining = assignments.filter(
|
|
13652
|
+
(a) => (a.status === "pending" || a.status === "in_progress") && a.id !== displayedAssignment.id
|
|
13653
|
+
);
|
|
13654
|
+
if (remaining.length > 0) {
|
|
13655
|
+
nav.replace({ name: "TEST_DETAIL", testId: remaining[0].id });
|
|
13628
13656
|
} else {
|
|
13629
13657
|
nav.reset();
|
|
13630
13658
|
}
|
|
@@ -13813,7 +13841,7 @@ var styles2 = import_react_native4.StyleSheet.create({
|
|
|
13813
13841
|
var import_react5 = __toESM(require("react"));
|
|
13814
13842
|
var import_react_native5 = require("react-native");
|
|
13815
13843
|
function TestListScreen({ nav }) {
|
|
13816
|
-
const { assignments, refreshAssignments } = useBugBear();
|
|
13844
|
+
const { assignments, currentAssignment, refreshAssignments } = useBugBear();
|
|
13817
13845
|
const [filter, setFilter] = (0, import_react5.useState)("all");
|
|
13818
13846
|
const [collapsedFolders, setCollapsedFolders] = (0, import_react5.useState)(/* @__PURE__ */ new Set());
|
|
13819
13847
|
const groupedAssignments = (0, import_react5.useMemo)(() => {
|
|
@@ -13868,11 +13896,12 @@ function TestListScreen({ nav }) {
|
|
|
13868
13896
|
if (filtered.length === 0 && filter !== "all") return null;
|
|
13869
13897
|
return /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { key: folderId, style: styles3.folder }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.TouchableOpacity, { style: styles3.folderHeader, onPress: () => toggleFolder(folderId) }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles3.folderToggle }, isCollapsed ? "\u25B6" : "\u25BC"), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles3.folderName }, folder.group?.name || "Ungrouped"), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles3.folderProgress }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: [styles3.folderProgressFill, { width: `${Math.round((folder.stats.passed + folder.stats.failed) / folder.stats.total * 100)}%` }] })), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles3.folderCount }, folder.stats.passed + folder.stats.failed, "/", folder.stats.total)), !isCollapsed && filtered.map((assignment) => {
|
|
13870
13898
|
const badge = getStatusBadge(assignment.status);
|
|
13899
|
+
const isCurrent = currentAssignment?.id === assignment.id;
|
|
13871
13900
|
return /* @__PURE__ */ import_react5.default.createElement(
|
|
13872
13901
|
import_react_native5.TouchableOpacity,
|
|
13873
13902
|
{
|
|
13874
13903
|
key: assignment.id,
|
|
13875
|
-
style: styles3.testItem,
|
|
13904
|
+
style: [styles3.testItem, isCurrent && styles3.testItemCurrent],
|
|
13876
13905
|
onPress: () => nav.push({ name: "TEST_DETAIL", testId: assignment.id })
|
|
13877
13906
|
},
|
|
13878
13907
|
/* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles3.testBadge }, badge.icon),
|
|
@@ -13895,6 +13924,7 @@ var styles3 = import_react_native5.StyleSheet.create({
|
|
|
13895
13924
|
folderProgressFill: { height: "100%", backgroundColor: colors.green, borderRadius: 2 },
|
|
13896
13925
|
folderCount: { fontSize: 12, color: colors.textMuted, width: 30, textAlign: "right" },
|
|
13897
13926
|
testItem: { flexDirection: "row", alignItems: "center", paddingVertical: 10, paddingHorizontal: 12, borderRadius: 8, marginBottom: 4, backgroundColor: colors.card },
|
|
13927
|
+
testItemCurrent: { backgroundColor: "rgba(59, 130, 246, 0.15)", borderLeftWidth: 3, borderLeftColor: colors.blue },
|
|
13898
13928
|
testBadge: { fontSize: 16, marginRight: 10, width: 20 },
|
|
13899
13929
|
testInfo: { flex: 1 },
|
|
13900
13930
|
testTitle: { fontSize: 14, color: colors.textPrimary, marginBottom: 2 },
|
|
@@ -13904,105 +13934,11 @@ var styles3 = import_react_native5.StyleSheet.create({
|
|
|
13904
13934
|
});
|
|
13905
13935
|
|
|
13906
13936
|
// src/widget/screens/TestFeedbackScreen.tsx
|
|
13907
|
-
var
|
|
13908
|
-
var
|
|
13909
|
-
function TestFeedbackScreen({ status, assignmentId, nav }) {
|
|
13910
|
-
const { client, assignments, refreshAssignments } = useBugBear();
|
|
13911
|
-
const [rating, setRating] = (0, import_react6.useState)(5);
|
|
13912
|
-
const [note, setNote] = (0, import_react6.useState)("");
|
|
13913
|
-
const [flags, setFlags] = (0, import_react6.useState)({ isOutdated: false, needsMoreDetail: false, stepsUnclear: false, expectedResultUnclear: false });
|
|
13914
|
-
const [submitting, setSubmitting] = (0, import_react6.useState)(false);
|
|
13915
|
-
const assignment = assignments.find((a) => a.id === assignmentId);
|
|
13916
|
-
const showFlags = rating < 4;
|
|
13917
|
-
const handleSubmit = async () => {
|
|
13918
|
-
setSubmitting(true);
|
|
13919
|
-
if (client && assignment) {
|
|
13920
|
-
await client.submitTestFeedback(assignment.testCase.id, {
|
|
13921
|
-
rating,
|
|
13922
|
-
note: note.trim() || void 0,
|
|
13923
|
-
qualityFlags: showFlags ? flags : void 0
|
|
13924
|
-
});
|
|
13925
|
-
}
|
|
13926
|
-
await refreshAssignments();
|
|
13927
|
-
setSubmitting(false);
|
|
13928
|
-
if (status === "failed") {
|
|
13929
|
-
nav.replace({ name: "REPORT", prefill: { type: "test_fail", assignmentId, testCaseId: assignment?.testCase.id } });
|
|
13930
|
-
} else {
|
|
13931
|
-
const remaining = assignments.filter((a) => (a.status === "pending" || a.status === "in_progress") && a.id !== assignmentId);
|
|
13932
|
-
if (remaining.length > 0) {
|
|
13933
|
-
nav.replace({ name: "TEST_DETAIL" });
|
|
13934
|
-
} else {
|
|
13935
|
-
nav.reset();
|
|
13936
|
-
}
|
|
13937
|
-
}
|
|
13938
|
-
};
|
|
13939
|
-
const handleSkip = () => {
|
|
13940
|
-
if (status === "failed") {
|
|
13941
|
-
nav.replace({ name: "REPORT", prefill: { type: "test_fail", assignmentId, testCaseId: assignment?.testCase.id } });
|
|
13942
|
-
} else {
|
|
13943
|
-
const remaining = assignments.filter((a) => (a.status === "pending" || a.status === "in_progress") && a.id !== assignmentId);
|
|
13944
|
-
if (remaining.length > 0) {
|
|
13945
|
-
nav.replace({ name: "TEST_DETAIL" });
|
|
13946
|
-
} else {
|
|
13947
|
-
nav.reset();
|
|
13948
|
-
}
|
|
13949
|
-
}
|
|
13950
|
-
};
|
|
13951
|
-
return /* @__PURE__ */ import_react6.default.createElement(import_react_native6.View, { style: styles4.container }, /* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: styles4.header }, status === "passed" ? "\u2705 Test Passed!" : "\u274C Test Failed"), /* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: styles4.subheader }, "Rate this test case"), /* @__PURE__ */ import_react6.default.createElement(import_react_native6.View, { style: styles4.starRow }, [1, 2, 3, 4, 5].map((n) => /* @__PURE__ */ import_react6.default.createElement(import_react_native6.TouchableOpacity, { key: n, onPress: () => setRating(n), style: styles4.starButton }, /* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: [styles4.star, n <= rating && styles4.starActive] }, n <= rating ? "\u2605" : "\u2606")))), showFlags && /* @__PURE__ */ import_react6.default.createElement(import_react_native6.View, { style: styles4.flagsSection }, /* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: styles4.flagsLabel }, "What could be improved?"), [
|
|
13952
|
-
{ key: "isOutdated", label: "Test is outdated" },
|
|
13953
|
-
{ key: "needsMoreDetail", label: "Needs more detail" },
|
|
13954
|
-
{ key: "stepsUnclear", label: "Steps are unclear" },
|
|
13955
|
-
{ key: "expectedResultUnclear", label: "Expected result unclear" }
|
|
13956
|
-
].map(({ key, label }) => /* @__PURE__ */ import_react6.default.createElement(
|
|
13957
|
-
import_react_native6.TouchableOpacity,
|
|
13958
|
-
{
|
|
13959
|
-
key,
|
|
13960
|
-
style: [styles4.flagItem, flags[key] && styles4.flagItemActive],
|
|
13961
|
-
onPress: () => setFlags((prev) => ({ ...prev, [key]: !prev[key] }))
|
|
13962
|
-
},
|
|
13963
|
-
/* @__PURE__ */ import_react6.default.createElement(import_react_native6.View, { style: [styles4.flagCheck, flags[key] && styles4.flagCheckActive] }, flags[key] && /* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: styles4.flagCheckmark }, "\u2713")),
|
|
13964
|
-
/* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: [styles4.flagText, flags[key] && styles4.flagTextActive] }, label)
|
|
13965
|
-
))), /* @__PURE__ */ import_react6.default.createElement(
|
|
13966
|
-
import_react_native6.TextInput,
|
|
13967
|
-
{
|
|
13968
|
-
style: styles4.noteInput,
|
|
13969
|
-
value: note,
|
|
13970
|
-
onChangeText: setNote,
|
|
13971
|
-
placeholder: "Add a note (optional)",
|
|
13972
|
-
placeholderTextColor: colors.textMuted,
|
|
13973
|
-
multiline: true
|
|
13974
|
-
}
|
|
13975
|
-
), /* @__PURE__ */ import_react6.default.createElement(import_react_native6.View, { style: styles4.actions }, /* @__PURE__ */ import_react6.default.createElement(import_react_native6.TouchableOpacity, { style: styles4.skipButton, onPress: handleSkip }, /* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: styles4.skipText }, "Skip")), /* @__PURE__ */ import_react6.default.createElement(import_react_native6.TouchableOpacity, { style: [shared.primaryButton, { flex: 2 }], onPress: handleSubmit, disabled: submitting }, /* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: shared.primaryButtonText }, submitting ? "Submitting..." : "Submit"))));
|
|
13976
|
-
}
|
|
13977
|
-
var styles4 = import_react_native6.StyleSheet.create({
|
|
13978
|
-
container: { paddingTop: 8 },
|
|
13979
|
-
header: { fontSize: 22, fontWeight: "700", color: colors.textPrimary, textAlign: "center", marginBottom: 4 },
|
|
13980
|
-
subheader: { fontSize: 14, color: colors.textMuted, textAlign: "center", marginBottom: 20 },
|
|
13981
|
-
starRow: { flexDirection: "row", justifyContent: "center", gap: 8, marginBottom: 20 },
|
|
13982
|
-
starButton: { padding: 4 },
|
|
13983
|
-
star: { fontSize: 36, color: colors.textDim },
|
|
13984
|
-
starActive: { color: "#facc15" },
|
|
13985
|
-
flagsSection: { marginBottom: 16 },
|
|
13986
|
-
flagsLabel: { fontSize: 14, fontWeight: "500", color: colors.textSecondary, marginBottom: 10 },
|
|
13987
|
-
flagItem: { flexDirection: "row", alignItems: "center", paddingVertical: 10, paddingHorizontal: 12, borderRadius: 8, marginBottom: 4, backgroundColor: colors.card },
|
|
13988
|
-
flagItemActive: { backgroundColor: colors.yellowDark, borderWidth: 1, borderColor: colors.yellow },
|
|
13989
|
-
flagCheck: { width: 20, height: 20, borderRadius: 4, borderWidth: 2, borderColor: colors.border, marginRight: 10, justifyContent: "center", alignItems: "center" },
|
|
13990
|
-
flagCheckActive: { backgroundColor: colors.yellow, borderColor: colors.yellow },
|
|
13991
|
-
flagCheckmark: { fontSize: 12, fontWeight: "bold", color: "#000" },
|
|
13992
|
-
flagText: { fontSize: 14, color: colors.textSecondary },
|
|
13993
|
-
flagTextActive: { color: colors.yellow },
|
|
13994
|
-
noteInput: { backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border, borderRadius: 10, padding: 12, fontSize: 14, color: colors.textPrimary, minHeight: 60, textAlignVertical: "top", marginBottom: 16 },
|
|
13995
|
-
actions: { flexDirection: "row", gap: 10 },
|
|
13996
|
-
skipButton: { flex: 1, paddingVertical: 14, borderRadius: 12, alignItems: "center", backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border },
|
|
13997
|
-
skipText: { fontSize: 15, color: colors.textSecondary }
|
|
13998
|
-
});
|
|
13999
|
-
|
|
14000
|
-
// src/widget/screens/ReportScreen.tsx
|
|
14001
|
-
var import_react10 = __toESM(require("react"));
|
|
14002
|
-
var import_react_native9 = require("react-native");
|
|
13937
|
+
var import_react9 = __toESM(require("react"));
|
|
13938
|
+
var import_react_native8 = require("react-native");
|
|
14003
13939
|
|
|
14004
13940
|
// src/widget/useImageAttachments.ts
|
|
14005
|
-
var
|
|
13941
|
+
var import_react6 = require("react");
|
|
14006
13942
|
var launchImageLibrary = null;
|
|
14007
13943
|
var launchCamera = null;
|
|
14008
13944
|
try {
|
|
@@ -14013,8 +13949,8 @@ try {
|
|
|
14013
13949
|
}
|
|
14014
13950
|
var IMAGE_PICKER_AVAILABLE = launchImageLibrary !== null;
|
|
14015
13951
|
function useImageAttachments(uploadFn, maxImages, bucket = "screenshots") {
|
|
14016
|
-
const [images, setImages] = (0,
|
|
14017
|
-
const pickFromGallery = (0,
|
|
13952
|
+
const [images, setImages] = (0, import_react6.useState)([]);
|
|
13953
|
+
const pickFromGallery = (0, import_react6.useCallback)(async () => {
|
|
14018
13954
|
if (!launchImageLibrary || images.length >= maxImages) return;
|
|
14019
13955
|
launchImageLibrary(
|
|
14020
13956
|
{ mediaType: "photo", quality: 0.7, maxWidth: 1920, maxHeight: 1920, selectionLimit: maxImages - images.length },
|
|
@@ -14038,7 +13974,7 @@ function useImageAttachments(uploadFn, maxImages, bucket = "screenshots") {
|
|
|
14038
13974
|
}
|
|
14039
13975
|
);
|
|
14040
13976
|
}, [images.length, maxImages, uploadFn, bucket]);
|
|
14041
|
-
const pickFromCamera = (0,
|
|
13977
|
+
const pickFromCamera = (0, import_react6.useCallback)(async () => {
|
|
14042
13978
|
if (!launchCamera || images.length >= maxImages) return;
|
|
14043
13979
|
launchCamera(
|
|
14044
13980
|
{ mediaType: "photo", quality: 0.7, maxWidth: 1920, maxHeight: 1920 },
|
|
@@ -14061,35 +13997,35 @@ function useImageAttachments(uploadFn, maxImages, bucket = "screenshots") {
|
|
|
14061
13997
|
}
|
|
14062
13998
|
);
|
|
14063
13999
|
}, [images.length, maxImages, uploadFn, bucket]);
|
|
14064
|
-
const removeImage = (0,
|
|
14000
|
+
const removeImage = (0, import_react6.useCallback)((id) => {
|
|
14065
14001
|
setImages((prev) => prev.filter((img) => img.id !== id));
|
|
14066
14002
|
}, []);
|
|
14067
|
-
const clear = (0,
|
|
14003
|
+
const clear = (0, import_react6.useCallback)(() => {
|
|
14068
14004
|
setImages([]);
|
|
14069
14005
|
}, []);
|
|
14070
14006
|
const isUploading = images.some((img) => img.status === "uploading");
|
|
14071
14007
|
const hasError = images.some((img) => img.status === "error");
|
|
14072
|
-
const getAttachments = (0,
|
|
14008
|
+
const getAttachments = (0, import_react6.useCallback)(() => {
|
|
14073
14009
|
return images.filter((img) => img.status === "done" && img.remoteUrl).map((img) => ({ type: "image", url: img.remoteUrl, name: img.name }));
|
|
14074
14010
|
}, [images]);
|
|
14075
|
-
const getScreenshotUrls = (0,
|
|
14011
|
+
const getScreenshotUrls = (0, import_react6.useCallback)(() => {
|
|
14076
14012
|
return images.filter((img) => img.status === "done" && img.remoteUrl).map((img) => img.remoteUrl);
|
|
14077
14013
|
}, [images]);
|
|
14078
14014
|
return { images, pickFromGallery, pickFromCamera, removeImage, clear, isUploading, hasError, getAttachments, getScreenshotUrls };
|
|
14079
14015
|
}
|
|
14080
14016
|
|
|
14081
14017
|
// src/widget/ImagePickerButtons.tsx
|
|
14082
|
-
var import_react9 = __toESM(require("react"));
|
|
14083
|
-
var import_react_native8 = require("react-native");
|
|
14084
|
-
|
|
14085
|
-
// src/widget/ImagePreviewStrip.tsx
|
|
14086
14018
|
var import_react8 = __toESM(require("react"));
|
|
14087
14019
|
var import_react_native7 = require("react-native");
|
|
14020
|
+
|
|
14021
|
+
// src/widget/ImagePreviewStrip.tsx
|
|
14022
|
+
var import_react7 = __toESM(require("react"));
|
|
14023
|
+
var import_react_native6 = require("react-native");
|
|
14088
14024
|
function ImagePreviewStrip({ images, onRemove }) {
|
|
14089
14025
|
if (images.length === 0) return null;
|
|
14090
|
-
return /* @__PURE__ */
|
|
14026
|
+
return /* @__PURE__ */ import_react7.default.createElement(import_react_native6.ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, style: styles4.strip }, images.map((img) => /* @__PURE__ */ import_react7.default.createElement(import_react_native6.View, { key: img.id, style: styles4.thumbContainer }, /* @__PURE__ */ import_react7.default.createElement(import_react_native6.Image, { source: { uri: img.localUri }, style: styles4.thumb }), img.status === "uploading" && /* @__PURE__ */ import_react7.default.createElement(import_react_native6.View, { style: styles4.thumbOverlay }, /* @__PURE__ */ import_react7.default.createElement(import_react_native6.ActivityIndicator, { size: "small", color: "#fff" })), img.status === "error" && /* @__PURE__ */ import_react7.default.createElement(import_react_native6.View, { style: [styles4.thumbOverlay, styles4.thumbOverlayError] }, /* @__PURE__ */ import_react7.default.createElement(import_react_native6.Text, { style: styles4.thumbErrorText }, "!")), /* @__PURE__ */ import_react7.default.createElement(import_react_native6.TouchableOpacity, { style: styles4.thumbRemove, onPress: () => onRemove(img.id) }, /* @__PURE__ */ import_react7.default.createElement(import_react_native6.Text, { style: styles4.thumbRemoveText }, "\u2715")))));
|
|
14091
14027
|
}
|
|
14092
|
-
var
|
|
14028
|
+
var styles4 = import_react_native6.StyleSheet.create({
|
|
14093
14029
|
strip: {
|
|
14094
14030
|
flexDirection: "row",
|
|
14095
14031
|
marginTop: 4
|
|
@@ -14108,7 +14044,7 @@ var styles5 = import_react_native7.StyleSheet.create({
|
|
|
14108
14044
|
borderRadius: 8
|
|
14109
14045
|
},
|
|
14110
14046
|
thumbOverlay: {
|
|
14111
|
-
...
|
|
14047
|
+
...import_react_native6.StyleSheet.absoluteFillObject,
|
|
14112
14048
|
backgroundColor: "rgba(0,0,0,0.5)",
|
|
14113
14049
|
justifyContent: "center",
|
|
14114
14050
|
alignItems: "center",
|
|
@@ -14143,25 +14079,25 @@ var styles5 = import_react_native7.StyleSheet.create({
|
|
|
14143
14079
|
// src/widget/ImagePickerButtons.tsx
|
|
14144
14080
|
function ImagePickerButtons({ images, maxImages, onPickGallery, onPickCamera, onRemove, label }) {
|
|
14145
14081
|
if (!IMAGE_PICKER_AVAILABLE) return null;
|
|
14146
|
-
return /* @__PURE__ */
|
|
14147
|
-
|
|
14082
|
+
return /* @__PURE__ */ import_react8.default.createElement(import_react_native7.View, { style: styles5.section }, label && /* @__PURE__ */ import_react8.default.createElement(import_react_native7.Text, { style: styles5.label }, label), /* @__PURE__ */ import_react8.default.createElement(import_react_native7.View, { style: styles5.buttonRow }, /* @__PURE__ */ import_react8.default.createElement(
|
|
14083
|
+
import_react_native7.TouchableOpacity,
|
|
14148
14084
|
{
|
|
14149
|
-
style:
|
|
14085
|
+
style: styles5.pickButton,
|
|
14150
14086
|
onPress: onPickGallery,
|
|
14151
14087
|
disabled: images.length >= maxImages
|
|
14152
14088
|
},
|
|
14153
|
-
/* @__PURE__ */
|
|
14154
|
-
), /* @__PURE__ */
|
|
14155
|
-
|
|
14089
|
+
/* @__PURE__ */ import_react8.default.createElement(import_react_native7.Text, { style: [styles5.pickButtonText, images.length >= maxImages && styles5.pickButtonDisabled] }, "Gallery")
|
|
14090
|
+
), /* @__PURE__ */ import_react8.default.createElement(
|
|
14091
|
+
import_react_native7.TouchableOpacity,
|
|
14156
14092
|
{
|
|
14157
|
-
style:
|
|
14093
|
+
style: styles5.pickButton,
|
|
14158
14094
|
onPress: onPickCamera,
|
|
14159
14095
|
disabled: images.length >= maxImages
|
|
14160
14096
|
},
|
|
14161
|
-
/* @__PURE__ */
|
|
14162
|
-
), /* @__PURE__ */
|
|
14097
|
+
/* @__PURE__ */ import_react8.default.createElement(import_react_native7.Text, { style: [styles5.pickButtonText, images.length >= maxImages && styles5.pickButtonDisabled] }, "Camera")
|
|
14098
|
+
), /* @__PURE__ */ import_react8.default.createElement(import_react_native7.Text, { style: styles5.countText }, images.length, "/", maxImages)), /* @__PURE__ */ import_react8.default.createElement(ImagePreviewStrip, { images, onRemove }));
|
|
14163
14099
|
}
|
|
14164
|
-
var
|
|
14100
|
+
var styles5 = import_react_native7.StyleSheet.create({
|
|
14165
14101
|
section: {
|
|
14166
14102
|
marginTop: 12,
|
|
14167
14103
|
marginBottom: 4
|
|
@@ -14200,7 +14136,123 @@ var styles6 = import_react_native8.StyleSheet.create({
|
|
|
14200
14136
|
}
|
|
14201
14137
|
});
|
|
14202
14138
|
|
|
14139
|
+
// src/widget/screens/TestFeedbackScreen.tsx
|
|
14140
|
+
function TestFeedbackScreen({ status, assignmentId, nav }) {
|
|
14141
|
+
const { client, assignments, refreshAssignments, uploadImage } = useBugBear();
|
|
14142
|
+
const images = useImageAttachments(uploadImage, 3, "screenshots");
|
|
14143
|
+
const [rating, setRating] = (0, import_react9.useState)(5);
|
|
14144
|
+
const [note, setNote] = (0, import_react9.useState)("");
|
|
14145
|
+
const [flags, setFlags] = (0, import_react9.useState)({ isOutdated: false, needsMoreDetail: false, stepsUnclear: false, expectedResultUnclear: false });
|
|
14146
|
+
const [submitting, setSubmitting] = (0, import_react9.useState)(false);
|
|
14147
|
+
const assignment = assignments.find((a) => a.id === assignmentId);
|
|
14148
|
+
const showFlags = rating < 4;
|
|
14149
|
+
const handleSubmit = async () => {
|
|
14150
|
+
setSubmitting(true);
|
|
14151
|
+
if (client && assignment) {
|
|
14152
|
+
const screenshotUrls = images.getScreenshotUrls();
|
|
14153
|
+
await client.submitTestFeedback({
|
|
14154
|
+
testCaseId: assignment.testCase.id,
|
|
14155
|
+
assignmentId,
|
|
14156
|
+
feedback: {
|
|
14157
|
+
rating,
|
|
14158
|
+
feedbackNote: note.trim() || void 0,
|
|
14159
|
+
...showFlags ? {
|
|
14160
|
+
isOutdated: flags.isOutdated,
|
|
14161
|
+
needsMoreDetail: flags.needsMoreDetail,
|
|
14162
|
+
stepsUnclear: flags.stepsUnclear,
|
|
14163
|
+
expectedResultUnclear: flags.expectedResultUnclear
|
|
14164
|
+
} : {}
|
|
14165
|
+
},
|
|
14166
|
+
screenshotUrls: screenshotUrls.length > 0 ? screenshotUrls : void 0
|
|
14167
|
+
});
|
|
14168
|
+
}
|
|
14169
|
+
await refreshAssignments();
|
|
14170
|
+
setSubmitting(false);
|
|
14171
|
+
if (status === "failed") {
|
|
14172
|
+
nav.replace({ name: "REPORT", prefill: { type: "test_fail", assignmentId, testCaseId: assignment?.testCase.id } });
|
|
14173
|
+
} else {
|
|
14174
|
+
const remaining = assignments.filter((a) => (a.status === "pending" || a.status === "in_progress") && a.id !== assignmentId);
|
|
14175
|
+
if (remaining.length > 0) {
|
|
14176
|
+
nav.replace({ name: "TEST_DETAIL", testId: remaining[0].id });
|
|
14177
|
+
} else {
|
|
14178
|
+
nav.reset();
|
|
14179
|
+
}
|
|
14180
|
+
}
|
|
14181
|
+
};
|
|
14182
|
+
const handleSkip = () => {
|
|
14183
|
+
if (status === "failed") {
|
|
14184
|
+
nav.replace({ name: "REPORT", prefill: { type: "test_fail", assignmentId, testCaseId: assignment?.testCase.id } });
|
|
14185
|
+
} else {
|
|
14186
|
+
const remaining = assignments.filter((a) => (a.status === "pending" || a.status === "in_progress") && a.id !== assignmentId);
|
|
14187
|
+
if (remaining.length > 0) {
|
|
14188
|
+
nav.replace({ name: "TEST_DETAIL", testId: remaining[0].id });
|
|
14189
|
+
} else {
|
|
14190
|
+
nav.reset();
|
|
14191
|
+
}
|
|
14192
|
+
}
|
|
14193
|
+
};
|
|
14194
|
+
return /* @__PURE__ */ import_react9.default.createElement(import_react_native8.View, { style: styles6.container }, /* @__PURE__ */ import_react9.default.createElement(import_react_native8.Text, { style: styles6.header }, status === "passed" ? "\u2705 Test Passed!" : "\u274C Test Failed"), /* @__PURE__ */ import_react9.default.createElement(import_react_native8.Text, { style: styles6.subheader }, "Rate this test case"), /* @__PURE__ */ import_react9.default.createElement(import_react_native8.View, { style: styles6.starRow }, [1, 2, 3, 4, 5].map((n) => /* @__PURE__ */ import_react9.default.createElement(import_react_native8.TouchableOpacity, { key: n, onPress: () => setRating(n), style: styles6.starButton }, /* @__PURE__ */ import_react9.default.createElement(import_react_native8.Text, { style: [styles6.star, n <= rating && styles6.starActive] }, n <= rating ? "\u2605" : "\u2606")))), showFlags && /* @__PURE__ */ import_react9.default.createElement(import_react_native8.View, { style: styles6.flagsSection }, /* @__PURE__ */ import_react9.default.createElement(import_react_native8.Text, { style: styles6.flagsLabel }, "What could be improved?"), [
|
|
14195
|
+
{ key: "isOutdated", label: "Test is outdated" },
|
|
14196
|
+
{ key: "needsMoreDetail", label: "Needs more detail" },
|
|
14197
|
+
{ key: "stepsUnclear", label: "Steps are unclear" },
|
|
14198
|
+
{ key: "expectedResultUnclear", label: "Expected result unclear" }
|
|
14199
|
+
].map(({ key, label }) => /* @__PURE__ */ import_react9.default.createElement(
|
|
14200
|
+
import_react_native8.TouchableOpacity,
|
|
14201
|
+
{
|
|
14202
|
+
key,
|
|
14203
|
+
style: [styles6.flagItem, flags[key] && styles6.flagItemActive],
|
|
14204
|
+
onPress: () => setFlags((prev) => ({ ...prev, [key]: !prev[key] }))
|
|
14205
|
+
},
|
|
14206
|
+
/* @__PURE__ */ import_react9.default.createElement(import_react_native8.View, { style: [styles6.flagCheck, flags[key] && styles6.flagCheckActive] }, flags[key] && /* @__PURE__ */ import_react9.default.createElement(import_react_native8.Text, { style: styles6.flagCheckmark }, "\u2713")),
|
|
14207
|
+
/* @__PURE__ */ import_react9.default.createElement(import_react_native8.Text, { style: [styles6.flagText, flags[key] && styles6.flagTextActive] }, label)
|
|
14208
|
+
))), /* @__PURE__ */ import_react9.default.createElement(
|
|
14209
|
+
import_react_native8.TextInput,
|
|
14210
|
+
{
|
|
14211
|
+
style: styles6.noteInput,
|
|
14212
|
+
value: note,
|
|
14213
|
+
onChangeText: setNote,
|
|
14214
|
+
placeholder: "Add a note (optional)",
|
|
14215
|
+
placeholderTextColor: colors.textMuted,
|
|
14216
|
+
multiline: true
|
|
14217
|
+
}
|
|
14218
|
+
), /* @__PURE__ */ import_react9.default.createElement(
|
|
14219
|
+
ImagePickerButtons,
|
|
14220
|
+
{
|
|
14221
|
+
images: images.images,
|
|
14222
|
+
maxImages: 3,
|
|
14223
|
+
onPickGallery: images.pickFromGallery,
|
|
14224
|
+
onPickCamera: images.pickFromCamera,
|
|
14225
|
+
onRemove: images.removeImage,
|
|
14226
|
+
label: "Screenshots (optional)"
|
|
14227
|
+
}
|
|
14228
|
+
), /* @__PURE__ */ import_react9.default.createElement(import_react_native8.View, { style: styles6.actions }, /* @__PURE__ */ import_react9.default.createElement(import_react_native8.TouchableOpacity, { style: styles6.skipButton, onPress: handleSkip }, /* @__PURE__ */ import_react9.default.createElement(import_react_native8.Text, { style: styles6.skipText }, "Skip")), /* @__PURE__ */ import_react9.default.createElement(import_react_native8.TouchableOpacity, { style: [shared.primaryButton, { flex: 2, opacity: submitting || images.isUploading ? 0.5 : 1 }], onPress: handleSubmit, disabled: submitting || images.isUploading }, /* @__PURE__ */ import_react9.default.createElement(import_react_native8.Text, { style: shared.primaryButtonText }, images.isUploading ? "Uploading..." : submitting ? "Submitting..." : "Submit"))));
|
|
14229
|
+
}
|
|
14230
|
+
var styles6 = import_react_native8.StyleSheet.create({
|
|
14231
|
+
container: { paddingTop: 8 },
|
|
14232
|
+
header: { fontSize: 22, fontWeight: "700", color: colors.textPrimary, textAlign: "center", marginBottom: 4 },
|
|
14233
|
+
subheader: { fontSize: 14, color: colors.textMuted, textAlign: "center", marginBottom: 20 },
|
|
14234
|
+
starRow: { flexDirection: "row", justifyContent: "center", gap: 8, marginBottom: 20 },
|
|
14235
|
+
starButton: { padding: 4 },
|
|
14236
|
+
star: { fontSize: 36, color: colors.textDim },
|
|
14237
|
+
starActive: { color: "#facc15" },
|
|
14238
|
+
flagsSection: { marginBottom: 16 },
|
|
14239
|
+
flagsLabel: { fontSize: 14, fontWeight: "500", color: colors.textSecondary, marginBottom: 10 },
|
|
14240
|
+
flagItem: { flexDirection: "row", alignItems: "center", paddingVertical: 10, paddingHorizontal: 12, borderRadius: 8, marginBottom: 4, backgroundColor: colors.card },
|
|
14241
|
+
flagItemActive: { backgroundColor: colors.yellowDark, borderWidth: 1, borderColor: colors.yellow },
|
|
14242
|
+
flagCheck: { width: 20, height: 20, borderRadius: 4, borderWidth: 2, borderColor: colors.border, marginRight: 10, justifyContent: "center", alignItems: "center" },
|
|
14243
|
+
flagCheckActive: { backgroundColor: colors.yellow, borderColor: colors.yellow },
|
|
14244
|
+
flagCheckmark: { fontSize: 12, fontWeight: "bold", color: "#000" },
|
|
14245
|
+
flagText: { fontSize: 14, color: colors.textSecondary },
|
|
14246
|
+
flagTextActive: { color: colors.yellow },
|
|
14247
|
+
noteInput: { backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border, borderRadius: 10, padding: 12, fontSize: 14, color: colors.textPrimary, minHeight: 60, textAlignVertical: "top", marginBottom: 16 },
|
|
14248
|
+
actions: { flexDirection: "row", gap: 10 },
|
|
14249
|
+
skipButton: { flex: 1, paddingVertical: 14, borderRadius: 12, alignItems: "center", backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border },
|
|
14250
|
+
skipText: { fontSize: 15, color: colors.textSecondary }
|
|
14251
|
+
});
|
|
14252
|
+
|
|
14203
14253
|
// src/widget/screens/ReportScreen.tsx
|
|
14254
|
+
var import_react10 = __toESM(require("react"));
|
|
14255
|
+
var import_react_native9 = require("react-native");
|
|
14204
14256
|
function ReportScreen({ nav, prefill }) {
|
|
14205
14257
|
const { client, getDeviceInfo, uploadImage, refreshAssignments } = useBugBear();
|
|
14206
14258
|
const [reportType, setReportType] = (0, import_react10.useState)(prefill?.type || "bug");
|
|
@@ -14812,7 +14864,7 @@ function BugBearButton({
|
|
|
14812
14864
|
onPress: () => setModalVisible(true),
|
|
14813
14865
|
activeOpacity: draggable ? 1 : 0.7
|
|
14814
14866
|
},
|
|
14815
|
-
/* @__PURE__ */ import_react16.default.createElement(import_react_native15.
|
|
14867
|
+
/* @__PURE__ */ import_react16.default.createElement(import_react_native15.Image, { source: { uri: BUGBEAR_LOGO_BASE64 }, style: styles13.fabIcon }),
|
|
14816
14868
|
badgeCount > 0 && /* @__PURE__ */ import_react16.default.createElement(import_react_native15.View, { style: styles13.badge }, /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: styles13.badgeText }, badgeCount > 9 ? "9+" : badgeCount))
|
|
14817
14869
|
)
|
|
14818
14870
|
), /* @__PURE__ */ import_react16.default.createElement(
|
|
@@ -14862,7 +14914,9 @@ var styles13 = import_react_native15.StyleSheet.create({
|
|
|
14862
14914
|
elevation: 8
|
|
14863
14915
|
},
|
|
14864
14916
|
fabIcon: {
|
|
14865
|
-
|
|
14917
|
+
width: 32,
|
|
14918
|
+
height: 32,
|
|
14919
|
+
borderRadius: 16
|
|
14866
14920
|
},
|
|
14867
14921
|
badge: {
|
|
14868
14922
|
position: "absolute",
|
package/dist/index.mjs
CHANGED
|
@@ -11628,6 +11628,11 @@ var BugBearClient = class {
|
|
|
11628
11628
|
try {
|
|
11629
11629
|
const { data: currentAssignment, error: fetchError } = await this.supabase.from("test_assignments").select("status, started_at").eq("id", assignmentId).single();
|
|
11630
11630
|
if (fetchError || !currentAssignment) {
|
|
11631
|
+
console.error("BugBear: Assignment not found", {
|
|
11632
|
+
message: fetchError?.message,
|
|
11633
|
+
code: fetchError?.code,
|
|
11634
|
+
assignmentId
|
|
11635
|
+
});
|
|
11631
11636
|
return { success: false, error: "Assignment not found" };
|
|
11632
11637
|
}
|
|
11633
11638
|
const updateData = { status };
|
|
@@ -11652,7 +11657,15 @@ var BugBearClient = class {
|
|
|
11652
11657
|
}
|
|
11653
11658
|
const { error } = await this.supabase.from("test_assignments").update(updateData).eq("id", assignmentId);
|
|
11654
11659
|
if (error) {
|
|
11655
|
-
console.error("BugBear: Failed to update assignment status",
|
|
11660
|
+
console.error("BugBear: Failed to update assignment status", {
|
|
11661
|
+
message: error.message,
|
|
11662
|
+
details: error.details,
|
|
11663
|
+
hint: error.hint,
|
|
11664
|
+
code: error.code,
|
|
11665
|
+
assignmentId,
|
|
11666
|
+
status,
|
|
11667
|
+
updateData
|
|
11668
|
+
});
|
|
11656
11669
|
return { success: false, error: error.message };
|
|
11657
11670
|
}
|
|
11658
11671
|
if (options?.feedback && ["passed", "failed", "blocked"].includes(status)) {
|
|
@@ -11714,7 +11727,7 @@ var BugBearClient = class {
|
|
|
11714
11727
|
if (!testerInfo) {
|
|
11715
11728
|
return { success: false, error: "Not authenticated as tester" };
|
|
11716
11729
|
}
|
|
11717
|
-
const { testCaseId, assignmentId, feedback, timeToCompleteSeconds } = options;
|
|
11730
|
+
const { testCaseId, assignmentId, feedback, timeToCompleteSeconds, screenshotUrls } = options;
|
|
11718
11731
|
if (feedback.rating < 1 || feedback.rating > 5) {
|
|
11719
11732
|
return { success: false, error: "Rating must be between 1 and 5" };
|
|
11720
11733
|
}
|
|
@@ -11734,7 +11747,8 @@ var BugBearClient = class {
|
|
|
11734
11747
|
steps_unclear: feedback.stepsUnclear || false,
|
|
11735
11748
|
expected_result_unclear: feedback.expectedResultUnclear || false,
|
|
11736
11749
|
platform: this.getDeviceInfo().platform,
|
|
11737
|
-
time_to_complete_seconds: timeToCompleteSeconds || null
|
|
11750
|
+
time_to_complete_seconds: timeToCompleteSeconds || null,
|
|
11751
|
+
screenshot_urls: screenshotUrls || []
|
|
11738
11752
|
});
|
|
11739
11753
|
if (feedbackError) {
|
|
11740
11754
|
console.error("BugBear: Failed to submit feedback", feedbackError);
|
|
@@ -12049,19 +12063,21 @@ var BugBearClient = class {
|
|
|
12049
12063
|
/**
|
|
12050
12064
|
* Upload a screenshot (web - uses File/Blob)
|
|
12051
12065
|
*/
|
|
12052
|
-
async uploadScreenshot(file, filename) {
|
|
12066
|
+
async uploadScreenshot(file, filename, bucket = "screenshots") {
|
|
12053
12067
|
try {
|
|
12054
|
-
const
|
|
12068
|
+
const contentType = file.type || "image/png";
|
|
12069
|
+
const ext = contentType.includes("png") ? "png" : "jpg";
|
|
12070
|
+
const name = filename || `screenshot-${Date.now()}.${ext}`;
|
|
12055
12071
|
const path = `${this.config.projectId}/${name}`;
|
|
12056
|
-
const { error } = await this.supabase.storage.from(
|
|
12057
|
-
contentType
|
|
12072
|
+
const { error } = await this.supabase.storage.from(bucket).upload(path, file, {
|
|
12073
|
+
contentType,
|
|
12058
12074
|
upsert: false
|
|
12059
12075
|
});
|
|
12060
12076
|
if (error) {
|
|
12061
12077
|
console.error("BugBear: Failed to upload screenshot", error);
|
|
12062
12078
|
return null;
|
|
12063
12079
|
}
|
|
12064
|
-
const { data: { publicUrl } } = this.supabase.storage.from(
|
|
12080
|
+
const { data: { publicUrl } } = this.supabase.storage.from(bucket).getPublicUrl(path);
|
|
12065
12081
|
return publicUrl;
|
|
12066
12082
|
} catch (err) {
|
|
12067
12083
|
console.error("BugBear: Error uploading screenshot", err);
|
|
@@ -13027,7 +13043,7 @@ function BugBearProvider({ config, children, appVersion, enabled = true }) {
|
|
|
13027
13043
|
(a) => a.status === "in_progress"
|
|
13028
13044
|
) || assignments.find(
|
|
13029
13045
|
(a) => a.status === "pending"
|
|
13030
|
-
) ||
|
|
13046
|
+
) || null;
|
|
13031
13047
|
const shouldShowWidget = isQAEnabled && isTester;
|
|
13032
13048
|
return /* @__PURE__ */ React.createElement(
|
|
13033
13049
|
BugBearContext.Provider,
|
|
@@ -13074,6 +13090,7 @@ import React14, { useState as useState10, useRef as useRef2 } from "react";
|
|
|
13074
13090
|
import {
|
|
13075
13091
|
View as View13,
|
|
13076
13092
|
Text as Text13,
|
|
13093
|
+
Image as Image3,
|
|
13077
13094
|
TouchableOpacity as TouchableOpacity12,
|
|
13078
13095
|
Modal as Modal2,
|
|
13079
13096
|
ScrollView as ScrollView2,
|
|
@@ -13086,6 +13103,9 @@ import {
|
|
|
13086
13103
|
ActivityIndicator as ActivityIndicator2
|
|
13087
13104
|
} from "react-native";
|
|
13088
13105
|
|
|
13106
|
+
// src/widget/logo.ts
|
|
13107
|
+
var BUGBEAR_LOGO_BASE64 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAJhlWElmTU0AKgAAAAgABAEaAAUAAAABAAAAPgEbAAUAAAABAAAARgEoAAMAAAABAAIAAIdpAAQAAAABAAAATgAAAAAAAABIAAAAAQAAAEgAAAABAASQBAACAAAAFAAAAISgAQADAAAAAQABAACgAgAEAAAAAQAAAECgAwAEAAAAAQAAAEAAAAAAMjAyNjowMToyNCAxNjoyMTozOAAvO6cIAAAACXBIWXMAAAsTAAALEwEAmpwYAAACo2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNi4wLjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczpJcHRjNHhtcEV4dD0iaHR0cDovL2lwdGMub3JnL3N0ZC9JcHRjNHhtcEV4dC8yMDA4LTAyLTI5LyIKICAgICAgICAgICAgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIj4KICAgICAgICAgPElwdGM0eG1wRXh0OkRpZ2l0YWxTb3VyY2VUeXBlPmh0dHA6Ly9jdi5pcHRjLm9yZy9uZXdzY29kZXMvZGlnaXRhbHNvdXJjZXR5cGUvdHJhaW5lZEFsZ29yaXRobWljTWVkaWE8L0lwdGM0eG1wRXh0OkRpZ2l0YWxTb3VyY2VUeXBlPgogICAgICAgICA8SXB0YzR4bXBFeHQ6RGlnSW1hZ2VHVUlEPmZjNzJlN2Q2LTYyYTEtNDE1ZS04MjY5LWM2NjA4MjY0OWRiMDwvSXB0YzR4bXBFeHQ6RGlnSW1hZ2VHVUlEPgogICAgICAgICA8eG1wOkNyZWF0ZURhdGU+MjAyNi0wMS0yNFQxNjoyMTozODwveG1wOkNyZWF0ZURhdGU+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+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";
|
|
13108
|
+
|
|
13089
13109
|
// src/widget/navigation.ts
|
|
13090
13110
|
import { useReducer } from "react";
|
|
13091
13111
|
function navReducer(state, action) {
|
|
@@ -13590,7 +13610,14 @@ function TestDetailScreen({ testId, nav }) {
|
|
|
13590
13610
|
if (!client || !displayedAssignment) return;
|
|
13591
13611
|
await client.failAssignment(displayedAssignment.id);
|
|
13592
13612
|
await refreshAssignments();
|
|
13593
|
-
nav.replace({
|
|
13613
|
+
nav.replace({
|
|
13614
|
+
name: "REPORT",
|
|
13615
|
+
prefill: {
|
|
13616
|
+
type: "test_fail",
|
|
13617
|
+
assignmentId: displayedAssignment.id,
|
|
13618
|
+
testCaseId: displayedAssignment.testCase.id
|
|
13619
|
+
}
|
|
13620
|
+
});
|
|
13594
13621
|
}, [client, displayedAssignment, refreshAssignments, nav]);
|
|
13595
13622
|
const handleSkip = useCallback2(async () => {
|
|
13596
13623
|
if (!client || !displayedAssignment || !selectedSkipReason) return;
|
|
@@ -13604,9 +13631,11 @@ function TestDetailScreen({ testId, nav }) {
|
|
|
13604
13631
|
setSelectedSkipReason(null);
|
|
13605
13632
|
setSkipNotes("");
|
|
13606
13633
|
setSkipping(false);
|
|
13607
|
-
const remaining = assignments.filter(
|
|
13608
|
-
|
|
13609
|
-
|
|
13634
|
+
const remaining = assignments.filter(
|
|
13635
|
+
(a) => (a.status === "pending" || a.status === "in_progress") && a.id !== displayedAssignment.id
|
|
13636
|
+
);
|
|
13637
|
+
if (remaining.length > 0) {
|
|
13638
|
+
nav.replace({ name: "TEST_DETAIL", testId: remaining[0].id });
|
|
13610
13639
|
} else {
|
|
13611
13640
|
nav.reset();
|
|
13612
13641
|
}
|
|
@@ -13795,7 +13824,7 @@ var styles2 = StyleSheet3.create({
|
|
|
13795
13824
|
import React4, { useState as useState3, useMemo as useMemo2, useCallback as useCallback3 } from "react";
|
|
13796
13825
|
import { View as View3, Text as Text3, TouchableOpacity as TouchableOpacity3, StyleSheet as StyleSheet4 } from "react-native";
|
|
13797
13826
|
function TestListScreen({ nav }) {
|
|
13798
|
-
const { assignments, refreshAssignments } = useBugBear();
|
|
13827
|
+
const { assignments, currentAssignment, refreshAssignments } = useBugBear();
|
|
13799
13828
|
const [filter, setFilter] = useState3("all");
|
|
13800
13829
|
const [collapsedFolders, setCollapsedFolders] = useState3(/* @__PURE__ */ new Set());
|
|
13801
13830
|
const groupedAssignments = useMemo2(() => {
|
|
@@ -13850,11 +13879,12 @@ function TestListScreen({ nav }) {
|
|
|
13850
13879
|
if (filtered.length === 0 && filter !== "all") return null;
|
|
13851
13880
|
return /* @__PURE__ */ React4.createElement(View3, { key: folderId, style: styles3.folder }, /* @__PURE__ */ React4.createElement(TouchableOpacity3, { style: styles3.folderHeader, onPress: () => toggleFolder(folderId) }, /* @__PURE__ */ React4.createElement(Text3, { style: styles3.folderToggle }, isCollapsed ? "\u25B6" : "\u25BC"), /* @__PURE__ */ React4.createElement(Text3, { style: styles3.folderName }, folder.group?.name || "Ungrouped"), /* @__PURE__ */ React4.createElement(View3, { style: styles3.folderProgress }, /* @__PURE__ */ React4.createElement(View3, { style: [styles3.folderProgressFill, { width: `${Math.round((folder.stats.passed + folder.stats.failed) / folder.stats.total * 100)}%` }] })), /* @__PURE__ */ React4.createElement(Text3, { style: styles3.folderCount }, folder.stats.passed + folder.stats.failed, "/", folder.stats.total)), !isCollapsed && filtered.map((assignment) => {
|
|
13852
13881
|
const badge = getStatusBadge(assignment.status);
|
|
13882
|
+
const isCurrent = currentAssignment?.id === assignment.id;
|
|
13853
13883
|
return /* @__PURE__ */ React4.createElement(
|
|
13854
13884
|
TouchableOpacity3,
|
|
13855
13885
|
{
|
|
13856
13886
|
key: assignment.id,
|
|
13857
|
-
style: styles3.testItem,
|
|
13887
|
+
style: [styles3.testItem, isCurrent && styles3.testItemCurrent],
|
|
13858
13888
|
onPress: () => nav.push({ name: "TEST_DETAIL", testId: assignment.id })
|
|
13859
13889
|
},
|
|
13860
13890
|
/* @__PURE__ */ React4.createElement(Text3, { style: styles3.testBadge }, badge.icon),
|
|
@@ -13877,6 +13907,7 @@ var styles3 = StyleSheet4.create({
|
|
|
13877
13907
|
folderProgressFill: { height: "100%", backgroundColor: colors.green, borderRadius: 2 },
|
|
13878
13908
|
folderCount: { fontSize: 12, color: colors.textMuted, width: 30, textAlign: "right" },
|
|
13879
13909
|
testItem: { flexDirection: "row", alignItems: "center", paddingVertical: 10, paddingHorizontal: 12, borderRadius: 8, marginBottom: 4, backgroundColor: colors.card },
|
|
13910
|
+
testItemCurrent: { backgroundColor: "rgba(59, 130, 246, 0.15)", borderLeftWidth: 3, borderLeftColor: colors.blue },
|
|
13880
13911
|
testBadge: { fontSize: 16, marginRight: 10, width: 20 },
|
|
13881
13912
|
testInfo: { flex: 1 },
|
|
13882
13913
|
testTitle: { fontSize: 14, color: colors.textPrimary, marginBottom: 2 },
|
|
@@ -13886,105 +13917,11 @@ var styles3 = StyleSheet4.create({
|
|
|
13886
13917
|
});
|
|
13887
13918
|
|
|
13888
13919
|
// src/widget/screens/TestFeedbackScreen.tsx
|
|
13889
|
-
import
|
|
13890
|
-
import { View as
|
|
13891
|
-
function TestFeedbackScreen({ status, assignmentId, nav }) {
|
|
13892
|
-
const { client, assignments, refreshAssignments } = useBugBear();
|
|
13893
|
-
const [rating, setRating] = useState4(5);
|
|
13894
|
-
const [note, setNote] = useState4("");
|
|
13895
|
-
const [flags, setFlags] = useState4({ isOutdated: false, needsMoreDetail: false, stepsUnclear: false, expectedResultUnclear: false });
|
|
13896
|
-
const [submitting, setSubmitting] = useState4(false);
|
|
13897
|
-
const assignment = assignments.find((a) => a.id === assignmentId);
|
|
13898
|
-
const showFlags = rating < 4;
|
|
13899
|
-
const handleSubmit = async () => {
|
|
13900
|
-
setSubmitting(true);
|
|
13901
|
-
if (client && assignment) {
|
|
13902
|
-
await client.submitTestFeedback(assignment.testCase.id, {
|
|
13903
|
-
rating,
|
|
13904
|
-
note: note.trim() || void 0,
|
|
13905
|
-
qualityFlags: showFlags ? flags : void 0
|
|
13906
|
-
});
|
|
13907
|
-
}
|
|
13908
|
-
await refreshAssignments();
|
|
13909
|
-
setSubmitting(false);
|
|
13910
|
-
if (status === "failed") {
|
|
13911
|
-
nav.replace({ name: "REPORT", prefill: { type: "test_fail", assignmentId, testCaseId: assignment?.testCase.id } });
|
|
13912
|
-
} else {
|
|
13913
|
-
const remaining = assignments.filter((a) => (a.status === "pending" || a.status === "in_progress") && a.id !== assignmentId);
|
|
13914
|
-
if (remaining.length > 0) {
|
|
13915
|
-
nav.replace({ name: "TEST_DETAIL" });
|
|
13916
|
-
} else {
|
|
13917
|
-
nav.reset();
|
|
13918
|
-
}
|
|
13919
|
-
}
|
|
13920
|
-
};
|
|
13921
|
-
const handleSkip = () => {
|
|
13922
|
-
if (status === "failed") {
|
|
13923
|
-
nav.replace({ name: "REPORT", prefill: { type: "test_fail", assignmentId, testCaseId: assignment?.testCase.id } });
|
|
13924
|
-
} else {
|
|
13925
|
-
const remaining = assignments.filter((a) => (a.status === "pending" || a.status === "in_progress") && a.id !== assignmentId);
|
|
13926
|
-
if (remaining.length > 0) {
|
|
13927
|
-
nav.replace({ name: "TEST_DETAIL" });
|
|
13928
|
-
} else {
|
|
13929
|
-
nav.reset();
|
|
13930
|
-
}
|
|
13931
|
-
}
|
|
13932
|
-
};
|
|
13933
|
-
return /* @__PURE__ */ React5.createElement(View4, { style: styles4.container }, /* @__PURE__ */ React5.createElement(Text4, { style: styles4.header }, status === "passed" ? "\u2705 Test Passed!" : "\u274C Test Failed"), /* @__PURE__ */ React5.createElement(Text4, { style: styles4.subheader }, "Rate this test case"), /* @__PURE__ */ React5.createElement(View4, { style: styles4.starRow }, [1, 2, 3, 4, 5].map((n) => /* @__PURE__ */ React5.createElement(TouchableOpacity4, { key: n, onPress: () => setRating(n), style: styles4.starButton }, /* @__PURE__ */ React5.createElement(Text4, { style: [styles4.star, n <= rating && styles4.starActive] }, n <= rating ? "\u2605" : "\u2606")))), showFlags && /* @__PURE__ */ React5.createElement(View4, { style: styles4.flagsSection }, /* @__PURE__ */ React5.createElement(Text4, { style: styles4.flagsLabel }, "What could be improved?"), [
|
|
13934
|
-
{ key: "isOutdated", label: "Test is outdated" },
|
|
13935
|
-
{ key: "needsMoreDetail", label: "Needs more detail" },
|
|
13936
|
-
{ key: "stepsUnclear", label: "Steps are unclear" },
|
|
13937
|
-
{ key: "expectedResultUnclear", label: "Expected result unclear" }
|
|
13938
|
-
].map(({ key, label }) => /* @__PURE__ */ React5.createElement(
|
|
13939
|
-
TouchableOpacity4,
|
|
13940
|
-
{
|
|
13941
|
-
key,
|
|
13942
|
-
style: [styles4.flagItem, flags[key] && styles4.flagItemActive],
|
|
13943
|
-
onPress: () => setFlags((prev) => ({ ...prev, [key]: !prev[key] }))
|
|
13944
|
-
},
|
|
13945
|
-
/* @__PURE__ */ React5.createElement(View4, { style: [styles4.flagCheck, flags[key] && styles4.flagCheckActive] }, flags[key] && /* @__PURE__ */ React5.createElement(Text4, { style: styles4.flagCheckmark }, "\u2713")),
|
|
13946
|
-
/* @__PURE__ */ React5.createElement(Text4, { style: [styles4.flagText, flags[key] && styles4.flagTextActive] }, label)
|
|
13947
|
-
))), /* @__PURE__ */ React5.createElement(
|
|
13948
|
-
TextInput2,
|
|
13949
|
-
{
|
|
13950
|
-
style: styles4.noteInput,
|
|
13951
|
-
value: note,
|
|
13952
|
-
onChangeText: setNote,
|
|
13953
|
-
placeholder: "Add a note (optional)",
|
|
13954
|
-
placeholderTextColor: colors.textMuted,
|
|
13955
|
-
multiline: true
|
|
13956
|
-
}
|
|
13957
|
-
), /* @__PURE__ */ React5.createElement(View4, { style: styles4.actions }, /* @__PURE__ */ React5.createElement(TouchableOpacity4, { style: styles4.skipButton, onPress: handleSkip }, /* @__PURE__ */ React5.createElement(Text4, { style: styles4.skipText }, "Skip")), /* @__PURE__ */ React5.createElement(TouchableOpacity4, { style: [shared.primaryButton, { flex: 2 }], onPress: handleSubmit, disabled: submitting }, /* @__PURE__ */ React5.createElement(Text4, { style: shared.primaryButtonText }, submitting ? "Submitting..." : "Submit"))));
|
|
13958
|
-
}
|
|
13959
|
-
var styles4 = StyleSheet5.create({
|
|
13960
|
-
container: { paddingTop: 8 },
|
|
13961
|
-
header: { fontSize: 22, fontWeight: "700", color: colors.textPrimary, textAlign: "center", marginBottom: 4 },
|
|
13962
|
-
subheader: { fontSize: 14, color: colors.textMuted, textAlign: "center", marginBottom: 20 },
|
|
13963
|
-
starRow: { flexDirection: "row", justifyContent: "center", gap: 8, marginBottom: 20 },
|
|
13964
|
-
starButton: { padding: 4 },
|
|
13965
|
-
star: { fontSize: 36, color: colors.textDim },
|
|
13966
|
-
starActive: { color: "#facc15" },
|
|
13967
|
-
flagsSection: { marginBottom: 16 },
|
|
13968
|
-
flagsLabel: { fontSize: 14, fontWeight: "500", color: colors.textSecondary, marginBottom: 10 },
|
|
13969
|
-
flagItem: { flexDirection: "row", alignItems: "center", paddingVertical: 10, paddingHorizontal: 12, borderRadius: 8, marginBottom: 4, backgroundColor: colors.card },
|
|
13970
|
-
flagItemActive: { backgroundColor: colors.yellowDark, borderWidth: 1, borderColor: colors.yellow },
|
|
13971
|
-
flagCheck: { width: 20, height: 20, borderRadius: 4, borderWidth: 2, borderColor: colors.border, marginRight: 10, justifyContent: "center", alignItems: "center" },
|
|
13972
|
-
flagCheckActive: { backgroundColor: colors.yellow, borderColor: colors.yellow },
|
|
13973
|
-
flagCheckmark: { fontSize: 12, fontWeight: "bold", color: "#000" },
|
|
13974
|
-
flagText: { fontSize: 14, color: colors.textSecondary },
|
|
13975
|
-
flagTextActive: { color: colors.yellow },
|
|
13976
|
-
noteInput: { backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border, borderRadius: 10, padding: 12, fontSize: 14, color: colors.textPrimary, minHeight: 60, textAlignVertical: "top", marginBottom: 16 },
|
|
13977
|
-
actions: { flexDirection: "row", gap: 10 },
|
|
13978
|
-
skipButton: { flex: 1, paddingVertical: 14, borderRadius: 12, alignItems: "center", backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border },
|
|
13979
|
-
skipText: { fontSize: 15, color: colors.textSecondary }
|
|
13980
|
-
});
|
|
13981
|
-
|
|
13982
|
-
// src/widget/screens/ReportScreen.tsx
|
|
13983
|
-
import React8, { useState as useState6 } from "react";
|
|
13984
|
-
import { View as View7, Text as Text7, TouchableOpacity as TouchableOpacity7, TextInput as TextInput3, StyleSheet as StyleSheet8 } from "react-native";
|
|
13920
|
+
import React7, { useState as useState5 } from "react";
|
|
13921
|
+
import { View as View6, Text as Text6, TouchableOpacity as TouchableOpacity6, TextInput as TextInput2, StyleSheet as StyleSheet7 } from "react-native";
|
|
13985
13922
|
|
|
13986
13923
|
// src/widget/useImageAttachments.ts
|
|
13987
|
-
import { useState as
|
|
13924
|
+
import { useState as useState4, useCallback as useCallback4 } from "react";
|
|
13988
13925
|
var launchImageLibrary = null;
|
|
13989
13926
|
var launchCamera = null;
|
|
13990
13927
|
try {
|
|
@@ -13995,7 +13932,7 @@ try {
|
|
|
13995
13932
|
}
|
|
13996
13933
|
var IMAGE_PICKER_AVAILABLE = launchImageLibrary !== null;
|
|
13997
13934
|
function useImageAttachments(uploadFn, maxImages, bucket = "screenshots") {
|
|
13998
|
-
const [images, setImages] =
|
|
13935
|
+
const [images, setImages] = useState4([]);
|
|
13999
13936
|
const pickFromGallery = useCallback4(async () => {
|
|
14000
13937
|
if (!launchImageLibrary || images.length >= maxImages) return;
|
|
14001
13938
|
launchImageLibrary(
|
|
@@ -14061,17 +13998,17 @@ function useImageAttachments(uploadFn, maxImages, bucket = "screenshots") {
|
|
|
14061
13998
|
}
|
|
14062
13999
|
|
|
14063
14000
|
// src/widget/ImagePickerButtons.tsx
|
|
14064
|
-
import
|
|
14065
|
-
import { View as
|
|
14001
|
+
import React6 from "react";
|
|
14002
|
+
import { View as View5, Text as Text5, TouchableOpacity as TouchableOpacity5, StyleSheet as StyleSheet6 } from "react-native";
|
|
14066
14003
|
|
|
14067
14004
|
// src/widget/ImagePreviewStrip.tsx
|
|
14068
|
-
import
|
|
14069
|
-
import { View as
|
|
14005
|
+
import React5 from "react";
|
|
14006
|
+
import { View as View4, Text as Text4, TouchableOpacity as TouchableOpacity4, ScrollView, Image, ActivityIndicator, StyleSheet as StyleSheet5 } from "react-native";
|
|
14070
14007
|
function ImagePreviewStrip({ images, onRemove }) {
|
|
14071
14008
|
if (images.length === 0) return null;
|
|
14072
|
-
return /* @__PURE__ */
|
|
14009
|
+
return /* @__PURE__ */ React5.createElement(ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, style: styles4.strip }, images.map((img) => /* @__PURE__ */ React5.createElement(View4, { key: img.id, style: styles4.thumbContainer }, /* @__PURE__ */ React5.createElement(Image, { source: { uri: img.localUri }, style: styles4.thumb }), img.status === "uploading" && /* @__PURE__ */ React5.createElement(View4, { style: styles4.thumbOverlay }, /* @__PURE__ */ React5.createElement(ActivityIndicator, { size: "small", color: "#fff" })), img.status === "error" && /* @__PURE__ */ React5.createElement(View4, { style: [styles4.thumbOverlay, styles4.thumbOverlayError] }, /* @__PURE__ */ React5.createElement(Text4, { style: styles4.thumbErrorText }, "!")), /* @__PURE__ */ React5.createElement(TouchableOpacity4, { style: styles4.thumbRemove, onPress: () => onRemove(img.id) }, /* @__PURE__ */ React5.createElement(Text4, { style: styles4.thumbRemoveText }, "\u2715")))));
|
|
14073
14010
|
}
|
|
14074
|
-
var
|
|
14011
|
+
var styles4 = StyleSheet5.create({
|
|
14075
14012
|
strip: {
|
|
14076
14013
|
flexDirection: "row",
|
|
14077
14014
|
marginTop: 4
|
|
@@ -14090,7 +14027,7 @@ var styles5 = StyleSheet6.create({
|
|
|
14090
14027
|
borderRadius: 8
|
|
14091
14028
|
},
|
|
14092
14029
|
thumbOverlay: {
|
|
14093
|
-
...
|
|
14030
|
+
...StyleSheet5.absoluteFillObject,
|
|
14094
14031
|
backgroundColor: "rgba(0,0,0,0.5)",
|
|
14095
14032
|
justifyContent: "center",
|
|
14096
14033
|
alignItems: "center",
|
|
@@ -14125,25 +14062,25 @@ var styles5 = StyleSheet6.create({
|
|
|
14125
14062
|
// src/widget/ImagePickerButtons.tsx
|
|
14126
14063
|
function ImagePickerButtons({ images, maxImages, onPickGallery, onPickCamera, onRemove, label }) {
|
|
14127
14064
|
if (!IMAGE_PICKER_AVAILABLE) return null;
|
|
14128
|
-
return /* @__PURE__ */
|
|
14129
|
-
|
|
14065
|
+
return /* @__PURE__ */ React6.createElement(View5, { style: styles5.section }, label && /* @__PURE__ */ React6.createElement(Text5, { style: styles5.label }, label), /* @__PURE__ */ React6.createElement(View5, { style: styles5.buttonRow }, /* @__PURE__ */ React6.createElement(
|
|
14066
|
+
TouchableOpacity5,
|
|
14130
14067
|
{
|
|
14131
|
-
style:
|
|
14068
|
+
style: styles5.pickButton,
|
|
14132
14069
|
onPress: onPickGallery,
|
|
14133
14070
|
disabled: images.length >= maxImages
|
|
14134
14071
|
},
|
|
14135
|
-
/* @__PURE__ */
|
|
14136
|
-
), /* @__PURE__ */
|
|
14137
|
-
|
|
14072
|
+
/* @__PURE__ */ React6.createElement(Text5, { style: [styles5.pickButtonText, images.length >= maxImages && styles5.pickButtonDisabled] }, "Gallery")
|
|
14073
|
+
), /* @__PURE__ */ React6.createElement(
|
|
14074
|
+
TouchableOpacity5,
|
|
14138
14075
|
{
|
|
14139
|
-
style:
|
|
14076
|
+
style: styles5.pickButton,
|
|
14140
14077
|
onPress: onPickCamera,
|
|
14141
14078
|
disabled: images.length >= maxImages
|
|
14142
14079
|
},
|
|
14143
|
-
/* @__PURE__ */
|
|
14144
|
-
), /* @__PURE__ */
|
|
14080
|
+
/* @__PURE__ */ React6.createElement(Text5, { style: [styles5.pickButtonText, images.length >= maxImages && styles5.pickButtonDisabled] }, "Camera")
|
|
14081
|
+
), /* @__PURE__ */ React6.createElement(Text5, { style: styles5.countText }, images.length, "/", maxImages)), /* @__PURE__ */ React6.createElement(ImagePreviewStrip, { images, onRemove }));
|
|
14145
14082
|
}
|
|
14146
|
-
var
|
|
14083
|
+
var styles5 = StyleSheet6.create({
|
|
14147
14084
|
section: {
|
|
14148
14085
|
marginTop: 12,
|
|
14149
14086
|
marginBottom: 4
|
|
@@ -14182,7 +14119,123 @@ var styles6 = StyleSheet7.create({
|
|
|
14182
14119
|
}
|
|
14183
14120
|
});
|
|
14184
14121
|
|
|
14122
|
+
// src/widget/screens/TestFeedbackScreen.tsx
|
|
14123
|
+
function TestFeedbackScreen({ status, assignmentId, nav }) {
|
|
14124
|
+
const { client, assignments, refreshAssignments, uploadImage } = useBugBear();
|
|
14125
|
+
const images = useImageAttachments(uploadImage, 3, "screenshots");
|
|
14126
|
+
const [rating, setRating] = useState5(5);
|
|
14127
|
+
const [note, setNote] = useState5("");
|
|
14128
|
+
const [flags, setFlags] = useState5({ isOutdated: false, needsMoreDetail: false, stepsUnclear: false, expectedResultUnclear: false });
|
|
14129
|
+
const [submitting, setSubmitting] = useState5(false);
|
|
14130
|
+
const assignment = assignments.find((a) => a.id === assignmentId);
|
|
14131
|
+
const showFlags = rating < 4;
|
|
14132
|
+
const handleSubmit = async () => {
|
|
14133
|
+
setSubmitting(true);
|
|
14134
|
+
if (client && assignment) {
|
|
14135
|
+
const screenshotUrls = images.getScreenshotUrls();
|
|
14136
|
+
await client.submitTestFeedback({
|
|
14137
|
+
testCaseId: assignment.testCase.id,
|
|
14138
|
+
assignmentId,
|
|
14139
|
+
feedback: {
|
|
14140
|
+
rating,
|
|
14141
|
+
feedbackNote: note.trim() || void 0,
|
|
14142
|
+
...showFlags ? {
|
|
14143
|
+
isOutdated: flags.isOutdated,
|
|
14144
|
+
needsMoreDetail: flags.needsMoreDetail,
|
|
14145
|
+
stepsUnclear: flags.stepsUnclear,
|
|
14146
|
+
expectedResultUnclear: flags.expectedResultUnclear
|
|
14147
|
+
} : {}
|
|
14148
|
+
},
|
|
14149
|
+
screenshotUrls: screenshotUrls.length > 0 ? screenshotUrls : void 0
|
|
14150
|
+
});
|
|
14151
|
+
}
|
|
14152
|
+
await refreshAssignments();
|
|
14153
|
+
setSubmitting(false);
|
|
14154
|
+
if (status === "failed") {
|
|
14155
|
+
nav.replace({ name: "REPORT", prefill: { type: "test_fail", assignmentId, testCaseId: assignment?.testCase.id } });
|
|
14156
|
+
} else {
|
|
14157
|
+
const remaining = assignments.filter((a) => (a.status === "pending" || a.status === "in_progress") && a.id !== assignmentId);
|
|
14158
|
+
if (remaining.length > 0) {
|
|
14159
|
+
nav.replace({ name: "TEST_DETAIL", testId: remaining[0].id });
|
|
14160
|
+
} else {
|
|
14161
|
+
nav.reset();
|
|
14162
|
+
}
|
|
14163
|
+
}
|
|
14164
|
+
};
|
|
14165
|
+
const handleSkip = () => {
|
|
14166
|
+
if (status === "failed") {
|
|
14167
|
+
nav.replace({ name: "REPORT", prefill: { type: "test_fail", assignmentId, testCaseId: assignment?.testCase.id } });
|
|
14168
|
+
} else {
|
|
14169
|
+
const remaining = assignments.filter((a) => (a.status === "pending" || a.status === "in_progress") && a.id !== assignmentId);
|
|
14170
|
+
if (remaining.length > 0) {
|
|
14171
|
+
nav.replace({ name: "TEST_DETAIL", testId: remaining[0].id });
|
|
14172
|
+
} else {
|
|
14173
|
+
nav.reset();
|
|
14174
|
+
}
|
|
14175
|
+
}
|
|
14176
|
+
};
|
|
14177
|
+
return /* @__PURE__ */ React7.createElement(View6, { style: styles6.container }, /* @__PURE__ */ React7.createElement(Text6, { style: styles6.header }, status === "passed" ? "\u2705 Test Passed!" : "\u274C Test Failed"), /* @__PURE__ */ React7.createElement(Text6, { style: styles6.subheader }, "Rate this test case"), /* @__PURE__ */ React7.createElement(View6, { style: styles6.starRow }, [1, 2, 3, 4, 5].map((n) => /* @__PURE__ */ React7.createElement(TouchableOpacity6, { key: n, onPress: () => setRating(n), style: styles6.starButton }, /* @__PURE__ */ React7.createElement(Text6, { style: [styles6.star, n <= rating && styles6.starActive] }, n <= rating ? "\u2605" : "\u2606")))), showFlags && /* @__PURE__ */ React7.createElement(View6, { style: styles6.flagsSection }, /* @__PURE__ */ React7.createElement(Text6, { style: styles6.flagsLabel }, "What could be improved?"), [
|
|
14178
|
+
{ key: "isOutdated", label: "Test is outdated" },
|
|
14179
|
+
{ key: "needsMoreDetail", label: "Needs more detail" },
|
|
14180
|
+
{ key: "stepsUnclear", label: "Steps are unclear" },
|
|
14181
|
+
{ key: "expectedResultUnclear", label: "Expected result unclear" }
|
|
14182
|
+
].map(({ key, label }) => /* @__PURE__ */ React7.createElement(
|
|
14183
|
+
TouchableOpacity6,
|
|
14184
|
+
{
|
|
14185
|
+
key,
|
|
14186
|
+
style: [styles6.flagItem, flags[key] && styles6.flagItemActive],
|
|
14187
|
+
onPress: () => setFlags((prev) => ({ ...prev, [key]: !prev[key] }))
|
|
14188
|
+
},
|
|
14189
|
+
/* @__PURE__ */ React7.createElement(View6, { style: [styles6.flagCheck, flags[key] && styles6.flagCheckActive] }, flags[key] && /* @__PURE__ */ React7.createElement(Text6, { style: styles6.flagCheckmark }, "\u2713")),
|
|
14190
|
+
/* @__PURE__ */ React7.createElement(Text6, { style: [styles6.flagText, flags[key] && styles6.flagTextActive] }, label)
|
|
14191
|
+
))), /* @__PURE__ */ React7.createElement(
|
|
14192
|
+
TextInput2,
|
|
14193
|
+
{
|
|
14194
|
+
style: styles6.noteInput,
|
|
14195
|
+
value: note,
|
|
14196
|
+
onChangeText: setNote,
|
|
14197
|
+
placeholder: "Add a note (optional)",
|
|
14198
|
+
placeholderTextColor: colors.textMuted,
|
|
14199
|
+
multiline: true
|
|
14200
|
+
}
|
|
14201
|
+
), /* @__PURE__ */ React7.createElement(
|
|
14202
|
+
ImagePickerButtons,
|
|
14203
|
+
{
|
|
14204
|
+
images: images.images,
|
|
14205
|
+
maxImages: 3,
|
|
14206
|
+
onPickGallery: images.pickFromGallery,
|
|
14207
|
+
onPickCamera: images.pickFromCamera,
|
|
14208
|
+
onRemove: images.removeImage,
|
|
14209
|
+
label: "Screenshots (optional)"
|
|
14210
|
+
}
|
|
14211
|
+
), /* @__PURE__ */ React7.createElement(View6, { style: styles6.actions }, /* @__PURE__ */ React7.createElement(TouchableOpacity6, { style: styles6.skipButton, onPress: handleSkip }, /* @__PURE__ */ React7.createElement(Text6, { style: styles6.skipText }, "Skip")), /* @__PURE__ */ React7.createElement(TouchableOpacity6, { style: [shared.primaryButton, { flex: 2, opacity: submitting || images.isUploading ? 0.5 : 1 }], onPress: handleSubmit, disabled: submitting || images.isUploading }, /* @__PURE__ */ React7.createElement(Text6, { style: shared.primaryButtonText }, images.isUploading ? "Uploading..." : submitting ? "Submitting..." : "Submit"))));
|
|
14212
|
+
}
|
|
14213
|
+
var styles6 = StyleSheet7.create({
|
|
14214
|
+
container: { paddingTop: 8 },
|
|
14215
|
+
header: { fontSize: 22, fontWeight: "700", color: colors.textPrimary, textAlign: "center", marginBottom: 4 },
|
|
14216
|
+
subheader: { fontSize: 14, color: colors.textMuted, textAlign: "center", marginBottom: 20 },
|
|
14217
|
+
starRow: { flexDirection: "row", justifyContent: "center", gap: 8, marginBottom: 20 },
|
|
14218
|
+
starButton: { padding: 4 },
|
|
14219
|
+
star: { fontSize: 36, color: colors.textDim },
|
|
14220
|
+
starActive: { color: "#facc15" },
|
|
14221
|
+
flagsSection: { marginBottom: 16 },
|
|
14222
|
+
flagsLabel: { fontSize: 14, fontWeight: "500", color: colors.textSecondary, marginBottom: 10 },
|
|
14223
|
+
flagItem: { flexDirection: "row", alignItems: "center", paddingVertical: 10, paddingHorizontal: 12, borderRadius: 8, marginBottom: 4, backgroundColor: colors.card },
|
|
14224
|
+
flagItemActive: { backgroundColor: colors.yellowDark, borderWidth: 1, borderColor: colors.yellow },
|
|
14225
|
+
flagCheck: { width: 20, height: 20, borderRadius: 4, borderWidth: 2, borderColor: colors.border, marginRight: 10, justifyContent: "center", alignItems: "center" },
|
|
14226
|
+
flagCheckActive: { backgroundColor: colors.yellow, borderColor: colors.yellow },
|
|
14227
|
+
flagCheckmark: { fontSize: 12, fontWeight: "bold", color: "#000" },
|
|
14228
|
+
flagText: { fontSize: 14, color: colors.textSecondary },
|
|
14229
|
+
flagTextActive: { color: colors.yellow },
|
|
14230
|
+
noteInput: { backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border, borderRadius: 10, padding: 12, fontSize: 14, color: colors.textPrimary, minHeight: 60, textAlignVertical: "top", marginBottom: 16 },
|
|
14231
|
+
actions: { flexDirection: "row", gap: 10 },
|
|
14232
|
+
skipButton: { flex: 1, paddingVertical: 14, borderRadius: 12, alignItems: "center", backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border },
|
|
14233
|
+
skipText: { fontSize: 15, color: colors.textSecondary }
|
|
14234
|
+
});
|
|
14235
|
+
|
|
14185
14236
|
// src/widget/screens/ReportScreen.tsx
|
|
14237
|
+
import React8, { useState as useState6 } from "react";
|
|
14238
|
+
import { View as View7, Text as Text7, TouchableOpacity as TouchableOpacity7, TextInput as TextInput3, StyleSheet as StyleSheet8 } from "react-native";
|
|
14186
14239
|
function ReportScreen({ nav, prefill }) {
|
|
14187
14240
|
const { client, getDeviceInfo, uploadImage, refreshAssignments } = useBugBear();
|
|
14188
14241
|
const [reportType, setReportType] = useState6(prefill?.type || "bug");
|
|
@@ -14794,7 +14847,7 @@ function BugBearButton({
|
|
|
14794
14847
|
onPress: () => setModalVisible(true),
|
|
14795
14848
|
activeOpacity: draggable ? 1 : 0.7
|
|
14796
14849
|
},
|
|
14797
|
-
/* @__PURE__ */ React14.createElement(
|
|
14850
|
+
/* @__PURE__ */ React14.createElement(Image3, { source: { uri: BUGBEAR_LOGO_BASE64 }, style: styles13.fabIcon }),
|
|
14798
14851
|
badgeCount > 0 && /* @__PURE__ */ React14.createElement(View13, { style: styles13.badge }, /* @__PURE__ */ React14.createElement(Text13, { style: styles13.badgeText }, badgeCount > 9 ? "9+" : badgeCount))
|
|
14799
14852
|
)
|
|
14800
14853
|
), /* @__PURE__ */ React14.createElement(
|
|
@@ -14844,7 +14897,9 @@ var styles13 = StyleSheet14.create({
|
|
|
14844
14897
|
elevation: 8
|
|
14845
14898
|
},
|
|
14846
14899
|
fabIcon: {
|
|
14847
|
-
|
|
14900
|
+
width: 32,
|
|
14901
|
+
height: 32,
|
|
14902
|
+
borderRadius: 16
|
|
14848
14903
|
},
|
|
14849
14904
|
badge: {
|
|
14850
14905
|
position: "absolute",
|