@bbearai/react 0.5.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -602,7 +602,7 @@ function MessageListScreenSkeleton() {
602
602
  // src/widget/screens/HomeScreen.tsx
603
603
  var import_jsx_runtime3 = require("react/jsx-runtime");
604
604
  function HomeScreen({ nav }) {
605
- const { assignments, unreadCount, threads, refreshAssignments, refreshThreads, issueCounts, refreshIssueCounts, isLoading } = useBugBear();
605
+ const { assignments, unreadCount, threads, refreshAssignments, refreshThreads, issueCounts, refreshIssueCounts, dashboardUrl, isLoading } = useBugBear();
606
606
  (0, import_react3.useEffect)(() => {
607
607
  refreshAssignments();
608
608
  refreshThreads();
@@ -1007,6 +1007,33 @@ function HomeScreen({ nav }) {
1007
1007
  " tests completed"
1008
1008
  ] })
1009
1009
  ] }),
1010
+ dashboardUrl && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1011
+ "a",
1012
+ {
1013
+ href: dashboardUrl,
1014
+ target: "_blank",
1015
+ rel: "noopener noreferrer",
1016
+ style: {
1017
+ display: "flex",
1018
+ alignItems: "center",
1019
+ backgroundColor: colors.card,
1020
+ border: `1px solid ${colors.border}`,
1021
+ borderRadius: 12,
1022
+ padding: 14,
1023
+ marginBottom: 16,
1024
+ textDecoration: "none",
1025
+ cursor: "pointer"
1026
+ },
1027
+ children: [
1028
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { fontSize: 22, marginRight: 12 }, children: "\u{1F310}" }),
1029
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { style: { flex: 1 }, children: [
1030
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { display: "block", fontSize: 14, fontWeight: 600, color: colors.textPrimary }, children: "Open Dashboard" }),
1031
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { display: "block", fontSize: 12, color: colors.textMuted, marginTop: 2 }, children: "View analytics, history & more" })
1032
+ ] }),
1033
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { fontSize: 16, color: colors.textMuted, marginLeft: 8 }, children: "\u2192" })
1034
+ ]
1035
+ }
1036
+ ),
1010
1037
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { display: "flex", justifyContent: "center", padding: "8px 0" }, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1011
1038
  "button",
1012
1039
  {
@@ -1097,6 +1124,37 @@ function TestDetailScreen({ testId, nav }) {
1097
1124
  setIsSubmitting(false);
1098
1125
  }
1099
1126
  }, [client, displayedAssignment, refreshAssignments, nav, isSubmitting]);
1127
+ const handleReopen = (0, import_react4.useCallback)(async () => {
1128
+ if (!client || !displayedAssignment || isSubmitting) return;
1129
+ setIsSubmitting(true);
1130
+ try {
1131
+ await client.reopenAssignment(displayedAssignment.id);
1132
+ await refreshAssignments();
1133
+ } finally {
1134
+ setIsSubmitting(false);
1135
+ }
1136
+ }, [client, displayedAssignment, refreshAssignments, isSubmitting]);
1137
+ const handleChangeResult = (0, import_react4.useCallback)(async (newStatus) => {
1138
+ if (!client || !displayedAssignment || isSubmitting) return;
1139
+ setIsSubmitting(true);
1140
+ try {
1141
+ await client.reopenAssignment(displayedAssignment.id);
1142
+ await client.updateAssignmentStatus(displayedAssignment.id, newStatus);
1143
+ await refreshAssignments();
1144
+ if (newStatus === "failed") {
1145
+ nav.replace({
1146
+ name: "REPORT",
1147
+ prefill: {
1148
+ type: "test_fail",
1149
+ assignmentId: displayedAssignment.id,
1150
+ testCaseId: displayedAssignment.testCase.id
1151
+ }
1152
+ });
1153
+ }
1154
+ } finally {
1155
+ setIsSubmitting(false);
1156
+ }
1157
+ }, [client, displayedAssignment, refreshAssignments, nav, isSubmitting]);
1100
1158
  const handleSkip = (0, import_react4.useCallback)(async () => {
1101
1159
  if (!client || !displayedAssignment || !selectedSkipReason) return;
1102
1160
  setSkipping(true);
@@ -1612,7 +1670,113 @@ function TestDetailScreen({ testId, nav }) {
1612
1670
  ]
1613
1671
  }
1614
1672
  ),
1615
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", gap: 10, marginTop: 8 }, children: [
1673
+ displayedAssignment.status === "passed" || displayedAssignment.status === "failed" || displayedAssignment.status === "skipped" || displayedAssignment.status === "blocked" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
1674
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1675
+ "div",
1676
+ {
1677
+ style: {
1678
+ display: "flex",
1679
+ alignItems: "center",
1680
+ justifyContent: "center",
1681
+ gap: 6,
1682
+ padding: "8px 12px",
1683
+ borderRadius: 8,
1684
+ marginTop: 8,
1685
+ marginBottom: 8,
1686
+ backgroundColor: displayedAssignment.status === "passed" ? colors.greenDark : displayedAssignment.status === "failed" ? colors.redDark : colors.card,
1687
+ border: `1px solid ${displayedAssignment.status === "passed" ? colors.green : displayedAssignment.status === "failed" ? colors.red : colors.border}`
1688
+ },
1689
+ children: [
1690
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { fontSize: 14 }, children: displayedAssignment.status === "passed" ? "\u2705" : displayedAssignment.status === "failed" ? "\u274C" : displayedAssignment.status === "skipped" ? "\u23ED" : "\u{1F6AB}" }),
1691
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1692
+ "span",
1693
+ {
1694
+ style: {
1695
+ fontSize: 13,
1696
+ fontWeight: 600,
1697
+ color: displayedAssignment.status === "passed" ? colors.green : displayedAssignment.status === "failed" ? "#fca5a5" : colors.textSecondary
1698
+ },
1699
+ children: [
1700
+ "Marked as ",
1701
+ displayedAssignment.status.charAt(0).toUpperCase() + displayedAssignment.status.slice(1)
1702
+ ]
1703
+ }
1704
+ )
1705
+ ]
1706
+ }
1707
+ ),
1708
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", gap: 10, marginTop: 4 }, children: [
1709
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1710
+ "button",
1711
+ {
1712
+ type: "button",
1713
+ onClick: handleReopen,
1714
+ disabled: isSubmitting,
1715
+ style: {
1716
+ flex: 1,
1717
+ paddingTop: 14,
1718
+ paddingBottom: 14,
1719
+ borderRadius: 12,
1720
+ textAlign: "center",
1721
+ backgroundColor: colors.card,
1722
+ border: `1px solid ${colors.blue}`,
1723
+ cursor: isSubmitting ? "not-allowed" : "pointer",
1724
+ fontSize: 14,
1725
+ fontWeight: 600,
1726
+ color: colors.blue,
1727
+ opacity: isSubmitting ? 0.5 : 1
1728
+ },
1729
+ children: isSubmitting ? "Reopening..." : "\u{1F504} Reopen Test"
1730
+ }
1731
+ ),
1732
+ displayedAssignment.status === "passed" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1733
+ "button",
1734
+ {
1735
+ type: "button",
1736
+ onClick: () => handleChangeResult("failed"),
1737
+ disabled: isSubmitting,
1738
+ style: {
1739
+ flex: 1,
1740
+ paddingTop: 14,
1741
+ paddingBottom: 14,
1742
+ borderRadius: 12,
1743
+ textAlign: "center",
1744
+ backgroundColor: colors.redDark,
1745
+ border: `1px solid ${colors.red}`,
1746
+ cursor: isSubmitting ? "not-allowed" : "pointer",
1747
+ fontSize: 14,
1748
+ fontWeight: 600,
1749
+ color: "#fca5a5",
1750
+ opacity: isSubmitting ? 0.5 : 1
1751
+ },
1752
+ children: "Change to Fail"
1753
+ }
1754
+ ),
1755
+ displayedAssignment.status === "failed" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1756
+ "button",
1757
+ {
1758
+ type: "button",
1759
+ onClick: () => handleChangeResult("passed"),
1760
+ disabled: isSubmitting,
1761
+ style: {
1762
+ flex: 1,
1763
+ paddingTop: 14,
1764
+ paddingBottom: 14,
1765
+ borderRadius: 12,
1766
+ textAlign: "center",
1767
+ backgroundColor: colors.greenDark,
1768
+ border: `1px solid ${colors.green}`,
1769
+ cursor: isSubmitting ? "not-allowed" : "pointer",
1770
+ fontSize: 14,
1771
+ fontWeight: 600,
1772
+ color: "#86efac",
1773
+ opacity: isSubmitting ? 0.5 : 1
1774
+ },
1775
+ children: "Change to Pass"
1776
+ }
1777
+ )
1778
+ ] })
1779
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", gap: 10, marginTop: 8 }, children: [
1616
1780
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1617
1781
  "button",
1618
1782
  {
@@ -1831,10 +1995,11 @@ function TestDetailScreen({ testId, nav }) {
1831
1995
  var import_react5 = require("react");
1832
1996
  var import_jsx_runtime5 = require("react/jsx-runtime");
1833
1997
  function TestListScreen({ nav }) {
1834
- const { assignments, currentAssignment, refreshAssignments, isLoading } = useBugBear();
1998
+ const { assignments, currentAssignment, refreshAssignments, dashboardUrl, isLoading } = useBugBear();
1835
1999
  const [filter, setFilter] = (0, import_react5.useState)("all");
1836
2000
  const [roleFilter, setRoleFilter] = (0, import_react5.useState)(null);
1837
2001
  const [trackFilter, setTrackFilter] = (0, import_react5.useState)(null);
2002
+ const [platformFilter, setPlatformFilter] = (0, import_react5.useState)("web");
1838
2003
  const [searchQuery, setSearchQuery] = (0, import_react5.useState)("");
1839
2004
  const [sortMode, setSortMode] = (0, import_react5.useState)("priority");
1840
2005
  const [collapsedFolders, setCollapsedFolders] = (0, import_react5.useState)(/* @__PURE__ */ new Set());
@@ -1852,6 +2017,13 @@ function TestListScreen({ nav }) {
1852
2017
  }
1853
2018
  return Array.from(trackMap.values());
1854
2019
  }, [assignments]);
2020
+ const availablePlatforms = (0, import_react5.useMemo)(() => {
2021
+ const set = /* @__PURE__ */ new Set();
2022
+ for (const a of assignments) {
2023
+ if (a.testCase.platforms) a.testCase.platforms.forEach((p) => set.add(p));
2024
+ }
2025
+ return Array.from(set).sort();
2026
+ }, [assignments]);
1855
2027
  const selectedRole = availableRoles.find((r) => r.id === roleFilter);
1856
2028
  const groupedAssignments = (0, import_react5.useMemo)(() => {
1857
2029
  const groups = /* @__PURE__ */ new Map();
@@ -1912,6 +2084,7 @@ function TestListScreen({ nav }) {
1912
2084
  });
1913
2085
  }, []);
1914
2086
  const filterAssignment = (0, import_react5.useCallback)((a) => {
2087
+ if (platformFilter && a.testCase.platforms && !a.testCase.platforms.includes(platformFilter)) return false;
1915
2088
  if (roleFilter && a.testCase.role?.id !== roleFilter) return false;
1916
2089
  if (trackFilter && a.testCase.track?.id !== trackFilter) return false;
1917
2090
  if (searchQuery) {
@@ -1924,7 +2097,7 @@ function TestListScreen({ nav }) {
1924
2097
  if (filter === "done") return a.status === "passed";
1925
2098
  if (filter === "reopened") return a.status === "failed";
1926
2099
  return true;
1927
- }, [roleFilter, trackFilter, searchQuery, filter]);
2100
+ }, [platformFilter, roleFilter, trackFilter, searchQuery, filter]);
1928
2101
  const pendingCount = assignments.filter(
1929
2102
  (a) => a.status === "pending" || a.status === "in_progress"
1930
2103
  ).length;
@@ -2058,6 +2231,59 @@ function TestListScreen({ nav }) {
2058
2231
  }
2059
2232
  }
2060
2233
  ) }),
2234
+ availablePlatforms.length >= 2 && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { display: "flex", gap: 4, marginBottom: 8 }, children: [
2235
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2236
+ "button",
2237
+ {
2238
+ type: "button",
2239
+ onClick: () => setPlatformFilter(null),
2240
+ style: {
2241
+ padding: "3px 8px",
2242
+ borderRadius: 6,
2243
+ backgroundColor: !platformFilter ? colors.card : "transparent",
2244
+ border: !platformFilter ? `1px solid ${colors.border}` : "1px solid transparent",
2245
+ cursor: "pointer",
2246
+ fontSize: 11,
2247
+ color: !platformFilter ? colors.textPrimary : colors.textMuted,
2248
+ fontWeight: !platformFilter ? 600 : 400,
2249
+ whiteSpace: "nowrap"
2250
+ },
2251
+ children: "All Platforms"
2252
+ }
2253
+ ),
2254
+ availablePlatforms.map((p) => {
2255
+ const isActive = platformFilter === p;
2256
+ const label = p === "ios" ? "iOS" : p === "android" ? "Android" : p === "web" ? "Web" : p;
2257
+ const icon = p === "ios" ? "\u{1F4F1}" : p === "android" ? "\u{1F916}" : p === "web" ? "\u{1F310}" : "\u{1F4CB}";
2258
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
2259
+ "button",
2260
+ {
2261
+ type: "button",
2262
+ onClick: () => setPlatformFilter(isActive ? null : p),
2263
+ style: {
2264
+ display: "flex",
2265
+ alignItems: "center",
2266
+ gap: 4,
2267
+ padding: "3px 8px",
2268
+ borderRadius: 6,
2269
+ backgroundColor: isActive ? colors.blue + "20" : "transparent",
2270
+ border: isActive ? `1px solid ${colors.blue}60` : "1px solid transparent",
2271
+ cursor: "pointer",
2272
+ fontSize: 11,
2273
+ color: isActive ? colors.blue : colors.textMuted,
2274
+ fontWeight: isActive ? 600 : 400,
2275
+ whiteSpace: "nowrap"
2276
+ },
2277
+ children: [
2278
+ icon,
2279
+ " ",
2280
+ label
2281
+ ]
2282
+ },
2283
+ p
2284
+ );
2285
+ })
2286
+ ] }),
2061
2287
  (availableTracks.length >= 2 || true) && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 6, marginBottom: 12 }, children: [
2062
2288
  availableTracks.length >= 2 && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { style: { display: "flex", gap: 4, flex: 1, overflow: "auto" }, children: [
2063
2289
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
@@ -2371,7 +2597,27 @@ function TestListScreen({ nav }) {
2371
2597
  ]
2372
2598
  }
2373
2599
  ),
2374
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { display: "flex", justifyContent: "center", paddingTop: 12, paddingBottom: 8 }, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2600
+ dashboardUrl && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2601
+ "a",
2602
+ {
2603
+ href: `${dashboardUrl}/test-cases`,
2604
+ target: "_blank",
2605
+ rel: "noopener noreferrer",
2606
+ style: {
2607
+ display: "flex",
2608
+ alignItems: "center",
2609
+ justifyContent: "center",
2610
+ gap: 6,
2611
+ paddingTop: 12,
2612
+ fontSize: 13,
2613
+ fontWeight: 500,
2614
+ color: colors.blue,
2615
+ textDecoration: "none"
2616
+ },
2617
+ children: "\u{1F310} Manage on Dashboard \u2192"
2618
+ }
2619
+ ),
2620
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { style: { display: "flex", justifyContent: "center", paddingTop: 8, paddingBottom: 8 }, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2375
2621
  "button",
2376
2622
  {
2377
2623
  type: "button",
@@ -3373,7 +3619,7 @@ var styles3 = {
3373
3619
  // src/widget/screens/MessageListScreen.tsx
3374
3620
  var import_jsx_runtime12 = require("react/jsx-runtime");
3375
3621
  function MessageListScreen({ nav }) {
3376
- const { threads, unreadCount, refreshThreads, isLoading } = useBugBear();
3622
+ const { threads, unreadCount, refreshThreads, dashboardUrl, isLoading } = useBugBear();
3377
3623
  if (isLoading) return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(MessageListScreenSkeleton, {});
3378
3624
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { children: [
3379
3625
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
@@ -3562,6 +3808,26 @@ function MessageListScreen({ nav }) {
3562
3808
  },
3563
3809
  thread.id
3564
3810
  )) }),
3811
+ dashboardUrl && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
3812
+ "a",
3813
+ {
3814
+ href: `${dashboardUrl}/discussions`,
3815
+ target: "_blank",
3816
+ rel: "noopener noreferrer",
3817
+ style: {
3818
+ display: "flex",
3819
+ alignItems: "center",
3820
+ justifyContent: "center",
3821
+ gap: 6,
3822
+ paddingTop: 12,
3823
+ fontSize: 13,
3824
+ fontWeight: 500,
3825
+ color: colors.blue,
3826
+ textDecoration: "none"
3827
+ },
3828
+ children: "\u{1F310} View on Dashboard \u2192"
3829
+ }
3830
+ ),
3565
3831
  /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
3566
3832
  "div",
3567
3833
  {
@@ -3569,7 +3835,7 @@ function MessageListScreen({ nav }) {
3569
3835
  display: "flex",
3570
3836
  justifyContent: "space-between",
3571
3837
  alignItems: "center",
3572
- paddingTop: 12,
3838
+ paddingTop: 8,
3573
3839
  paddingLeft: 4,
3574
3840
  paddingRight: 4
3575
3841
  },
@@ -4693,6 +4959,7 @@ var SEVERITY_CONFIG = {
4693
4959
  low: { label: "Low", color: "#71717a", bg: "#27272a" }
4694
4960
  };
4695
4961
  function IssueDetailScreen({ nav, issue }) {
4962
+ const { dashboardUrl } = useBugBear();
4696
4963
  const statusConfig = STATUS_LABELS[issue.status] || { label: issue.status, bg: "#27272a", color: "#a1a1aa" };
4697
4964
  const severityConfig = issue.severity ? SEVERITY_CONFIG[issue.severity] : null;
4698
4965
  return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { children: [
@@ -4810,7 +5077,29 @@ function IssueDetailScreen({ nav, issue }) {
4810
5077
  " \xB7 Updated ",
4811
5078
  formatRelativeTime(issue.updatedAt)
4812
5079
  ] })
4813
- ] })
5080
+ ] }),
5081
+ dashboardUrl && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
5082
+ "a",
5083
+ {
5084
+ href: `${dashboardUrl}/reports`,
5085
+ target: "_blank",
5086
+ rel: "noopener noreferrer",
5087
+ style: {
5088
+ display: "flex",
5089
+ alignItems: "center",
5090
+ justifyContent: "center",
5091
+ gap: 6,
5092
+ marginTop: 12,
5093
+ padding: "10px 0",
5094
+ fontSize: 13,
5095
+ fontWeight: 500,
5096
+ color: colors.blue,
5097
+ textDecoration: "none",
5098
+ borderTop: `1px solid ${colors.border}`
5099
+ },
5100
+ children: "\u{1F310} View on Dashboard \u2192"
5101
+ }
5102
+ )
4814
5103
  ] });
4815
5104
  }
4816
5105
 
package/dist/index.mjs CHANGED
@@ -563,7 +563,7 @@ function MessageListScreenSkeleton() {
563
563
  // src/widget/screens/HomeScreen.tsx
564
564
  import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
565
565
  function HomeScreen({ nav }) {
566
- const { assignments, unreadCount, threads, refreshAssignments, refreshThreads, issueCounts, refreshIssueCounts, isLoading } = useBugBear();
566
+ const { assignments, unreadCount, threads, refreshAssignments, refreshThreads, issueCounts, refreshIssueCounts, dashboardUrl, isLoading } = useBugBear();
567
567
  useEffect2(() => {
568
568
  refreshAssignments();
569
569
  refreshThreads();
@@ -968,6 +968,33 @@ function HomeScreen({ nav }) {
968
968
  " tests completed"
969
969
  ] })
970
970
  ] }),
971
+ dashboardUrl && /* @__PURE__ */ jsxs2(
972
+ "a",
973
+ {
974
+ href: dashboardUrl,
975
+ target: "_blank",
976
+ rel: "noopener noreferrer",
977
+ style: {
978
+ display: "flex",
979
+ alignItems: "center",
980
+ backgroundColor: colors.card,
981
+ border: `1px solid ${colors.border}`,
982
+ borderRadius: 12,
983
+ padding: 14,
984
+ marginBottom: 16,
985
+ textDecoration: "none",
986
+ cursor: "pointer"
987
+ },
988
+ children: [
989
+ /* @__PURE__ */ jsx3("span", { style: { fontSize: 22, marginRight: 12 }, children: "\u{1F310}" }),
990
+ /* @__PURE__ */ jsxs2("span", { style: { flex: 1 }, children: [
991
+ /* @__PURE__ */ jsx3("span", { style: { display: "block", fontSize: 14, fontWeight: 600, color: colors.textPrimary }, children: "Open Dashboard" }),
992
+ /* @__PURE__ */ jsx3("span", { style: { display: "block", fontSize: 12, color: colors.textMuted, marginTop: 2 }, children: "View analytics, history & more" })
993
+ ] }),
994
+ /* @__PURE__ */ jsx3("span", { style: { fontSize: 16, color: colors.textMuted, marginLeft: 8 }, children: "\u2192" })
995
+ ]
996
+ }
997
+ ),
971
998
  /* @__PURE__ */ jsx3("div", { style: { display: "flex", justifyContent: "center", padding: "8px 0" }, children: /* @__PURE__ */ jsx3(
972
999
  "button",
973
1000
  {
@@ -1058,6 +1085,37 @@ function TestDetailScreen({ testId, nav }) {
1058
1085
  setIsSubmitting(false);
1059
1086
  }
1060
1087
  }, [client, displayedAssignment, refreshAssignments, nav, isSubmitting]);
1088
+ const handleReopen = useCallback2(async () => {
1089
+ if (!client || !displayedAssignment || isSubmitting) return;
1090
+ setIsSubmitting(true);
1091
+ try {
1092
+ await client.reopenAssignment(displayedAssignment.id);
1093
+ await refreshAssignments();
1094
+ } finally {
1095
+ setIsSubmitting(false);
1096
+ }
1097
+ }, [client, displayedAssignment, refreshAssignments, isSubmitting]);
1098
+ const handleChangeResult = useCallback2(async (newStatus) => {
1099
+ if (!client || !displayedAssignment || isSubmitting) return;
1100
+ setIsSubmitting(true);
1101
+ try {
1102
+ await client.reopenAssignment(displayedAssignment.id);
1103
+ await client.updateAssignmentStatus(displayedAssignment.id, newStatus);
1104
+ await refreshAssignments();
1105
+ if (newStatus === "failed") {
1106
+ nav.replace({
1107
+ name: "REPORT",
1108
+ prefill: {
1109
+ type: "test_fail",
1110
+ assignmentId: displayedAssignment.id,
1111
+ testCaseId: displayedAssignment.testCase.id
1112
+ }
1113
+ });
1114
+ }
1115
+ } finally {
1116
+ setIsSubmitting(false);
1117
+ }
1118
+ }, [client, displayedAssignment, refreshAssignments, nav, isSubmitting]);
1061
1119
  const handleSkip = useCallback2(async () => {
1062
1120
  if (!client || !displayedAssignment || !selectedSkipReason) return;
1063
1121
  setSkipping(true);
@@ -1573,7 +1631,113 @@ function TestDetailScreen({ testId, nav }) {
1573
1631
  ]
1574
1632
  }
1575
1633
  ),
1576
- /* @__PURE__ */ jsxs3("div", { style: { display: "flex", gap: 10, marginTop: 8 }, children: [
1634
+ displayedAssignment.status === "passed" || displayedAssignment.status === "failed" || displayedAssignment.status === "skipped" || displayedAssignment.status === "blocked" ? /* @__PURE__ */ jsxs3(Fragment, { children: [
1635
+ /* @__PURE__ */ jsxs3(
1636
+ "div",
1637
+ {
1638
+ style: {
1639
+ display: "flex",
1640
+ alignItems: "center",
1641
+ justifyContent: "center",
1642
+ gap: 6,
1643
+ padding: "8px 12px",
1644
+ borderRadius: 8,
1645
+ marginTop: 8,
1646
+ marginBottom: 8,
1647
+ backgroundColor: displayedAssignment.status === "passed" ? colors.greenDark : displayedAssignment.status === "failed" ? colors.redDark : colors.card,
1648
+ border: `1px solid ${displayedAssignment.status === "passed" ? colors.green : displayedAssignment.status === "failed" ? colors.red : colors.border}`
1649
+ },
1650
+ children: [
1651
+ /* @__PURE__ */ jsx4("span", { style: { fontSize: 14 }, children: displayedAssignment.status === "passed" ? "\u2705" : displayedAssignment.status === "failed" ? "\u274C" : displayedAssignment.status === "skipped" ? "\u23ED" : "\u{1F6AB}" }),
1652
+ /* @__PURE__ */ jsxs3(
1653
+ "span",
1654
+ {
1655
+ style: {
1656
+ fontSize: 13,
1657
+ fontWeight: 600,
1658
+ color: displayedAssignment.status === "passed" ? colors.green : displayedAssignment.status === "failed" ? "#fca5a5" : colors.textSecondary
1659
+ },
1660
+ children: [
1661
+ "Marked as ",
1662
+ displayedAssignment.status.charAt(0).toUpperCase() + displayedAssignment.status.slice(1)
1663
+ ]
1664
+ }
1665
+ )
1666
+ ]
1667
+ }
1668
+ ),
1669
+ /* @__PURE__ */ jsxs3("div", { style: { display: "flex", gap: 10, marginTop: 4 }, children: [
1670
+ /* @__PURE__ */ jsx4(
1671
+ "button",
1672
+ {
1673
+ type: "button",
1674
+ onClick: handleReopen,
1675
+ disabled: isSubmitting,
1676
+ style: {
1677
+ flex: 1,
1678
+ paddingTop: 14,
1679
+ paddingBottom: 14,
1680
+ borderRadius: 12,
1681
+ textAlign: "center",
1682
+ backgroundColor: colors.card,
1683
+ border: `1px solid ${colors.blue}`,
1684
+ cursor: isSubmitting ? "not-allowed" : "pointer",
1685
+ fontSize: 14,
1686
+ fontWeight: 600,
1687
+ color: colors.blue,
1688
+ opacity: isSubmitting ? 0.5 : 1
1689
+ },
1690
+ children: isSubmitting ? "Reopening..." : "\u{1F504} Reopen Test"
1691
+ }
1692
+ ),
1693
+ displayedAssignment.status === "passed" && /* @__PURE__ */ jsx4(
1694
+ "button",
1695
+ {
1696
+ type: "button",
1697
+ onClick: () => handleChangeResult("failed"),
1698
+ disabled: isSubmitting,
1699
+ style: {
1700
+ flex: 1,
1701
+ paddingTop: 14,
1702
+ paddingBottom: 14,
1703
+ borderRadius: 12,
1704
+ textAlign: "center",
1705
+ backgroundColor: colors.redDark,
1706
+ border: `1px solid ${colors.red}`,
1707
+ cursor: isSubmitting ? "not-allowed" : "pointer",
1708
+ fontSize: 14,
1709
+ fontWeight: 600,
1710
+ color: "#fca5a5",
1711
+ opacity: isSubmitting ? 0.5 : 1
1712
+ },
1713
+ children: "Change to Fail"
1714
+ }
1715
+ ),
1716
+ displayedAssignment.status === "failed" && /* @__PURE__ */ jsx4(
1717
+ "button",
1718
+ {
1719
+ type: "button",
1720
+ onClick: () => handleChangeResult("passed"),
1721
+ disabled: isSubmitting,
1722
+ style: {
1723
+ flex: 1,
1724
+ paddingTop: 14,
1725
+ paddingBottom: 14,
1726
+ borderRadius: 12,
1727
+ textAlign: "center",
1728
+ backgroundColor: colors.greenDark,
1729
+ border: `1px solid ${colors.green}`,
1730
+ cursor: isSubmitting ? "not-allowed" : "pointer",
1731
+ fontSize: 14,
1732
+ fontWeight: 600,
1733
+ color: "#86efac",
1734
+ opacity: isSubmitting ? 0.5 : 1
1735
+ },
1736
+ children: "Change to Pass"
1737
+ }
1738
+ )
1739
+ ] })
1740
+ ] }) : /* @__PURE__ */ jsxs3("div", { style: { display: "flex", gap: 10, marginTop: 8 }, children: [
1577
1741
  /* @__PURE__ */ jsx4(
1578
1742
  "button",
1579
1743
  {
@@ -1792,10 +1956,11 @@ function TestDetailScreen({ testId, nav }) {
1792
1956
  import { useState as useState3, useMemo, useCallback as useCallback3 } from "react";
1793
1957
  import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
1794
1958
  function TestListScreen({ nav }) {
1795
- const { assignments, currentAssignment, refreshAssignments, isLoading } = useBugBear();
1959
+ const { assignments, currentAssignment, refreshAssignments, dashboardUrl, isLoading } = useBugBear();
1796
1960
  const [filter, setFilter] = useState3("all");
1797
1961
  const [roleFilter, setRoleFilter] = useState3(null);
1798
1962
  const [trackFilter, setTrackFilter] = useState3(null);
1963
+ const [platformFilter, setPlatformFilter] = useState3("web");
1799
1964
  const [searchQuery, setSearchQuery] = useState3("");
1800
1965
  const [sortMode, setSortMode] = useState3("priority");
1801
1966
  const [collapsedFolders, setCollapsedFolders] = useState3(/* @__PURE__ */ new Set());
@@ -1813,6 +1978,13 @@ function TestListScreen({ nav }) {
1813
1978
  }
1814
1979
  return Array.from(trackMap.values());
1815
1980
  }, [assignments]);
1981
+ const availablePlatforms = useMemo(() => {
1982
+ const set = /* @__PURE__ */ new Set();
1983
+ for (const a of assignments) {
1984
+ if (a.testCase.platforms) a.testCase.platforms.forEach((p) => set.add(p));
1985
+ }
1986
+ return Array.from(set).sort();
1987
+ }, [assignments]);
1816
1988
  const selectedRole = availableRoles.find((r) => r.id === roleFilter);
1817
1989
  const groupedAssignments = useMemo(() => {
1818
1990
  const groups = /* @__PURE__ */ new Map();
@@ -1873,6 +2045,7 @@ function TestListScreen({ nav }) {
1873
2045
  });
1874
2046
  }, []);
1875
2047
  const filterAssignment = useCallback3((a) => {
2048
+ if (platformFilter && a.testCase.platforms && !a.testCase.platforms.includes(platformFilter)) return false;
1876
2049
  if (roleFilter && a.testCase.role?.id !== roleFilter) return false;
1877
2050
  if (trackFilter && a.testCase.track?.id !== trackFilter) return false;
1878
2051
  if (searchQuery) {
@@ -1885,7 +2058,7 @@ function TestListScreen({ nav }) {
1885
2058
  if (filter === "done") return a.status === "passed";
1886
2059
  if (filter === "reopened") return a.status === "failed";
1887
2060
  return true;
1888
- }, [roleFilter, trackFilter, searchQuery, filter]);
2061
+ }, [platformFilter, roleFilter, trackFilter, searchQuery, filter]);
1889
2062
  const pendingCount = assignments.filter(
1890
2063
  (a) => a.status === "pending" || a.status === "in_progress"
1891
2064
  ).length;
@@ -2019,6 +2192,59 @@ function TestListScreen({ nav }) {
2019
2192
  }
2020
2193
  }
2021
2194
  ) }),
2195
+ availablePlatforms.length >= 2 && /* @__PURE__ */ jsxs4("div", { style: { display: "flex", gap: 4, marginBottom: 8 }, children: [
2196
+ /* @__PURE__ */ jsx5(
2197
+ "button",
2198
+ {
2199
+ type: "button",
2200
+ onClick: () => setPlatformFilter(null),
2201
+ style: {
2202
+ padding: "3px 8px",
2203
+ borderRadius: 6,
2204
+ backgroundColor: !platformFilter ? colors.card : "transparent",
2205
+ border: !platformFilter ? `1px solid ${colors.border}` : "1px solid transparent",
2206
+ cursor: "pointer",
2207
+ fontSize: 11,
2208
+ color: !platformFilter ? colors.textPrimary : colors.textMuted,
2209
+ fontWeight: !platformFilter ? 600 : 400,
2210
+ whiteSpace: "nowrap"
2211
+ },
2212
+ children: "All Platforms"
2213
+ }
2214
+ ),
2215
+ availablePlatforms.map((p) => {
2216
+ const isActive = platformFilter === p;
2217
+ const label = p === "ios" ? "iOS" : p === "android" ? "Android" : p === "web" ? "Web" : p;
2218
+ const icon = p === "ios" ? "\u{1F4F1}" : p === "android" ? "\u{1F916}" : p === "web" ? "\u{1F310}" : "\u{1F4CB}";
2219
+ return /* @__PURE__ */ jsxs4(
2220
+ "button",
2221
+ {
2222
+ type: "button",
2223
+ onClick: () => setPlatformFilter(isActive ? null : p),
2224
+ style: {
2225
+ display: "flex",
2226
+ alignItems: "center",
2227
+ gap: 4,
2228
+ padding: "3px 8px",
2229
+ borderRadius: 6,
2230
+ backgroundColor: isActive ? colors.blue + "20" : "transparent",
2231
+ border: isActive ? `1px solid ${colors.blue}60` : "1px solid transparent",
2232
+ cursor: "pointer",
2233
+ fontSize: 11,
2234
+ color: isActive ? colors.blue : colors.textMuted,
2235
+ fontWeight: isActive ? 600 : 400,
2236
+ whiteSpace: "nowrap"
2237
+ },
2238
+ children: [
2239
+ icon,
2240
+ " ",
2241
+ label
2242
+ ]
2243
+ },
2244
+ p
2245
+ );
2246
+ })
2247
+ ] }),
2022
2248
  (availableTracks.length >= 2 || true) && /* @__PURE__ */ jsxs4("div", { style: { display: "flex", alignItems: "center", gap: 6, marginBottom: 12 }, children: [
2023
2249
  availableTracks.length >= 2 && /* @__PURE__ */ jsxs4("div", { style: { display: "flex", gap: 4, flex: 1, overflow: "auto" }, children: [
2024
2250
  /* @__PURE__ */ jsx5(
@@ -2332,7 +2558,27 @@ function TestListScreen({ nav }) {
2332
2558
  ]
2333
2559
  }
2334
2560
  ),
2335
- /* @__PURE__ */ jsx5("div", { style: { display: "flex", justifyContent: "center", paddingTop: 12, paddingBottom: 8 }, children: /* @__PURE__ */ jsx5(
2561
+ dashboardUrl && /* @__PURE__ */ jsx5(
2562
+ "a",
2563
+ {
2564
+ href: `${dashboardUrl}/test-cases`,
2565
+ target: "_blank",
2566
+ rel: "noopener noreferrer",
2567
+ style: {
2568
+ display: "flex",
2569
+ alignItems: "center",
2570
+ justifyContent: "center",
2571
+ gap: 6,
2572
+ paddingTop: 12,
2573
+ fontSize: 13,
2574
+ fontWeight: 500,
2575
+ color: colors.blue,
2576
+ textDecoration: "none"
2577
+ },
2578
+ children: "\u{1F310} Manage on Dashboard \u2192"
2579
+ }
2580
+ ),
2581
+ /* @__PURE__ */ jsx5("div", { style: { display: "flex", justifyContent: "center", paddingTop: 8, paddingBottom: 8 }, children: /* @__PURE__ */ jsx5(
2336
2582
  "button",
2337
2583
  {
2338
2584
  type: "button",
@@ -3334,7 +3580,7 @@ var styles3 = {
3334
3580
  // src/widget/screens/MessageListScreen.tsx
3335
3581
  import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
3336
3582
  function MessageListScreen({ nav }) {
3337
- const { threads, unreadCount, refreshThreads, isLoading } = useBugBear();
3583
+ const { threads, unreadCount, refreshThreads, dashboardUrl, isLoading } = useBugBear();
3338
3584
  if (isLoading) return /* @__PURE__ */ jsx12(MessageListScreenSkeleton, {});
3339
3585
  return /* @__PURE__ */ jsxs11("div", { children: [
3340
3586
  /* @__PURE__ */ jsx12(
@@ -3523,6 +3769,26 @@ function MessageListScreen({ nav }) {
3523
3769
  },
3524
3770
  thread.id
3525
3771
  )) }),
3772
+ dashboardUrl && /* @__PURE__ */ jsx12(
3773
+ "a",
3774
+ {
3775
+ href: `${dashboardUrl}/discussions`,
3776
+ target: "_blank",
3777
+ rel: "noopener noreferrer",
3778
+ style: {
3779
+ display: "flex",
3780
+ alignItems: "center",
3781
+ justifyContent: "center",
3782
+ gap: 6,
3783
+ paddingTop: 12,
3784
+ fontSize: 13,
3785
+ fontWeight: 500,
3786
+ color: colors.blue,
3787
+ textDecoration: "none"
3788
+ },
3789
+ children: "\u{1F310} View on Dashboard \u2192"
3790
+ }
3791
+ ),
3526
3792
  /* @__PURE__ */ jsxs11(
3527
3793
  "div",
3528
3794
  {
@@ -3530,7 +3796,7 @@ function MessageListScreen({ nav }) {
3530
3796
  display: "flex",
3531
3797
  justifyContent: "space-between",
3532
3798
  alignItems: "center",
3533
- paddingTop: 12,
3799
+ paddingTop: 8,
3534
3800
  paddingLeft: 4,
3535
3801
  paddingRight: 4
3536
3802
  },
@@ -4654,6 +4920,7 @@ var SEVERITY_CONFIG = {
4654
4920
  low: { label: "Low", color: "#71717a", bg: "#27272a" }
4655
4921
  };
4656
4922
  function IssueDetailScreen({ nav, issue }) {
4923
+ const { dashboardUrl } = useBugBear();
4657
4924
  const statusConfig = STATUS_LABELS[issue.status] || { label: issue.status, bg: "#27272a", color: "#a1a1aa" };
4658
4925
  const severityConfig = issue.severity ? SEVERITY_CONFIG[issue.severity] : null;
4659
4926
  return /* @__PURE__ */ jsxs16("div", { children: [
@@ -4771,7 +5038,29 @@ function IssueDetailScreen({ nav, issue }) {
4771
5038
  " \xB7 Updated ",
4772
5039
  formatRelativeTime(issue.updatedAt)
4773
5040
  ] })
4774
- ] })
5041
+ ] }),
5042
+ dashboardUrl && /* @__PURE__ */ jsx17(
5043
+ "a",
5044
+ {
5045
+ href: `${dashboardUrl}/reports`,
5046
+ target: "_blank",
5047
+ rel: "noopener noreferrer",
5048
+ style: {
5049
+ display: "flex",
5050
+ alignItems: "center",
5051
+ justifyContent: "center",
5052
+ gap: 6,
5053
+ marginTop: 12,
5054
+ padding: "10px 0",
5055
+ fontSize: 13,
5056
+ fontWeight: 500,
5057
+ color: colors.blue,
5058
+ textDecoration: "none",
5059
+ borderTop: `1px solid ${colors.border}`
5060
+ },
5061
+ children: "\u{1F310} View on Dashboard \u2192"
5062
+ }
5063
+ )
4775
5064
  ] });
4776
5065
  }
4777
5066
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bbearai/react",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "BugBear React components for web apps",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",