@bbearai/react-native 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/index.js +115 -11
  2. package/dist/index.mjs +115 -11
  3. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -13009,6 +13009,8 @@ function BugBearProvider({ config, children, appVersion, enabled = true }) {
13009
13009
  }, [enabled, config, initializeBugBear]);
13010
13010
  const currentAssignment = assignments.find(
13011
13011
  (a) => a.status === "in_progress"
13012
+ ) || assignments.find(
13013
+ (a) => a.status === "pending"
13012
13014
  ) || assignments[0] || null;
13013
13015
  const shouldShowWidget = isQAEnabled && isTester;
13014
13016
  return /* @__PURE__ */ import_react.default.createElement(
@@ -13267,6 +13269,15 @@ function BugBearButton({
13267
13269
  else if (assignment.status === "skipped") folder.stats.skipped++;
13268
13270
  else folder.stats.pending++;
13269
13271
  }
13272
+ const priorityOrder = { P0: 0, P1: 1, P2: 2, P3: 3 };
13273
+ const statusOrder = { in_progress: 0, pending: 1, failed: 2, skipped: 3, passed: 4 };
13274
+ for (const folder of groups.values()) {
13275
+ folder.assignments.sort((a, b) => {
13276
+ const statusDiff = (statusOrder[a.status] ?? 5) - (statusOrder[b.status] ?? 5);
13277
+ if (statusDiff !== 0) return statusDiff;
13278
+ return (priorityOrder[a.testCase.priority] ?? 4) - (priorityOrder[b.testCase.priority] ?? 4);
13279
+ });
13280
+ }
13270
13281
  const sortedGroups = Array.from(groups.values()).sort((a, b) => {
13271
13282
  if (!a.group && !b.group) return 0;
13272
13283
  if (!a.group) return 1;
@@ -13308,6 +13319,19 @@ function BugBearButton({
13308
13319
  }
13309
13320
  const pendingCount = assignments.filter((a) => a.status === "pending").length;
13310
13321
  const inProgressCount = assignments.filter((a) => a.status === "in_progress").length;
13322
+ const passedCount = assignments.filter((a) => a.status === "passed").length;
13323
+ const failedCount = assignments.filter((a) => a.status === "failed").length;
13324
+ const [testFilter, setTestFilter] = (0, import_react2.useState)("all");
13325
+ const handleNextTest = () => {
13326
+ const nextTest = assignments.find(
13327
+ (a) => a.id !== displayedAssignment?.id && (a.status === "pending" || a.status === "in_progress")
13328
+ );
13329
+ if (nextTest) {
13330
+ setSelectedTestId(nextTest.id);
13331
+ setTestView("detail");
13332
+ setShowSteps(false);
13333
+ }
13334
+ };
13311
13335
  const handlePass = async () => {
13312
13336
  if (!displayedAssignment) return;
13313
13337
  setPendingFeedbackStatus("passed");
@@ -13375,7 +13399,7 @@ function BugBearButton({
13375
13399
  setShowFeedbackPrompt(false);
13376
13400
  setPendingFeedbackStatus(null);
13377
13401
  setActiveTab("report");
13378
- setReportType("test_fail");
13402
+ setReportType("bug");
13379
13403
  }
13380
13404
  };
13381
13405
  const handleSkipFeedback = () => {
@@ -13450,14 +13474,19 @@ function BugBearButton({
13450
13474
  await markAsRead(thread.id);
13451
13475
  }
13452
13476
  };
13477
+ const [messageSendError, setMessageSendError] = (0, import_react2.useState)(false);
13453
13478
  const handleSendReply = async () => {
13454
13479
  if (!selectedThread || !replyText.trim()) return;
13455
13480
  setSendingReply(true);
13481
+ setMessageSendError(false);
13456
13482
  const success = await sendMessage(selectedThread.id, replyText.trim());
13457
13483
  if (success) {
13458
13484
  setReplyText("");
13459
13485
  const messages = await getThreadMessages(selectedThread.id);
13460
13486
  setThreadMessages(messages);
13487
+ } else {
13488
+ setMessageSendError(true);
13489
+ setTimeout(() => setMessageSendError(false), 3e3);
13461
13490
  }
13462
13491
  setSendingReply(false);
13463
13492
  };
@@ -13762,7 +13791,7 @@ function BugBearButton({
13762
13791
  style: [styles.tab, activeTab === "tests" && styles.activeTab],
13763
13792
  onPress: () => setActiveTab("tests")
13764
13793
  },
13765
- /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: [styles.tabText, activeTab === "tests" && styles.activeTabText] }, "Tests ", pendingCount > 0 && `(${pendingCount})`)
13794
+ /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: [styles.tabText, activeTab === "tests" && styles.activeTabText] }, "Tests ", assignments.length > 0 && `(${passedCount + failedCount}/${assignments.length})`)
13766
13795
  ), /* @__PURE__ */ import_react2.default.createElement(
13767
13796
  import_react_native2.TouchableOpacity,
13768
13797
  {
@@ -13786,7 +13815,15 @@ function BugBearButton({
13786
13815
  /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: [styles.tabText, activeTab === "report" && styles.activeTabText] }, "Report")
13787
13816
  )), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.ScrollView, { style: styles.content }, activeTab === "tests" && /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, null, assignments.length === 0 ? /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.emptyState }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.emptyEmoji }, "\u2705"), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.emptyTitle }, "All caught up!"), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.emptySubtitle }, "No tests assigned")) : testView === "list" ? (
13788
13817
  /* List View - Show tests grouped by folder */
13789
- /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, null, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.listHeader }, assignments.length, " test", assignments.length !== 1 ? "s" : "", " assigned"), groupedAssignments.map((folder) => {
13818
+ /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, null, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.listHeader }, assignments.length, " test", assignments.length !== 1 ? "s" : "", " \xB7 ", passedCount, " passed \xB7 ", failedCount, " failed"), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.filterBar }, ["all", "pending", "completed"].map((f) => /* @__PURE__ */ import_react2.default.createElement(
13819
+ import_react_native2.TouchableOpacity,
13820
+ {
13821
+ key: f,
13822
+ style: [styles.filterChip, testFilter === f && styles.filterChipActive],
13823
+ onPress: () => setTestFilter(f)
13824
+ },
13825
+ /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: [styles.filterChipText, testFilter === f && styles.filterChipTextActive] }, f === "all" ? `All (${assignments.length})` : f === "pending" ? `To Do (${pendingCount + inProgressCount})` : `Done (${passedCount + failedCount})`)
13826
+ ))), groupedAssignments.map((folder) => {
13790
13827
  const groupId = folder.group?.id || "ungrouped";
13791
13828
  const isCollapsed = collapsedFolders.has(groupId);
13792
13829
  const completedCount = folder.stats.passed + folder.stats.failed + folder.stats.skipped;
@@ -13806,7 +13843,11 @@ function BugBearButton({
13806
13843
  { width: `${completedCount / folder.stats.total * 100}%` }
13807
13844
  ]
13808
13845
  }
13809
- )), !isCollapsed && folder.assignments.map((assignment) => {
13846
+ )), !isCollapsed && folder.assignments.filter((a) => {
13847
+ if (testFilter === "pending") return a.status === "pending" || a.status === "in_progress";
13848
+ if (testFilter === "completed") return a.status === "passed" || a.status === "failed" || a.status === "skipped";
13849
+ return true;
13850
+ }).map((assignment) => {
13810
13851
  const statusBadge = getStatusBadge(assignment.status);
13811
13852
  return /* @__PURE__ */ import_react2.default.createElement(
13812
13853
  import_react_native2.TouchableOpacity,
@@ -13905,7 +13946,10 @@ function BugBearButton({
13905
13946
  style: styles.backButton
13906
13947
  },
13907
13948
  /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.backButtonText }, "\u2190 All Tests (", assignments.length, ")")
13908
- ), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.testCard }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.testHeader }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.testKey }, displayedAssignment.testCase.testKey), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.testHeaderBadges }, displayedAssignment.testCase.track && /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: [styles.trackBadge, { backgroundColor: displayedAssignment.testCase.track.color }] }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.trackBadgeText }, templateInfo[displayedAssignment.testCase.track.testTemplate || "steps"].icon)), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: [
13949
+ ), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.testCard }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.testHeader }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.testKey }, displayedAssignment.testCase.testKey), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.testHeaderBadges }, (() => {
13950
+ const badge = getStatusBadge(displayedAssignment.status);
13951
+ return /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: [styles.statusBadge, { backgroundColor: badge.color + "20" }] }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.statusBadgeIcon }, badge.icon), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: [styles.statusBadgeText, { color: badge.color }] }, badge.label));
13952
+ })(), displayedAssignment.testCase.track && /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: [styles.trackBadge, { backgroundColor: displayedAssignment.testCase.track.color }] }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.trackBadgeText }, templateInfo[displayedAssignment.testCase.track.testTemplate || "steps"].icon)), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: [
13909
13953
  styles.priorityBadge,
13910
13954
  displayedAssignment.testCase.priority === "P0" && styles.priorityP0,
13911
13955
  displayedAssignment.testCase.priority === "P1" && styles.priorityP1
@@ -13946,7 +13990,14 @@ function BugBearButton({
13946
13990
  disabled: submitting || skipping
13947
13991
  },
13948
13992
  /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.passButtonText }, submitting ? "..." : "\u2713 Pass")
13949
- )))
13993
+ )), pendingCount > 1 || pendingCount === 1 && displayedAssignment.status !== "pending" ? /* @__PURE__ */ import_react2.default.createElement(
13994
+ import_react_native2.TouchableOpacity,
13995
+ {
13996
+ style: styles.nextTestButton,
13997
+ onPress: handleNextTest
13998
+ },
13999
+ /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.nextTestButtonText }, "Next Test \u25B6")
14000
+ ) : null)
13950
14001
  ) : null), activeTab === "messages" && /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, null, messageView === "compose" ? (
13951
14002
  /* Compose New Message View */
13952
14003
  /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, null, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.TouchableOpacity, { onPress: handleBackToThreadList, style: styles.backButton }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.backButtonText }, "\u2190 Back to Messages")), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.composeHeader }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.composeTitle }, "New Message"), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.composeSubtitle }, "Send a message to the QA team")), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.composeForm }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.label }, "Subject"), /* @__PURE__ */ import_react2.default.createElement(
@@ -14330,7 +14381,7 @@ function BugBearButton({
14330
14381
  /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.closeProfileButtonText }, "\u2190 Back")
14331
14382
  ))), activeTab === "messages" && messageView === "thread" && selectedThread ? (
14332
14383
  /* Reply Composer */
14333
- /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.replyComposer }, /* @__PURE__ */ import_react2.default.createElement(
14384
+ /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, null, messageSendError && /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.messageSendError }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.messageSendErrorText }, "Failed to send. Tap Send to retry.")), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.replyComposer }, /* @__PURE__ */ import_react2.default.createElement(
14334
14385
  import_react_native2.TextInput,
14335
14386
  {
14336
14387
  style: styles.replyInput,
@@ -14352,10 +14403,10 @@ function BugBearButton({
14352
14403
  disabled: !replyText.trim() || sendingReply
14353
14404
  },
14354
14405
  /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.sendButtonText }, sendingReply ? "..." : "Send")
14355
- ))
14406
+ )))
14356
14407
  ) : (
14357
14408
  /* Standard Footer */
14358
- /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.footer }, activeTab === "messages" ? /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.footerText }, threads.length, " thread", threads.length !== 1 ? "s" : "", " \xB7 ", unreadCount, " unread"), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.TouchableOpacity, { onPress: refreshThreads }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.refreshText }, "\u21BB Refresh"))) : activeTab === "explore" ? /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.footerText }, activeSession ? `${sessionFindings.length} findings` : "No active session"), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.TouchableOpacity, { onPress: refreshSession }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.refreshText }, "\u21BB Refresh"))) : /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.footerText }, pendingCount, " pending \xB7 ", inProgressCount, " in progress"), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.TouchableOpacity, { onPress: refreshAssignments }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.refreshText }, "\u21BB Refresh"))))
14409
+ /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.footer }, activeTab === "messages" ? /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.footerText }, threads.length, " thread", threads.length !== 1 ? "s" : "", " \xB7 ", unreadCount, " unread"), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.TouchableOpacity, { onPress: refreshThreads }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.refreshText }, "\u21BB Refresh"))) : activeTab === "explore" ? /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.footerText }, activeSession ? `${sessionFindings.length} findings` : "No active session"), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.TouchableOpacity, { onPress: refreshSession }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.refreshText }, "\u21BB Refresh"))) : /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.footerText }, passedCount + failedCount, "/", assignments.length, " done \xB7 ", pendingCount, " pending"), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.TouchableOpacity, { onPress: refreshAssignments }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.refreshText }, "\u21BB Refresh"))))
14359
14410
  ))
14360
14411
  )
14361
14412
  ), /* @__PURE__ */ import_react2.default.createElement(
@@ -14545,7 +14596,8 @@ var styles = import_react_native2.StyleSheet.create({
14545
14596
  justifyContent: "space-between",
14546
14597
  alignItems: "center",
14547
14598
  paddingHorizontal: 16,
14548
- paddingVertical: 12,
14599
+ paddingTop: 12,
14600
+ paddingBottom: import_react_native2.Platform.OS === "ios" ? 34 : 12,
14549
14601
  borderTopWidth: 1,
14550
14602
  borderTopColor: "#27272a",
14551
14603
  backgroundColor: "#09090b"
@@ -15293,7 +15345,9 @@ var styles = import_react_native2.StyleSheet.create({
15293
15345
  replyComposer: {
15294
15346
  flexDirection: "row",
15295
15347
  alignItems: "flex-end",
15296
- padding: 12,
15348
+ paddingHorizontal: 12,
15349
+ paddingTop: 12,
15350
+ paddingBottom: import_react_native2.Platform.OS === "ios" ? 34 : 12,
15297
15351
  borderTopWidth: 1,
15298
15352
  borderTopColor: "#27272a",
15299
15353
  backgroundColor: "#18181b"
@@ -15888,6 +15942,56 @@ var styles = import_react_native2.StyleSheet.create({
15888
15942
  height: "100%",
15889
15943
  backgroundColor: "#22c55e",
15890
15944
  borderRadius: 2
15945
+ },
15946
+ nextTestButton: {
15947
+ backgroundColor: "#27272a",
15948
+ paddingVertical: 10,
15949
+ borderRadius: 10,
15950
+ alignItems: "center",
15951
+ marginTop: 8,
15952
+ borderWidth: 1,
15953
+ borderColor: "#3f3f46"
15954
+ },
15955
+ nextTestButtonText: {
15956
+ fontSize: 14,
15957
+ fontWeight: "500",
15958
+ color: "#a1a1aa"
15959
+ },
15960
+ filterBar: {
15961
+ flexDirection: "row",
15962
+ gap: 6,
15963
+ marginBottom: 10
15964
+ },
15965
+ filterChip: {
15966
+ paddingHorizontal: 10,
15967
+ paddingVertical: 5,
15968
+ borderRadius: 12,
15969
+ backgroundColor: "#27272a",
15970
+ borderWidth: 1,
15971
+ borderColor: "#3f3f46"
15972
+ },
15973
+ filterChipActive: {
15974
+ backgroundColor: "#1e3a5f",
15975
+ borderColor: "#3B82F6"
15976
+ },
15977
+ filterChipText: {
15978
+ fontSize: 12,
15979
+ color: "#71717a"
15980
+ },
15981
+ filterChipTextActive: {
15982
+ color: "#93c5fd"
15983
+ },
15984
+ messageSendError: {
15985
+ backgroundColor: "#7f1d1d",
15986
+ paddingHorizontal: 12,
15987
+ paddingVertical: 6,
15988
+ borderTopWidth: 1,
15989
+ borderTopColor: "#991b1b"
15990
+ },
15991
+ messageSendErrorText: {
15992
+ fontSize: 12,
15993
+ color: "#fca5a5",
15994
+ textAlign: "center"
15891
15995
  }
15892
15996
  });
15893
15997
  var exploreStyles = import_react_native2.StyleSheet.create({
package/dist/index.mjs CHANGED
@@ -12971,6 +12971,8 @@ function BugBearProvider({ config, children, appVersion, enabled = true }) {
12971
12971
  }, [enabled, config, initializeBugBear]);
12972
12972
  const currentAssignment = assignments.find(
12973
12973
  (a) => a.status === "in_progress"
12974
+ ) || assignments.find(
12975
+ (a) => a.status === "pending"
12974
12976
  ) || assignments[0] || null;
12975
12977
  const shouldShowWidget = isQAEnabled && isTester;
12976
12978
  return /* @__PURE__ */ React.createElement(
@@ -13242,6 +13244,15 @@ function BugBearButton({
13242
13244
  else if (assignment.status === "skipped") folder.stats.skipped++;
13243
13245
  else folder.stats.pending++;
13244
13246
  }
13247
+ const priorityOrder = { P0: 0, P1: 1, P2: 2, P3: 3 };
13248
+ const statusOrder = { in_progress: 0, pending: 1, failed: 2, skipped: 3, passed: 4 };
13249
+ for (const folder of groups.values()) {
13250
+ folder.assignments.sort((a, b) => {
13251
+ const statusDiff = (statusOrder[a.status] ?? 5) - (statusOrder[b.status] ?? 5);
13252
+ if (statusDiff !== 0) return statusDiff;
13253
+ return (priorityOrder[a.testCase.priority] ?? 4) - (priorityOrder[b.testCase.priority] ?? 4);
13254
+ });
13255
+ }
13245
13256
  const sortedGroups = Array.from(groups.values()).sort((a, b) => {
13246
13257
  if (!a.group && !b.group) return 0;
13247
13258
  if (!a.group) return 1;
@@ -13283,6 +13294,19 @@ function BugBearButton({
13283
13294
  }
13284
13295
  const pendingCount = assignments.filter((a) => a.status === "pending").length;
13285
13296
  const inProgressCount = assignments.filter((a) => a.status === "in_progress").length;
13297
+ const passedCount = assignments.filter((a) => a.status === "passed").length;
13298
+ const failedCount = assignments.filter((a) => a.status === "failed").length;
13299
+ const [testFilter, setTestFilter] = useState2("all");
13300
+ const handleNextTest = () => {
13301
+ const nextTest = assignments.find(
13302
+ (a) => a.id !== displayedAssignment?.id && (a.status === "pending" || a.status === "in_progress")
13303
+ );
13304
+ if (nextTest) {
13305
+ setSelectedTestId(nextTest.id);
13306
+ setTestView("detail");
13307
+ setShowSteps(false);
13308
+ }
13309
+ };
13286
13310
  const handlePass = async () => {
13287
13311
  if (!displayedAssignment) return;
13288
13312
  setPendingFeedbackStatus("passed");
@@ -13350,7 +13374,7 @@ function BugBearButton({
13350
13374
  setShowFeedbackPrompt(false);
13351
13375
  setPendingFeedbackStatus(null);
13352
13376
  setActiveTab("report");
13353
- setReportType("test_fail");
13377
+ setReportType("bug");
13354
13378
  }
13355
13379
  };
13356
13380
  const handleSkipFeedback = () => {
@@ -13425,14 +13449,19 @@ function BugBearButton({
13425
13449
  await markAsRead(thread.id);
13426
13450
  }
13427
13451
  };
13452
+ const [messageSendError, setMessageSendError] = useState2(false);
13428
13453
  const handleSendReply = async () => {
13429
13454
  if (!selectedThread || !replyText.trim()) return;
13430
13455
  setSendingReply(true);
13456
+ setMessageSendError(false);
13431
13457
  const success = await sendMessage(selectedThread.id, replyText.trim());
13432
13458
  if (success) {
13433
13459
  setReplyText("");
13434
13460
  const messages = await getThreadMessages(selectedThread.id);
13435
13461
  setThreadMessages(messages);
13462
+ } else {
13463
+ setMessageSendError(true);
13464
+ setTimeout(() => setMessageSendError(false), 3e3);
13436
13465
  }
13437
13466
  setSendingReply(false);
13438
13467
  };
@@ -13737,7 +13766,7 @@ function BugBearButton({
13737
13766
  style: [styles.tab, activeTab === "tests" && styles.activeTab],
13738
13767
  onPress: () => setActiveTab("tests")
13739
13768
  },
13740
- /* @__PURE__ */ React2.createElement(Text, { style: [styles.tabText, activeTab === "tests" && styles.activeTabText] }, "Tests ", pendingCount > 0 && `(${pendingCount})`)
13769
+ /* @__PURE__ */ React2.createElement(Text, { style: [styles.tabText, activeTab === "tests" && styles.activeTabText] }, "Tests ", assignments.length > 0 && `(${passedCount + failedCount}/${assignments.length})`)
13741
13770
  ), /* @__PURE__ */ React2.createElement(
13742
13771
  TouchableOpacity,
13743
13772
  {
@@ -13761,7 +13790,15 @@ function BugBearButton({
13761
13790
  /* @__PURE__ */ React2.createElement(Text, { style: [styles.tabText, activeTab === "report" && styles.activeTabText] }, "Report")
13762
13791
  )), /* @__PURE__ */ React2.createElement(ScrollView, { style: styles.content }, activeTab === "tests" && /* @__PURE__ */ React2.createElement(View, null, assignments.length === 0 ? /* @__PURE__ */ React2.createElement(View, { style: styles.emptyState }, /* @__PURE__ */ React2.createElement(Text, { style: styles.emptyEmoji }, "\u2705"), /* @__PURE__ */ React2.createElement(Text, { style: styles.emptyTitle }, "All caught up!"), /* @__PURE__ */ React2.createElement(Text, { style: styles.emptySubtitle }, "No tests assigned")) : testView === "list" ? (
13763
13792
  /* List View - Show tests grouped by folder */
13764
- /* @__PURE__ */ React2.createElement(View, null, /* @__PURE__ */ React2.createElement(Text, { style: styles.listHeader }, assignments.length, " test", assignments.length !== 1 ? "s" : "", " assigned"), groupedAssignments.map((folder) => {
13793
+ /* @__PURE__ */ React2.createElement(View, null, /* @__PURE__ */ React2.createElement(Text, { style: styles.listHeader }, assignments.length, " test", assignments.length !== 1 ? "s" : "", " \xB7 ", passedCount, " passed \xB7 ", failedCount, " failed"), /* @__PURE__ */ React2.createElement(View, { style: styles.filterBar }, ["all", "pending", "completed"].map((f) => /* @__PURE__ */ React2.createElement(
13794
+ TouchableOpacity,
13795
+ {
13796
+ key: f,
13797
+ style: [styles.filterChip, testFilter === f && styles.filterChipActive],
13798
+ onPress: () => setTestFilter(f)
13799
+ },
13800
+ /* @__PURE__ */ React2.createElement(Text, { style: [styles.filterChipText, testFilter === f && styles.filterChipTextActive] }, f === "all" ? `All (${assignments.length})` : f === "pending" ? `To Do (${pendingCount + inProgressCount})` : `Done (${passedCount + failedCount})`)
13801
+ ))), groupedAssignments.map((folder) => {
13765
13802
  const groupId = folder.group?.id || "ungrouped";
13766
13803
  const isCollapsed = collapsedFolders.has(groupId);
13767
13804
  const completedCount = folder.stats.passed + folder.stats.failed + folder.stats.skipped;
@@ -13781,7 +13818,11 @@ function BugBearButton({
13781
13818
  { width: `${completedCount / folder.stats.total * 100}%` }
13782
13819
  ]
13783
13820
  }
13784
- )), !isCollapsed && folder.assignments.map((assignment) => {
13821
+ )), !isCollapsed && folder.assignments.filter((a) => {
13822
+ if (testFilter === "pending") return a.status === "pending" || a.status === "in_progress";
13823
+ if (testFilter === "completed") return a.status === "passed" || a.status === "failed" || a.status === "skipped";
13824
+ return true;
13825
+ }).map((assignment) => {
13785
13826
  const statusBadge = getStatusBadge(assignment.status);
13786
13827
  return /* @__PURE__ */ React2.createElement(
13787
13828
  TouchableOpacity,
@@ -13880,7 +13921,10 @@ function BugBearButton({
13880
13921
  style: styles.backButton
13881
13922
  },
13882
13923
  /* @__PURE__ */ React2.createElement(Text, { style: styles.backButtonText }, "\u2190 All Tests (", assignments.length, ")")
13883
- ), /* @__PURE__ */ React2.createElement(View, { style: styles.testCard }, /* @__PURE__ */ React2.createElement(View, { style: styles.testHeader }, /* @__PURE__ */ React2.createElement(Text, { style: styles.testKey }, displayedAssignment.testCase.testKey), /* @__PURE__ */ React2.createElement(View, { style: styles.testHeaderBadges }, displayedAssignment.testCase.track && /* @__PURE__ */ React2.createElement(View, { style: [styles.trackBadge, { backgroundColor: displayedAssignment.testCase.track.color }] }, /* @__PURE__ */ React2.createElement(Text, { style: styles.trackBadgeText }, templateInfo[displayedAssignment.testCase.track.testTemplate || "steps"].icon)), /* @__PURE__ */ React2.createElement(View, { style: [
13924
+ ), /* @__PURE__ */ React2.createElement(View, { style: styles.testCard }, /* @__PURE__ */ React2.createElement(View, { style: styles.testHeader }, /* @__PURE__ */ React2.createElement(Text, { style: styles.testKey }, displayedAssignment.testCase.testKey), /* @__PURE__ */ React2.createElement(View, { style: styles.testHeaderBadges }, (() => {
13925
+ const badge = getStatusBadge(displayedAssignment.status);
13926
+ return /* @__PURE__ */ React2.createElement(View, { style: [styles.statusBadge, { backgroundColor: badge.color + "20" }] }, /* @__PURE__ */ React2.createElement(Text, { style: styles.statusBadgeIcon }, badge.icon), /* @__PURE__ */ React2.createElement(Text, { style: [styles.statusBadgeText, { color: badge.color }] }, badge.label));
13927
+ })(), displayedAssignment.testCase.track && /* @__PURE__ */ React2.createElement(View, { style: [styles.trackBadge, { backgroundColor: displayedAssignment.testCase.track.color }] }, /* @__PURE__ */ React2.createElement(Text, { style: styles.trackBadgeText }, templateInfo[displayedAssignment.testCase.track.testTemplate || "steps"].icon)), /* @__PURE__ */ React2.createElement(View, { style: [
13884
13928
  styles.priorityBadge,
13885
13929
  displayedAssignment.testCase.priority === "P0" && styles.priorityP0,
13886
13930
  displayedAssignment.testCase.priority === "P1" && styles.priorityP1
@@ -13921,7 +13965,14 @@ function BugBearButton({
13921
13965
  disabled: submitting || skipping
13922
13966
  },
13923
13967
  /* @__PURE__ */ React2.createElement(Text, { style: styles.passButtonText }, submitting ? "..." : "\u2713 Pass")
13924
- )))
13968
+ )), pendingCount > 1 || pendingCount === 1 && displayedAssignment.status !== "pending" ? /* @__PURE__ */ React2.createElement(
13969
+ TouchableOpacity,
13970
+ {
13971
+ style: styles.nextTestButton,
13972
+ onPress: handleNextTest
13973
+ },
13974
+ /* @__PURE__ */ React2.createElement(Text, { style: styles.nextTestButtonText }, "Next Test \u25B6")
13975
+ ) : null)
13925
13976
  ) : null), activeTab === "messages" && /* @__PURE__ */ React2.createElement(View, null, messageView === "compose" ? (
13926
13977
  /* Compose New Message View */
13927
13978
  /* @__PURE__ */ React2.createElement(View, null, /* @__PURE__ */ React2.createElement(TouchableOpacity, { onPress: handleBackToThreadList, style: styles.backButton }, /* @__PURE__ */ React2.createElement(Text, { style: styles.backButtonText }, "\u2190 Back to Messages")), /* @__PURE__ */ React2.createElement(View, { style: styles.composeHeader }, /* @__PURE__ */ React2.createElement(Text, { style: styles.composeTitle }, "New Message"), /* @__PURE__ */ React2.createElement(Text, { style: styles.composeSubtitle }, "Send a message to the QA team")), /* @__PURE__ */ React2.createElement(View, { style: styles.composeForm }, /* @__PURE__ */ React2.createElement(Text, { style: styles.label }, "Subject"), /* @__PURE__ */ React2.createElement(
@@ -14305,7 +14356,7 @@ function BugBearButton({
14305
14356
  /* @__PURE__ */ React2.createElement(Text, { style: styles.closeProfileButtonText }, "\u2190 Back")
14306
14357
  ))), activeTab === "messages" && messageView === "thread" && selectedThread ? (
14307
14358
  /* Reply Composer */
14308
- /* @__PURE__ */ React2.createElement(View, { style: styles.replyComposer }, /* @__PURE__ */ React2.createElement(
14359
+ /* @__PURE__ */ React2.createElement(View, null, messageSendError && /* @__PURE__ */ React2.createElement(View, { style: styles.messageSendError }, /* @__PURE__ */ React2.createElement(Text, { style: styles.messageSendErrorText }, "Failed to send. Tap Send to retry.")), /* @__PURE__ */ React2.createElement(View, { style: styles.replyComposer }, /* @__PURE__ */ React2.createElement(
14309
14360
  TextInput,
14310
14361
  {
14311
14362
  style: styles.replyInput,
@@ -14327,10 +14378,10 @@ function BugBearButton({
14327
14378
  disabled: !replyText.trim() || sendingReply
14328
14379
  },
14329
14380
  /* @__PURE__ */ React2.createElement(Text, { style: styles.sendButtonText }, sendingReply ? "..." : "Send")
14330
- ))
14381
+ )))
14331
14382
  ) : (
14332
14383
  /* Standard Footer */
14333
- /* @__PURE__ */ React2.createElement(View, { style: styles.footer }, activeTab === "messages" ? /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Text, { style: styles.footerText }, threads.length, " thread", threads.length !== 1 ? "s" : "", " \xB7 ", unreadCount, " unread"), /* @__PURE__ */ React2.createElement(TouchableOpacity, { onPress: refreshThreads }, /* @__PURE__ */ React2.createElement(Text, { style: styles.refreshText }, "\u21BB Refresh"))) : activeTab === "explore" ? /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Text, { style: styles.footerText }, activeSession ? `${sessionFindings.length} findings` : "No active session"), /* @__PURE__ */ React2.createElement(TouchableOpacity, { onPress: refreshSession }, /* @__PURE__ */ React2.createElement(Text, { style: styles.refreshText }, "\u21BB Refresh"))) : /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Text, { style: styles.footerText }, pendingCount, " pending \xB7 ", inProgressCount, " in progress"), /* @__PURE__ */ React2.createElement(TouchableOpacity, { onPress: refreshAssignments }, /* @__PURE__ */ React2.createElement(Text, { style: styles.refreshText }, "\u21BB Refresh"))))
14384
+ /* @__PURE__ */ React2.createElement(View, { style: styles.footer }, activeTab === "messages" ? /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Text, { style: styles.footerText }, threads.length, " thread", threads.length !== 1 ? "s" : "", " \xB7 ", unreadCount, " unread"), /* @__PURE__ */ React2.createElement(TouchableOpacity, { onPress: refreshThreads }, /* @__PURE__ */ React2.createElement(Text, { style: styles.refreshText }, "\u21BB Refresh"))) : activeTab === "explore" ? /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Text, { style: styles.footerText }, activeSession ? `${sessionFindings.length} findings` : "No active session"), /* @__PURE__ */ React2.createElement(TouchableOpacity, { onPress: refreshSession }, /* @__PURE__ */ React2.createElement(Text, { style: styles.refreshText }, "\u21BB Refresh"))) : /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Text, { style: styles.footerText }, passedCount + failedCount, "/", assignments.length, " done \xB7 ", pendingCount, " pending"), /* @__PURE__ */ React2.createElement(TouchableOpacity, { onPress: refreshAssignments }, /* @__PURE__ */ React2.createElement(Text, { style: styles.refreshText }, "\u21BB Refresh"))))
14334
14385
  ))
14335
14386
  )
14336
14387
  ), /* @__PURE__ */ React2.createElement(
@@ -14520,7 +14571,8 @@ var styles = StyleSheet.create({
14520
14571
  justifyContent: "space-between",
14521
14572
  alignItems: "center",
14522
14573
  paddingHorizontal: 16,
14523
- paddingVertical: 12,
14574
+ paddingTop: 12,
14575
+ paddingBottom: Platform2.OS === "ios" ? 34 : 12,
14524
14576
  borderTopWidth: 1,
14525
14577
  borderTopColor: "#27272a",
14526
14578
  backgroundColor: "#09090b"
@@ -15268,7 +15320,9 @@ var styles = StyleSheet.create({
15268
15320
  replyComposer: {
15269
15321
  flexDirection: "row",
15270
15322
  alignItems: "flex-end",
15271
- padding: 12,
15323
+ paddingHorizontal: 12,
15324
+ paddingTop: 12,
15325
+ paddingBottom: Platform2.OS === "ios" ? 34 : 12,
15272
15326
  borderTopWidth: 1,
15273
15327
  borderTopColor: "#27272a",
15274
15328
  backgroundColor: "#18181b"
@@ -15863,6 +15917,56 @@ var styles = StyleSheet.create({
15863
15917
  height: "100%",
15864
15918
  backgroundColor: "#22c55e",
15865
15919
  borderRadius: 2
15920
+ },
15921
+ nextTestButton: {
15922
+ backgroundColor: "#27272a",
15923
+ paddingVertical: 10,
15924
+ borderRadius: 10,
15925
+ alignItems: "center",
15926
+ marginTop: 8,
15927
+ borderWidth: 1,
15928
+ borderColor: "#3f3f46"
15929
+ },
15930
+ nextTestButtonText: {
15931
+ fontSize: 14,
15932
+ fontWeight: "500",
15933
+ color: "#a1a1aa"
15934
+ },
15935
+ filterBar: {
15936
+ flexDirection: "row",
15937
+ gap: 6,
15938
+ marginBottom: 10
15939
+ },
15940
+ filterChip: {
15941
+ paddingHorizontal: 10,
15942
+ paddingVertical: 5,
15943
+ borderRadius: 12,
15944
+ backgroundColor: "#27272a",
15945
+ borderWidth: 1,
15946
+ borderColor: "#3f3f46"
15947
+ },
15948
+ filterChipActive: {
15949
+ backgroundColor: "#1e3a5f",
15950
+ borderColor: "#3B82F6"
15951
+ },
15952
+ filterChipText: {
15953
+ fontSize: 12,
15954
+ color: "#71717a"
15955
+ },
15956
+ filterChipTextActive: {
15957
+ color: "#93c5fd"
15958
+ },
15959
+ messageSendError: {
15960
+ backgroundColor: "#7f1d1d",
15961
+ paddingHorizontal: 12,
15962
+ paddingVertical: 6,
15963
+ borderTopWidth: 1,
15964
+ borderTopColor: "#991b1b"
15965
+ },
15966
+ messageSendErrorText: {
15967
+ fontSize: 12,
15968
+ color: "#fca5a5",
15969
+ textAlign: "center"
15866
15970
  }
15867
15971
  });
15868
15972
  var exploreStyles = StyleSheet.create({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bbearai/react-native",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "BugBear React Native components for mobile apps",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",