@bbearai/react-native 0.3.2 → 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.
Files changed (3) hide show
  1. package/dist/index.js +192 -138
  2. package/dist/index.mjs +187 -132
  3. 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", error);
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 name = filename || `screenshot-${Date.now()}.png`;
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("screenshots").upload(path, file, {
12088
- contentType: "image/png",
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("screenshots").getPublicUrl(path);
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
- ) || assignments[0] || null;
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+CjwveDp4bXBtZXRhPgri4oBIAAAU90lEQVR4Ae1aCZRV1ZXd97355/q/fv2q/2ukBigsChFlEgEFHLCNreIQQouLaByyDG1iVmsbY2sSo4YY55XExETNZKJGIq0goggOiIICIpaMBTVT8/Tn8d3e95esJmv1kiqEjqu77lrvj2+4Z99z9tnnvAeMjlEERhEYRWAUgVEERhEYReD/KQL6P9hudX21yX/UPMT/3oVt1UB6OiCm0N7xvG4x35185xxEku+D/N4IaLv4eStyXTsxONjPzyd0nFgAHI5SxBKX08CvGoacVh2A67RyoLaE1nsFchw0V0ik0gIDMaCxR6KuBdjZItHeL1p53HrAeHbatEkbtm3bljoRSJwYAByOchr+bU7466eWI7BkFjC/VsBlA9r6JA50Ac29QH9UIp0BnFbAnwtU+QXGBQS8LoEW/r9yq8TfPpToDooPGCmPLVq08PkXXniBR3xZx3wYXNObuXLts8drcuV3Nbn7Z7p84huaXDBJyDyXinWNm1VCOD/bHBKai7/ZuBlSCCEr/UJeN1+Tr92myT0P6HL5Yl0WedVxxgaLxTHzy2m+3V7BVXrV79Hkb68dmvjtF2sy4FZG6xJWj0R+iURZtYR/DL+7pbDSaJdborJ2aKuokSiqICAEZYgY5eQxkH/+VwJJIK4/WyNAWgSa9TYp5Ynx3mNC12I5g6t+YH6tJut+qsunbtBliZfBzRWFp4BGjxsyfOxECXe+dHsc8mtfGcv/1T6a1B1OqTtdUnjzCUyOrKnOlzcsrpbLloyTM6YEsuc5qwZy232aXHGzLgsIMmD5Q2FhoeuY5ns8DzIMY76uiZ5l52my+TFNXjVbTY6GubwSY7ja4yZJBMr4G73A5pAlRR75/pOnyxfuGCNvuPIUuerRqfIny6pkwGtIXUDeclmO7H/5ZCnXFEn5ik+ar5bLN35xsjyp2icdVsgnv6nJnct1WVOSDYk1Pp/P80Xs+UJuZLFYpqdS6VfL81HwnfMknnkf2NHMUxYUAbk+wDTpyBJGdwvyLQmEEgKr7xmLs/0N6IMXrpwcPPJ8J37/gY7ukIlcSxwbbtXgjKXQy4zQFwdKCkiO4w00ynJc8tMBfLK3D3cs1HDtWcAljwC72rSXKyvLFjc2NnLvkY9jFkIOprhEIvEKL1nm4SI0xvLJY04kEmnE+pnTNAJhswOpDEosg7h/oR2nVTuwZGI34tEkHDKKrn6B3OIAbr0kB5fPzkPzgIF7nw8ih4f/+n2Bv2y3Yc1ODYV6GpOKwtjda8PW/Qls3MNTWyTuXyywcpusaekM50rTXHv33SMH4Jg8wOVyFUYikZVV5d5Z9367BhdM1ZBnTXLBJQ4FgWdWHcLyZ7oQ0uidzN6TS6NYfQ1lUCwDrz8Du5PJQnlGjg2ZYBLPviWw/I0UuhIGTvOZaIoCNQ4fvlVbhJZwEvXxFnznkiQuecrEp50ENS8P6O7AQ1dJnFYucOGDPJ2wXZlMxp4bKQTH4gGWVCr17KQJ/nPWPDQB8/wHYRzaD9l1CIm2dtjCfTh3Tg5mVAL/+fYgZo1L4xfXWGH2pBANm4jGNLjzNLJEBql4Gp8e1BF0FyJqteHCGV7IjIamQ0l8f2oVxlUFUJbvhjUZhd0Vwoo6oM+gp3m5CYENW8NYepYAQxDr68zZBQX5z0aj0fBIQDgWABbn5NhvX8lYnpTeiUx/ED0DwLptAh/v46UjJhyJEMYXZBDrATwW4PozkugPSq4SECjVYLeTEzkMft8TtOKRlUEEZBIN7WF8f6kPda0uzC0isdB4zWKBmeqhNw3iDx8JJD1+hhcBtDthxpPYvDuOh64UWL8L7sautI0Ivpo9+TBfjgWA5VeclXfS18d3wTBj2Nyo47qndWza5cTGgxY8vzMNIybhZTjY4gJpWj1zoonCYoFAoZr7kPFCY3SkBCz9JpWgjhvOIVcYBK0/hEWXBPDM1gyqNQPhgQE4bE1Y25rGm8052dUXDJ9sBeFwoLctSOUosXgm8NfN5kSPp2BFIhHtG6b92UpsuPvC6XQWp9Pp+2r90jneF4c3R+LG31pwYd4YLJ1QhAVlPvgtNjy2I4pCq5kFYS5Ln6RDw49WAu8TrNnVGmh7tvwTJErFG5UFJgooFwJFOr72OwP1Df047+xcrHijFVP97aiLx/DgZgMxL4sIw1DePzR0cgnHx/voORcLbGmAtaErFacXrBva4eivI/IA0zSv5La4tT+Dmy8Q6KKmjzQV4SuTypBXmIccrxM1BW64qNZfahzEeeUSiRwdi55i4eN34+oFLhTaE0wQJjlQQHmBk1LGk0vxR6OsVgsWLcjH5mYdVYz5eJ4TT2zqxR/32BF0snrioBrMekDWl5hp4t1BlOSZmFcDvLzdHOPz5T0di8USRzefHjmcnQ7vQ+MXTizRYNIFe1i8OjIGZpblwZHr4qQE3VuHxePCBZPGoJKTtbDY/eFqibNnuPCnqzPIa+/F3h1pDPTS4M9CQXmzaTKmGdfB3hS6Nx3CrSfHUJsXxY1zU6iPORB1BIAc95CuiEcgB7qzpIueDq5AKIvinzaZmDlOFVIo7xuMnHl4zkd7H7YHeDweXyKRvO+Bf9Fy6rny8ZjAGaUZJGI+ON1UK1xBtSLpZAZ9A70IuLuRa89ge9iCB74mUb8lig4WuFHOt1+RY56gMFQHDfkz7UdvOzPHZuCxt1KYWmxiyoQUkmwZvPkRF1MB1teJAgJRFChCWcVYFJWUwWkxkAwPoL07hivPEGjoBuo7BK9grj2a8er/oSAaxp7BaHSC04bAKWVAgrn9yfck5pHR82QjetoNah4vkmTlWJx1rLUZYzwpOFwaRY6AEUyg7ZDAxhbW/UmJSXR5F4mrdjq9Zsh+gseygaCs2G8iXDYV68wSnNa2GktnOvDIS2FEIhLT/+kKlI6fCDfT4Ccb16FtXx2E1YrqqbPQUn8AW+p3Mww0rN1pnq6KJVVZHs204YdAWp5Ejsuuc2O3xADFyr+/amJbbwKxzC6Ewtsp+uogjYNIJFPZGHcyBM4cm0ZkUOK5fQIPfSLx1F6BBxvy8OY+iThTowpnJYqkKeD0sBhgzyCTiMBROQevfaqhzB5CVaENp5xzKTLcORyN4JWnH8UHyU60nTsDbfEB7NnyLnzFJVhVX4zCbGUgK70cRzNe/T/sEKCPXzqlUsw/fSxX/20CW1CMnpgFr9czF7eb2NyZwofcWgc0jKNQc7LSLS4V8HAaTa0CP9koEE5JOJx2/OXltXhzRyPGZQ6imJyiCFEnEA67wLZ2JzZs68M7b75GYrMgGDPx4qHJSKcon91eHPxgI9pqS3HR/cuxfNESbK0owsC2nUi2t1IrFKLC3omPmmFLpuUfM5kM3fHzx/A9gE0b1vYYjKkT0hFsXKpAMcziKuzXSrA5mId1zQJTJlLf868k21wqxTFEs4QXJmEiN4BYNIGHl9+DuvYItjXScK6+jVVze1jDW002TK8ROGuqA1+dbuBHV+XjrztzkBI21hgxekYcHekgxA1XY28shFWtBxEaWw559ixyDz0tGkN9nw05dlgTiQz14dHHsDmAp7K5KMNV/GcBUCAwblUMgjJWMp+5ZAg+9jJ6mSGYwdDalPVu7GjRURNIYtBposuSg9fXDvHTHUsFWrnvA5sK8VqjH30xK8prapGx1+Pec7ZBC7fjozYf4lof8irHYaClEenZ0yEKfNgfCWJ/qB+wO6BXkJioD4g4wkkDFj3OuekMhrSa7OeOkQCgQvUz0jqCW9SPCghukbjEgxslrj9Fg49a37AI8oPAjGkaNlwzAWZfOzq7Ili3m7VBMI1/nmvHg69b8fhLdCsXUxpbBsloEEYqAudFuegI9WIgriOlR0EBhvBgL+TE82lohiFD5yWDSkUioXA2BSuFZJD31NJwDMu7RwJAdJDEpxqb2ZE1/LPPvLCMx1HE7tZPL6RDDGTYtdIQoHDz+dgEYkYwuxo48RSqcoGb5qmV0dBqVuCr8zV09Lbhb1sH0BFk2gt34/ZFhTwugPp9/TTGzGoMpT1SmSQXQRlI25QcVNdNpaDtbyDY7JZQhwRyUkiRUEmlwyqKRgJAZ8eApGur1CW5CMoIdSF6gHICavar5lHOsgfSzJLYSZmshJsrR2Tb3iIdzTJ+JK1h9U4BK4+dNo36xmPgrm+MwQ0XJ9F4KIHdO7tx3QU+rHynB6cUZ+CzJ9GaoPGMf02Qs9voKd0UQnR9ugUv1grr3kYWTQbxjaLanyZxsgi3aj1Icl5HGSMAQBxoYktbeYCK794kz07jskJG0moziZkVTFOsBi3kCiXTYxRLar2y6Vi5J8nurQ+BW1eYaAtm8PDlrfjKHD+E28r0pWOwLYKt3U6kXxyAx96LWjaVJrjjaBhMIMp7JDZWgNjH0nvyxKwnsJqC5ZX1sNI1Y4kkTi/sY/msQhG9Ho+9I6jmeJQxfAAs2p72ATNDoafXUJa/18J4yFPyRS0/gWAee+lTEzdPptG6QJSAAAABJRU5ErkJggg==";
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({ name: "TEST_FEEDBACK", status: "failed", assignmentId: displayedAssignment.id });
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((a) => a.status === "pending" || a.status === "in_progress");
13626
- if (remaining.length > 1) {
13627
- nav.replace({ name: "TEST_DETAIL" });
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 import_react6 = __toESM(require("react"));
13908
- var import_react_native6 = require("react-native");
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 import_react7 = require("react");
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, import_react7.useState)([]);
14017
- const pickFromGallery = (0, import_react7.useCallback)(async () => {
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, import_react7.useCallback)(async () => {
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, import_react7.useCallback)((id) => {
14000
+ const removeImage = (0, import_react6.useCallback)((id) => {
14065
14001
  setImages((prev) => prev.filter((img) => img.id !== id));
14066
14002
  }, []);
14067
- const clear = (0, import_react7.useCallback)(() => {
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, import_react7.useCallback)(() => {
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, import_react7.useCallback)(() => {
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__ */ import_react8.default.createElement(import_react_native7.ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, style: styles5.strip }, images.map((img) => /* @__PURE__ */ import_react8.default.createElement(import_react_native7.View, { key: img.id, style: styles5.thumbContainer }, /* @__PURE__ */ import_react8.default.createElement(import_react_native7.Image, { source: { uri: img.localUri }, style: styles5.thumb }), img.status === "uploading" && /* @__PURE__ */ import_react8.default.createElement(import_react_native7.View, { style: styles5.thumbOverlay }, /* @__PURE__ */ import_react8.default.createElement(import_react_native7.ActivityIndicator, { size: "small", color: "#fff" })), img.status === "error" && /* @__PURE__ */ import_react8.default.createElement(import_react_native7.View, { style: [styles5.thumbOverlay, styles5.thumbOverlayError] }, /* @__PURE__ */ import_react8.default.createElement(import_react_native7.Text, { style: styles5.thumbErrorText }, "!")), /* @__PURE__ */ import_react8.default.createElement(import_react_native7.TouchableOpacity, { style: styles5.thumbRemove, onPress: () => onRemove(img.id) }, /* @__PURE__ */ import_react8.default.createElement(import_react_native7.Text, { style: styles5.thumbRemoveText }, "\u2715")))));
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 styles5 = import_react_native7.StyleSheet.create({
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
- ...import_react_native7.StyleSheet.absoluteFillObject,
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__ */ import_react9.default.createElement(import_react_native8.View, { style: styles6.section }, label && /* @__PURE__ */ import_react9.default.createElement(import_react_native8.Text, { style: styles6.label }, label), /* @__PURE__ */ import_react9.default.createElement(import_react_native8.View, { style: styles6.buttonRow }, /* @__PURE__ */ import_react9.default.createElement(
14147
- import_react_native8.TouchableOpacity,
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: styles6.pickButton,
14085
+ style: styles5.pickButton,
14150
14086
  onPress: onPickGallery,
14151
14087
  disabled: images.length >= maxImages
14152
14088
  },
14153
- /* @__PURE__ */ import_react9.default.createElement(import_react_native8.Text, { style: [styles6.pickButtonText, images.length >= maxImages && styles6.pickButtonDisabled] }, "Gallery")
14154
- ), /* @__PURE__ */ import_react9.default.createElement(
14155
- import_react_native8.TouchableOpacity,
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: styles6.pickButton,
14093
+ style: styles5.pickButton,
14158
14094
  onPress: onPickCamera,
14159
14095
  disabled: images.length >= maxImages
14160
14096
  },
14161
- /* @__PURE__ */ import_react9.default.createElement(import_react_native8.Text, { style: [styles6.pickButtonText, images.length >= maxImages && styles6.pickButtonDisabled] }, "Camera")
14162
- ), /* @__PURE__ */ import_react9.default.createElement(import_react_native8.Text, { style: styles6.countText }, images.length, "/", maxImages)), /* @__PURE__ */ import_react9.default.createElement(ImagePreviewStrip, { images, onRemove }));
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 styles6 = import_react_native8.StyleSheet.create({
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.Text, { style: styles13.fabIcon }, "\u{1F41B}"),
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
- fontSize: 24
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", error);
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 name = filename || `screenshot-${Date.now()}.png`;
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("screenshots").upload(path, file, {
12057
- contentType: "image/png",
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("screenshots").getPublicUrl(path);
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
- ) || assignments[0] || null;
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({ name: "TEST_FEEDBACK", status: "failed", assignmentId: displayedAssignment.id });
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((a) => a.status === "pending" || a.status === "in_progress");
13608
- if (remaining.length > 1) {
13609
- nav.replace({ name: "TEST_DETAIL" });
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 React5, { useState as useState4 } from "react";
13890
- import { View as View4, Text as Text4, TouchableOpacity as TouchableOpacity4, TextInput as TextInput2, StyleSheet as StyleSheet5 } from "react-native";
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 useState5, useCallback as useCallback4 } from "react";
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] = useState5([]);
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 React7 from "react";
14065
- import { View as View6, Text as Text6, TouchableOpacity as TouchableOpacity6, StyleSheet as StyleSheet7 } from "react-native";
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 React6 from "react";
14069
- import { View as View5, Text as Text5, TouchableOpacity as TouchableOpacity5, ScrollView, Image, ActivityIndicator, StyleSheet as StyleSheet6 } from "react-native";
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__ */ React6.createElement(ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, style: styles5.strip }, images.map((img) => /* @__PURE__ */ React6.createElement(View5, { key: img.id, style: styles5.thumbContainer }, /* @__PURE__ */ React6.createElement(Image, { source: { uri: img.localUri }, style: styles5.thumb }), img.status === "uploading" && /* @__PURE__ */ React6.createElement(View5, { style: styles5.thumbOverlay }, /* @__PURE__ */ React6.createElement(ActivityIndicator, { size: "small", color: "#fff" })), img.status === "error" && /* @__PURE__ */ React6.createElement(View5, { style: [styles5.thumbOverlay, styles5.thumbOverlayError] }, /* @__PURE__ */ React6.createElement(Text5, { style: styles5.thumbErrorText }, "!")), /* @__PURE__ */ React6.createElement(TouchableOpacity5, { style: styles5.thumbRemove, onPress: () => onRemove(img.id) }, /* @__PURE__ */ React6.createElement(Text5, { style: styles5.thumbRemoveText }, "\u2715")))));
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 styles5 = StyleSheet6.create({
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
- ...StyleSheet6.absoluteFillObject,
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__ */ React7.createElement(View6, { style: styles6.section }, label && /* @__PURE__ */ React7.createElement(Text6, { style: styles6.label }, label), /* @__PURE__ */ React7.createElement(View6, { style: styles6.buttonRow }, /* @__PURE__ */ React7.createElement(
14129
- TouchableOpacity6,
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: styles6.pickButton,
14068
+ style: styles5.pickButton,
14132
14069
  onPress: onPickGallery,
14133
14070
  disabled: images.length >= maxImages
14134
14071
  },
14135
- /* @__PURE__ */ React7.createElement(Text6, { style: [styles6.pickButtonText, images.length >= maxImages && styles6.pickButtonDisabled] }, "Gallery")
14136
- ), /* @__PURE__ */ React7.createElement(
14137
- TouchableOpacity6,
14072
+ /* @__PURE__ */ React6.createElement(Text5, { style: [styles5.pickButtonText, images.length >= maxImages && styles5.pickButtonDisabled] }, "Gallery")
14073
+ ), /* @__PURE__ */ React6.createElement(
14074
+ TouchableOpacity5,
14138
14075
  {
14139
- style: styles6.pickButton,
14076
+ style: styles5.pickButton,
14140
14077
  onPress: onPickCamera,
14141
14078
  disabled: images.length >= maxImages
14142
14079
  },
14143
- /* @__PURE__ */ React7.createElement(Text6, { style: [styles6.pickButtonText, images.length >= maxImages && styles6.pickButtonDisabled] }, "Camera")
14144
- ), /* @__PURE__ */ React7.createElement(Text6, { style: styles6.countText }, images.length, "/", maxImages)), /* @__PURE__ */ React7.createElement(ImagePreviewStrip, { images, onRemove }));
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 styles6 = StyleSheet7.create({
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(Text13, { style: styles13.fabIcon }, "\u{1F41B}"),
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
- fontSize: 24
14900
+ width: 32,
14901
+ height: 32,
14902
+ borderRadius: 16
14848
14903
  },
14849
14904
  badge: {
14850
14905
  position: "absolute",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bbearai/react-native",
3
- "version": "0.3.2",
3
+ "version": "0.3.4",
4
4
  "description": "BugBear React Native components for mobile apps",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",