@bbearai/react-native 0.5.7 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -42,14 +42,14 @@ module.exports = __toCommonJS(index_exports);
42
42
  var import_react = __toESM(require("react"));
43
43
 
44
44
  // ../../node_modules/tslib/tslib.es6.mjs
45
- function __rest(s, e) {
45
+ function __rest(s2, e) {
46
46
  var t = {};
47
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
48
- t[p] = s[p];
49
- if (s != null && typeof Object.getOwnPropertySymbols === "function")
50
- for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
51
- if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
52
- t[p[i]] = s[p[i]];
47
+ for (var p in s2) if (Object.prototype.hasOwnProperty.call(s2, p) && e.indexOf(p) < 0)
48
+ t[p] = s2[p];
49
+ if (s2 != null && typeof Object.getOwnPropertySymbols === "function")
50
+ for (var i = 0, p = Object.getOwnPropertySymbols(s2); i < p.length; i++) {
51
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s2, p[i]))
52
+ t[p[i]] = s2[p[i]];
53
53
  }
54
54
  return t;
55
55
  }
@@ -864,9 +864,9 @@ var PostgrestFilterBuilder = class extends PostgrestTransformBuilder {
864
864
  * @param values - The values array to filter with
865
865
  */
866
866
  in(column, values) {
867
- const cleanedValues = Array.from(new Set(values)).map((s) => {
868
- if (typeof s === "string" && PostgrestReservedCharsRegexp.test(s)) return `"${s}"`;
869
- else return `${s}`;
867
+ const cleanedValues = Array.from(new Set(values)).map((s2) => {
868
+ if (typeof s2 === "string" && PostgrestReservedCharsRegexp.test(s2)) return `"${s2}"`;
869
+ else return `${s2}`;
870
870
  }).join(",");
871
871
  this.url.searchParams.append(column, `in.(${cleanedValues})`);
872
872
  return this;
@@ -878,9 +878,9 @@ var PostgrestFilterBuilder = class extends PostgrestTransformBuilder {
878
878
  * @param values - The values array to filter with
879
879
  */
880
880
  notIn(column, values) {
881
- const cleanedValues = Array.from(new Set(values)).map((s) => {
882
- if (typeof s === "string" && PostgrestReservedCharsRegexp.test(s)) return `"${s}"`;
883
- else return `${s}`;
881
+ const cleanedValues = Array.from(new Set(values)).map((s2) => {
882
+ if (typeof s2 === "string" && PostgrestReservedCharsRegexp.test(s2)) return `"${s2}"`;
883
+ else return `${s2}`;
884
884
  }).join(",");
885
885
  this.url.searchParams.append(column, `not.in.(${cleanedValues})`);
886
886
  return this;
@@ -11635,31 +11635,306 @@ function captureError(error, errorInfo) {
11635
11635
  componentStack: errorInfo?.componentStack
11636
11636
  };
11637
11637
  }
11638
+ var LocalStorageAdapter = class {
11639
+ constructor() {
11640
+ this.fallback = /* @__PURE__ */ new Map();
11641
+ }
11642
+ get isAvailable() {
11643
+ try {
11644
+ const key = "__bugbear_test__";
11645
+ localStorage.setItem(key, "1");
11646
+ localStorage.removeItem(key);
11647
+ return true;
11648
+ } catch {
11649
+ return false;
11650
+ }
11651
+ }
11652
+ async getItem(key) {
11653
+ if (this.isAvailable) return localStorage.getItem(key);
11654
+ return this.fallback.get(key) ?? null;
11655
+ }
11656
+ async setItem(key, value) {
11657
+ if (this.isAvailable) {
11658
+ localStorage.setItem(key, value);
11659
+ } else {
11660
+ this.fallback.set(key, value);
11661
+ }
11662
+ }
11663
+ async removeItem(key) {
11664
+ if (this.isAvailable) {
11665
+ localStorage.removeItem(key);
11666
+ } else {
11667
+ this.fallback.delete(key);
11668
+ }
11669
+ }
11670
+ };
11671
+ var OfflineQueue = class {
11672
+ constructor(config) {
11673
+ this.items = [];
11674
+ this.storageKey = "bugbear_offline_queue";
11675
+ this.flushing = false;
11676
+ this.handlers = /* @__PURE__ */ new Map();
11677
+ this.maxItems = config.maxItems ?? 50;
11678
+ this.maxRetries = config.maxRetries ?? 5;
11679
+ this.storage = config.storage ?? new LocalStorageAdapter();
11680
+ }
11681
+ // ── Flush handler registration ──────────────────────────────
11682
+ /** Register a handler that replays a queued operation. */
11683
+ registerHandler(type, handler) {
11684
+ this.handlers.set(type, handler);
11685
+ }
11686
+ // ── Change listener ─────────────────────────────────────────
11687
+ /** Subscribe to queue count changes (for UI badges). */
11688
+ onChange(callback) {
11689
+ this.listener = callback;
11690
+ }
11691
+ notify() {
11692
+ this.listener?.(this.items.length);
11693
+ }
11694
+ // ── Persistence ─────────────────────────────────────────────
11695
+ /** Load queue from persistent storage. Call once after construction. */
11696
+ async load() {
11697
+ try {
11698
+ const raw = await this.storage.getItem(this.storageKey);
11699
+ if (raw) {
11700
+ this.items = JSON.parse(raw);
11701
+ }
11702
+ } catch {
11703
+ this.items = [];
11704
+ }
11705
+ this.notify();
11706
+ }
11707
+ async save() {
11708
+ try {
11709
+ await this.storage.setItem(this.storageKey, JSON.stringify(this.items));
11710
+ } catch (err) {
11711
+ console.error("BugBear: Failed to persist offline queue", err);
11712
+ }
11713
+ this.notify();
11714
+ }
11715
+ // ── Enqueue ─────────────────────────────────────────────────
11716
+ /** Add a failed operation to the queue. Returns the item ID. */
11717
+ async enqueue(type, payload) {
11718
+ if (this.items.length >= this.maxItems) {
11719
+ this.items.shift();
11720
+ }
11721
+ const id = `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
11722
+ this.items.push({ id, type, payload, createdAt: Date.now(), retries: 0 });
11723
+ await this.save();
11724
+ return id;
11725
+ }
11726
+ // ── Accessors ───────────────────────────────────────────────
11727
+ /** Number of items waiting to be flushed. */
11728
+ get count() {
11729
+ return this.items.length;
11730
+ }
11731
+ /** Read-only snapshot of pending items. */
11732
+ get pending() {
11733
+ return [...this.items];
11734
+ }
11735
+ /** Whether a flush is currently in progress. */
11736
+ get isFlushing() {
11737
+ return this.flushing;
11738
+ }
11739
+ // ── Flush ───────────────────────────────────────────────────
11740
+ /**
11741
+ * Process all queued items in FIFO order.
11742
+ * Stops early if a network error is encountered (still offline).
11743
+ */
11744
+ async flush() {
11745
+ if (this.flushing || this.items.length === 0) {
11746
+ return { flushed: 0, failed: 0 };
11747
+ }
11748
+ this.flushing = true;
11749
+ let flushed = 0;
11750
+ let failed = 0;
11751
+ const snapshot = [...this.items];
11752
+ for (const item of snapshot) {
11753
+ const handler = this.handlers.get(item.type);
11754
+ if (!handler) {
11755
+ failed++;
11756
+ continue;
11757
+ }
11758
+ try {
11759
+ const result = await handler(item.payload);
11760
+ if (result.success) {
11761
+ this.items = this.items.filter((i) => i.id !== item.id);
11762
+ flushed++;
11763
+ } else if (isNetworkError(result.error)) {
11764
+ break;
11765
+ } else {
11766
+ const idx = this.items.findIndex((i) => i.id === item.id);
11767
+ if (idx !== -1) {
11768
+ this.items[idx].retries++;
11769
+ if (this.items[idx].retries >= this.maxRetries) {
11770
+ this.items.splice(idx, 1);
11771
+ }
11772
+ }
11773
+ failed++;
11774
+ }
11775
+ } catch {
11776
+ break;
11777
+ }
11778
+ }
11779
+ await this.save();
11780
+ this.flushing = false;
11781
+ return { flushed, failed };
11782
+ }
11783
+ // ── Clear ───────────────────────────────────────────────────
11784
+ /** Drop all queued items. */
11785
+ async clear() {
11786
+ this.items = [];
11787
+ await this.save();
11788
+ }
11789
+ };
11790
+ function isNetworkError(error) {
11791
+ if (!error) return false;
11792
+ const msg = error.toLowerCase();
11793
+ return msg.includes("failed to fetch") || msg.includes("networkerror") || msg.includes("network request failed") || msg.includes("timeout") || msg.includes("econnrefused") || msg.includes("enotfound") || msg.includes("load failed") || // Safari
11794
+ msg.includes("the internet connection appears to be offline") || msg.includes("a]server with the specified hostname could not be found");
11795
+ }
11638
11796
  var formatPgError = (e) => {
11639
11797
  if (!e || typeof e !== "object") return { raw: e };
11640
11798
  const { message, code, details, hint } = e;
11641
11799
  return { message, code, details, hint };
11642
11800
  };
11643
- var DEFAULT_SUPABASE_URL = "https://kyxgzjnqgvapvlnvqawz.supabase.co";
11644
- var getEnvVar = (key) => {
11645
- try {
11646
- if (typeof process !== "undefined" && process.env) {
11647
- return process.env[key];
11648
- }
11649
- } catch {
11650
- }
11651
- return void 0;
11652
- };
11653
- var HOSTED_BUGBEAR_ANON_KEY = getEnvVar("BUGBEAR_ANON_KEY") || getEnvVar("NEXT_PUBLIC_BUGBEAR_ANON_KEY") || "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Imt5eGd6am5xZ3ZhcHZsbnZxYXd6Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjkyNjgwNDIsImV4cCI6MjA4NDg0NDA0Mn0.NUkAlCHLFjeRoisbmNUVoGb4R6uQ8xs5LAEIX1BWTwU";
11654
11801
  var BugBearClient = class {
11655
11802
  constructor(config) {
11656
11803
  this.navigationHistory = [];
11657
11804
  this.reportSubmitInFlight = false;
11805
+ this._queue = null;
11806
+ this.realtimeChannels = [];
11807
+ if (!config.supabaseUrl) {
11808
+ throw new Error("BugBear: supabaseUrl is required. Get it from your BugBear project settings.");
11809
+ }
11810
+ if (!config.supabaseAnonKey) {
11811
+ throw new Error("BugBear: supabaseAnonKey is required. Get it from your BugBear project settings.");
11812
+ }
11658
11813
  this.config = config;
11659
- this.supabase = createClient(
11660
- config.supabaseUrl || DEFAULT_SUPABASE_URL,
11661
- config.supabaseAnonKey || HOSTED_BUGBEAR_ANON_KEY
11662
- );
11814
+ this.supabase = createClient(config.supabaseUrl, config.supabaseAnonKey);
11815
+ if (config.offlineQueue?.enabled) {
11816
+ this._queue = new OfflineQueue({
11817
+ enabled: true,
11818
+ maxItems: config.offlineQueue.maxItems,
11819
+ maxRetries: config.offlineQueue.maxRetries
11820
+ });
11821
+ this.registerQueueHandlers();
11822
+ }
11823
+ }
11824
+ // ── Offline Queue ─────────────────────────────────────────
11825
+ /**
11826
+ * Access the offline queue (if enabled).
11827
+ * Use this to check queue.count, subscribe to changes, or trigger flush.
11828
+ */
11829
+ get queue() {
11830
+ return this._queue;
11831
+ }
11832
+ /**
11833
+ * Initialize the offline queue with a platform-specific storage adapter.
11834
+ * Must be called after construction for React Native (which supplies AsyncStorage).
11835
+ * Web callers can skip this — LocalStorageAdapter is the default.
11836
+ */
11837
+ async initQueue(storage) {
11838
+ if (!this._queue) return;
11839
+ if (storage) {
11840
+ this._queue = new OfflineQueue({
11841
+ enabled: true,
11842
+ maxItems: this.config.offlineQueue?.maxItems,
11843
+ maxRetries: this.config.offlineQueue?.maxRetries,
11844
+ storage
11845
+ });
11846
+ this.registerQueueHandlers();
11847
+ }
11848
+ await this._queue.load();
11849
+ }
11850
+ registerQueueHandlers() {
11851
+ if (!this._queue) return;
11852
+ this._queue.registerHandler("report", async (payload) => {
11853
+ const { error } = await this.supabase.from("reports").insert(payload).select("id").single();
11854
+ if (error) return { success: false, error: error.message };
11855
+ return { success: true };
11856
+ });
11857
+ this._queue.registerHandler("message", async (payload) => {
11858
+ const { error } = await this.supabase.from("discussion_messages").insert(payload);
11859
+ if (error) return { success: false, error: error.message };
11860
+ return { success: true };
11861
+ });
11862
+ this._queue.registerHandler("feedback", async (payload) => {
11863
+ const { error } = await this.supabase.from("test_feedback").insert(payload);
11864
+ if (error) return { success: false, error: error.message };
11865
+ return { success: true };
11866
+ });
11867
+ }
11868
+ // ── Realtime Subscriptions ─────────────────────────────────
11869
+ /** Whether realtime is enabled in config. */
11870
+ get realtimeEnabled() {
11871
+ return !!this.config.realtime?.enabled;
11872
+ }
11873
+ /**
11874
+ * Subscribe to postgres_changes on relevant tables.
11875
+ * Each callback fires when the corresponding table has changes —
11876
+ * the provider should call its refresh function in response.
11877
+ * Returns a cleanup function that unsubscribes all channels.
11878
+ */
11879
+ subscribeToChanges(callbacks) {
11880
+ this.unsubscribeAll();
11881
+ const projectId = this.config.projectId;
11882
+ const debounce = (fn, ms = 500) => {
11883
+ let timer;
11884
+ return () => {
11885
+ clearTimeout(timer);
11886
+ timer = setTimeout(fn, ms);
11887
+ };
11888
+ };
11889
+ if (callbacks.onAssignmentChange) {
11890
+ const debouncedCb = debounce(callbacks.onAssignmentChange);
11891
+ const channel = this.supabase.channel("bugbear-assignments").on("postgres_changes", {
11892
+ event: "*",
11893
+ schema: "public",
11894
+ table: "test_assignments",
11895
+ filter: `project_id=eq.${projectId}`
11896
+ }, debouncedCb).subscribe((status) => {
11897
+ if (status === "CHANNEL_ERROR") {
11898
+ console.warn("BugBear: Realtime subscription failed for test_assignments");
11899
+ }
11900
+ });
11901
+ this.realtimeChannels.push(channel);
11902
+ }
11903
+ if (callbacks.onMessageChange) {
11904
+ const debouncedCb = debounce(callbacks.onMessageChange);
11905
+ const channel = this.supabase.channel("bugbear-messages").on("postgres_changes", {
11906
+ event: "INSERT",
11907
+ schema: "public",
11908
+ table: "discussion_messages"
11909
+ }, debouncedCb).subscribe((status) => {
11910
+ if (status === "CHANNEL_ERROR") {
11911
+ console.warn("BugBear: Realtime subscription failed for discussion_messages");
11912
+ }
11913
+ });
11914
+ this.realtimeChannels.push(channel);
11915
+ }
11916
+ if (callbacks.onReportChange) {
11917
+ const debouncedCb = debounce(callbacks.onReportChange);
11918
+ const channel = this.supabase.channel("bugbear-reports").on("postgres_changes", {
11919
+ event: "UPDATE",
11920
+ schema: "public",
11921
+ table: "reports",
11922
+ filter: `project_id=eq.${projectId}`
11923
+ }, debouncedCb).subscribe((status) => {
11924
+ if (status === "CHANNEL_ERROR") {
11925
+ console.warn("BugBear: Realtime subscription failed for reports");
11926
+ }
11927
+ });
11928
+ this.realtimeChannels.push(channel);
11929
+ }
11930
+ return () => this.unsubscribeAll();
11931
+ }
11932
+ /** Remove all active Realtime channels. */
11933
+ unsubscribeAll() {
11934
+ for (const channel of this.realtimeChannels) {
11935
+ this.supabase.removeChannel(channel);
11936
+ }
11937
+ this.realtimeChannels = [];
11663
11938
  }
11664
11939
  /**
11665
11940
  * Track navigation for context.
@@ -11719,6 +11994,7 @@ var BugBearClient = class {
11719
11994
  return { success: false, error: "A report is already being submitted" };
11720
11995
  }
11721
11996
  this.reportSubmitInFlight = true;
11997
+ let fullReport;
11722
11998
  try {
11723
11999
  const validationError = this.validateReport(report);
11724
12000
  if (validationError) {
@@ -11735,7 +12011,7 @@ var BugBearClient = class {
11735
12011
  return { success: false, error: "User not authenticated" };
11736
12012
  }
11737
12013
  const testerInfo = await this.getTesterInfo();
11738
- const fullReport = {
12014
+ fullReport = {
11739
12015
  project_id: this.config.projectId,
11740
12016
  reporter_id: userInfo.id,
11741
12017
  // User ID from host app (required)
@@ -11762,6 +12038,10 @@ var BugBearClient = class {
11762
12038
  };
11763
12039
  const { data, error } = await this.supabase.from("reports").insert(fullReport).select("id").single();
11764
12040
  if (error) {
12041
+ if (this._queue && isNetworkError(error.message)) {
12042
+ await this._queue.enqueue("report", fullReport);
12043
+ return { success: false, queued: true, error: "Queued \u2014 will send when online" };
12044
+ }
11765
12045
  console.error("BugBear: Failed to submit report", error.message);
11766
12046
  return { success: false, error: error.message };
11767
12047
  }
@@ -11771,6 +12051,10 @@ var BugBearClient = class {
11771
12051
  return { success: true, reportId: data.id };
11772
12052
  } catch (err) {
11773
12053
  const message = err instanceof Error ? err.message : "Unknown error";
12054
+ if (this._queue && fullReport && isNetworkError(message)) {
12055
+ await this._queue.enqueue("report", fullReport);
12056
+ return { success: false, queued: true, error: "Queued \u2014 will send when online" };
12057
+ }
11774
12058
  return { success: false, error: message };
11775
12059
  } finally {
11776
12060
  this.reportSubmitInFlight = false;
@@ -11780,14 +12064,18 @@ var BugBearClient = class {
11780
12064
  * Get assigned tests for current user
11781
12065
  * First looks up the tester by email, then fetches their assignments
11782
12066
  */
11783
- async getAssignedTests() {
12067
+ async getAssignedTests(options) {
11784
12068
  try {
11785
12069
  const testerInfo = await this.getTesterInfo();
11786
12070
  if (!testerInfo) return [];
11787
- const { data, error } = await this.supabase.from("test_assignments").select(`
12071
+ const pageSize = Math.min(options?.pageSize ?? 100, 100);
12072
+ const from = (options?.page ?? 0) * pageSize;
12073
+ const to = from + pageSize - 1;
12074
+ const selectFields = `
11788
12075
  id,
11789
12076
  status,
11790
12077
  started_at,
12078
+ completed_at,
11791
12079
  skip_reason,
11792
12080
  is_verification,
11793
12081
  original_report_id,
@@ -11822,20 +12110,24 @@ var BugBearClient = class {
11822
12110
  color,
11823
12111
  description,
11824
12112
  login_hint
11825
- )
12113
+ ),
12114
+ platforms
11826
12115
  )
11827
- `).eq("project_id", this.config.projectId).eq("tester_id", testerInfo.id).in("status", ["pending", "in_progress"]).order("created_at", { ascending: true }).limit(100);
11828
- if (error) {
11829
- console.error("BugBear: Failed to fetch assignments", formatPgError(error));
12116
+ `;
12117
+ const twentyFourHoursAgo = new Date(Date.now() - 24 * 60 * 60 * 1e3).toISOString();
12118
+ const [pendingResult, completedResult] = await Promise.all([
12119
+ this.supabase.from("test_assignments").select(selectFields).eq("project_id", this.config.projectId).eq("tester_id", testerInfo.id).in("status", ["pending", "in_progress"]).order("created_at", { ascending: true }).range(from, to),
12120
+ this.supabase.from("test_assignments").select(selectFields).eq("project_id", this.config.projectId).eq("tester_id", testerInfo.id).in("status", ["passed", "failed", "skipped", "blocked"]).gte("completed_at", twentyFourHoursAgo).order("completed_at", { ascending: false }).limit(50)
12121
+ ]);
12122
+ if (pendingResult.error) {
12123
+ console.error("BugBear: Failed to fetch assignments", formatPgError(pendingResult.error));
11830
12124
  return [];
11831
12125
  }
11832
- const mapped = (data || []).filter((item) => {
11833
- if (!item.test_case) {
11834
- console.warn("BugBear: Assignment returned without test_case", { id: item.id });
11835
- return false;
11836
- }
11837
- return true;
11838
- }).map((item) => ({
12126
+ const allData = [
12127
+ ...pendingResult.data || [],
12128
+ ...completedResult.data || []
12129
+ ];
12130
+ const mapItem = (item) => ({
11839
12131
  id: item.id,
11840
12132
  status: item.status,
11841
12133
  startedAt: item.started_at,
@@ -11873,12 +12165,24 @@ var BugBearClient = class {
11873
12165
  color: item.test_case.role.color,
11874
12166
  description: item.test_case.role.description,
11875
12167
  loginHint: item.test_case.role.login_hint
11876
- } : void 0
12168
+ } : void 0,
12169
+ platforms: item.test_case.platforms || void 0
11877
12170
  }
11878
- }));
12171
+ });
12172
+ const mapped = allData.filter((item) => {
12173
+ if (!item.test_case) {
12174
+ console.warn("BugBear: Assignment returned without test_case", { id: item.id });
12175
+ return false;
12176
+ }
12177
+ return true;
12178
+ }).map(mapItem);
11879
12179
  mapped.sort((a, b) => {
11880
12180
  if (a.isVerification && !b.isVerification) return -1;
11881
12181
  if (!a.isVerification && b.isVerification) return 1;
12182
+ const aActive = a.status === "pending" || a.status === "in_progress";
12183
+ const bActive = b.status === "pending" || b.status === "in_progress";
12184
+ if (aActive && !bActive) return -1;
12185
+ if (!aActive && bActive) return 1;
11882
12186
  return 0;
11883
12187
  });
11884
12188
  return mapped;
@@ -12043,6 +12347,36 @@ var BugBearClient = class {
12043
12347
  async failAssignment(assignmentId) {
12044
12348
  return this.updateAssignmentStatus(assignmentId, "failed");
12045
12349
  }
12350
+ /**
12351
+ * Reopen a completed assignment — sets it back to in_progress with a fresh timer.
12352
+ * Clears completed_at and duration_seconds so it can be re-evaluated.
12353
+ */
12354
+ async reopenAssignment(assignmentId) {
12355
+ try {
12356
+ const { data: current, error: fetchError } = await this.supabase.from("test_assignments").select("status").eq("id", assignmentId).single();
12357
+ if (fetchError || !current) {
12358
+ return { success: false, error: "Assignment not found" };
12359
+ }
12360
+ if (current.status === "pending" || current.status === "in_progress") {
12361
+ return { success: true };
12362
+ }
12363
+ const { error } = await this.supabase.from("test_assignments").update({
12364
+ status: "in_progress",
12365
+ started_at: (/* @__PURE__ */ new Date()).toISOString(),
12366
+ completed_at: null,
12367
+ duration_seconds: null,
12368
+ skip_reason: null
12369
+ }).eq("id", assignmentId).eq("status", current.status);
12370
+ if (error) {
12371
+ console.error("BugBear: Failed to reopen assignment", error);
12372
+ return { success: false, error: error.message };
12373
+ }
12374
+ return { success: true };
12375
+ } catch (err) {
12376
+ const message = err instanceof Error ? err.message : "Unknown error";
12377
+ return { success: false, error: message };
12378
+ }
12379
+ }
12046
12380
  /**
12047
12381
  * Skip a test assignment with a required reason
12048
12382
  * Marks the assignment as 'skipped' and records why it was skipped
@@ -12083,6 +12417,7 @@ var BugBearClient = class {
12083
12417
  * This empowers testers to shape better tests over time
12084
12418
  */
12085
12419
  async submitTestFeedback(options) {
12420
+ let feedbackPayload;
12086
12421
  try {
12087
12422
  const testerInfo = await this.getTesterInfo();
12088
12423
  if (!testerInfo) {
@@ -12102,7 +12437,7 @@ var BugBearClient = class {
12102
12437
  return { success: false, error: `${name} must be between 1 and 5` };
12103
12438
  }
12104
12439
  }
12105
- const { error: feedbackError } = await this.supabase.from("test_feedback").insert({
12440
+ feedbackPayload = {
12106
12441
  project_id: this.config.projectId,
12107
12442
  test_case_id: testCaseId,
12108
12443
  assignment_id: assignmentId || null,
@@ -12120,8 +12455,13 @@ var BugBearClient = class {
12120
12455
  platform: this.getDeviceInfo().platform,
12121
12456
  time_to_complete_seconds: timeToCompleteSeconds || null,
12122
12457
  screenshot_urls: screenshotUrls || []
12123
- });
12458
+ };
12459
+ const { error: feedbackError } = await this.supabase.from("test_feedback").insert(feedbackPayload);
12124
12460
  if (feedbackError) {
12461
+ if (this._queue && isNetworkError(feedbackError.message)) {
12462
+ await this._queue.enqueue("feedback", feedbackPayload);
12463
+ return { success: false, queued: true, error: "Queued \u2014 will send when online" };
12464
+ }
12125
12465
  console.error("BugBear: Failed to submit feedback", feedbackError);
12126
12466
  return { success: false, error: feedbackError.message };
12127
12467
  }
@@ -12138,6 +12478,10 @@ var BugBearClient = class {
12138
12478
  return { success: true };
12139
12479
  } catch (err) {
12140
12480
  const message = err instanceof Error ? err.message : "Unknown error";
12481
+ if (this._queue && feedbackPayload && isNetworkError(message)) {
12482
+ await this._queue.enqueue("feedback", feedbackPayload);
12483
+ return { success: false, queued: true, error: "Queued \u2014 will send when online" };
12484
+ }
12141
12485
  console.error("BugBear: Error submitting feedback", err);
12142
12486
  return { success: false, error: message };
12143
12487
  }
@@ -12772,6 +13116,7 @@ var BugBearClient = class {
12772
13116
  * Send a message to a thread
12773
13117
  */
12774
13118
  async sendMessage(threadId, content, attachments) {
13119
+ let insertData;
12775
13120
  try {
12776
13121
  const testerInfo = await this.getTesterInfo();
12777
13122
  if (!testerInfo) {
@@ -12783,7 +13128,7 @@ var BugBearClient = class {
12783
13128
  console.error("BugBear: Rate limit exceeded for messages");
12784
13129
  return false;
12785
13130
  }
12786
- const insertData = {
13131
+ insertData = {
12787
13132
  thread_id: threadId,
12788
13133
  sender_type: "tester",
12789
13134
  sender_tester_id: testerInfo.id,
@@ -12798,12 +13143,21 @@ var BugBearClient = class {
12798
13143
  }
12799
13144
  const { error } = await this.supabase.from("discussion_messages").insert(insertData);
12800
13145
  if (error) {
13146
+ if (this._queue && isNetworkError(error.message)) {
13147
+ await this._queue.enqueue("message", insertData);
13148
+ return false;
13149
+ }
12801
13150
  console.error("BugBear: Failed to send message", formatPgError(error));
12802
13151
  return false;
12803
13152
  }
12804
13153
  await this.markThreadAsRead(threadId);
12805
13154
  return true;
12806
13155
  } catch (err) {
13156
+ const message = err instanceof Error ? err.message : "Unknown error";
13157
+ if (this._queue && insertData && isNetworkError(message)) {
13158
+ await this._queue.enqueue("message", insertData);
13159
+ return false;
13160
+ }
12807
13161
  console.error("BugBear: Error sending message", err);
12808
13162
  return false;
12809
13163
  }
@@ -12990,7 +13344,7 @@ var BugBearClient = class {
12990
13344
  console.error("BugBear: Failed to fetch session history", formatPgError(error));
12991
13345
  return [];
12992
13346
  }
12993
- return (data || []).map((s) => this.transformSession(s));
13347
+ return (data || []).map((s2) => this.transformSession(s2));
12994
13348
  } catch (err) {
12995
13349
  console.error("BugBear: Error fetching session history", err);
12996
13350
  return [];
@@ -13179,6 +13533,7 @@ var BugBearContext = (0, import_react.createContext)({
13179
13533
  issueCounts: { open: 0, done: 0, reopened: 0 },
13180
13534
  refreshIssueCounts: async () => {
13181
13535
  },
13536
+ queuedCount: 0,
13182
13537
  dashboardUrl: void 0,
13183
13538
  onError: void 0
13184
13539
  });
@@ -13195,6 +13550,7 @@ function BugBearProvider({ config, children, appVersion, enabled = true }) {
13195
13550
  const [threads, setThreads] = (0, import_react.useState)([]);
13196
13551
  const [unreadCount, setUnreadCount] = (0, import_react.useState)(0);
13197
13552
  const [issueCounts, setIssueCounts] = (0, import_react.useState)({ open: 0, done: 0, reopened: 0 });
13553
+ const [queuedCount, setQueuedCount] = (0, import_react.useState)(0);
13198
13554
  const [activeSession, setActiveSession] = (0, import_react.useState)(null);
13199
13555
  const [sessionFindings, setSessionFindings] = (0, import_react.useState)([]);
13200
13556
  const hasInitialized = (0, import_react.useRef)(false);
@@ -13354,18 +13710,46 @@ function BugBearProvider({ config, children, appVersion, enabled = true }) {
13354
13710
  hasInitialized.current = true;
13355
13711
  contextCapture.startCapture();
13356
13712
  const newClient = createBugBear(config);
13713
+ if (newClient.queue) {
13714
+ newClient.queue.onChange(setQueuedCount);
13715
+ newClient.initQueue();
13716
+ }
13357
13717
  setClient(newClient);
13358
13718
  initializeBugBear(newClient);
13359
13719
  }
13360
13720
  }, [enabled, config, initializeBugBear]);
13721
+ (0, import_react.useEffect)(() => {
13722
+ if (!client?.queue) return;
13723
+ const subscription = import_react_native.AppState.addEventListener("change", (state) => {
13724
+ if (state === "active" && client.queue && client.queue.count > 0) {
13725
+ client.queue.flush();
13726
+ }
13727
+ });
13728
+ if (client.queue.count > 0) {
13729
+ client.queue.flush();
13730
+ }
13731
+ return () => subscription.remove();
13732
+ }, [client]);
13361
13733
  (0, import_react.useEffect)(() => {
13362
13734
  if (!client || !isTester || !isQAEnabled) return;
13735
+ let unsubscribe;
13736
+ if (client.realtimeEnabled) {
13737
+ unsubscribe = client.subscribeToChanges({
13738
+ onAssignmentChange: refreshAssignments,
13739
+ onMessageChange: refreshThreads,
13740
+ onReportChange: refreshIssueCounts
13741
+ });
13742
+ }
13743
+ const pollInterval = client.realtimeEnabled ? 12e4 : 3e4;
13363
13744
  const interval = setInterval(() => {
13364
13745
  refreshThreads();
13365
13746
  refreshIssueCounts();
13366
- }, 3e4);
13367
- return () => clearInterval(interval);
13368
- }, [client, isTester, isQAEnabled, refreshThreads, refreshIssueCounts]);
13747
+ }, pollInterval);
13748
+ return () => {
13749
+ clearInterval(interval);
13750
+ unsubscribe?.();
13751
+ };
13752
+ }, [client, isTester, isQAEnabled, refreshThreads, refreshIssueCounts, refreshAssignments]);
13369
13753
  const currentAssignment = assignments.find(
13370
13754
  (a) => a.status === "in_progress"
13371
13755
  ) || assignments.find(
@@ -13408,6 +13792,7 @@ function BugBearProvider({ config, children, appVersion, enabled = true }) {
13408
13792
  // Issue tracking
13409
13793
  issueCounts,
13410
13794
  refreshIssueCounts,
13795
+ queuedCount,
13411
13796
  dashboardUrl: config.dashboardUrl,
13412
13797
  onError: config.onError
13413
13798
  }
@@ -13417,8 +13802,8 @@ function BugBearProvider({ config, children, appVersion, enabled = true }) {
13417
13802
  }
13418
13803
 
13419
13804
  // src/BugBearButton.tsx
13420
- var import_react19 = __toESM(require("react"));
13421
- var import_react_native18 = require("react-native");
13805
+ var import_react20 = __toESM(require("react"));
13806
+ var import_react_native19 = require("react-native");
13422
13807
 
13423
13808
  // src/widget/logo.ts
13424
13809
  var BUGBEAR_LOGO_BASE64 = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAYAAADimHc4AAAAAXNSR0IArs4c6QAAAJhlWElmTU0AKgAAAAgABAEaAAUAAAABAAAAPgEbAAUAAAABAAAARgEoAAMAAAABAAIAAIdpAAQAAAABAAAATgAAAAAAAABIAAAAAQAAAEgAAAABAASQBAACAAAAFAAAAISgAQADAAAAAQABAACgAgAEAAAAAQAAAGCgAwAEAAAAAQAAAGAAAAAAMjAyNjowMToyNCAxNjoyMTozOABbbVCuAAAACXBIWXMAAAsTAAALEwEAmpwYAAACo2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNi4wLjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczpJcHRjNHhtcEV4dD0iaHR0cDovL2lwdGMub3JnL3N0ZC9JcHRjNHhtcEV4dC8yMDA4LTAyLTI5LyIKICAgICAgICAgICAgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIj4KICAgICAgICAgPElwdGM0eG1wRXh0OkRpZ2l0YWxTb3VyY2VUeXBlPmh0dHA6Ly9jdi5pcHRjLm9yZy9uZXdzY29kZXMvZGlnaXRhbHNvdXJjZXR5cGUvdHJhaW5lZEFsZ29yaXRobWljTWVkaWE8L0lwdGM0eG1wRXh0OkRpZ2l0YWxTb3VyY2VUeXBlPgogICAgICAgICA8SXB0YzR4bXBFeHQ6RGlnSW1hZ2VHVUlEPmZjNzJlN2Q2LTYyYTEtNDE1ZS04MjY5LWM2NjA4MjY0OWRiMDwvSXB0YzR4bXBFeHQ6RGlnSW1hZ2VHVUlEPgogICAgICAgICA8eG1wOkNyZWF0ZURhdGU+MjAyNi0wMS0yNFQxNjoyMTozODwveG1wOkNyZWF0ZURhdGU+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICA8L3JkZjpSREY+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";
@@ -13562,9 +13947,9 @@ var shared = import_react_native2.StyleSheet.create({
13562
13947
  function formatElapsedTime(seconds) {
13563
13948
  const h = Math.floor(seconds / 3600);
13564
13949
  const m = Math.floor(seconds % 3600 / 60);
13565
- const s = seconds % 60;
13566
- if (h > 0) return `${h}:${m.toString().padStart(2, "0")}:${s.toString().padStart(2, "0")}`;
13567
- return `${m}:${s.toString().padStart(2, "0")}`;
13950
+ const s2 = seconds % 60;
13951
+ if (h > 0) return `${h}:${m.toString().padStart(2, "0")}:${s2.toString().padStart(2, "0")}`;
13952
+ return `${m}:${s2.toString().padStart(2, "0")}`;
13568
13953
  }
13569
13954
  function formatRelativeTime(dateString) {
13570
13955
  const date = new Date(dateString);
@@ -13636,11 +14021,90 @@ var templateInfo = {
13636
14021
  };
13637
14022
 
13638
14023
  // src/widget/screens/HomeScreen.tsx
14024
+ var import_react4 = __toESM(require("react"));
14025
+ var import_react_native4 = require("react-native");
14026
+
14027
+ // src/widget/Skeleton.tsx
13639
14028
  var import_react3 = __toESM(require("react"));
13640
14029
  var import_react_native3 = require("react-native");
13641
- function HomeScreen({ nav }) {
13642
- const { assignments, unreadCount, threads, refreshAssignments, refreshThreads, issueCounts, refreshIssueCounts, dashboardUrl } = useBugBear();
14030
+ function usePulse(delay = 0) {
14031
+ const opacity = (0, import_react3.useRef)(new import_react_native3.Animated.Value(0.6)).current;
13643
14032
  (0, import_react3.useEffect)(() => {
14033
+ const timeout = setTimeout(() => {
14034
+ import_react_native3.Animated.loop(
14035
+ import_react_native3.Animated.sequence([
14036
+ import_react_native3.Animated.timing(opacity, { toValue: 0.25, duration: 750, useNativeDriver: true }),
14037
+ import_react_native3.Animated.timing(opacity, { toValue: 0.6, duration: 750, useNativeDriver: true })
14038
+ ])
14039
+ ).start();
14040
+ }, delay);
14041
+ return () => clearTimeout(timeout);
14042
+ }, [opacity, delay]);
14043
+ return opacity;
14044
+ }
14045
+ function Bar({ width = "100%", height = 12, radius = 6, delay = 0 }) {
14046
+ const opacity = usePulse(delay);
14047
+ return /* @__PURE__ */ import_react3.default.createElement(
14048
+ import_react_native3.Animated.View,
14049
+ {
14050
+ style: {
14051
+ width,
14052
+ height,
14053
+ borderRadius: radius,
14054
+ backgroundColor: colors.border,
14055
+ opacity
14056
+ }
14057
+ }
14058
+ );
14059
+ }
14060
+ function Circle({ size = 20, delay = 0 }) {
14061
+ const opacity = usePulse(delay);
14062
+ return /* @__PURE__ */ import_react3.default.createElement(
14063
+ import_react_native3.Animated.View,
14064
+ {
14065
+ style: {
14066
+ width: size,
14067
+ height: size,
14068
+ borderRadius: size / 2,
14069
+ backgroundColor: colors.border,
14070
+ opacity
14071
+ }
14072
+ }
14073
+ );
14074
+ }
14075
+ function HomeScreenSkeleton() {
14076
+ return /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, null, /* @__PURE__ */ import_react3.default.createElement(Bar, { width: "100%", height: 100, radius: 16 }), /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: { height: 20 } }), /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: s.actionGrid }, [0, 1, 2, 3].map((i) => /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { key: i, style: s.actionCard }, /* @__PURE__ */ import_react3.default.createElement(Circle, { size: 28, delay: i * 80 }), /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: { height: 8 } }), /* @__PURE__ */ import_react3.default.createElement(Bar, { width: 60, height: 10, delay: i * 80 })))), /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: s.issueGrid }, [0, 1, 2].map((i) => /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { key: i, style: s.issueCard }, /* @__PURE__ */ import_react3.default.createElement(Bar, { width: 30, height: 18, delay: i * 100 }), /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: { height: 6 } }), /* @__PURE__ */ import_react3.default.createElement(Bar, { width: 40, height: 8, delay: i * 100 })))), /* @__PURE__ */ import_react3.default.createElement(Bar, { width: "100%", height: 6, radius: 3 }), /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: { height: 8 } }), /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: { alignItems: "center" } }, /* @__PURE__ */ import_react3.default.createElement(Bar, { width: 120, height: 10 })));
14077
+ }
14078
+ function TestItemSkeleton({ delay = 0 }) {
14079
+ return /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: s.testItem }, /* @__PURE__ */ import_react3.default.createElement(Circle, { size: 18, delay }), /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: { flex: 1, marginLeft: 10 } }, /* @__PURE__ */ import_react3.default.createElement(Bar, { width: "70%", height: 11, delay }), /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: { height: 4 } }), /* @__PURE__ */ import_react3.default.createElement(Bar, { width: "45%", height: 8, delay })), /* @__PURE__ */ import_react3.default.createElement(Bar, { width: 50, height: 18, radius: 6, delay }));
14080
+ }
14081
+ function TestListScreenSkeleton() {
14082
+ return /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, null, /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: s.filterRow }, [55, 50, 50, 70].map((w, i) => /* @__PURE__ */ import_react3.default.createElement(Bar, { key: i, width: w, height: 28, radius: 8, delay: i * 50 }))), /* @__PURE__ */ import_react3.default.createElement(Bar, { width: "100%", height: 36, radius: 8 }), /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: { height: 10 } }), [0, 1].map((g) => /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { key: g, style: { marginBottom: 12 } }, /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: s.folderHeader }, /* @__PURE__ */ import_react3.default.createElement(Bar, { width: 12, height: 10 }), /* @__PURE__ */ import_react3.default.createElement(Bar, { width: "40%", height: 12 }), /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: { flex: 1 } }), /* @__PURE__ */ import_react3.default.createElement(Bar, { width: 40, height: 4, radius: 2 }), /* @__PURE__ */ import_react3.default.createElement(Bar, { width: 24, height: 10 })), [0, 1, 2].map((i) => /* @__PURE__ */ import_react3.default.createElement(TestItemSkeleton, { key: i, delay: (g * 3 + i) * 80 })))));
14083
+ }
14084
+ function IssueListScreenSkeleton() {
14085
+ return /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, null, [0, 1, 2, 3].map((i) => /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { key: i, style: s.issueRow }, /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: s.issueRowTop }, /* @__PURE__ */ import_react3.default.createElement(Circle, { size: 8, delay: i * 100 }), /* @__PURE__ */ import_react3.default.createElement(Bar, { width: "65%", height: 11, delay: i * 100 })), /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: s.issueRowBottom }, /* @__PURE__ */ import_react3.default.createElement(Bar, { width: "40%", height: 8, delay: i * 100 }), /* @__PURE__ */ import_react3.default.createElement(Bar, { width: 40, height: 8, delay: i * 100 })))));
14086
+ }
14087
+ function MessageListScreenSkeleton() {
14088
+ return /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, null, /* @__PURE__ */ import_react3.default.createElement(Bar, { width: "100%", height: 44, radius: 12 }), /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: { height: 16 } }), [0, 1, 2, 3].map((i) => /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { key: i, style: s.threadRow }, /* @__PURE__ */ import_react3.default.createElement(Circle, { size: 20, delay: i * 100 }), /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: { flex: 1, marginLeft: 10 } }, /* @__PURE__ */ import_react3.default.createElement(Bar, { width: "55%", height: 11, delay: i * 100 }), /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: { height: 5 } }), /* @__PURE__ */ import_react3.default.createElement(Bar, { width: "80%", height: 9, delay: i * 100 })), /* @__PURE__ */ import_react3.default.createElement(Bar, { width: 30, height: 8, delay: i * 100 }))));
14089
+ }
14090
+ var s = import_react_native3.StyleSheet.create({
14091
+ actionGrid: { flexDirection: "row", flexWrap: "wrap", gap: 12, marginBottom: 20 },
14092
+ actionCard: { width: "47%", backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border, borderRadius: 12, padding: 16, alignItems: "center" },
14093
+ issueGrid: { flexDirection: "row", gap: 10, marginBottom: 20 },
14094
+ issueCard: { flex: 1, backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border, borderRadius: 10, paddingVertical: 12, paddingHorizontal: 8, alignItems: "center" },
14095
+ filterRow: { flexDirection: "row", gap: 8, marginBottom: 8 },
14096
+ folderHeader: { flexDirection: "row", alignItems: "center", gap: 8, paddingVertical: 8, paddingHorizontal: 4 },
14097
+ testItem: { flexDirection: "row", alignItems: "center", paddingVertical: 10, paddingHorizontal: 12, borderRadius: 8, marginBottom: 4, backgroundColor: colors.card },
14098
+ issueRow: { backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border, borderRadius: 10, padding: 14, marginBottom: 8 },
14099
+ issueRowTop: { flexDirection: "row", alignItems: "center", gap: 8 },
14100
+ issueRowBottom: { flexDirection: "row", justifyContent: "space-between", marginTop: 8 },
14101
+ threadRow: { flexDirection: "row", alignItems: "flex-start", padding: 12, borderRadius: 10, marginBottom: 4, backgroundColor: colors.card }
14102
+ });
14103
+
14104
+ // src/widget/screens/HomeScreen.tsx
14105
+ function HomeScreen({ nav }) {
14106
+ const { assignments, unreadCount, threads, refreshAssignments, refreshThreads, issueCounts, refreshIssueCounts, dashboardUrl, isLoading } = useBugBear();
14107
+ (0, import_react4.useEffect)(() => {
13644
14108
  refreshAssignments();
13645
14109
  refreshThreads();
13646
14110
  refreshIssueCounts();
@@ -13650,104 +14114,105 @@ function HomeScreen({ nav }) {
13650
14114
  const retestCount = pendingAssignments.filter((a) => a.isVerification).length;
13651
14115
  const completedCount = assignments.filter((a) => a.status === "passed" || a.status === "failed").length;
13652
14116
  const totalTests = assignments.length;
13653
- return /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, null, pendingCount > 0 ? /* @__PURE__ */ import_react3.default.createElement(
13654
- import_react_native3.TouchableOpacity,
14117
+ if (isLoading) return /* @__PURE__ */ import_react4.default.createElement(HomeScreenSkeleton, null);
14118
+ return /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, null, pendingCount > 0 ? /* @__PURE__ */ import_react4.default.createElement(
14119
+ import_react_native4.TouchableOpacity,
13655
14120
  {
13656
14121
  style: [styles.heroBanner, styles.heroBannerTests],
13657
14122
  onPress: () => nav.push({ name: "TEST_DETAIL" }),
13658
14123
  activeOpacity: 0.8
13659
14124
  },
13660
- /* @__PURE__ */ import_react3.default.createElement(import_react_native3.Text, { style: styles.heroCount }, pendingCount),
13661
- /* @__PURE__ */ import_react3.default.createElement(import_react_native3.Text, { style: styles.heroLabel }, "test", pendingCount !== 1 ? "s" : "", " waiting"),
13662
- retestCount > 0 && /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: styles.retestPill }, /* @__PURE__ */ import_react3.default.createElement(import_react_native3.Text, { style: styles.retestPillText }, "\u{1F504} ", retestCount, " retest", retestCount !== 1 ? "s" : "")),
13663
- /* @__PURE__ */ import_react3.default.createElement(import_react_native3.Text, { style: styles.heroAction }, "Start Testing \u2192")
13664
- ) : unreadCount > 0 ? /* @__PURE__ */ import_react3.default.createElement(
13665
- import_react_native3.TouchableOpacity,
14125
+ /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.heroCount }, pendingCount),
14126
+ /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.heroLabel }, "test", pendingCount !== 1 ? "s" : "", " waiting"),
14127
+ retestCount > 0 && /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: styles.retestPill }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.retestPillText }, "\u{1F504} ", retestCount, " retest", retestCount !== 1 ? "s" : "")),
14128
+ /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.heroAction }, "Start Testing \u2192")
14129
+ ) : unreadCount > 0 ? /* @__PURE__ */ import_react4.default.createElement(
14130
+ import_react_native4.TouchableOpacity,
13666
14131
  {
13667
14132
  style: [styles.heroBanner, styles.heroBannerMessages],
13668
14133
  onPress: () => nav.push({ name: "MESSAGE_LIST" }),
13669
14134
  activeOpacity: 0.8
13670
14135
  },
13671
- /* @__PURE__ */ import_react3.default.createElement(import_react_native3.Text, { style: styles.heroCount }, unreadCount),
13672
- /* @__PURE__ */ import_react3.default.createElement(import_react_native3.Text, { style: styles.heroLabel }, "unread message", unreadCount !== 1 ? "s" : ""),
13673
- /* @__PURE__ */ import_react3.default.createElement(import_react_native3.Text, { style: styles.heroAction }, "View Messages \u2192")
13674
- ) : /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: [styles.heroBanner, styles.heroBannerClear] }, /* @__PURE__ */ import_react3.default.createElement(import_react_native3.Text, { style: styles.heroClearEmoji }, "\u2705"), /* @__PURE__ */ import_react3.default.createElement(import_react_native3.Text, { style: styles.heroClearTitle }, "All caught up!"), totalTests > 0 && /* @__PURE__ */ import_react3.default.createElement(import_react_native3.Text, { style: styles.heroClearSub }, completedCount, "/", totalTests, " tests completed")), /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: styles.actionGrid }, /* @__PURE__ */ import_react3.default.createElement(
13675
- import_react_native3.TouchableOpacity,
14136
+ /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.heroCount }, unreadCount),
14137
+ /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.heroLabel }, "unread message", unreadCount !== 1 ? "s" : ""),
14138
+ /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.heroAction }, "View Messages \u2192")
14139
+ ) : /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: [styles.heroBanner, styles.heroBannerClear] }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.heroClearEmoji }, "\u2705"), /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.heroClearTitle }, "All caught up!"), totalTests > 0 && /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.heroClearSub }, completedCount, "/", totalTests, " tests completed")), /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: styles.actionGrid }, /* @__PURE__ */ import_react4.default.createElement(
14140
+ import_react_native4.TouchableOpacity,
13676
14141
  {
13677
14142
  style: styles.actionCard,
13678
14143
  onPress: () => nav.push({ name: "TEST_LIST" }),
13679
14144
  activeOpacity: 0.7
13680
14145
  },
13681
- /* @__PURE__ */ import_react3.default.createElement(import_react_native3.Text, { style: styles.actionIcon }, "\u2705"),
13682
- /* @__PURE__ */ import_react3.default.createElement(import_react_native3.Text, { style: styles.actionLabel }, "Tests"),
13683
- pendingCount > 0 && /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: styles.actionBadge }, /* @__PURE__ */ import_react3.default.createElement(import_react_native3.Text, { style: styles.actionBadgeText }, pendingCount))
13684
- ), /* @__PURE__ */ import_react3.default.createElement(
13685
- import_react_native3.TouchableOpacity,
14146
+ /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.actionIcon }, "\u2705"),
14147
+ /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.actionLabel }, "Tests"),
14148
+ pendingCount > 0 && /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: styles.actionBadge }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.actionBadgeText }, pendingCount))
14149
+ ), /* @__PURE__ */ import_react4.default.createElement(
14150
+ import_react_native4.TouchableOpacity,
13686
14151
  {
13687
14152
  style: styles.actionCard,
13688
14153
  onPress: () => nav.push({ name: "REPORT", prefill: { type: "bug" } }),
13689
14154
  activeOpacity: 0.7
13690
14155
  },
13691
- /* @__PURE__ */ import_react3.default.createElement(import_react_native3.Text, { style: styles.actionIcon }, "\u{1F41B}"),
13692
- /* @__PURE__ */ import_react3.default.createElement(import_react_native3.Text, { style: styles.actionLabel }, "Report Bug")
13693
- ), /* @__PURE__ */ import_react3.default.createElement(
13694
- import_react_native3.TouchableOpacity,
14156
+ /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.actionIcon }, "\u{1F41B}"),
14157
+ /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.actionLabel }, "Report Bug")
14158
+ ), /* @__PURE__ */ import_react4.default.createElement(
14159
+ import_react_native4.TouchableOpacity,
13695
14160
  {
13696
14161
  style: styles.actionCard,
13697
14162
  onPress: () => nav.push({ name: "REPORT", prefill: { type: "feedback" } }),
13698
14163
  activeOpacity: 0.7
13699
14164
  },
13700
- /* @__PURE__ */ import_react3.default.createElement(import_react_native3.Text, { style: styles.actionIcon }, "\u{1F4A1}"),
13701
- /* @__PURE__ */ import_react3.default.createElement(import_react_native3.Text, { style: styles.actionLabel }, "Feedback")
13702
- ), /* @__PURE__ */ import_react3.default.createElement(
13703
- import_react_native3.TouchableOpacity,
14165
+ /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.actionIcon }, "\u{1F4A1}"),
14166
+ /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.actionLabel }, "Feedback")
14167
+ ), /* @__PURE__ */ import_react4.default.createElement(
14168
+ import_react_native4.TouchableOpacity,
13704
14169
  {
13705
14170
  style: styles.actionCard,
13706
14171
  onPress: () => nav.push({ name: "MESSAGE_LIST" }),
13707
14172
  activeOpacity: 0.7
13708
14173
  },
13709
- /* @__PURE__ */ import_react3.default.createElement(import_react_native3.Text, { style: styles.actionIcon }, "\u{1F4AC}"),
13710
- /* @__PURE__ */ import_react3.default.createElement(import_react_native3.Text, { style: styles.actionLabel }, "Messages"),
13711
- unreadCount > 0 && /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: [styles.actionBadge, styles.actionBadgeMsg] }, /* @__PURE__ */ import_react3.default.createElement(import_react_native3.Text, { style: styles.actionBadgeText }, unreadCount))
13712
- )), /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: styles.issueGrid }, /* @__PURE__ */ import_react3.default.createElement(
13713
- import_react_native3.TouchableOpacity,
14174
+ /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.actionIcon }, "\u{1F4AC}"),
14175
+ /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.actionLabel }, "Messages"),
14176
+ unreadCount > 0 && /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: [styles.actionBadge, styles.actionBadgeMsg] }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.actionBadgeText }, unreadCount))
14177
+ )), /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: styles.issueGrid }, /* @__PURE__ */ import_react4.default.createElement(
14178
+ import_react_native4.TouchableOpacity,
13714
14179
  {
13715
14180
  style: [styles.issueCard, styles.issueCardOpen],
13716
14181
  onPress: () => nav.push({ name: "ISSUE_LIST", category: "open" }),
13717
14182
  activeOpacity: 0.7
13718
14183
  },
13719
- /* @__PURE__ */ import_react3.default.createElement(import_react_native3.Text, { style: styles.issueCountOpen }, issueCounts.open),
13720
- /* @__PURE__ */ import_react3.default.createElement(import_react_native3.Text, { style: styles.issueLabel }, "Open")
13721
- ), /* @__PURE__ */ import_react3.default.createElement(
13722
- import_react_native3.TouchableOpacity,
14184
+ /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.issueCountOpen }, issueCounts.open),
14185
+ /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.issueLabel }, "Open")
14186
+ ), /* @__PURE__ */ import_react4.default.createElement(
14187
+ import_react_native4.TouchableOpacity,
13723
14188
  {
13724
14189
  style: [styles.issueCard, styles.issueCardDone],
13725
14190
  onPress: () => nav.push({ name: "ISSUE_LIST", category: "done" }),
13726
14191
  activeOpacity: 0.7
13727
14192
  },
13728
- /* @__PURE__ */ import_react3.default.createElement(import_react_native3.Text, { style: styles.issueCountDone }, issueCounts.done),
13729
- /* @__PURE__ */ import_react3.default.createElement(import_react_native3.Text, { style: styles.issueLabel }, "Done")
13730
- ), /* @__PURE__ */ import_react3.default.createElement(
13731
- import_react_native3.TouchableOpacity,
14193
+ /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.issueCountDone }, issueCounts.done),
14194
+ /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.issueLabel }, "Done")
14195
+ ), /* @__PURE__ */ import_react4.default.createElement(
14196
+ import_react_native4.TouchableOpacity,
13732
14197
  {
13733
14198
  style: [styles.issueCard, styles.issueCardReopened],
13734
14199
  onPress: () => nav.push({ name: "ISSUE_LIST", category: "reopened" }),
13735
14200
  activeOpacity: 0.7
13736
14201
  },
13737
- /* @__PURE__ */ import_react3.default.createElement(import_react_native3.Text, { style: styles.issueCountReopened }, issueCounts.reopened),
13738
- /* @__PURE__ */ import_react3.default.createElement(import_react_native3.Text, { style: styles.issueLabel }, "Reopened")
13739
- )), totalTests > 0 && /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: styles.progressSection }, /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: styles.progressBar }, /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: [styles.progressFill, { width: `${Math.round(completedCount / totalTests * 100)}%` }] })), /* @__PURE__ */ import_react3.default.createElement(import_react_native3.Text, { style: styles.progressText }, completedCount, "/", totalTests, " tests completed")), dashboardUrl && /* @__PURE__ */ import_react3.default.createElement(
13740
- import_react_native3.TouchableOpacity,
14202
+ /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.issueCountReopened }, issueCounts.reopened),
14203
+ /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.issueLabel }, "Reopened")
14204
+ )), totalTests > 0 && /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: styles.progressSection }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: styles.progressBar }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: [styles.progressFill, { width: `${Math.round(completedCount / totalTests * 100)}%` }] })), /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.progressText }, completedCount, "/", totalTests, " tests completed")), dashboardUrl && /* @__PURE__ */ import_react4.default.createElement(
14205
+ import_react_native4.TouchableOpacity,
13741
14206
  {
13742
14207
  style: styles.webAppLink,
13743
- onPress: () => import_react_native3.Linking.openURL(dashboardUrl),
14208
+ onPress: () => import_react_native4.Linking.openURL(dashboardUrl),
13744
14209
  activeOpacity: 0.7
13745
14210
  },
13746
- /* @__PURE__ */ import_react3.default.createElement(import_react_native3.Text, { style: styles.webAppIcon }, "\u{1F310}"),
13747
- /* @__PURE__ */ import_react3.default.createElement(import_react_native3.View, { style: styles.webAppTextWrap }, /* @__PURE__ */ import_react3.default.createElement(import_react_native3.Text, { style: styles.webAppTitle }, "Open Web Dashboard"), /* @__PURE__ */ import_react3.default.createElement(import_react_native3.Text, { style: styles.webAppSub }, "View analytics, history & more")),
13748
- /* @__PURE__ */ import_react3.default.createElement(import_react_native3.Text, { style: styles.webAppArrow }, "\u2192")
13749
- ), /* @__PURE__ */ import_react3.default.createElement(
13750
- import_react_native3.TouchableOpacity,
14211
+ /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.webAppIcon }, "\u{1F310}"),
14212
+ /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: styles.webAppTextWrap }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.webAppTitle }, "Open Web Dashboard"), /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.webAppSub }, "View analytics, history & more")),
14213
+ /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.webAppArrow }, "\u2192")
14214
+ ), /* @__PURE__ */ import_react4.default.createElement(
14215
+ import_react_native4.TouchableOpacity,
13751
14216
  {
13752
14217
  style: styles.refreshButton,
13753
14218
  onPress: () => {
@@ -13756,10 +14221,10 @@ function HomeScreen({ nav }) {
13756
14221
  refreshIssueCounts();
13757
14222
  }
13758
14223
  },
13759
- /* @__PURE__ */ import_react3.default.createElement(import_react_native3.Text, { style: styles.refreshText }, "\u21BB Refresh")
14224
+ /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles.refreshText }, "\u21BB Refresh")
13760
14225
  ));
13761
14226
  }
13762
- var styles = import_react_native3.StyleSheet.create({
14227
+ var styles = import_react_native4.StyleSheet.create({
13763
14228
  heroBanner: {
13764
14229
  borderRadius: 16,
13765
14230
  padding: 24,
@@ -13983,26 +14448,26 @@ var styles = import_react_native3.StyleSheet.create({
13983
14448
  });
13984
14449
 
13985
14450
  // src/widget/screens/TestDetailScreen.tsx
13986
- var import_react4 = __toESM(require("react"));
13987
- var import_react_native4 = require("react-native");
14451
+ var import_react5 = __toESM(require("react"));
14452
+ var import_react_native5 = require("react-native");
13988
14453
  function TestDetailScreen({ testId, nav }) {
13989
14454
  const { client, assignments, currentAssignment, refreshAssignments, getDeviceInfo, onNavigate } = useBugBear();
13990
14455
  const displayedAssignment = testId ? assignments.find((a) => a.id === testId) || currentAssignment : currentAssignment;
13991
- const [showSteps, setShowSteps] = (0, import_react4.useState)(true);
13992
- const [showDetails, setShowDetails] = (0, import_react4.useState)(false);
13993
- const [criteriaResults, setCriteriaResults] = (0, import_react4.useState)({});
13994
- const [assignmentElapsedTime, setAssignmentElapsedTime] = (0, import_react4.useState)(0);
13995
- const [showSkipModal, setShowSkipModal] = (0, import_react4.useState)(false);
13996
- const [selectedSkipReason, setSelectedSkipReason] = (0, import_react4.useState)(null);
13997
- const [skipNotes, setSkipNotes] = (0, import_react4.useState)("");
13998
- const [skipping, setSkipping] = (0, import_react4.useState)(false);
13999
- const [isSubmitting, setIsSubmitting] = (0, import_react4.useState)(false);
14000
- (0, import_react4.useEffect)(() => {
14456
+ const [showSteps, setShowSteps] = (0, import_react5.useState)(true);
14457
+ const [showDetails, setShowDetails] = (0, import_react5.useState)(false);
14458
+ const [criteriaResults, setCriteriaResults] = (0, import_react5.useState)({});
14459
+ const [assignmentElapsedTime, setAssignmentElapsedTime] = (0, import_react5.useState)(0);
14460
+ const [showSkipModal, setShowSkipModal] = (0, import_react5.useState)(false);
14461
+ const [selectedSkipReason, setSelectedSkipReason] = (0, import_react5.useState)(null);
14462
+ const [skipNotes, setSkipNotes] = (0, import_react5.useState)("");
14463
+ const [skipping, setSkipping] = (0, import_react5.useState)(false);
14464
+ const [isSubmitting, setIsSubmitting] = (0, import_react5.useState)(false);
14465
+ (0, import_react5.useEffect)(() => {
14001
14466
  setCriteriaResults({});
14002
14467
  setShowSteps(true);
14003
14468
  setShowDetails(false);
14004
14469
  }, [displayedAssignment?.id]);
14005
- (0, import_react4.useEffect)(() => {
14470
+ (0, import_react5.useEffect)(() => {
14006
14471
  const active = displayedAssignment?.status === "in_progress" ? displayedAssignment : null;
14007
14472
  if (!active?.startedAt) {
14008
14473
  setAssignmentElapsedTime(0);
@@ -14018,9 +14483,9 @@ function TestDetailScreen({ testId, nav }) {
14018
14483
  const pendingTests = assignments.filter((a) => a.status === "pending" || a.status === "in_progress");
14019
14484
  const allTests = assignments;
14020
14485
  const currentIndex = displayedAssignment ? assignments.indexOf(displayedAssignment) : -1;
14021
- const handlePass = (0, import_react4.useCallback)(async () => {
14486
+ const handlePass = (0, import_react5.useCallback)(async () => {
14022
14487
  if (!client || !displayedAssignment || isSubmitting) return;
14023
- import_react_native4.Keyboard.dismiss();
14488
+ import_react_native5.Keyboard.dismiss();
14024
14489
  setIsSubmitting(true);
14025
14490
  try {
14026
14491
  await client.passAssignment(displayedAssignment.id);
@@ -14030,9 +14495,9 @@ function TestDetailScreen({ testId, nav }) {
14030
14495
  setIsSubmitting(false);
14031
14496
  }
14032
14497
  }, [client, displayedAssignment, refreshAssignments, nav, isSubmitting]);
14033
- const handleFail = (0, import_react4.useCallback)(async () => {
14498
+ const handleFail = (0, import_react5.useCallback)(async () => {
14034
14499
  if (!client || !displayedAssignment || isSubmitting) return;
14035
- import_react_native4.Keyboard.dismiss();
14500
+ import_react_native5.Keyboard.dismiss();
14036
14501
  setIsSubmitting(true);
14037
14502
  try {
14038
14503
  const result = await client.failAssignment(displayedAssignment.id);
@@ -14052,9 +14517,42 @@ function TestDetailScreen({ testId, nav }) {
14052
14517
  setIsSubmitting(false);
14053
14518
  }
14054
14519
  }, [client, displayedAssignment, refreshAssignments, nav, isSubmitting]);
14055
- const handleSkip = (0, import_react4.useCallback)(async () => {
14520
+ const handleReopen = (0, import_react5.useCallback)(async () => {
14521
+ if (!client || !displayedAssignment || isSubmitting) return;
14522
+ import_react_native5.Keyboard.dismiss();
14523
+ setIsSubmitting(true);
14524
+ try {
14525
+ await client.reopenAssignment(displayedAssignment.id);
14526
+ await refreshAssignments();
14527
+ } finally {
14528
+ setIsSubmitting(false);
14529
+ }
14530
+ }, [client, displayedAssignment, refreshAssignments, isSubmitting]);
14531
+ const handleChangeResult = (0, import_react5.useCallback)(async (newStatus) => {
14532
+ if (!client || !displayedAssignment || isSubmitting) return;
14533
+ import_react_native5.Keyboard.dismiss();
14534
+ setIsSubmitting(true);
14535
+ try {
14536
+ await client.reopenAssignment(displayedAssignment.id);
14537
+ await client.updateAssignmentStatus(displayedAssignment.id, newStatus);
14538
+ await refreshAssignments();
14539
+ if (newStatus === "failed") {
14540
+ nav.replace({
14541
+ name: "REPORT",
14542
+ prefill: {
14543
+ type: "test_fail",
14544
+ assignmentId: displayedAssignment.id,
14545
+ testCaseId: displayedAssignment.testCase.id
14546
+ }
14547
+ });
14548
+ }
14549
+ } finally {
14550
+ setIsSubmitting(false);
14551
+ }
14552
+ }, [client, displayedAssignment, refreshAssignments, nav, isSubmitting]);
14553
+ const handleSkip = (0, import_react5.useCallback)(async () => {
14056
14554
  if (!client || !displayedAssignment || !selectedSkipReason) return;
14057
- import_react_native4.Keyboard.dismiss();
14555
+ import_react_native5.Keyboard.dismiss();
14058
14556
  setSkipping(true);
14059
14557
  await client.skipAssignment(displayedAssignment.id, {
14060
14558
  reason: selectedSkipReason,
@@ -14078,15 +14576,15 @@ function TestDetailScreen({ testId, nav }) {
14078
14576
  }
14079
14577
  }, [client, displayedAssignment, selectedSkipReason, skipNotes, refreshAssignments, assignments, nav]);
14080
14578
  if (!displayedAssignment) {
14081
- return /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: shared.emptyState }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: shared.emptyEmoji }, "\u2705"), /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: shared.emptyTitle }, "No tests assigned"), /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: shared.emptySubtitle }, "Check back later for new tests"), /* @__PURE__ */ import_react4.default.createElement(import_react_native4.TouchableOpacity, { style: [shared.primaryButton, { marginTop: 20, paddingHorizontal: 24 }], onPress: () => nav.reset() }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: shared.primaryButtonText }, "Go Home")));
14579
+ return /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: shared.emptyState }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: shared.emptyEmoji }, "\u2705"), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: shared.emptyTitle }, "No tests assigned"), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: shared.emptySubtitle }, "Check back later for new tests"), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.TouchableOpacity, { style: [shared.primaryButton, { marginTop: 20, paddingHorizontal: 24 }], onPress: () => nav.reset() }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: shared.primaryButtonText }, "Go Home")));
14082
14580
  }
14083
14581
  const testCase = displayedAssignment.testCase;
14084
14582
  const template = testCase.track?.testTemplate || "steps";
14085
14583
  const steps = testCase.steps;
14086
14584
  const info = templateInfo[template] || templateInfo.steps;
14087
14585
  const rubricMode = testCase.track?.rubricMode || "pass_fail";
14088
- return /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: styles2.container }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: styles2.topRow }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: styles2.positionInfo }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.positionText }, "Test ", currentIndex + 1, " of ", allTests.length), displayedAssignment.status === "in_progress" && assignmentElapsedTime > 0 && /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: styles2.timerBadge }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.timerText }, formatElapsedTime(assignmentElapsedTime)))), /* @__PURE__ */ import_react4.default.createElement(import_react_native4.TouchableOpacity, { onPress: () => nav.push({ name: "TEST_LIST" }) }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.viewAllLink }, "View All \u2192"))), displayedAssignment.isVerification && /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: styles2.retestBanner }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.retestIcon }, "\u{1F504}"), /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.retestLabel }, "Retest"), /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.retestSub }, "\u2014 Verify bug fix")), /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.testTitle }, testCase.title), testCase.key && /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.testKey }, testCase.key), /* @__PURE__ */ import_react4.default.createElement(import_react_native4.TouchableOpacity, { onPress: () => setShowSteps(!showSteps), style: styles2.sectionHeader }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.sectionHeaderText }, showSteps ? "\u25BC" : "\u25B6", " ", info.icon, " ", template === "freeform" ? "Instructions" : `${steps.length} ${template === "checklist" ? "items" : template === "rubric" ? "criteria" : "steps"}`)), showSteps && /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: styles2.templateContent }, template === "steps" && steps.map((step, idx) => /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { key: idx, style: styles2.step }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: styles2.stepNumber }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.stepNumberText }, step.stepNumber)), /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: styles2.stepBody }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.stepAction }, step.action), step.expectedResult && /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.stepExpected }, "\u2192 ", step.expectedResult)))), template === "checklist" && /* @__PURE__ */ import_react4.default.createElement(import_react4.default.Fragment, null, steps.map((step, idx) => /* @__PURE__ */ import_react4.default.createElement(
14089
- import_react_native4.TouchableOpacity,
14586
+ return /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles2.container }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles2.topRow }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles2.positionInfo }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.positionText }, "Test ", currentIndex + 1, " of ", allTests.length), displayedAssignment.status === "in_progress" && assignmentElapsedTime > 0 && /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles2.timerBadge }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.timerText }, formatElapsedTime(assignmentElapsedTime)))), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.TouchableOpacity, { onPress: () => nav.push({ name: "TEST_LIST" }) }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.viewAllLink }, "View All \u2192"))), displayedAssignment.isVerification && /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles2.retestBanner }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.retestIcon }, "\u{1F504}"), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.retestLabel }, "Retest"), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.retestSub }, "\u2014 Verify bug fix")), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.testTitle }, testCase.title), testCase.key && /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.testKey }, testCase.key), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.TouchableOpacity, { onPress: () => setShowSteps(!showSteps), style: styles2.sectionHeader }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.sectionHeaderText }, showSteps ? "\u25BC" : "\u25B6", " ", info.icon, " ", template === "freeform" ? "Instructions" : `${steps.length} ${template === "checklist" ? "items" : template === "rubric" ? "criteria" : "steps"}`)), showSteps && /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles2.templateContent }, template === "steps" && steps.map((step, idx) => /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { key: idx, style: styles2.step }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles2.stepNumber }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.stepNumberText }, step.stepNumber)), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles2.stepBody }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.stepAction }, step.action), step.expectedResult && /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.stepExpected }, "\u2192 ", step.expectedResult)))), template === "checklist" && /* @__PURE__ */ import_react5.default.createElement(import_react5.default.Fragment, null, steps.map((step, idx) => /* @__PURE__ */ import_react5.default.createElement(
14587
+ import_react_native5.TouchableOpacity,
14090
14588
  {
14091
14589
  key: idx,
14092
14590
  onPress: () => setCriteriaResults((prev) => {
@@ -14097,56 +14595,88 @@ function TestDetailScreen({ testId, nav }) {
14097
14595
  }),
14098
14596
  style: [styles2.checklistItem, criteriaResults[idx] === true && styles2.checklistItemChecked]
14099
14597
  },
14100
- /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: [styles2.checkbox, criteriaResults[idx] === true && styles2.checkboxChecked] }, criteriaResults[idx] === true && /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.checkmark }, "\u2713")),
14101
- /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: [styles2.checklistText, criteriaResults[idx] === true && styles2.checklistTextDone] }, step.action)
14102
- )), Object.keys(criteriaResults).length > 0 && /* @__PURE__ */ import_react4.default.createElement(import_react_native4.TouchableOpacity, { onPress: () => setCriteriaResults({}), style: styles2.resetRow }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.resetText }, "\u21BA Reset"))), template === "rubric" && steps.map((step, idx) => /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { key: idx, style: styles2.rubricItem }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.rubricTitle }, idx + 1, ". ", step.action), step.expectedResult && /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.rubricExpected }, step.expectedResult), rubricMode === "pass_fail" ? /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: styles2.passFailRow }, /* @__PURE__ */ import_react4.default.createElement(
14103
- import_react_native4.TouchableOpacity,
14598
+ /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: [styles2.checkbox, criteriaResults[idx] === true && styles2.checkboxChecked] }, criteriaResults[idx] === true && /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.checkmark }, "\u2713")),
14599
+ /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: [styles2.checklistText, criteriaResults[idx] === true && styles2.checklistTextDone] }, step.action)
14600
+ )), Object.keys(criteriaResults).length > 0 && /* @__PURE__ */ import_react5.default.createElement(import_react_native5.TouchableOpacity, { onPress: () => setCriteriaResults({}), style: styles2.resetRow }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.resetText }, "\u21BA Reset"))), template === "rubric" && steps.map((step, idx) => /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { key: idx, style: styles2.rubricItem }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.rubricTitle }, idx + 1, ". ", step.action), step.expectedResult && /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.rubricExpected }, step.expectedResult), rubricMode === "pass_fail" ? /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles2.passFailRow }, /* @__PURE__ */ import_react5.default.createElement(
14601
+ import_react_native5.TouchableOpacity,
14104
14602
  {
14105
14603
  onPress: () => setCriteriaResults((prev) => ({ ...prev, [idx]: true })),
14106
14604
  style: [styles2.pfButton, criteriaResults[idx] === true && styles2.pfButtonPass]
14107
14605
  },
14108
- /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: [styles2.pfButtonText, criteriaResults[idx] === true && styles2.pfButtonTextActive] }, "\u2713 Pass")
14109
- ), /* @__PURE__ */ import_react4.default.createElement(
14110
- import_react_native4.TouchableOpacity,
14606
+ /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: [styles2.pfButtonText, criteriaResults[idx] === true && styles2.pfButtonTextActive] }, "\u2713 Pass")
14607
+ ), /* @__PURE__ */ import_react5.default.createElement(
14608
+ import_react_native5.TouchableOpacity,
14111
14609
  {
14112
14610
  onPress: () => setCriteriaResults((prev) => ({ ...prev, [idx]: false })),
14113
14611
  style: [styles2.pfButton, criteriaResults[idx] === false && styles2.pfButtonFail]
14114
14612
  },
14115
- /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: [styles2.pfButtonText, criteriaResults[idx] === false && styles2.pfButtonTextActive] }, "\u2717 Fail")
14116
- )) : /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: styles2.ratingRow }, [1, 2, 3, 4, 5].map((n) => /* @__PURE__ */ import_react4.default.createElement(
14117
- import_react_native4.TouchableOpacity,
14613
+ /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: [styles2.pfButtonText, criteriaResults[idx] === false && styles2.pfButtonTextActive] }, "\u2717 Fail")
14614
+ )) : /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles2.ratingRow }, [1, 2, 3, 4, 5].map((n) => /* @__PURE__ */ import_react5.default.createElement(
14615
+ import_react_native5.TouchableOpacity,
14118
14616
  {
14119
14617
  key: n,
14120
14618
  onPress: () => setCriteriaResults((prev) => ({ ...prev, [idx]: n })),
14121
14619
  style: [styles2.ratingBtn, criteriaResults[idx] === n && styles2.ratingBtnActive]
14122
14620
  },
14123
- /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: [styles2.ratingBtnText, criteriaResults[idx] === n && styles2.ratingBtnTextActive] }, n)
14124
- ))))), template === "freeform" && /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: styles2.freeformBox }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.freeformText }, "Review the area and note:"), /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.freeformBullet }, "\u2022 What works well"), /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.freeformBullet }, "\u2022 Issues or concerns"), /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.freeformBullet }, "\u2022 Suggestions"))), testCase.expectedResult && /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: styles2.expectedBox }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.expectedLabel }, "\u2705 Expected Result"), /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.expectedText }, testCase.expectedResult)), testCase.targetRoute && onNavigate && /* @__PURE__ */ import_react4.default.createElement(
14125
- import_react_native4.TouchableOpacity,
14621
+ /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: [styles2.ratingBtnText, criteriaResults[idx] === n && styles2.ratingBtnTextActive] }, n)
14622
+ ))))), template === "freeform" && /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles2.freeformBox }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.freeformText }, "Review the area and note:"), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.freeformBullet }, "\u2022 What works well"), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.freeformBullet }, "\u2022 Issues or concerns"), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.freeformBullet }, "\u2022 Suggestions"))), testCase.expectedResult && /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles2.expectedBox }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.expectedLabel }, "\u2705 Expected Result"), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.expectedText }, testCase.expectedResult)), testCase.targetRoute && onNavigate && /* @__PURE__ */ import_react5.default.createElement(
14623
+ import_react_native5.TouchableOpacity,
14126
14624
  {
14127
14625
  style: styles2.navigateButton,
14128
14626
  onPress: () => {
14129
- import_react_native4.Keyboard.dismiss();
14627
+ import_react_native5.Keyboard.dismiss();
14130
14628
  onNavigate(testCase.targetRoute);
14131
14629
  nav.closeWidget?.();
14132
14630
  }
14133
14631
  },
14134
- /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.navigateText }, "\u{1F9ED} Go to test location")
14135
- ), /* @__PURE__ */ import_react4.default.createElement(import_react_native4.TouchableOpacity, { onPress: () => setShowDetails(!showDetails), style: styles2.detailsToggle }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.detailsToggleText }, showDetails ? "\u25BC" : "\u25B6", " Details")), showDetails && /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: styles2.detailsSection }, testCase.key && /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.detailMeta }, testCase.key, " \xB7 ", testCase.priority, " \xB7 ", info.name), testCase.description && /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.detailDesc }, testCase.description), testCase.group && /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: styles2.folderProgress }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.folderName }, "\u{1F4C1} ", testCase.group.name))), /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: styles2.actionButtons }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.TouchableOpacity, { style: [styles2.actionBtn, styles2.failBtn, isSubmitting && { opacity: 0.5 }], onPress: handleFail, disabled: isSubmitting }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.failBtnText }, isSubmitting ? "Failing..." : "Fail")), /* @__PURE__ */ import_react4.default.createElement(import_react_native4.TouchableOpacity, { style: [styles2.actionBtn, styles2.skipBtn, isSubmitting && { opacity: 0.5 }], onPress: () => setShowSkipModal(true), disabled: isSubmitting }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.skipBtnText }, "Skip")), /* @__PURE__ */ import_react4.default.createElement(import_react_native4.TouchableOpacity, { style: [styles2.actionBtn, styles2.passBtn, isSubmitting && { opacity: 0.5 }], onPress: handlePass, disabled: isSubmitting }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.passBtnText }, isSubmitting ? "Passing..." : "Pass"))), /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Modal, { visible: showSkipModal, transparent: true, animationType: "fade" }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: styles2.modalOverlay }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: styles2.modalContent }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.modalTitle }, "Skip this test?"), /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.modalSubtitle }, "Select a reason:"), [
14632
+ /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.navigateText }, "\u{1F9ED} Go to test location")
14633
+ ), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.TouchableOpacity, { onPress: () => setShowDetails(!showDetails), style: styles2.detailsToggle }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.detailsToggleText }, showDetails ? "\u25BC" : "\u25B6", " Details")), showDetails && /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles2.detailsSection }, testCase.key && /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.detailMeta }, testCase.key, " \xB7 ", testCase.priority, " \xB7 ", info.name), testCase.description && /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.detailDesc }, testCase.description), testCase.group && /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles2.folderProgress }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.folderName }, "\u{1F4C1} ", testCase.group.name))), displayedAssignment.status === "passed" || displayedAssignment.status === "failed" || displayedAssignment.status === "skipped" || displayedAssignment.status === "blocked" ? /* @__PURE__ */ import_react5.default.createElement(import_react5.default.Fragment, null, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: [
14634
+ styles2.completedBanner,
14635
+ displayedAssignment.status === "passed" && styles2.completedBannerPass,
14636
+ displayedAssignment.status === "failed" && styles2.completedBannerFail
14637
+ ] }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.completedIcon }, displayedAssignment.status === "passed" ? "\u2705" : displayedAssignment.status === "failed" ? "\u274C" : displayedAssignment.status === "skipped" ? "\u23ED" : "\u{1F6AB}"), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: [
14638
+ styles2.completedLabel,
14639
+ displayedAssignment.status === "passed" && { color: colors.green },
14640
+ displayedAssignment.status === "failed" && { color: "#fca5a5" }
14641
+ ] }, "Marked as ", displayedAssignment.status.charAt(0).toUpperCase() + displayedAssignment.status.slice(1))), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: [styles2.actionButtons, { marginTop: 4 }] }, /* @__PURE__ */ import_react5.default.createElement(
14642
+ import_react_native5.TouchableOpacity,
14643
+ {
14644
+ style: [styles2.actionBtn, styles2.reopenBtn, isSubmitting && { opacity: 0.5 }],
14645
+ onPress: handleReopen,
14646
+ disabled: isSubmitting
14647
+ },
14648
+ /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.reopenBtnText }, isSubmitting ? "Reopening..." : "\u{1F504} Reopen Test")
14649
+ ), displayedAssignment.status === "passed" && /* @__PURE__ */ import_react5.default.createElement(
14650
+ import_react_native5.TouchableOpacity,
14651
+ {
14652
+ style: [styles2.actionBtn, styles2.failBtn, isSubmitting && { opacity: 0.5 }],
14653
+ onPress: () => handleChangeResult("failed"),
14654
+ disabled: isSubmitting
14655
+ },
14656
+ /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.failBtnText }, "Change to Fail")
14657
+ ), displayedAssignment.status === "failed" && /* @__PURE__ */ import_react5.default.createElement(
14658
+ import_react_native5.TouchableOpacity,
14659
+ {
14660
+ style: [styles2.actionBtn, styles2.passBtn, isSubmitting && { opacity: 0.5 }],
14661
+ onPress: () => handleChangeResult("passed"),
14662
+ disabled: isSubmitting
14663
+ },
14664
+ /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.passBtnText }, "Change to Pass")
14665
+ ))) : /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles2.actionButtons }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.TouchableOpacity, { style: [styles2.actionBtn, styles2.failBtn, isSubmitting && { opacity: 0.5 }], onPress: handleFail, disabled: isSubmitting }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.failBtnText }, isSubmitting ? "Failing..." : "Fail")), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.TouchableOpacity, { style: [styles2.actionBtn, styles2.skipBtn, isSubmitting && { opacity: 0.5 }], onPress: () => setShowSkipModal(true), disabled: isSubmitting }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.skipBtnText }, "Skip")), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.TouchableOpacity, { style: [styles2.actionBtn, styles2.passBtn, isSubmitting && { opacity: 0.5 }], onPress: handlePass, disabled: isSubmitting }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.passBtnText }, isSubmitting ? "Passing..." : "Pass"))), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Modal, { visible: showSkipModal, transparent: true, animationType: "fade" }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles2.modalOverlay }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles2.modalContent }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.modalTitle }, "Skip this test?"), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.modalSubtitle }, "Select a reason:"), [
14136
14666
  { reason: "blocked", label: "\u{1F6AB} Blocked by a bug" },
14137
14667
  { reason: "not_ready", label: "\u{1F6A7} Feature not ready" },
14138
14668
  { reason: "dependency", label: "\u{1F517} Needs another test first" },
14139
14669
  { reason: "other", label: "\u{1F4DD} Other reason" }
14140
- ].map(({ reason, label }) => /* @__PURE__ */ import_react4.default.createElement(
14141
- import_react_native4.TouchableOpacity,
14670
+ ].map(({ reason, label }) => /* @__PURE__ */ import_react5.default.createElement(
14671
+ import_react_native5.TouchableOpacity,
14142
14672
  {
14143
14673
  key: reason,
14144
14674
  style: [styles2.skipOption, selectedSkipReason === reason && styles2.skipOptionActive],
14145
14675
  onPress: () => setSelectedSkipReason(reason)
14146
14676
  },
14147
- /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: [styles2.skipOptionText, selectedSkipReason === reason && styles2.skipOptionTextActive] }, label)
14148
- )), /* @__PURE__ */ import_react4.default.createElement(
14149
- import_react_native4.TextInput,
14677
+ /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: [styles2.skipOptionText, selectedSkipReason === reason && styles2.skipOptionTextActive] }, label)
14678
+ )), /* @__PURE__ */ import_react5.default.createElement(
14679
+ import_react_native5.TextInput,
14150
14680
  {
14151
14681
  style: styles2.skipNotes,
14152
14682
  value: skipNotes,
@@ -14155,21 +14685,21 @@ function TestDetailScreen({ testId, nav }) {
14155
14685
  placeholderTextColor: colors.textMuted,
14156
14686
  multiline: true
14157
14687
  }
14158
- ), /* @__PURE__ */ import_react4.default.createElement(import_react_native4.View, { style: styles2.skipActions }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.TouchableOpacity, { style: styles2.skipCancel, onPress: () => {
14688
+ ), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles2.skipActions }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.TouchableOpacity, { style: styles2.skipCancel, onPress: () => {
14159
14689
  setShowSkipModal(false);
14160
14690
  setSelectedSkipReason(null);
14161
14691
  setSkipNotes("");
14162
- } }, /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.skipCancelText }, "Cancel")), /* @__PURE__ */ import_react4.default.createElement(
14163
- import_react_native4.TouchableOpacity,
14692
+ } }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.skipCancelText }, "Cancel")), /* @__PURE__ */ import_react5.default.createElement(
14693
+ import_react_native5.TouchableOpacity,
14164
14694
  {
14165
14695
  style: [styles2.skipConfirm, !selectedSkipReason && { opacity: 0.4 }],
14166
14696
  onPress: handleSkip,
14167
14697
  disabled: !selectedSkipReason || skipping
14168
14698
  },
14169
- /* @__PURE__ */ import_react4.default.createElement(import_react_native4.Text, { style: styles2.skipConfirmText }, skipping ? "Skipping..." : "Skip Test")
14699
+ /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles2.skipConfirmText }, skipping ? "Skipping..." : "Skip Test")
14170
14700
  ))))));
14171
14701
  }
14172
- var styles2 = import_react_native4.StyleSheet.create({
14702
+ var styles2 = import_react_native5.StyleSheet.create({
14173
14703
  container: { paddingBottom: 16 },
14174
14704
  retestBanner: { flexDirection: "row", alignItems: "center", gap: 6, backgroundColor: "#422006", borderWidth: 1, borderColor: "#854d0e", borderRadius: 8, paddingVertical: 6, paddingHorizontal: 10, marginBottom: 10 },
14175
14705
  retestIcon: { fontSize: 14 },
@@ -14237,6 +14767,14 @@ var styles2 = import_react_native4.StyleSheet.create({
14237
14767
  detailDesc: { fontSize: 13, color: colors.textSecondary, lineHeight: 18 },
14238
14768
  folderProgress: { marginTop: 8 },
14239
14769
  folderName: { fontSize: 12, color: colors.textMuted },
14770
+ // Completed state
14771
+ completedBanner: { flexDirection: "row", alignItems: "center", justifyContent: "center", gap: 6, paddingVertical: 8, paddingHorizontal: 12, borderRadius: 8, marginTop: 8, marginBottom: 8, backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border },
14772
+ completedBannerPass: { backgroundColor: colors.greenDark, borderColor: colors.green },
14773
+ completedBannerFail: { backgroundColor: colors.redDark, borderColor: colors.red },
14774
+ completedIcon: { fontSize: 14 },
14775
+ completedLabel: { fontSize: 13, fontWeight: "600", color: colors.textSecondary },
14776
+ reopenBtn: { backgroundColor: colors.card, borderWidth: 1, borderColor: colors.blue },
14777
+ reopenBtnText: { fontSize: 14, fontWeight: "600", color: colors.blue },
14240
14778
  // Action buttons
14241
14779
  actionButtons: { flexDirection: "row", gap: 10, marginTop: 8 },
14242
14780
  actionBtn: { flex: 1, paddingVertical: 14, borderRadius: 12, alignItems: "center" },
@@ -14264,25 +14802,43 @@ var styles2 = import_react_native4.StyleSheet.create({
14264
14802
  });
14265
14803
 
14266
14804
  // src/widget/screens/TestListScreen.tsx
14267
- var import_react5 = __toESM(require("react"));
14268
- var import_react_native5 = require("react-native");
14805
+ var import_react6 = __toESM(require("react"));
14806
+ var import_react_native6 = require("react-native");
14269
14807
  function TestListScreen({ nav }) {
14270
- const { assignments, currentAssignment, refreshAssignments } = useBugBear();
14271
- const [filter, setFilter] = (0, import_react5.useState)("all");
14272
- const [roleFilter, setRoleFilter] = (0, import_react5.useState)(null);
14273
- const [collapsedFolders, setCollapsedFolders] = (0, import_react5.useState)(/* @__PURE__ */ new Set());
14274
- (0, import_react5.useEffect)(() => {
14808
+ const { assignments, currentAssignment, refreshAssignments, dashboardUrl, isLoading } = useBugBear();
14809
+ const [filter, setFilter] = (0, import_react6.useState)("all");
14810
+ const [roleFilter, setRoleFilter] = (0, import_react6.useState)(null);
14811
+ const [trackFilter, setTrackFilter] = (0, import_react6.useState)(null);
14812
+ const [platformFilter, setPlatformFilter] = (0, import_react6.useState)(import_react_native6.Platform.OS === "android" ? "android" : "ios");
14813
+ const [searchQuery, setSearchQuery] = (0, import_react6.useState)("");
14814
+ const [sortMode, setSortMode] = (0, import_react6.useState)("priority");
14815
+ const [collapsedFolders, setCollapsedFolders] = (0, import_react6.useState)(/* @__PURE__ */ new Set());
14816
+ (0, import_react6.useEffect)(() => {
14275
14817
  refreshAssignments();
14276
14818
  }, []);
14277
- const availableRoles = (0, import_react5.useMemo)(() => {
14819
+ const availableRoles = (0, import_react6.useMemo)(() => {
14278
14820
  const roleMap = /* @__PURE__ */ new Map();
14279
14821
  for (const a of assignments) {
14280
14822
  if (a.testCase.role) roleMap.set(a.testCase.role.id, a.testCase.role);
14281
14823
  }
14282
14824
  return Array.from(roleMap.values());
14283
14825
  }, [assignments]);
14826
+ const availableTracks = (0, import_react6.useMemo)(() => {
14827
+ const trackMap = /* @__PURE__ */ new Map();
14828
+ for (const a of assignments) {
14829
+ if (a.testCase.track) trackMap.set(a.testCase.track.id, a.testCase.track);
14830
+ }
14831
+ return Array.from(trackMap.values());
14832
+ }, [assignments]);
14833
+ const availablePlatforms = (0, import_react6.useMemo)(() => {
14834
+ const set = /* @__PURE__ */ new Set();
14835
+ for (const a of assignments) {
14836
+ if (a.testCase.platforms) a.testCase.platforms.forEach((p) => set.add(p));
14837
+ }
14838
+ return Array.from(set).sort();
14839
+ }, [assignments]);
14284
14840
  const selectedRole = availableRoles.find((r) => r.id === roleFilter);
14285
- const groupedAssignments = (0, import_react5.useMemo)(() => {
14841
+ const groupedAssignments = (0, import_react6.useMemo)(() => {
14286
14842
  const groups = /* @__PURE__ */ new Map();
14287
14843
  for (const assignment of assignments) {
14288
14844
  const groupId = assignment.testCase.group?.id || "ungrouped";
@@ -14304,6 +14860,8 @@ function TestListScreen({ nav }) {
14304
14860
  folder.assignments.sort((a, b) => {
14305
14861
  if (a.isVerification && !b.isVerification) return -1;
14306
14862
  if (!a.isVerification && b.isVerification) return 1;
14863
+ if (sortMode === "alpha") return a.testCase.title.localeCompare(b.testCase.title);
14864
+ if (sortMode === "recent") return 0;
14307
14865
  const sd = (statusOrder[a.status] ?? 5) - (statusOrder[b.status] ?? 5);
14308
14866
  if (sd !== 0) return sd;
14309
14867
  return (priorityOrder[a.testCase.priority] ?? 4) - (priorityOrder[b.testCase.priority] ?? 4);
@@ -14315,8 +14873,8 @@ function TestListScreen({ nav }) {
14315
14873
  if (!b.group) return -1;
14316
14874
  return a.group.sortOrder - b.group.sortOrder;
14317
14875
  });
14318
- }, [assignments]);
14319
- const toggleFolder = (0, import_react5.useCallback)((id) => {
14876
+ }, [assignments, sortMode]);
14877
+ const toggleFolder = (0, import_react6.useCallback)((id) => {
14320
14878
  setCollapsedFolders((prev) => {
14321
14879
  const next = new Set(prev);
14322
14880
  if (next.has(id)) next.delete(id);
@@ -14324,29 +14882,38 @@ function TestListScreen({ nav }) {
14324
14882
  return next;
14325
14883
  });
14326
14884
  }, []);
14327
- const filterAssignment = (a) => {
14885
+ const filterAssignment = (0, import_react6.useCallback)((a) => {
14886
+ if (platformFilter && a.testCase.platforms && !a.testCase.platforms.includes(platformFilter)) return false;
14328
14887
  if (roleFilter && a.testCase.role?.id !== roleFilter) return false;
14888
+ if (trackFilter && a.testCase.track?.id !== trackFilter) return false;
14889
+ if (searchQuery) {
14890
+ const q = searchQuery.toLowerCase();
14891
+ const titleMatch = a.testCase.title.toLowerCase().includes(q);
14892
+ const keyMatch = a.testCase.testKey.toLowerCase().includes(q);
14893
+ if (!titleMatch && !keyMatch) return false;
14894
+ }
14329
14895
  if (filter === "pending") return a.status === "pending" || a.status === "in_progress";
14330
14896
  if (filter === "done") return a.status === "passed";
14331
14897
  if (filter === "reopened") return a.status === "failed";
14332
14898
  return true;
14333
- };
14334
- return /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, null, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles3.filterBar }, [
14899
+ }, [platformFilter, roleFilter, trackFilter, searchQuery, filter]);
14900
+ if (isLoading) return /* @__PURE__ */ import_react6.default.createElement(TestListScreenSkeleton, null);
14901
+ return /* @__PURE__ */ import_react6.default.createElement(import_react_native6.View, null, /* @__PURE__ */ import_react6.default.createElement(import_react_native6.View, { style: styles3.filterBar }, [
14335
14902
  { key: "all", label: "All", count: assignments.length },
14336
14903
  { key: "pending", label: "To Do", count: assignments.filter((a) => a.status === "pending" || a.status === "in_progress").length },
14337
14904
  { key: "done", label: "Done", count: assignments.filter((a) => a.status === "passed").length },
14338
14905
  { key: "reopened", label: "Re Opened", count: assignments.filter((a) => a.status === "failed").length }
14339
- ].map((f) => /* @__PURE__ */ import_react5.default.createElement(import_react_native5.TouchableOpacity, { key: f.key, style: [styles3.filterBtn, filter === f.key && styles3.filterBtnActive], onPress: () => setFilter(f.key) }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: [styles3.filterBtnText, filter === f.key && styles3.filterBtnTextActive] }, f.label, " (", f.count, ")")))), availableRoles.length >= 2 && /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles3.roleSection }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, style: styles3.roleBar }, /* @__PURE__ */ import_react5.default.createElement(
14340
- import_react_native5.TouchableOpacity,
14906
+ ].map((f) => /* @__PURE__ */ import_react6.default.createElement(import_react_native6.TouchableOpacity, { key: f.key, style: [styles3.filterBtn, filter === f.key && styles3.filterBtnActive], onPress: () => setFilter(f.key) }, /* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: [styles3.filterBtnText, filter === f.key && styles3.filterBtnTextActive] }, f.label, " (", f.count, ")")))), availableRoles.length >= 2 && /* @__PURE__ */ import_react6.default.createElement(import_react_native6.View, { style: styles3.roleSection }, /* @__PURE__ */ import_react6.default.createElement(import_react_native6.ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, style: styles3.roleBar }, /* @__PURE__ */ import_react6.default.createElement(
14907
+ import_react_native6.TouchableOpacity,
14341
14908
  {
14342
14909
  style: [styles3.roleBtn, !roleFilter && styles3.roleBtnActive],
14343
14910
  onPress: () => setRoleFilter(null)
14344
14911
  },
14345
- /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: [styles3.roleBtnText, !roleFilter && styles3.roleBtnTextActive] }, "All Roles")
14912
+ /* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: [styles3.roleBtnText, !roleFilter && styles3.roleBtnTextActive] }, "All Roles")
14346
14913
  ), availableRoles.map((role) => {
14347
14914
  const isActive = roleFilter === role.id;
14348
- return /* @__PURE__ */ import_react5.default.createElement(
14349
- import_react_native5.TouchableOpacity,
14915
+ return /* @__PURE__ */ import_react6.default.createElement(
14916
+ import_react_native6.TouchableOpacity,
14350
14917
  {
14351
14918
  key: role.id,
14352
14919
  style: [
@@ -14355,33 +14922,95 @@ function TestListScreen({ nav }) {
14355
14922
  ],
14356
14923
  onPress: () => setRoleFilter(isActive ? null : role.id)
14357
14924
  },
14358
- /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: [styles3.roleDot, { backgroundColor: role.color }] }),
14359
- /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: [styles3.roleBtnText, isActive && { color: role.color, fontWeight: "600" }] }, role.name)
14925
+ /* @__PURE__ */ import_react6.default.createElement(import_react_native6.View, { style: [styles3.roleDot, { backgroundColor: role.color }] }),
14926
+ /* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: [styles3.roleBtnText, isActive && { color: role.color, fontWeight: "600" }] }, role.name)
14360
14927
  );
14361
- })), selectedRole?.loginHint && /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: [styles3.loginHint, { backgroundColor: selectedRole.color + "10", borderColor: selectedRole.color + "30" }] }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles3.loginHintText }, "Log in as: ", selectedRole.loginHint))), groupedAssignments.map((folder) => {
14928
+ })), selectedRole?.loginHint && /* @__PURE__ */ import_react6.default.createElement(import_react_native6.View, { style: [styles3.loginHint, { backgroundColor: selectedRole.color + "10", borderColor: selectedRole.color + "30" }] }, /* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: styles3.loginHintText }, "Log in as: ", selectedRole.loginHint))), /* @__PURE__ */ import_react6.default.createElement(import_react_native6.View, { style: styles3.searchContainer }, /* @__PURE__ */ import_react6.default.createElement(
14929
+ import_react_native6.TextInput,
14930
+ {
14931
+ value: searchQuery,
14932
+ onChangeText: setSearchQuery,
14933
+ placeholder: "Search tests...",
14934
+ placeholderTextColor: colors.textMuted,
14935
+ style: styles3.searchInput
14936
+ }
14937
+ )), availablePlatforms.length >= 2 && /* @__PURE__ */ import_react6.default.createElement(import_react_native6.ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, style: styles3.platformBar }, /* @__PURE__ */ import_react6.default.createElement(
14938
+ import_react_native6.TouchableOpacity,
14939
+ {
14940
+ style: [styles3.platformBtn, !platformFilter && styles3.platformBtnActive],
14941
+ onPress: () => setPlatformFilter(null)
14942
+ },
14943
+ /* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: [styles3.platformBtnText, !platformFilter && styles3.platformBtnTextActive] }, "All Platforms")
14944
+ ), availablePlatforms.map((p) => {
14945
+ const isActive = platformFilter === p;
14946
+ const label = p === "ios" ? "iOS" : p === "android" ? "Android" : p === "web" ? "Web" : p;
14947
+ const icon = p === "ios" ? "\u{1F4F1}" : p === "android" ? "\u{1F916}" : p === "web" ? "\u{1F310}" : "\u{1F4CB}";
14948
+ return /* @__PURE__ */ import_react6.default.createElement(
14949
+ import_react_native6.TouchableOpacity,
14950
+ {
14951
+ key: p,
14952
+ style: [
14953
+ styles3.platformBtn,
14954
+ isActive && { backgroundColor: colors.blue + "20", borderColor: colors.blue + "60", borderWidth: 1 }
14955
+ ],
14956
+ onPress: () => setPlatformFilter(isActive ? null : p)
14957
+ },
14958
+ /* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: [styles3.platformBtnText, isActive && { color: colors.blue, fontWeight: "600" }] }, icon, " ", label)
14959
+ );
14960
+ })), /* @__PURE__ */ import_react6.default.createElement(import_react_native6.View, { style: styles3.trackSortRow }, availableTracks.length >= 2 && /* @__PURE__ */ import_react6.default.createElement(import_react_native6.ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, style: { flex: 1 } }, /* @__PURE__ */ import_react6.default.createElement(
14961
+ import_react_native6.TouchableOpacity,
14962
+ {
14963
+ style: [styles3.trackBtn, !trackFilter && styles3.trackBtnActive],
14964
+ onPress: () => setTrackFilter(null)
14965
+ },
14966
+ /* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: [styles3.trackBtnText, !trackFilter && styles3.trackBtnTextActive] }, "All Tracks")
14967
+ ), availableTracks.map((track) => {
14968
+ const isActive = trackFilter === track.id;
14969
+ return /* @__PURE__ */ import_react6.default.createElement(
14970
+ import_react_native6.TouchableOpacity,
14971
+ {
14972
+ key: track.id,
14973
+ style: [styles3.trackBtn, isActive && { backgroundColor: track.color + "20", borderColor: track.color + "60", borderWidth: 1 }],
14974
+ onPress: () => setTrackFilter(isActive ? null : track.id)
14975
+ },
14976
+ /* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: [styles3.trackBtnText, isActive && { color: track.color, fontWeight: "600" }] }, track.icon, " ", track.name)
14977
+ );
14978
+ })), /* @__PURE__ */ import_react6.default.createElement(import_react_native6.View, { style: styles3.sortGroup }, [
14979
+ { key: "priority", label: "\u2195" },
14980
+ { key: "recent", label: "\u{1F550}" },
14981
+ { key: "alpha", label: "AZ" }
14982
+ ].map((s2) => /* @__PURE__ */ import_react6.default.createElement(
14983
+ import_react_native6.TouchableOpacity,
14984
+ {
14985
+ key: s2.key,
14986
+ style: [styles3.sortBtn, sortMode === s2.key && styles3.sortBtnActive],
14987
+ onPress: () => setSortMode(s2.key)
14988
+ },
14989
+ /* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: [styles3.sortBtnText, sortMode === s2.key && styles3.sortBtnTextActive] }, s2.label)
14990
+ )))), groupedAssignments.map((folder) => {
14362
14991
  const folderId = folder.group?.id || "ungrouped";
14363
14992
  const isCollapsed = collapsedFolders.has(folderId);
14364
14993
  const filtered = folder.assignments.filter(filterAssignment);
14365
14994
  if (filtered.length === 0 && filter !== "all") return null;
14366
- 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, numberOfLines: 1 }, 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: `${folder.stats.total > 0 ? Math.round((folder.stats.passed + folder.stats.failed) / folder.stats.total * 100) : 0}%` }] })), /* @__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) => {
14995
+ return /* @__PURE__ */ import_react6.default.createElement(import_react_native6.View, { key: folderId, style: styles3.folder }, /* @__PURE__ */ import_react6.default.createElement(import_react_native6.TouchableOpacity, { style: styles3.folderHeader, onPress: () => toggleFolder(folderId) }, /* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: styles3.folderToggle }, isCollapsed ? "\u25B6" : "\u25BC"), /* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: styles3.folderName, numberOfLines: 1 }, folder.group?.name || "Ungrouped"), /* @__PURE__ */ import_react6.default.createElement(import_react_native6.View, { style: styles3.folderProgress }, /* @__PURE__ */ import_react6.default.createElement(import_react_native6.View, { style: [styles3.folderProgressFill, { width: `${folder.stats.total > 0 ? Math.round((folder.stats.passed + folder.stats.failed) / folder.stats.total * 100) : 0}%` }] })), /* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: styles3.folderCount }, folder.stats.passed + folder.stats.failed, "/", folder.stats.total)), !isCollapsed && filtered.map((assignment) => {
14367
14996
  const badge = getStatusBadge(assignment.status);
14368
14997
  const isCurrent = currentAssignment?.id === assignment.id;
14369
- return /* @__PURE__ */ import_react5.default.createElement(
14370
- import_react_native5.TouchableOpacity,
14998
+ return /* @__PURE__ */ import_react6.default.createElement(
14999
+ import_react_native6.TouchableOpacity,
14371
15000
  {
14372
15001
  key: assignment.id,
14373
15002
  style: [styles3.testItem, isCurrent && styles3.testItemCurrent],
14374
15003
  onPress: () => nav.push({ name: "TEST_DETAIL", testId: assignment.id })
14375
15004
  },
14376
- /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles3.testBadge }, badge.icon),
14377
- /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles3.testInfo }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles3.testTitle, numberOfLines: 1 }, assignment.testCase.title), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles3.testMetaRow }, assignment.isVerification && /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles3.retestTag }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles3.retestTagText }, "Retest")), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles3.testMeta }, assignment.testCase.testKey, " \xB7 ", assignment.testCase.priority), assignment.testCase.role && /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: styles3.roleBadgeRow }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles3.testMeta }, " \xB7 "), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: [styles3.roleBadgeDot, { backgroundColor: assignment.testCase.role.color }] }), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: [styles3.testMeta, { color: assignment.testCase.role.color, fontWeight: "500" }] }, assignment.testCase.role.name)))),
14378
- /* @__PURE__ */ import_react5.default.createElement(import_react_native5.View, { style: [
15005
+ /* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: styles3.testBadge }, badge.icon),
15006
+ /* @__PURE__ */ import_react6.default.createElement(import_react_native6.View, { style: styles3.testInfo }, /* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: styles3.testTitle, numberOfLines: 1 }, assignment.testCase.title), /* @__PURE__ */ import_react6.default.createElement(import_react_native6.View, { style: styles3.testMetaRow }, assignment.isVerification && /* @__PURE__ */ import_react6.default.createElement(import_react_native6.View, { style: styles3.retestTag }, /* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: styles3.retestTagText }, "Retest")), /* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: styles3.testMeta }, assignment.testCase.testKey, " \xB7 ", assignment.testCase.priority), assignment.testCase.role && /* @__PURE__ */ import_react6.default.createElement(import_react_native6.View, { style: styles3.roleBadgeRow }, /* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: styles3.testMeta }, " \xB7 "), /* @__PURE__ */ import_react6.default.createElement(import_react_native6.View, { style: [styles3.roleBadgeDot, { backgroundColor: assignment.testCase.role.color }] }), /* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: [styles3.testMeta, { color: assignment.testCase.role.color, fontWeight: "500" }] }, assignment.testCase.role.name)))),
15007
+ /* @__PURE__ */ import_react6.default.createElement(import_react_native6.View, { style: [
14379
15008
  styles3.statusPill,
14380
15009
  {
14381
15010
  backgroundColor: assignment.status === "passed" ? "#14532d" : assignment.status === "failed" ? "#450a0a" : assignment.status === "in_progress" ? "#172554" : "#27272a",
14382
15011
  borderColor: assignment.status === "passed" ? "#166534" : assignment.status === "failed" ? "#7f1d1d" : assignment.status === "in_progress" ? "#1e3a5f" : "#3f3f46"
14383
15012
  }
14384
- ] }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: [
15013
+ ] }, /* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: [
14385
15014
  styles3.statusPillText,
14386
15015
  {
14387
15016
  color: assignment.status === "passed" ? "#4ade80" : assignment.status === "failed" ? "#f87171" : assignment.status === "in_progress" ? "#60a5fa" : "#d4d4d8"
@@ -14389,9 +15018,17 @@ function TestListScreen({ nav }) {
14389
15018
  ] }, badge.label))
14390
15019
  );
14391
15020
  }));
14392
- }), /* @__PURE__ */ import_react5.default.createElement(import_react_native5.TouchableOpacity, { style: styles3.refreshBtn, onPress: refreshAssignments }, /* @__PURE__ */ import_react5.default.createElement(import_react_native5.Text, { style: styles3.refreshText }, "\u21BB", " Refresh")));
15021
+ }), dashboardUrl && /* @__PURE__ */ import_react6.default.createElement(
15022
+ import_react_native6.TouchableOpacity,
15023
+ {
15024
+ style: styles3.dashboardLink,
15025
+ onPress: () => import_react_native6.Linking.openURL(`${dashboardUrl}/test-cases`),
15026
+ activeOpacity: 0.7
15027
+ },
15028
+ /* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: styles3.dashboardLinkText }, "\u{1F310}", " Manage on Dashboard ", "\u2192")
15029
+ ), /* @__PURE__ */ import_react6.default.createElement(import_react_native6.TouchableOpacity, { style: styles3.refreshBtn, onPress: refreshAssignments }, /* @__PURE__ */ import_react6.default.createElement(import_react_native6.Text, { style: styles3.refreshText }, "\u21BB", " Refresh")));
14393
15030
  }
14394
- var styles3 = import_react_native5.StyleSheet.create({
15031
+ var styles3 = import_react_native6.StyleSheet.create({
14395
15032
  filterBar: { flexDirection: "row", gap: 8, marginBottom: 8 },
14396
15033
  filterBtn: { paddingHorizontal: 12, paddingVertical: 6, borderRadius: 8, backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border },
14397
15034
  filterBtnActive: { backgroundColor: colors.blue, borderColor: colors.blue },
@@ -14426,16 +15063,35 @@ var styles3 = import_react_native5.StyleSheet.create({
14426
15063
  testMeta: { fontSize: 11, color: colors.textDim },
14427
15064
  statusPill: { paddingHorizontal: 8, paddingVertical: 3, borderRadius: 6, borderWidth: 1, marginLeft: 8 },
14428
15065
  statusPillText: { fontSize: 10, fontWeight: "600" },
14429
- refreshBtn: { alignItems: "center", paddingVertical: 12 },
15066
+ searchContainer: { marginBottom: 8 },
15067
+ searchInput: { backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border, borderRadius: 8, paddingHorizontal: 12, paddingVertical: 8, fontSize: 13, color: colors.textPrimary },
15068
+ platformBar: { flexDirection: "row", marginBottom: 8 },
15069
+ platformBtn: { flexDirection: "row", alignItems: "center", gap: 4, paddingHorizontal: 10, paddingVertical: 4, borderRadius: 6, marginRight: 6, borderWidth: 1, borderColor: "transparent" },
15070
+ platformBtnActive: { backgroundColor: colors.card, borderColor: colors.border },
15071
+ platformBtnText: { fontSize: 11, color: colors.textMuted },
15072
+ platformBtnTextActive: { color: colors.textPrimary, fontWeight: "600" },
15073
+ trackSortRow: { flexDirection: "row", alignItems: "center", marginBottom: 10, gap: 8 },
15074
+ trackBtn: { flexDirection: "row", alignItems: "center", gap: 4, paddingHorizontal: 10, paddingVertical: 4, borderRadius: 6, marginRight: 6, borderWidth: 1, borderColor: "transparent" },
15075
+ trackBtnActive: { backgroundColor: colors.card, borderColor: colors.border },
15076
+ trackBtnText: { fontSize: 11, color: colors.textMuted },
15077
+ trackBtnTextActive: { color: colors.textPrimary, fontWeight: "600" },
15078
+ sortGroup: { flexDirection: "row", gap: 2 },
15079
+ sortBtn: { paddingHorizontal: 8, paddingVertical: 4, borderRadius: 6, borderWidth: 1, borderColor: "transparent" },
15080
+ sortBtnActive: { backgroundColor: colors.card, borderColor: colors.border },
15081
+ sortBtnText: { fontSize: 11, color: colors.textMuted },
15082
+ sortBtnTextActive: { color: colors.textPrimary, fontWeight: "600" },
15083
+ dashboardLink: { alignItems: "center", paddingTop: 12 },
15084
+ dashboardLinkText: { fontSize: 13, fontWeight: "500", color: colors.blue },
15085
+ refreshBtn: { alignItems: "center", paddingVertical: 8 },
14430
15086
  refreshText: { fontSize: 13, color: colors.blue }
14431
15087
  });
14432
15088
 
14433
15089
  // src/widget/screens/TestFeedbackScreen.tsx
14434
- var import_react9 = __toESM(require("react"));
14435
- var import_react_native8 = require("react-native");
15090
+ var import_react10 = __toESM(require("react"));
15091
+ var import_react_native9 = require("react-native");
14436
15092
 
14437
15093
  // src/widget/useImageAttachments.ts
14438
- var import_react6 = require("react");
15094
+ var import_react7 = require("react");
14439
15095
  var launchImageLibrary = null;
14440
15096
  var launchCamera = null;
14441
15097
  try {
@@ -14446,8 +15102,8 @@ try {
14446
15102
  }
14447
15103
  var IMAGE_PICKER_AVAILABLE = launchImageLibrary !== null;
14448
15104
  function useImageAttachments(uploadFn, maxImages, bucket = "screenshots") {
14449
- const [images, setImages] = (0, import_react6.useState)([]);
14450
- const pickFromGallery = (0, import_react6.useCallback)(async () => {
15105
+ const [images, setImages] = (0, import_react7.useState)([]);
15106
+ const pickFromGallery = (0, import_react7.useCallback)(async () => {
14451
15107
  if (!launchImageLibrary || images.length >= maxImages) return;
14452
15108
  launchImageLibrary(
14453
15109
  { mediaType: "photo", quality: 0.7, maxWidth: 1920, maxHeight: 1920, selectionLimit: maxImages - images.length },
@@ -14481,7 +15137,7 @@ function useImageAttachments(uploadFn, maxImages, bucket = "screenshots") {
14481
15137
  }
14482
15138
  );
14483
15139
  }, [images.length, maxImages, uploadFn, bucket]);
14484
- const pickFromCamera = (0, import_react6.useCallback)(async () => {
15140
+ const pickFromCamera = (0, import_react7.useCallback)(async () => {
14485
15141
  if (!launchCamera || images.length >= maxImages) return;
14486
15142
  launchCamera(
14487
15143
  { mediaType: "photo", quality: 0.7, maxWidth: 1920, maxHeight: 1920 },
@@ -14514,35 +15170,35 @@ function useImageAttachments(uploadFn, maxImages, bucket = "screenshots") {
14514
15170
  }
14515
15171
  );
14516
15172
  }, [images.length, maxImages, uploadFn, bucket]);
14517
- const removeImage = (0, import_react6.useCallback)((id) => {
15173
+ const removeImage = (0, import_react7.useCallback)((id) => {
14518
15174
  setImages((prev) => prev.filter((img) => img.id !== id));
14519
15175
  }, []);
14520
- const clear = (0, import_react6.useCallback)(() => {
15176
+ const clear = (0, import_react7.useCallback)(() => {
14521
15177
  setImages([]);
14522
15178
  }, []);
14523
15179
  const isUploading = images.some((img) => img.status === "uploading");
14524
15180
  const hasError = images.some((img) => img.status === "error");
14525
- const getAttachments = (0, import_react6.useCallback)(() => {
15181
+ const getAttachments = (0, import_react7.useCallback)(() => {
14526
15182
  return images.filter((img) => img.status === "done" && img.remoteUrl).map((img) => ({ type: "image", url: img.remoteUrl, name: img.name }));
14527
15183
  }, [images]);
14528
- const getScreenshotUrls = (0, import_react6.useCallback)(() => {
15184
+ const getScreenshotUrls = (0, import_react7.useCallback)(() => {
14529
15185
  return images.filter((img) => img.status === "done" && img.remoteUrl).map((img) => img.remoteUrl);
14530
15186
  }, [images]);
14531
15187
  return { images, pickFromGallery, pickFromCamera, removeImage, clear, isUploading, hasError, getAttachments, getScreenshotUrls };
14532
15188
  }
14533
15189
 
14534
15190
  // src/widget/ImagePickerButtons.tsx
14535
- var import_react8 = __toESM(require("react"));
14536
- var import_react_native7 = require("react-native");
15191
+ var import_react9 = __toESM(require("react"));
15192
+ var import_react_native8 = require("react-native");
14537
15193
 
14538
15194
  // src/widget/ImagePreviewStrip.tsx
14539
- var import_react7 = __toESM(require("react"));
14540
- var import_react_native6 = require("react-native");
15195
+ var import_react8 = __toESM(require("react"));
15196
+ var import_react_native7 = require("react-native");
14541
15197
  function ImagePreviewStrip({ images, onRemove }) {
14542
15198
  if (images.length === 0) return null;
14543
- 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")))));
15199
+ return /* @__PURE__ */ import_react8.default.createElement(import_react_native7.ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, style: styles4.strip }, images.map((img) => /* @__PURE__ */ import_react8.default.createElement(import_react_native7.View, { key: img.id, style: styles4.thumbContainer }, /* @__PURE__ */ import_react8.default.createElement(import_react_native7.Image, { source: { uri: img.localUri }, style: styles4.thumb }), img.status === "uploading" && /* @__PURE__ */ import_react8.default.createElement(import_react_native7.View, { style: styles4.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: [styles4.thumbOverlay, styles4.thumbOverlayError] }, /* @__PURE__ */ import_react8.default.createElement(import_react_native7.Text, { style: styles4.thumbErrorText }, "!")), /* @__PURE__ */ import_react8.default.createElement(import_react_native7.TouchableOpacity, { style: styles4.thumbRemove, onPress: () => onRemove(img.id) }, /* @__PURE__ */ import_react8.default.createElement(import_react_native7.Text, { style: styles4.thumbRemoveText }, "\u2715")))));
14544
15200
  }
14545
- var styles4 = import_react_native6.StyleSheet.create({
15201
+ var styles4 = import_react_native7.StyleSheet.create({
14546
15202
  strip: {
14547
15203
  flexDirection: "row",
14548
15204
  marginTop: 4
@@ -14561,7 +15217,7 @@ var styles4 = import_react_native6.StyleSheet.create({
14561
15217
  borderRadius: 8
14562
15218
  },
14563
15219
  thumbOverlay: {
14564
- ...import_react_native6.StyleSheet.absoluteFillObject,
15220
+ ...import_react_native7.StyleSheet.absoluteFillObject,
14565
15221
  backgroundColor: "rgba(0,0,0,0.5)",
14566
15222
  justifyContent: "center",
14567
15223
  alignItems: "center",
@@ -14596,25 +15252,25 @@ var styles4 = import_react_native6.StyleSheet.create({
14596
15252
  // src/widget/ImagePickerButtons.tsx
14597
15253
  function ImagePickerButtons({ images, maxImages, onPickGallery, onPickCamera, onRemove, label }) {
14598
15254
  if (!IMAGE_PICKER_AVAILABLE) return null;
14599
- 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(
14600
- import_react_native7.TouchableOpacity,
15255
+ return /* @__PURE__ */ import_react9.default.createElement(import_react_native8.View, { style: styles5.section }, label && /* @__PURE__ */ import_react9.default.createElement(import_react_native8.Text, { style: styles5.label }, label), /* @__PURE__ */ import_react9.default.createElement(import_react_native8.View, { style: styles5.buttonRow }, /* @__PURE__ */ import_react9.default.createElement(
15256
+ import_react_native8.TouchableOpacity,
14601
15257
  {
14602
15258
  style: styles5.pickButton,
14603
15259
  onPress: onPickGallery,
14604
15260
  disabled: images.length >= maxImages
14605
15261
  },
14606
- /* @__PURE__ */ import_react8.default.createElement(import_react_native7.Text, { style: [styles5.pickButtonText, images.length >= maxImages && styles5.pickButtonDisabled] }, "Gallery")
14607
- ), /* @__PURE__ */ import_react8.default.createElement(
14608
- import_react_native7.TouchableOpacity,
15262
+ /* @__PURE__ */ import_react9.default.createElement(import_react_native8.Text, { style: [styles5.pickButtonText, images.length >= maxImages && styles5.pickButtonDisabled] }, "Gallery")
15263
+ ), /* @__PURE__ */ import_react9.default.createElement(
15264
+ import_react_native8.TouchableOpacity,
14609
15265
  {
14610
15266
  style: styles5.pickButton,
14611
15267
  onPress: onPickCamera,
14612
15268
  disabled: images.length >= maxImages
14613
15269
  },
14614
- /* @__PURE__ */ import_react8.default.createElement(import_react_native7.Text, { style: [styles5.pickButtonText, images.length >= maxImages && styles5.pickButtonDisabled] }, "Camera")
14615
- ), /* @__PURE__ */ import_react8.default.createElement(import_react_native7.Text, { style: styles5.countText }, images.length, "/", maxImages)), /* @__PURE__ */ import_react8.default.createElement(ImagePreviewStrip, { images, onRemove }));
15270
+ /* @__PURE__ */ import_react9.default.createElement(import_react_native8.Text, { style: [styles5.pickButtonText, images.length >= maxImages && styles5.pickButtonDisabled] }, "Camera")
15271
+ ), /* @__PURE__ */ import_react9.default.createElement(import_react_native8.Text, { style: styles5.countText }, images.length, "/", maxImages)), /* @__PURE__ */ import_react9.default.createElement(ImagePreviewStrip, { images, onRemove }));
14616
15272
  }
14617
- var styles5 = import_react_native7.StyleSheet.create({
15273
+ var styles5 = import_react_native8.StyleSheet.create({
14618
15274
  section: {
14619
15275
  marginTop: 12,
14620
15276
  marginBottom: 4
@@ -14657,10 +15313,10 @@ var styles5 = import_react_native7.StyleSheet.create({
14657
15313
  function TestFeedbackScreen({ status, assignmentId, nav }) {
14658
15314
  const { client, assignments, refreshAssignments, uploadImage } = useBugBear();
14659
15315
  const images = useImageAttachments(uploadImage, 3, "screenshots");
14660
- const [rating, setRating] = (0, import_react9.useState)(5);
14661
- const [note, setNote] = (0, import_react9.useState)("");
14662
- const [flags, setFlags] = (0, import_react9.useState)({ isOutdated: false, needsMoreDetail: false, stepsUnclear: false, expectedResultUnclear: false });
14663
- const [submitting, setSubmitting] = (0, import_react9.useState)(false);
15316
+ const [rating, setRating] = (0, import_react10.useState)(5);
15317
+ const [note, setNote] = (0, import_react10.useState)("");
15318
+ const [flags, setFlags] = (0, import_react10.useState)({ isOutdated: false, needsMoreDetail: false, stepsUnclear: false, expectedResultUnclear: false });
15319
+ const [submitting, setSubmitting] = (0, import_react10.useState)(false);
14664
15320
  const assignment = assignments.find((a) => a.id === assignmentId);
14665
15321
  const showFlags = rating < 4;
14666
15322
  const handleSubmit = async () => {
@@ -14715,22 +15371,22 @@ function TestFeedbackScreen({ status, assignmentId, nav }) {
14715
15371
  }
14716
15372
  }
14717
15373
  };
14718
- 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?"), [
15374
+ return /* @__PURE__ */ import_react10.default.createElement(import_react_native9.View, { style: styles6.container }, /* @__PURE__ */ import_react10.default.createElement(import_react_native9.Text, { style: styles6.header }, status === "passed" ? "\u2705 Test Passed!" : "\u274C Test Failed"), /* @__PURE__ */ import_react10.default.createElement(import_react_native9.Text, { style: styles6.subheader }, "Rate this test case"), /* @__PURE__ */ import_react10.default.createElement(import_react_native9.View, { style: styles6.starRow }, [1, 2, 3, 4, 5].map((n) => /* @__PURE__ */ import_react10.default.createElement(import_react_native9.TouchableOpacity, { key: n, onPress: () => setRating(n), style: styles6.starButton }, /* @__PURE__ */ import_react10.default.createElement(import_react_native9.Text, { style: [styles6.star, n <= rating && styles6.starActive] }, n <= rating ? "\u2605" : "\u2606")))), showFlags && /* @__PURE__ */ import_react10.default.createElement(import_react_native9.View, { style: styles6.flagsSection }, /* @__PURE__ */ import_react10.default.createElement(import_react_native9.Text, { style: styles6.flagsLabel }, "What could be improved?"), [
14719
15375
  { key: "isOutdated", label: "Test is outdated" },
14720
15376
  { key: "needsMoreDetail", label: "Needs more detail" },
14721
15377
  { key: "stepsUnclear", label: "Steps are unclear" },
14722
15378
  { key: "expectedResultUnclear", label: "Expected result unclear" }
14723
- ].map(({ key, label }) => /* @__PURE__ */ import_react9.default.createElement(
14724
- import_react_native8.TouchableOpacity,
15379
+ ].map(({ key, label }) => /* @__PURE__ */ import_react10.default.createElement(
15380
+ import_react_native9.TouchableOpacity,
14725
15381
  {
14726
15382
  key,
14727
15383
  style: [styles6.flagItem, flags[key] && styles6.flagItemActive],
14728
15384
  onPress: () => setFlags((prev) => ({ ...prev, [key]: !prev[key] }))
14729
15385
  },
14730
- /* @__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")),
14731
- /* @__PURE__ */ import_react9.default.createElement(import_react_native8.Text, { style: [styles6.flagText, flags[key] && styles6.flagTextActive] }, label)
14732
- ))), /* @__PURE__ */ import_react9.default.createElement(
14733
- import_react_native8.TextInput,
15386
+ /* @__PURE__ */ import_react10.default.createElement(import_react_native9.View, { style: [styles6.flagCheck, flags[key] && styles6.flagCheckActive] }, flags[key] && /* @__PURE__ */ import_react10.default.createElement(import_react_native9.Text, { style: styles6.flagCheckmark }, "\u2713")),
15387
+ /* @__PURE__ */ import_react10.default.createElement(import_react_native9.Text, { style: [styles6.flagText, flags[key] && styles6.flagTextActive] }, label)
15388
+ ))), /* @__PURE__ */ import_react10.default.createElement(
15389
+ import_react_native9.TextInput,
14734
15390
  {
14735
15391
  style: styles6.noteInput,
14736
15392
  value: note,
@@ -14739,7 +15395,7 @@ function TestFeedbackScreen({ status, assignmentId, nav }) {
14739
15395
  placeholderTextColor: colors.textMuted,
14740
15396
  multiline: true
14741
15397
  }
14742
- ), /* @__PURE__ */ import_react9.default.createElement(
15398
+ ), /* @__PURE__ */ import_react10.default.createElement(
14743
15399
  ImagePickerButtons,
14744
15400
  {
14745
15401
  images: images.images,
@@ -14749,9 +15405,9 @@ function TestFeedbackScreen({ status, assignmentId, nav }) {
14749
15405
  onRemove: images.removeImage,
14750
15406
  label: "Screenshots (optional)"
14751
15407
  }
14752
- ), /* @__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"))));
15408
+ ), /* @__PURE__ */ import_react10.default.createElement(import_react_native9.View, { style: styles6.actions }, /* @__PURE__ */ import_react10.default.createElement(import_react_native9.TouchableOpacity, { style: styles6.skipButton, onPress: handleSkip }, /* @__PURE__ */ import_react10.default.createElement(import_react_native9.Text, { style: styles6.skipText }, "Skip")), /* @__PURE__ */ import_react10.default.createElement(import_react_native9.TouchableOpacity, { style: [shared.primaryButton, { flex: 2, opacity: submitting || images.isUploading ? 0.5 : 1 }], onPress: handleSubmit, disabled: submitting || images.isUploading }, /* @__PURE__ */ import_react10.default.createElement(import_react_native9.Text, { style: shared.primaryButtonText }, images.isUploading ? "Uploading..." : submitting ? "Submitting..." : "Submit"))));
14753
15409
  }
14754
- var styles6 = import_react_native8.StyleSheet.create({
15410
+ var styles6 = import_react_native9.StyleSheet.create({
14755
15411
  container: { paddingTop: 8 },
14756
15412
  header: { fontSize: 22, fontWeight: "700", color: colors.textPrimary, textAlign: "center", marginBottom: 4 },
14757
15413
  subheader: { fontSize: 14, color: colors.textMuted, textAlign: "center", marginBottom: 20 },
@@ -14775,12 +15431,12 @@ var styles6 = import_react_native8.StyleSheet.create({
14775
15431
  });
14776
15432
 
14777
15433
  // src/widget/screens/ReportScreen.tsx
14778
- var import_react11 = __toESM(require("react"));
14779
- var import_react_native10 = require("react-native");
15434
+ var import_react12 = __toESM(require("react"));
15435
+ var import_react_native11 = require("react-native");
14780
15436
 
14781
15437
  // src/widget/CategoryPicker.tsx
14782
- var import_react10 = __toESM(require("react"));
14783
- var import_react_native9 = require("react-native");
15438
+ var import_react11 = __toESM(require("react"));
15439
+ var import_react_native10 = require("react-native");
14784
15440
  var categoryOptions = [
14785
15441
  { value: "ui_ux", label: "UI/UX", icon: "\u{1F3A8}" },
14786
15442
  { value: "functional", label: "Functional", icon: "\u2699\uFE0F" },
@@ -14789,64 +15445,64 @@ var categoryOptions = [
14789
15445
  { value: "other", label: "Other", icon: "\u{1F4DD}" }
14790
15446
  ];
14791
15447
  function CategoryPicker({ value, onChange, optional = true }) {
14792
- const [modalVisible, setModalVisible] = (0, import_react10.useState)(false);
15448
+ const [modalVisible, setModalVisible] = (0, import_react11.useState)(false);
14793
15449
  const selectedOption = value ? categoryOptions.find((o) => o.value === value) : null;
14794
15450
  const handleSelect = (category) => {
14795
15451
  onChange(category);
14796
15452
  setModalVisible(false);
14797
15453
  };
14798
- return /* @__PURE__ */ import_react10.default.createElement(import_react10.default.Fragment, null, /* @__PURE__ */ import_react10.default.createElement(
14799
- import_react_native9.TouchableOpacity,
15454
+ return /* @__PURE__ */ import_react11.default.createElement(import_react11.default.Fragment, null, /* @__PURE__ */ import_react11.default.createElement(
15455
+ import_react_native10.TouchableOpacity,
14800
15456
  {
14801
15457
  style: styles7.trigger,
14802
15458
  onPress: () => setModalVisible(true)
14803
15459
  },
14804
- /* @__PURE__ */ import_react10.default.createElement(import_react_native9.Text, { style: selectedOption ? styles7.triggerTextSelected : styles7.triggerTextPlaceholder }, selectedOption ? `${selectedOption.icon} ${selectedOption.label}` : optional ? "Select category (optional)" : "Select category"),
14805
- /* @__PURE__ */ import_react10.default.createElement(import_react_native9.Text, { style: styles7.chevron }, "\u25BC")
14806
- ), /* @__PURE__ */ import_react10.default.createElement(
14807
- import_react_native9.Modal,
15460
+ /* @__PURE__ */ import_react11.default.createElement(import_react_native10.Text, { style: selectedOption ? styles7.triggerTextSelected : styles7.triggerTextPlaceholder }, selectedOption ? `${selectedOption.icon} ${selectedOption.label}` : optional ? "Select category (optional)" : "Select category"),
15461
+ /* @__PURE__ */ import_react11.default.createElement(import_react_native10.Text, { style: styles7.chevron }, "\u25BC")
15462
+ ), /* @__PURE__ */ import_react11.default.createElement(
15463
+ import_react_native10.Modal,
14808
15464
  {
14809
15465
  visible: modalVisible,
14810
15466
  transparent: true,
14811
15467
  animationType: "fade",
14812
15468
  onRequestClose: () => setModalVisible(false)
14813
15469
  },
14814
- /* @__PURE__ */ import_react10.default.createElement(
14815
- import_react_native9.TouchableOpacity,
15470
+ /* @__PURE__ */ import_react11.default.createElement(
15471
+ import_react_native10.TouchableOpacity,
14816
15472
  {
14817
15473
  style: styles7.overlay,
14818
15474
  activeOpacity: 1,
14819
15475
  onPress: () => setModalVisible(false)
14820
15476
  },
14821
- /* @__PURE__ */ import_react10.default.createElement(import_react_native9.View, { style: styles7.modal, onStartShouldSetResponder: () => true }, /* @__PURE__ */ import_react10.default.createElement(import_react_native9.Text, { style: styles7.modalTitle }, "Select Category"), optional && /* @__PURE__ */ import_react10.default.createElement(
14822
- import_react_native9.TouchableOpacity,
15477
+ /* @__PURE__ */ import_react11.default.createElement(import_react_native10.View, { style: styles7.modal, onStartShouldSetResponder: () => true }, /* @__PURE__ */ import_react11.default.createElement(import_react_native10.Text, { style: styles7.modalTitle }, "Select Category"), optional && /* @__PURE__ */ import_react11.default.createElement(
15478
+ import_react_native10.TouchableOpacity,
14823
15479
  {
14824
15480
  style: [styles7.option, !value && styles7.optionSelected],
14825
15481
  onPress: () => handleSelect(null)
14826
15482
  },
14827
- /* @__PURE__ */ import_react10.default.createElement(import_react_native9.Text, { style: styles7.optionText }, "\u2014 None \u2014")
14828
- ), categoryOptions.map(({ value: optValue, label, icon }) => /* @__PURE__ */ import_react10.default.createElement(
14829
- import_react_native9.TouchableOpacity,
15483
+ /* @__PURE__ */ import_react11.default.createElement(import_react_native10.Text, { style: styles7.optionText }, "\u2014 None \u2014")
15484
+ ), categoryOptions.map(({ value: optValue, label, icon }) => /* @__PURE__ */ import_react11.default.createElement(
15485
+ import_react_native10.TouchableOpacity,
14830
15486
  {
14831
15487
  key: optValue,
14832
15488
  style: [styles7.option, value === optValue && styles7.optionSelected],
14833
15489
  onPress: () => handleSelect(optValue)
14834
15490
  },
14835
- /* @__PURE__ */ import_react10.default.createElement(import_react_native9.Text, { style: styles7.optionIcon }, icon),
14836
- /* @__PURE__ */ import_react10.default.createElement(import_react_native9.Text, { style: styles7.optionText }, label),
14837
- value === optValue && /* @__PURE__ */ import_react10.default.createElement(import_react_native9.Text, { style: styles7.checkmark }, "\u2713")
14838
- )), /* @__PURE__ */ import_react10.default.createElement(
14839
- import_react_native9.TouchableOpacity,
15491
+ /* @__PURE__ */ import_react11.default.createElement(import_react_native10.Text, { style: styles7.optionIcon }, icon),
15492
+ /* @__PURE__ */ import_react11.default.createElement(import_react_native10.Text, { style: styles7.optionText }, label),
15493
+ value === optValue && /* @__PURE__ */ import_react11.default.createElement(import_react_native10.Text, { style: styles7.checkmark }, "\u2713")
15494
+ )), /* @__PURE__ */ import_react11.default.createElement(
15495
+ import_react_native10.TouchableOpacity,
14840
15496
  {
14841
15497
  style: styles7.cancelButton,
14842
15498
  onPress: () => setModalVisible(false)
14843
15499
  },
14844
- /* @__PURE__ */ import_react10.default.createElement(import_react_native9.Text, { style: styles7.cancelText }, "Cancel")
15500
+ /* @__PURE__ */ import_react11.default.createElement(import_react_native10.Text, { style: styles7.cancelText }, "Cancel")
14845
15501
  ))
14846
15502
  )
14847
15503
  ));
14848
15504
  }
14849
- var styles7 = import_react_native9.StyleSheet.create({
15505
+ var styles7 = import_react_native10.StyleSheet.create({
14850
15506
  trigger: {
14851
15507
  flexDirection: "row",
14852
15508
  alignItems: "center",
@@ -14936,18 +15592,18 @@ var styles7 = import_react_native9.StyleSheet.create({
14936
15592
  // src/widget/screens/ReportScreen.tsx
14937
15593
  function ReportScreen({ nav, prefill }) {
14938
15594
  const { client, getDeviceInfo, uploadImage, refreshAssignments } = useBugBear();
14939
- const [reportType, setReportType] = (0, import_react11.useState)(prefill?.type || "bug");
14940
- const [severity, setSeverity] = (0, import_react11.useState)("medium");
14941
- const [category, setCategory] = (0, import_react11.useState)(null);
14942
- const [description, setDescription] = (0, import_react11.useState)("");
14943
- const [affectedScreen, setAffectedScreen] = (0, import_react11.useState)("");
14944
- const [submitting, setSubmitting] = (0, import_react11.useState)(false);
14945
- const [error, setError] = (0, import_react11.useState)(null);
14946
- const submittingRef = (0, import_react11.useRef)(false);
15595
+ const [reportType, setReportType] = (0, import_react12.useState)(prefill?.type || "bug");
15596
+ const [severity, setSeverity] = (0, import_react12.useState)("medium");
15597
+ const [category, setCategory] = (0, import_react12.useState)(null);
15598
+ const [description, setDescription] = (0, import_react12.useState)("");
15599
+ const [affectedScreen, setAffectedScreen] = (0, import_react12.useState)("");
15600
+ const [submitting, setSubmitting] = (0, import_react12.useState)(false);
15601
+ const [error, setError] = (0, import_react12.useState)(null);
15602
+ const submittingRef = (0, import_react12.useRef)(false);
14947
15603
  const images = useImageAttachments(uploadImage, 5, "screenshots");
14948
15604
  const isRetestFailure = prefill?.type === "test_fail";
14949
15605
  const isBugType = reportType === "bug" || reportType === "test_fail";
14950
- (0, import_react11.useEffect)(() => {
15606
+ (0, import_react12.useEffect)(() => {
14951
15607
  if (reportType === "feedback" || reportType === "suggestion") {
14952
15608
  setCategory("other");
14953
15609
  } else {
@@ -14996,21 +15652,21 @@ function ReportScreen({ nav, prefill }) {
14996
15652
  submittingRef.current = false;
14997
15653
  }
14998
15654
  };
14999
- return /* @__PURE__ */ import_react11.default.createElement(import_react_native10.View, null, isRetestFailure ? /* @__PURE__ */ import_react11.default.createElement(import_react11.default.Fragment, null, /* @__PURE__ */ import_react11.default.createElement(import_react_native10.View, { style: styles8.retestBanner }, /* @__PURE__ */ import_react11.default.createElement(import_react_native10.Text, { style: styles8.retestIcon }, "\u{1F504}"), /* @__PURE__ */ import_react11.default.createElement(import_react_native10.View, null, /* @__PURE__ */ import_react11.default.createElement(import_react_native10.Text, { style: styles8.retestTitle }, "Bug Still Present"), /* @__PURE__ */ import_react11.default.createElement(import_react_native10.Text, { style: styles8.retestSubtitle }, "The fix did not resolve this issue"))), /* @__PURE__ */ import_react11.default.createElement(import_react_native10.View, { style: styles8.section }, /* @__PURE__ */ import_react11.default.createElement(import_react_native10.Text, { style: shared.label }, "Severity"), /* @__PURE__ */ import_react11.default.createElement(import_react_native10.View, { style: styles8.severityRow }, [
15655
+ return /* @__PURE__ */ import_react12.default.createElement(import_react_native11.View, null, isRetestFailure ? /* @__PURE__ */ import_react12.default.createElement(import_react12.default.Fragment, null, /* @__PURE__ */ import_react12.default.createElement(import_react_native11.View, { style: styles8.retestBanner }, /* @__PURE__ */ import_react12.default.createElement(import_react_native11.Text, { style: styles8.retestIcon }, "\u{1F504}"), /* @__PURE__ */ import_react12.default.createElement(import_react_native11.View, null, /* @__PURE__ */ import_react12.default.createElement(import_react_native11.Text, { style: styles8.retestTitle }, "Bug Still Present"), /* @__PURE__ */ import_react12.default.createElement(import_react_native11.Text, { style: styles8.retestSubtitle }, "The fix did not resolve this issue"))), /* @__PURE__ */ import_react12.default.createElement(import_react_native11.View, { style: styles8.section }, /* @__PURE__ */ import_react12.default.createElement(import_react_native11.Text, { style: shared.label }, "Severity"), /* @__PURE__ */ import_react12.default.createElement(import_react_native11.View, { style: styles8.severityRow }, [
15000
15656
  { sev: "critical", color: "#ef4444" },
15001
15657
  { sev: "high", color: "#f97316" },
15002
15658
  { sev: "medium", color: "#eab308" },
15003
15659
  { sev: "low", color: "#6b7280" }
15004
- ].map(({ sev, color }) => /* @__PURE__ */ import_react11.default.createElement(
15005
- import_react_native10.TouchableOpacity,
15660
+ ].map(({ sev, color }) => /* @__PURE__ */ import_react12.default.createElement(
15661
+ import_react_native11.TouchableOpacity,
15006
15662
  {
15007
15663
  key: sev,
15008
15664
  style: [styles8.sevButton, severity === sev && { backgroundColor: `${color}30`, borderColor: color }],
15009
15665
  onPress: () => setSeverity(sev)
15010
15666
  },
15011
- /* @__PURE__ */ import_react11.default.createElement(import_react_native10.Text, { style: [styles8.sevText, severity === sev && { color }] }, sev)
15012
- )))), /* @__PURE__ */ import_react11.default.createElement(import_react_native10.View, { style: styles8.section }, /* @__PURE__ */ import_react11.default.createElement(import_react_native10.Text, { style: shared.label }, "Category (optional)"), /* @__PURE__ */ import_react11.default.createElement(CategoryPicker, { value: category, onChange: setCategory, optional: true })), /* @__PURE__ */ import_react11.default.createElement(import_react_native10.View, { style: styles8.section }, /* @__PURE__ */ import_react11.default.createElement(import_react_native10.Text, { style: shared.label }, "What went wrong?"), /* @__PURE__ */ import_react11.default.createElement(
15013
- import_react_native10.TextInput,
15667
+ /* @__PURE__ */ import_react12.default.createElement(import_react_native11.Text, { style: [styles8.sevText, severity === sev && { color }] }, sev)
15668
+ )))), /* @__PURE__ */ import_react12.default.createElement(import_react_native11.View, { style: styles8.section }, /* @__PURE__ */ import_react12.default.createElement(import_react_native11.Text, { style: shared.label }, "Category (optional)"), /* @__PURE__ */ import_react12.default.createElement(CategoryPicker, { value: category, onChange: setCategory, optional: true })), /* @__PURE__ */ import_react12.default.createElement(import_react_native11.View, { style: styles8.section }, /* @__PURE__ */ import_react12.default.createElement(import_react_native11.Text, { style: shared.label }, "What went wrong?"), /* @__PURE__ */ import_react12.default.createElement(
15669
+ import_react_native11.TextInput,
15014
15670
  {
15015
15671
  style: styles8.descInput,
15016
15672
  value: description,
@@ -15021,7 +15677,7 @@ function ReportScreen({ nav, prefill }) {
15021
15677
  numberOfLines: 4,
15022
15678
  textAlignVertical: "top"
15023
15679
  }
15024
- )), /* @__PURE__ */ import_react11.default.createElement(
15680
+ )), /* @__PURE__ */ import_react12.default.createElement(
15025
15681
  ImagePickerButtons,
15026
15682
  {
15027
15683
  images: images.images,
@@ -15031,42 +15687,42 @@ function ReportScreen({ nav, prefill }) {
15031
15687
  onRemove: images.removeImage,
15032
15688
  label: "Attachments (optional)"
15033
15689
  }
15034
- ), error && /* @__PURE__ */ import_react11.default.createElement(import_react_native10.View, { style: styles8.errorBanner }, /* @__PURE__ */ import_react11.default.createElement(import_react_native10.Text, { style: styles8.errorText }, error)), /* @__PURE__ */ import_react11.default.createElement(
15035
- import_react_native10.TouchableOpacity,
15690
+ ), error && /* @__PURE__ */ import_react12.default.createElement(import_react_native11.View, { style: styles8.errorBanner }, /* @__PURE__ */ import_react12.default.createElement(import_react_native11.Text, { style: styles8.errorText }, error)), /* @__PURE__ */ import_react12.default.createElement(
15691
+ import_react_native11.TouchableOpacity,
15036
15692
  {
15037
15693
  style: [shared.primaryButton, styles8.retestSubmitButton, (!description.trim() || submitting || images.isUploading) && shared.primaryButtonDisabled, { marginTop: 20 }],
15038
15694
  onPress: handleSubmit,
15039
15695
  disabled: !description.trim() || submitting || images.isUploading
15040
15696
  },
15041
- /* @__PURE__ */ import_react11.default.createElement(import_react_native10.Text, { style: shared.primaryButtonText }, images.isUploading ? "Uploading images..." : submitting ? "Submitting..." : error ? "Retry" : "Submit Failed Retest")
15042
- )) : /* @__PURE__ */ import_react11.default.createElement(import_react11.default.Fragment, null, /* @__PURE__ */ import_react11.default.createElement(import_react_native10.Text, { style: shared.label }, "What are you reporting?"), /* @__PURE__ */ import_react11.default.createElement(import_react_native10.View, { style: styles8.typeRow }, [
15697
+ /* @__PURE__ */ import_react12.default.createElement(import_react_native11.Text, { style: shared.primaryButtonText }, images.isUploading ? "Uploading images..." : submitting ? "Submitting..." : error ? "Retry" : "Submit Failed Retest")
15698
+ )) : /* @__PURE__ */ import_react12.default.createElement(import_react12.default.Fragment, null, /* @__PURE__ */ import_react12.default.createElement(import_react_native11.Text, { style: shared.label }, "What are you reporting?"), /* @__PURE__ */ import_react12.default.createElement(import_react_native11.View, { style: styles8.typeRow }, [
15043
15699
  { type: "bug", label: "Bug", icon: "\u{1F41B}" },
15044
15700
  { type: "feedback", label: "Feedback", icon: "\u{1F4A1}" },
15045
15701
  { type: "suggestion", label: "Idea", icon: "\u2728" }
15046
- ].map(({ type, label, icon }) => /* @__PURE__ */ import_react11.default.createElement(
15047
- import_react_native10.TouchableOpacity,
15702
+ ].map(({ type, label, icon }) => /* @__PURE__ */ import_react12.default.createElement(
15703
+ import_react_native11.TouchableOpacity,
15048
15704
  {
15049
15705
  key: type,
15050
15706
  style: [styles8.typeCard, reportType === type && styles8.typeCardActive],
15051
15707
  onPress: () => setReportType(type)
15052
15708
  },
15053
- /* @__PURE__ */ import_react11.default.createElement(import_react_native10.Text, { style: styles8.typeIcon }, icon),
15054
- /* @__PURE__ */ import_react11.default.createElement(import_react_native10.Text, { style: [styles8.typeLabel, reportType === type && styles8.typeLabelActive] }, label)
15055
- ))), isBugType && /* @__PURE__ */ import_react11.default.createElement(import_react_native10.View, { style: styles8.section }, /* @__PURE__ */ import_react11.default.createElement(import_react_native10.Text, { style: shared.label }, "Severity"), /* @__PURE__ */ import_react11.default.createElement(import_react_native10.View, { style: styles8.severityRow }, [
15709
+ /* @__PURE__ */ import_react12.default.createElement(import_react_native11.Text, { style: styles8.typeIcon }, icon),
15710
+ /* @__PURE__ */ import_react12.default.createElement(import_react_native11.Text, { style: [styles8.typeLabel, reportType === type && styles8.typeLabelActive] }, label)
15711
+ ))), isBugType && /* @__PURE__ */ import_react12.default.createElement(import_react_native11.View, { style: styles8.section }, /* @__PURE__ */ import_react12.default.createElement(import_react_native11.Text, { style: shared.label }, "Severity"), /* @__PURE__ */ import_react12.default.createElement(import_react_native11.View, { style: styles8.severityRow }, [
15056
15712
  { sev: "critical", color: "#ef4444" },
15057
15713
  { sev: "high", color: "#f97316" },
15058
15714
  { sev: "medium", color: "#eab308" },
15059
15715
  { sev: "low", color: "#6b7280" }
15060
- ].map(({ sev, color }) => /* @__PURE__ */ import_react11.default.createElement(
15061
- import_react_native10.TouchableOpacity,
15716
+ ].map(({ sev, color }) => /* @__PURE__ */ import_react12.default.createElement(
15717
+ import_react_native11.TouchableOpacity,
15062
15718
  {
15063
15719
  key: sev,
15064
15720
  style: [styles8.sevButton, severity === sev && { backgroundColor: `${color}30`, borderColor: color }],
15065
15721
  onPress: () => setSeverity(sev)
15066
15722
  },
15067
- /* @__PURE__ */ import_react11.default.createElement(import_react_native10.Text, { style: [styles8.sevText, severity === sev && { color }] }, sev)
15068
- )))), isBugType && /* @__PURE__ */ import_react11.default.createElement(import_react_native10.View, { style: styles8.section }, /* @__PURE__ */ import_react11.default.createElement(import_react_native10.Text, { style: shared.label }, "Category (optional)"), /* @__PURE__ */ import_react11.default.createElement(CategoryPicker, { value: category, onChange: setCategory, optional: true })), /* @__PURE__ */ import_react11.default.createElement(import_react_native10.View, { style: styles8.section }, /* @__PURE__ */ import_react11.default.createElement(import_react_native10.Text, { style: shared.label }, "What happened?"), /* @__PURE__ */ import_react11.default.createElement(
15069
- import_react_native10.TextInput,
15723
+ /* @__PURE__ */ import_react12.default.createElement(import_react_native11.Text, { style: [styles8.sevText, severity === sev && { color }] }, sev)
15724
+ )))), isBugType && /* @__PURE__ */ import_react12.default.createElement(import_react_native11.View, { style: styles8.section }, /* @__PURE__ */ import_react12.default.createElement(import_react_native11.Text, { style: shared.label }, "Category (optional)"), /* @__PURE__ */ import_react12.default.createElement(CategoryPicker, { value: category, onChange: setCategory, optional: true })), /* @__PURE__ */ import_react12.default.createElement(import_react_native11.View, { style: styles8.section }, /* @__PURE__ */ import_react12.default.createElement(import_react_native11.Text, { style: shared.label }, "What happened?"), /* @__PURE__ */ import_react12.default.createElement(
15725
+ import_react_native11.TextInput,
15070
15726
  {
15071
15727
  style: styles8.descInput,
15072
15728
  value: description,
@@ -15077,8 +15733,8 @@ function ReportScreen({ nav, prefill }) {
15077
15733
  numberOfLines: 4,
15078
15734
  textAlignVertical: "top"
15079
15735
  }
15080
- )), isBugType && /* @__PURE__ */ import_react11.default.createElement(import_react_native10.View, { style: styles8.section }, /* @__PURE__ */ import_react11.default.createElement(import_react_native10.Text, { style: shared.label }, "Which screen?"), /* @__PURE__ */ import_react11.default.createElement(
15081
- import_react_native10.TextInput,
15736
+ )), isBugType && /* @__PURE__ */ import_react12.default.createElement(import_react_native11.View, { style: styles8.section }, /* @__PURE__ */ import_react12.default.createElement(import_react_native11.Text, { style: shared.label }, "Which screen?"), /* @__PURE__ */ import_react12.default.createElement(
15737
+ import_react_native11.TextInput,
15082
15738
  {
15083
15739
  style: styles8.screenInput,
15084
15740
  value: affectedScreen,
@@ -15086,7 +15742,7 @@ function ReportScreen({ nav, prefill }) {
15086
15742
  placeholder: "e.g. Reservations, Settings...",
15087
15743
  placeholderTextColor: colors.textMuted
15088
15744
  }
15089
- ), /* @__PURE__ */ import_react11.default.createElement(import_react_native10.Text, { style: styles8.screenHint }, "Which screen or area was the bug on? (optional)")), /* @__PURE__ */ import_react11.default.createElement(
15745
+ ), /* @__PURE__ */ import_react12.default.createElement(import_react_native11.Text, { style: styles8.screenHint }, "Which screen or area was the bug on? (optional)")), /* @__PURE__ */ import_react12.default.createElement(
15090
15746
  ImagePickerButtons,
15091
15747
  {
15092
15748
  images: images.images,
@@ -15096,17 +15752,17 @@ function ReportScreen({ nav, prefill }) {
15096
15752
  onRemove: images.removeImage,
15097
15753
  label: "Screenshots (optional)"
15098
15754
  }
15099
- ), error && /* @__PURE__ */ import_react11.default.createElement(import_react_native10.View, { style: styles8.errorBanner }, /* @__PURE__ */ import_react11.default.createElement(import_react_native10.Text, { style: styles8.errorText }, error)), /* @__PURE__ */ import_react11.default.createElement(
15100
- import_react_native10.TouchableOpacity,
15755
+ ), error && /* @__PURE__ */ import_react12.default.createElement(import_react_native11.View, { style: styles8.errorBanner }, /* @__PURE__ */ import_react12.default.createElement(import_react_native11.Text, { style: styles8.errorText }, error)), /* @__PURE__ */ import_react12.default.createElement(
15756
+ import_react_native11.TouchableOpacity,
15101
15757
  {
15102
15758
  style: [shared.primaryButton, (!description.trim() || submitting || images.isUploading) && shared.primaryButtonDisabled, { marginTop: 20 }],
15103
15759
  onPress: handleSubmit,
15104
15760
  disabled: !description.trim() || submitting || images.isUploading
15105
15761
  },
15106
- /* @__PURE__ */ import_react11.default.createElement(import_react_native10.Text, { style: shared.primaryButtonText }, images.isUploading ? "Uploading images..." : submitting ? "Submitting..." : error ? "Retry" : "Submit Report")
15762
+ /* @__PURE__ */ import_react12.default.createElement(import_react_native11.Text, { style: shared.primaryButtonText }, images.isUploading ? "Uploading images..." : submitting ? "Submitting..." : error ? "Retry" : "Submit Report")
15107
15763
  )));
15108
15764
  }
15109
- var styles8 = import_react_native10.StyleSheet.create({
15765
+ var styles8 = import_react_native11.StyleSheet.create({
15110
15766
  typeRow: { flexDirection: "row", gap: 10, marginBottom: 20 },
15111
15767
  typeCard: { flex: 1, alignItems: "center", paddingVertical: 16, borderRadius: 12, backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border },
15112
15768
  typeCardActive: { borderColor: colors.blue, backgroundColor: "#172554" },
@@ -15130,16 +15786,16 @@ var styles8 = import_react_native10.StyleSheet.create({
15130
15786
  });
15131
15787
 
15132
15788
  // src/widget/screens/ReportSuccessScreen.tsx
15133
- var import_react12 = __toESM(require("react"));
15134
- var import_react_native11 = require("react-native");
15789
+ var import_react13 = __toESM(require("react"));
15790
+ var import_react_native12 = require("react-native");
15135
15791
  function ReportSuccessScreen({ nav }) {
15136
- (0, import_react12.useEffect)(() => {
15792
+ (0, import_react13.useEffect)(() => {
15137
15793
  const timer = setTimeout(() => nav.reset(), 2e3);
15138
15794
  return () => clearTimeout(timer);
15139
15795
  }, [nav]);
15140
- return /* @__PURE__ */ import_react12.default.createElement(import_react_native11.View, { style: styles9.container }, /* @__PURE__ */ import_react12.default.createElement(import_react_native11.Text, { style: styles9.emoji }, "\u{1F389}"), /* @__PURE__ */ import_react12.default.createElement(import_react_native11.Text, { style: styles9.title }, "Report submitted!"), /* @__PURE__ */ import_react12.default.createElement(import_react_native11.Text, { style: styles9.subtitle }, "Thank you for your feedback"));
15796
+ return /* @__PURE__ */ import_react13.default.createElement(import_react_native12.View, { style: styles9.container }, /* @__PURE__ */ import_react13.default.createElement(import_react_native12.Text, { style: styles9.emoji }, "\u{1F389}"), /* @__PURE__ */ import_react13.default.createElement(import_react_native12.Text, { style: styles9.title }, "Report submitted!"), /* @__PURE__ */ import_react13.default.createElement(import_react_native12.Text, { style: styles9.subtitle }, "Thank you for your feedback"));
15141
15797
  }
15142
- var styles9 = import_react_native11.StyleSheet.create({
15798
+ var styles9 = import_react_native12.StyleSheet.create({
15143
15799
  container: { alignItems: "center", paddingVertical: 60 },
15144
15800
  emoji: { fontSize: 48, marginBottom: 16 },
15145
15801
  title: { fontSize: 22, fontWeight: "700", color: colors.textPrimary, marginBottom: 6 },
@@ -15147,29 +15803,38 @@ var styles9 = import_react_native11.StyleSheet.create({
15147
15803
  });
15148
15804
 
15149
15805
  // src/widget/screens/MessageListScreen.tsx
15150
- var import_react13 = __toESM(require("react"));
15151
- var import_react_native12 = require("react-native");
15806
+ var import_react14 = __toESM(require("react"));
15807
+ var import_react_native13 = require("react-native");
15152
15808
  function MessageListScreen({ nav }) {
15153
- const { threads, unreadCount, refreshThreads } = useBugBear();
15154
- return /* @__PURE__ */ import_react13.default.createElement(import_react_native12.View, null, /* @__PURE__ */ import_react13.default.createElement(
15155
- import_react_native12.TouchableOpacity,
15809
+ const { threads, unreadCount, refreshThreads, dashboardUrl, isLoading } = useBugBear();
15810
+ if (isLoading) return /* @__PURE__ */ import_react14.default.createElement(MessageListScreenSkeleton, null);
15811
+ return /* @__PURE__ */ import_react14.default.createElement(import_react_native13.View, null, /* @__PURE__ */ import_react14.default.createElement(
15812
+ import_react_native13.TouchableOpacity,
15156
15813
  {
15157
15814
  style: styles10.newMsgButton,
15158
15815
  onPress: () => nav.push({ name: "COMPOSE_MESSAGE" })
15159
15816
  },
15160
- /* @__PURE__ */ import_react13.default.createElement(import_react_native12.Text, { style: styles10.newMsgText }, "\u2709\uFE0F New Message")
15161
- ), threads.length === 0 ? /* @__PURE__ */ import_react13.default.createElement(import_react_native12.View, { style: shared.emptyState }, /* @__PURE__ */ import_react13.default.createElement(import_react_native12.Text, { style: shared.emptyEmoji }, "\u{1F4AC}"), /* @__PURE__ */ import_react13.default.createElement(import_react_native12.Text, { style: shared.emptyTitle }, "No messages yet"), /* @__PURE__ */ import_react13.default.createElement(import_react_native12.Text, { style: shared.emptySubtitle }, "Start a conversation or wait for messages from admins")) : /* @__PURE__ */ import_react13.default.createElement(import_react_native12.View, null, threads.map((thread) => /* @__PURE__ */ import_react13.default.createElement(
15162
- import_react_native12.TouchableOpacity,
15817
+ /* @__PURE__ */ import_react14.default.createElement(import_react_native13.Text, { style: styles10.newMsgText }, "\u2709\uFE0F New Message")
15818
+ ), threads.length === 0 ? /* @__PURE__ */ import_react14.default.createElement(import_react_native13.View, { style: shared.emptyState }, /* @__PURE__ */ import_react14.default.createElement(import_react_native13.Text, { style: shared.emptyEmoji }, "\u{1F4AC}"), /* @__PURE__ */ import_react14.default.createElement(import_react_native13.Text, { style: shared.emptyTitle }, "No messages yet"), /* @__PURE__ */ import_react14.default.createElement(import_react_native13.Text, { style: shared.emptySubtitle }, "Start a conversation or wait for messages from admins")) : /* @__PURE__ */ import_react14.default.createElement(import_react_native13.View, null, threads.map((thread) => /* @__PURE__ */ import_react14.default.createElement(
15819
+ import_react_native13.TouchableOpacity,
15163
15820
  {
15164
15821
  key: thread.id,
15165
15822
  style: [styles10.threadItem, thread.unreadCount > 0 && styles10.threadItemUnread],
15166
15823
  onPress: () => nav.push({ name: "THREAD_DETAIL", thread })
15167
15824
  },
15168
- /* @__PURE__ */ import_react13.default.createElement(import_react_native12.View, { style: styles10.threadLeft }, /* @__PURE__ */ import_react13.default.createElement(import_react_native12.Text, { style: styles10.threadIcon }, getThreadTypeIcon(thread.threadType)), /* @__PURE__ */ import_react13.default.createElement(import_react_native12.View, { style: styles10.threadInfo }, /* @__PURE__ */ import_react13.default.createElement(import_react_native12.View, { style: styles10.threadTitleRow }, thread.isPinned && /* @__PURE__ */ import_react13.default.createElement(import_react_native12.Text, { style: styles10.pinIcon }, "\u{1F4CC}"), /* @__PURE__ */ import_react13.default.createElement(import_react_native12.Text, { style: styles10.threadSubject, numberOfLines: 1 }, thread.subject || "No subject")), thread.lastMessage && /* @__PURE__ */ import_react13.default.createElement(import_react_native12.Text, { style: styles10.threadPreview, numberOfLines: 1 }, thread.lastMessage.senderName, ": ", thread.lastMessage.content))),
15169
- /* @__PURE__ */ import_react13.default.createElement(import_react_native12.View, { style: styles10.threadRight }, /* @__PURE__ */ import_react13.default.createElement(import_react_native12.Text, { style: styles10.threadTime }, formatRelativeTime(thread.lastMessageAt)), thread.unreadCount > 0 && /* @__PURE__ */ import_react13.default.createElement(import_react_native12.View, { style: styles10.unreadBadge }, /* @__PURE__ */ import_react13.default.createElement(import_react_native12.Text, { style: styles10.unreadText }, thread.unreadCount)), thread.priority !== "normal" && /* @__PURE__ */ import_react13.default.createElement(import_react_native12.View, { style: [styles10.priorityDot, { backgroundColor: getPriorityColor(thread.priority) }] }))
15170
- ))), /* @__PURE__ */ import_react13.default.createElement(import_react_native12.View, { style: styles10.footer }, /* @__PURE__ */ import_react13.default.createElement(import_react_native12.Text, { style: styles10.footerText }, threads.length, " thread", threads.length !== 1 ? "s" : "", " \xB7 ", unreadCount, " unread"), /* @__PURE__ */ import_react13.default.createElement(import_react_native12.TouchableOpacity, { onPress: refreshThreads }, /* @__PURE__ */ import_react13.default.createElement(import_react_native12.Text, { style: styles10.refreshText }, "\u21BB Refresh"))));
15825
+ /* @__PURE__ */ import_react14.default.createElement(import_react_native13.View, { style: styles10.threadLeft }, /* @__PURE__ */ import_react14.default.createElement(import_react_native13.Text, { style: styles10.threadIcon }, getThreadTypeIcon(thread.threadType)), /* @__PURE__ */ import_react14.default.createElement(import_react_native13.View, { style: styles10.threadInfo }, /* @__PURE__ */ import_react14.default.createElement(import_react_native13.View, { style: styles10.threadTitleRow }, thread.isPinned && /* @__PURE__ */ import_react14.default.createElement(import_react_native13.Text, { style: styles10.pinIcon }, "\u{1F4CC}"), /* @__PURE__ */ import_react14.default.createElement(import_react_native13.Text, { style: styles10.threadSubject, numberOfLines: 1 }, thread.subject || "No subject")), thread.lastMessage && /* @__PURE__ */ import_react14.default.createElement(import_react_native13.Text, { style: styles10.threadPreview, numberOfLines: 1 }, thread.lastMessage.senderName, ": ", thread.lastMessage.content))),
15826
+ /* @__PURE__ */ import_react14.default.createElement(import_react_native13.View, { style: styles10.threadRight }, /* @__PURE__ */ import_react14.default.createElement(import_react_native13.Text, { style: styles10.threadTime }, formatRelativeTime(thread.lastMessageAt)), thread.unreadCount > 0 && /* @__PURE__ */ import_react14.default.createElement(import_react_native13.View, { style: styles10.unreadBadge }, /* @__PURE__ */ import_react14.default.createElement(import_react_native13.Text, { style: styles10.unreadText }, thread.unreadCount)), thread.priority !== "normal" && /* @__PURE__ */ import_react14.default.createElement(import_react_native13.View, { style: [styles10.priorityDot, { backgroundColor: getPriorityColor(thread.priority) }] }))
15827
+ ))), dashboardUrl && /* @__PURE__ */ import_react14.default.createElement(
15828
+ import_react_native13.TouchableOpacity,
15829
+ {
15830
+ style: styles10.dashboardLink,
15831
+ onPress: () => import_react_native13.Linking.openURL(`${dashboardUrl}/discussions`),
15832
+ activeOpacity: 0.7
15833
+ },
15834
+ /* @__PURE__ */ import_react14.default.createElement(import_react_native13.Text, { style: styles10.dashboardLinkText }, "\u{1F310}", " View on Dashboard ", "\u2192")
15835
+ ), /* @__PURE__ */ import_react14.default.createElement(import_react_native13.View, { style: styles10.footer }, /* @__PURE__ */ import_react14.default.createElement(import_react_native13.Text, { style: styles10.footerText }, threads.length, " thread", threads.length !== 1 ? "s" : "", " \xB7 ", unreadCount, " unread"), /* @__PURE__ */ import_react14.default.createElement(import_react_native13.TouchableOpacity, { onPress: refreshThreads }, /* @__PURE__ */ import_react14.default.createElement(import_react_native13.Text, { style: styles10.refreshText }, "\u21BB Refresh"))));
15171
15836
  }
15172
- var styles10 = import_react_native12.StyleSheet.create({
15837
+ var styles10 = import_react_native13.StyleSheet.create({
15173
15838
  newMsgButton: { backgroundColor: colors.blue, paddingVertical: 12, borderRadius: 12, alignItems: "center", marginBottom: 16 },
15174
15839
  newMsgText: { fontSize: 15, fontWeight: "600", color: "#fff" },
15175
15840
  threadItem: { flexDirection: "row", justifyContent: "space-between", paddingVertical: 12, paddingHorizontal: 12, borderRadius: 10, marginBottom: 4, backgroundColor: colors.card },
@@ -15186,23 +15851,25 @@ var styles10 = import_react_native12.StyleSheet.create({
15186
15851
  unreadBadge: { backgroundColor: colors.blue, borderRadius: 10, minWidth: 20, height: 20, justifyContent: "center", alignItems: "center", paddingHorizontal: 6 },
15187
15852
  unreadText: { fontSize: 11, fontWeight: "bold", color: "#fff" },
15188
15853
  priorityDot: { width: 8, height: 8, borderRadius: 4 },
15189
- footer: { flexDirection: "row", justifyContent: "space-between", alignItems: "center", paddingTop: 12, paddingHorizontal: 4 },
15854
+ dashboardLink: { alignItems: "center", paddingTop: 12 },
15855
+ dashboardLinkText: { fontSize: 13, fontWeight: "500", color: colors.blue },
15856
+ footer: { flexDirection: "row", justifyContent: "space-between", alignItems: "center", paddingTop: 8, paddingHorizontal: 4 },
15190
15857
  footerText: { fontSize: 12, color: colors.textMuted },
15191
15858
  refreshText: { fontSize: 13, color: colors.blue }
15192
15859
  });
15193
15860
 
15194
15861
  // src/widget/screens/ThreadDetailScreen.tsx
15195
- var import_react14 = __toESM(require("react"));
15196
- var import_react_native13 = require("react-native");
15862
+ var import_react15 = __toESM(require("react"));
15863
+ var import_react_native14 = require("react-native");
15197
15864
  function ThreadDetailScreen({ thread, nav }) {
15198
15865
  const { getThreadMessages, sendMessage, markAsRead, uploadImage } = useBugBear();
15199
- const [messages, setMessages] = (0, import_react14.useState)([]);
15200
- const [loading, setLoading] = (0, import_react14.useState)(true);
15201
- const [replyText, setReplyText] = (0, import_react14.useState)("");
15202
- const [sending, setSending] = (0, import_react14.useState)(false);
15203
- const [sendError, setSendError] = (0, import_react14.useState)(false);
15866
+ const [messages, setMessages] = (0, import_react15.useState)([]);
15867
+ const [loading, setLoading] = (0, import_react15.useState)(true);
15868
+ const [replyText, setReplyText] = (0, import_react15.useState)("");
15869
+ const [sending, setSending] = (0, import_react15.useState)(false);
15870
+ const [sendError, setSendError] = (0, import_react15.useState)(false);
15204
15871
  const replyImages = useImageAttachments(uploadImage, 3, "discussion-attachments");
15205
- (0, import_react14.useEffect)(() => {
15872
+ (0, import_react15.useEffect)(() => {
15206
15873
  let cancelled = false;
15207
15874
  setLoading(true);
15208
15875
  (async () => {
@@ -15247,18 +15914,18 @@ function ThreadDetailScreen({ thread, nav }) {
15247
15914
  }
15248
15915
  setSending(false);
15249
15916
  };
15250
- return /* @__PURE__ */ import_react14.default.createElement(import_react_native13.View, { style: styles11.container }, /* @__PURE__ */ import_react14.default.createElement(import_react_native13.View, { style: styles11.header }, /* @__PURE__ */ import_react14.default.createElement(import_react_native13.Text, { style: styles11.headerIcon }, getThreadTypeIcon(thread.threadType)), /* @__PURE__ */ import_react14.default.createElement(import_react_native13.Text, { style: styles11.headerSubject, numberOfLines: 2 }, thread.subject || "No subject")), loading ? /* @__PURE__ */ import_react14.default.createElement(import_react_native13.View, { style: styles11.loadingContainer }, /* @__PURE__ */ import_react14.default.createElement(import_react_native13.Text, { style: styles11.loadingText }, "Loading messages...")) : /* @__PURE__ */ import_react14.default.createElement(import_react_native13.View, { style: styles11.messagesContainer }, messages.map((msg) => /* @__PURE__ */ import_react14.default.createElement(
15251
- import_react_native13.View,
15917
+ return /* @__PURE__ */ import_react15.default.createElement(import_react_native14.View, { style: styles11.container }, /* @__PURE__ */ import_react15.default.createElement(import_react_native14.View, { style: styles11.header }, /* @__PURE__ */ import_react15.default.createElement(import_react_native14.Text, { style: styles11.headerIcon }, getThreadTypeIcon(thread.threadType)), /* @__PURE__ */ import_react15.default.createElement(import_react_native14.Text, { style: styles11.headerSubject, numberOfLines: 2 }, thread.subject || "No subject")), loading ? /* @__PURE__ */ import_react15.default.createElement(import_react_native14.View, { style: styles11.loadingContainer }, /* @__PURE__ */ import_react15.default.createElement(import_react_native14.Text, { style: styles11.loadingText }, "Loading messages...")) : /* @__PURE__ */ import_react15.default.createElement(import_react_native14.View, { style: styles11.messagesContainer }, messages.map((msg) => /* @__PURE__ */ import_react15.default.createElement(
15918
+ import_react_native14.View,
15252
15919
  {
15253
15920
  key: msg.id,
15254
15921
  style: [styles11.bubble, msg.senderType === "tester" ? styles11.bubbleTester : styles11.bubbleAdmin]
15255
15922
  },
15256
- /* @__PURE__ */ import_react14.default.createElement(import_react_native13.Text, { style: [styles11.sender, msg.senderType === "tester" && styles11.senderTester] }, msg.senderType === "tester" ? "You" : msg.senderName),
15257
- /* @__PURE__ */ import_react14.default.createElement(import_react_native13.Text, { style: [styles11.content, msg.senderType === "tester" && styles11.contentTester] }, msg.content),
15258
- msg.attachments && msg.attachments.length > 0 && /* @__PURE__ */ import_react14.default.createElement(import_react_native13.View, { style: styles11.attachments }, msg.attachments.filter((a) => a.type === "image").map((att, idx) => /* @__PURE__ */ import_react14.default.createElement(import_react_native13.Image, { key: idx, source: { uri: att.url }, style: styles11.attachmentImage, resizeMode: "cover" }))),
15259
- /* @__PURE__ */ import_react14.default.createElement(import_react_native13.Text, { style: [styles11.time, msg.senderType === "tester" && styles11.timeTester] }, formatMessageTime(msg.createdAt))
15260
- ))), sendError && /* @__PURE__ */ import_react14.default.createElement(import_react_native13.View, { style: styles11.errorBar }, /* @__PURE__ */ import_react14.default.createElement(import_react_native13.Text, { style: styles11.errorText }, "Failed to send. Tap Send to retry.")), replyImages.images.length > 0 && /* @__PURE__ */ import_react14.default.createElement(import_react_native13.View, { style: styles11.replyPreview }, /* @__PURE__ */ import_react14.default.createElement(ImagePreviewStrip, { images: replyImages.images, onRemove: replyImages.removeImage })), /* @__PURE__ */ import_react14.default.createElement(import_react_native13.View, { style: styles11.composer }, IMAGE_PICKER_AVAILABLE && /* @__PURE__ */ import_react14.default.createElement(import_react_native13.TouchableOpacity, { style: styles11.attachBtn, onPress: replyImages.pickFromGallery, disabled: replyImages.images.length >= 3 }, /* @__PURE__ */ import_react14.default.createElement(import_react_native13.Text, { style: styles11.attachBtnText }, "\u{1F4CE}")), /* @__PURE__ */ import_react14.default.createElement(
15261
- import_react_native13.TextInput,
15923
+ /* @__PURE__ */ import_react15.default.createElement(import_react_native14.Text, { style: [styles11.sender, msg.senderType === "tester" && styles11.senderTester] }, msg.senderType === "tester" ? "You" : msg.senderName),
15924
+ /* @__PURE__ */ import_react15.default.createElement(import_react_native14.Text, { style: [styles11.content, msg.senderType === "tester" && styles11.contentTester] }, msg.content),
15925
+ msg.attachments && msg.attachments.length > 0 && /* @__PURE__ */ import_react15.default.createElement(import_react_native14.View, { style: styles11.attachments }, msg.attachments.filter((a) => a.type === "image").map((att, idx) => /* @__PURE__ */ import_react15.default.createElement(import_react_native14.Image, { key: idx, source: { uri: att.url }, style: styles11.attachmentImage, resizeMode: "cover" }))),
15926
+ /* @__PURE__ */ import_react15.default.createElement(import_react_native14.Text, { style: [styles11.time, msg.senderType === "tester" && styles11.timeTester] }, formatMessageTime(msg.createdAt))
15927
+ ))), sendError && /* @__PURE__ */ import_react15.default.createElement(import_react_native14.View, { style: styles11.errorBar }, /* @__PURE__ */ import_react15.default.createElement(import_react_native14.Text, { style: styles11.errorText }, "Failed to send. Tap Send to retry.")), replyImages.images.length > 0 && /* @__PURE__ */ import_react15.default.createElement(import_react_native14.View, { style: styles11.replyPreview }, /* @__PURE__ */ import_react15.default.createElement(ImagePreviewStrip, { images: replyImages.images, onRemove: replyImages.removeImage })), /* @__PURE__ */ import_react15.default.createElement(import_react_native14.View, { style: styles11.composer }, IMAGE_PICKER_AVAILABLE && /* @__PURE__ */ import_react15.default.createElement(import_react_native14.TouchableOpacity, { style: styles11.attachBtn, onPress: replyImages.pickFromGallery, disabled: replyImages.images.length >= 3 }, /* @__PURE__ */ import_react15.default.createElement(import_react_native14.Text, { style: styles11.attachBtnText }, "\u{1F4CE}")), /* @__PURE__ */ import_react15.default.createElement(
15928
+ import_react_native14.TextInput,
15262
15929
  {
15263
15930
  style: styles11.replyInput,
15264
15931
  value: replyText,
@@ -15268,17 +15935,17 @@ function ThreadDetailScreen({ thread, nav }) {
15268
15935
  multiline: true,
15269
15936
  maxLength: 1e3
15270
15937
  }
15271
- ), /* @__PURE__ */ import_react14.default.createElement(
15272
- import_react_native13.TouchableOpacity,
15938
+ ), /* @__PURE__ */ import_react15.default.createElement(
15939
+ import_react_native14.TouchableOpacity,
15273
15940
  {
15274
15941
  style: [styles11.sendBtn, (!replyText.trim() && replyImages.images.length === 0 || sending || replyImages.isUploading) && styles11.sendBtnDisabled],
15275
15942
  onPress: handleSend,
15276
15943
  disabled: !replyText.trim() && replyImages.images.length === 0 || sending || replyImages.isUploading
15277
15944
  },
15278
- /* @__PURE__ */ import_react14.default.createElement(import_react_native13.Text, { style: styles11.sendBtnText }, sending ? "..." : "Send")
15945
+ /* @__PURE__ */ import_react15.default.createElement(import_react_native14.Text, { style: styles11.sendBtnText }, sending ? "..." : "Send")
15279
15946
  )));
15280
15947
  }
15281
- var styles11 = import_react_native13.StyleSheet.create({
15948
+ var styles11 = import_react_native14.StyleSheet.create({
15282
15949
  container: { flex: 1 },
15283
15950
  header: { flexDirection: "row", alignItems: "center", gap: 8, marginBottom: 16, paddingBottom: 12, borderBottomWidth: 1, borderBottomColor: colors.border },
15284
15951
  headerIcon: { fontSize: 20 },
@@ -15310,13 +15977,13 @@ var styles11 = import_react_native13.StyleSheet.create({
15310
15977
  });
15311
15978
 
15312
15979
  // src/widget/screens/ComposeMessageScreen.tsx
15313
- var import_react15 = __toESM(require("react"));
15314
- var import_react_native14 = require("react-native");
15980
+ var import_react16 = __toESM(require("react"));
15981
+ var import_react_native15 = require("react-native");
15315
15982
  function ComposeMessageScreen({ nav }) {
15316
15983
  const { createThread, uploadImage } = useBugBear();
15317
- const [subject, setSubject] = (0, import_react15.useState)("");
15318
- const [message, setMessage] = (0, import_react15.useState)("");
15319
- const [sending, setSending] = (0, import_react15.useState)(false);
15984
+ const [subject, setSubject] = (0, import_react16.useState)("");
15985
+ const [message, setMessage] = (0, import_react16.useState)("");
15986
+ const [sending, setSending] = (0, import_react16.useState)(false);
15320
15987
  const images = useImageAttachments(uploadImage, 3, "discussion-attachments");
15321
15988
  const handleSend = async () => {
15322
15989
  if (!subject.trim() || !message.trim() || sending || images.isUploading) return;
@@ -15332,8 +15999,8 @@ function ComposeMessageScreen({ nav }) {
15332
15999
  nav.pop();
15333
16000
  }
15334
16001
  };
15335
- return /* @__PURE__ */ import_react15.default.createElement(import_react_native14.View, null, /* @__PURE__ */ import_react15.default.createElement(import_react_native14.View, { style: styles12.header }, /* @__PURE__ */ import_react15.default.createElement(import_react_native14.Text, { style: styles12.title }, "New Message"), /* @__PURE__ */ import_react15.default.createElement(import_react_native14.Text, { style: styles12.subtitle }, "Send a message to the QA team")), /* @__PURE__ */ import_react15.default.createElement(import_react_native14.View, { style: styles12.form }, /* @__PURE__ */ import_react15.default.createElement(import_react_native14.Text, { style: shared.label }, "Subject"), /* @__PURE__ */ import_react15.default.createElement(
15336
- import_react_native14.TextInput,
16002
+ return /* @__PURE__ */ import_react16.default.createElement(import_react_native15.View, null, /* @__PURE__ */ import_react16.default.createElement(import_react_native15.View, { style: styles12.header }, /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: styles12.title }, "New Message"), /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: styles12.subtitle }, "Send a message to the QA team")), /* @__PURE__ */ import_react16.default.createElement(import_react_native15.View, { style: styles12.form }, /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: shared.label }, "Subject"), /* @__PURE__ */ import_react16.default.createElement(
16003
+ import_react_native15.TextInput,
15337
16004
  {
15338
16005
  style: styles12.subjectInput,
15339
16006
  value: subject,
@@ -15342,8 +16009,8 @@ function ComposeMessageScreen({ nav }) {
15342
16009
  placeholderTextColor: colors.textMuted,
15343
16010
  maxLength: 100
15344
16011
  }
15345
- ), /* @__PURE__ */ import_react15.default.createElement(import_react_native14.Text, { style: [shared.label, { marginTop: 16 }] }, "Message"), /* @__PURE__ */ import_react15.default.createElement(
15346
- import_react_native14.TextInput,
16012
+ ), /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: [shared.label, { marginTop: 16 }] }, "Message"), /* @__PURE__ */ import_react16.default.createElement(
16013
+ import_react_native15.TextInput,
15347
16014
  {
15348
16015
  style: styles12.messageInput,
15349
16016
  value: message,
@@ -15355,7 +16022,7 @@ function ComposeMessageScreen({ nav }) {
15355
16022
  textAlignVertical: "top",
15356
16023
  maxLength: 2e3
15357
16024
  }
15358
- ), /* @__PURE__ */ import_react15.default.createElement(
16025
+ ), /* @__PURE__ */ import_react16.default.createElement(
15359
16026
  ImagePickerButtons,
15360
16027
  {
15361
16028
  images: images.images,
@@ -15364,17 +16031,17 @@ function ComposeMessageScreen({ nav }) {
15364
16031
  onPickCamera: images.pickFromCamera,
15365
16032
  onRemove: images.removeImage
15366
16033
  }
15367
- ), /* @__PURE__ */ import_react15.default.createElement(
15368
- import_react_native14.TouchableOpacity,
16034
+ ), /* @__PURE__ */ import_react16.default.createElement(
16035
+ import_react_native15.TouchableOpacity,
15369
16036
  {
15370
16037
  style: [shared.primaryButton, (!subject.trim() || !message.trim() || sending || images.isUploading) && shared.primaryButtonDisabled, { marginTop: 20 }],
15371
16038
  onPress: handleSend,
15372
16039
  disabled: !subject.trim() || !message.trim() || sending || images.isUploading
15373
16040
  },
15374
- /* @__PURE__ */ import_react15.default.createElement(import_react_native14.Text, { style: shared.primaryButtonText }, images.isUploading ? "Uploading..." : sending ? "Sending..." : "Send Message")
16041
+ /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: shared.primaryButtonText }, images.isUploading ? "Uploading..." : sending ? "Sending..." : "Send Message")
15375
16042
  )));
15376
16043
  }
15377
- var styles12 = import_react_native14.StyleSheet.create({
16044
+ var styles12 = import_react_native15.StyleSheet.create({
15378
16045
  header: { marginBottom: 20 },
15379
16046
  title: { fontSize: 20, fontWeight: "600", color: colors.textPrimary, marginBottom: 4 },
15380
16047
  subtitle: { fontSize: 14, color: colors.textMuted },
@@ -15384,20 +16051,20 @@ var styles12 = import_react_native14.StyleSheet.create({
15384
16051
  });
15385
16052
 
15386
16053
  // src/widget/screens/ProfileScreen.tsx
15387
- var import_react16 = __toESM(require("react"));
15388
- var import_react_native15 = require("react-native");
16054
+ var import_react17 = __toESM(require("react"));
16055
+ var import_react_native16 = require("react-native");
15389
16056
  function ProfileScreen({ nav }) {
15390
16057
  const { testerInfo, assignments, updateTesterProfile, refreshTesterInfo } = useBugBear();
15391
- const [editing, setEditing] = (0, import_react16.useState)(false);
15392
- const [name, setName] = (0, import_react16.useState)(testerInfo?.name || "");
15393
- const [additionalEmails, setAdditionalEmails] = (0, import_react16.useState)(testerInfo?.additionalEmails || []);
15394
- const [newEmailInput, setNewEmailInput] = (0, import_react16.useState)("");
15395
- const [platforms, setPlatforms] = (0, import_react16.useState)(testerInfo?.platforms || []);
15396
- const [saving, setSaving] = (0, import_react16.useState)(false);
15397
- const [saved, setSaved] = (0, import_react16.useState)(false);
15398
- const [showDetails, setShowDetails] = (0, import_react16.useState)(false);
16058
+ const [editing, setEditing] = (0, import_react17.useState)(false);
16059
+ const [name, setName] = (0, import_react17.useState)(testerInfo?.name || "");
16060
+ const [additionalEmails, setAdditionalEmails] = (0, import_react17.useState)(testerInfo?.additionalEmails || []);
16061
+ const [newEmailInput, setNewEmailInput] = (0, import_react17.useState)("");
16062
+ const [platforms, setPlatforms] = (0, import_react17.useState)(testerInfo?.platforms || []);
16063
+ const [saving, setSaving] = (0, import_react17.useState)(false);
16064
+ const [saved, setSaved] = (0, import_react17.useState)(false);
16065
+ const [showDetails, setShowDetails] = (0, import_react17.useState)(false);
15399
16066
  const completedCount = assignments.filter((a) => a.status === "passed" || a.status === "failed").length;
15400
- (0, import_react16.useEffect)(() => {
16067
+ (0, import_react17.useEffect)(() => {
15401
16068
  if (testerInfo) {
15402
16069
  setName(testerInfo.name);
15403
16070
  setAdditionalEmails(testerInfo.additionalEmails || []);
@@ -15432,17 +16099,17 @@ function ProfileScreen({ nav }) {
15432
16099
  }
15433
16100
  };
15434
16101
  if (saved) {
15435
- return /* @__PURE__ */ import_react16.default.createElement(import_react_native15.View, { style: shared.emptyState }, /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: shared.emptyEmoji }, "\u2705"), /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: shared.emptyTitle }, "Profile saved!"));
16102
+ return /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { style: shared.emptyState }, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: shared.emptyEmoji }, "\u2705"), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: shared.emptyTitle }, "Profile saved!"));
15436
16103
  }
15437
16104
  if (!testerInfo) {
15438
- return /* @__PURE__ */ import_react16.default.createElement(import_react_native15.View, { style: shared.emptyState }, /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: shared.emptyEmoji }, "\u{1F464}"), /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: shared.emptyTitle }, "No profile found"));
16105
+ return /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { style: shared.emptyState }, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: shared.emptyEmoji }, "\u{1F464}"), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: shared.emptyTitle }, "No profile found"));
15439
16106
  }
15440
16107
  if (editing) {
15441
- return /* @__PURE__ */ import_react16.default.createElement(import_react_native15.View, null, /* @__PURE__ */ import_react16.default.createElement(import_react_native15.View, { style: styles13.editHeader }, /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: styles13.editTitle }, "Edit Profile"), /* @__PURE__ */ import_react16.default.createElement(import_react_native15.TouchableOpacity, { onPress: () => {
16108
+ return /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, null, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { style: styles13.editHeader }, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: styles13.editTitle }, "Edit Profile"), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.TouchableOpacity, { onPress: () => {
15442
16109
  setEditing(false);
15443
16110
  setNewEmailInput("");
15444
- } }, /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: styles13.cancelText }, "Cancel"))), /* @__PURE__ */ import_react16.default.createElement(import_react_native15.View, { style: styles13.field }, /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: shared.label }, "Name"), /* @__PURE__ */ import_react16.default.createElement(import_react_native15.TextInput, { style: styles13.input, value: name, onChangeText: setName, placeholder: "Your name", placeholderTextColor: colors.textMuted })), /* @__PURE__ */ import_react16.default.createElement(import_react_native15.View, { style: styles13.field }, /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: shared.label }, "Primary Email"), /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: styles13.emailFixed }, testerInfo.email)), /* @__PURE__ */ import_react16.default.createElement(import_react_native15.View, { style: styles13.field }, /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: shared.label }, "Additional Emails"), additionalEmails.map((email) => /* @__PURE__ */ import_react16.default.createElement(import_react_native15.View, { key: email, style: styles13.emailRow }, /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: styles13.emailText }, email), /* @__PURE__ */ import_react16.default.createElement(import_react_native15.TouchableOpacity, { onPress: () => setAdditionalEmails(additionalEmails.filter((e) => e !== email)) }, /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: styles13.removeEmail }, "\u2715")))), /* @__PURE__ */ import_react16.default.createElement(import_react_native15.View, { style: styles13.addEmailRow }, /* @__PURE__ */ import_react16.default.createElement(
15445
- import_react_native15.TextInput,
16111
+ } }, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: styles13.cancelText }, "Cancel"))), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { style: styles13.field }, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: shared.label }, "Name"), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.TextInput, { style: styles13.input, value: name, onChangeText: setName, placeholder: "Your name", placeholderTextColor: colors.textMuted })), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { style: styles13.field }, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: shared.label }, "Primary Email"), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: styles13.emailFixed }, testerInfo.email)), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { style: styles13.field }, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: shared.label }, "Additional Emails"), additionalEmails.map((email) => /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { key: email, style: styles13.emailRow }, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: styles13.emailText }, email), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.TouchableOpacity, { onPress: () => setAdditionalEmails(additionalEmails.filter((e) => e !== email)) }, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: styles13.removeEmail }, "\u2715")))), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { style: styles13.addEmailRow }, /* @__PURE__ */ import_react17.default.createElement(
16112
+ import_react_native16.TextInput,
15446
16113
  {
15447
16114
  style: [styles13.input, { flex: 1, marginRight: 8 }],
15448
16115
  value: newEmailInput,
@@ -15452,26 +16119,26 @@ function ProfileScreen({ nav }) {
15452
16119
  keyboardType: "email-address",
15453
16120
  autoCapitalize: "none"
15454
16121
  }
15455
- ), /* @__PURE__ */ import_react16.default.createElement(import_react_native15.TouchableOpacity, { style: styles13.addButton, onPress: handleAddEmail }, /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: styles13.addButtonText }, "Add")))), /* @__PURE__ */ import_react16.default.createElement(import_react_native15.View, { style: styles13.field }, /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: shared.label }, "Testing Platforms"), /* @__PURE__ */ import_react16.default.createElement(import_react_native15.View, { style: styles13.platformRow }, [{ key: "ios", label: "\u{1F4F1} iOS" }, { key: "android", label: "\u{1F916} Android" }, { key: "web", label: "\u{1F310} Web" }].map(({ key, label }) => /* @__PURE__ */ import_react16.default.createElement(
15456
- import_react_native15.TouchableOpacity,
16122
+ ), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.TouchableOpacity, { style: styles13.addButton, onPress: handleAddEmail }, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: styles13.addButtonText }, "Add")))), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { style: styles13.field }, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: shared.label }, "Testing Platforms"), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { style: styles13.platformRow }, [{ key: "ios", label: "\u{1F4F1} iOS" }, { key: "android", label: "\u{1F916} Android" }, { key: "web", label: "\u{1F310} Web" }].map(({ key, label }) => /* @__PURE__ */ import_react17.default.createElement(
16123
+ import_react_native16.TouchableOpacity,
15457
16124
  {
15458
16125
  key,
15459
16126
  style: [styles13.platformBtn, platforms.includes(key) && styles13.platformBtnActive],
15460
16127
  onPress: () => setPlatforms((prev) => prev.includes(key) ? prev.filter((p) => p !== key) : [...prev, key])
15461
16128
  },
15462
- /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: [styles13.platformText, platforms.includes(key) && styles13.platformTextActive] }, label)
15463
- )))), /* @__PURE__ */ import_react16.default.createElement(import_react_native15.TouchableOpacity, { style: [shared.primaryButton, { marginTop: 20 }], onPress: handleSave, disabled: saving }, /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: shared.primaryButtonText }, saving ? "Saving..." : "Save Profile")));
16129
+ /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: [styles13.platformText, platforms.includes(key) && styles13.platformTextActive] }, label)
16130
+ )))), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.TouchableOpacity, { style: [shared.primaryButton, { marginTop: 20 }], onPress: handleSave, disabled: saving }, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: shared.primaryButtonText }, saving ? "Saving..." : "Save Profile")));
15464
16131
  }
15465
- return /* @__PURE__ */ import_react16.default.createElement(import_react_native15.View, null, /* @__PURE__ */ import_react16.default.createElement(import_react_native15.View, { style: styles13.profileCard }, /* @__PURE__ */ import_react16.default.createElement(import_react_native15.View, { style: styles13.avatar }, /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: styles13.avatarText }, testerInfo.name.charAt(0).toUpperCase())), /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: styles13.profileName }, testerInfo.name), /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: styles13.profileEmail }, testerInfo.email)), /* @__PURE__ */ import_react16.default.createElement(import_react_native15.View, { style: styles13.statsRow }, /* @__PURE__ */ import_react16.default.createElement(import_react_native15.View, { style: styles13.statItem }, /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: styles13.statNumber }, completedCount), /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: styles13.statLabel }, "Completed")), /* @__PURE__ */ import_react16.default.createElement(import_react_native15.View, { style: styles13.statDivider }), /* @__PURE__ */ import_react16.default.createElement(import_react_native15.View, { style: styles13.statItem }, /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: styles13.statNumber }, assignments.length), /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: styles13.statLabel }, "Total Assigned"))), /* @__PURE__ */ import_react16.default.createElement(import_react_native15.TouchableOpacity, { onPress: () => setShowDetails(!showDetails), style: styles13.detailsToggle }, /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: styles13.detailsToggleText }, showDetails ? "\u25BC" : "\u25B6", " Details")), showDetails && /* @__PURE__ */ import_react16.default.createElement(import_react_native15.View, { style: styles13.detailsSection }, additionalEmails.length > 0 && /* @__PURE__ */ import_react16.default.createElement(import_react_native15.View, { style: styles13.detailBlock }, /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: styles13.detailLabel }, "Additional Emails"), additionalEmails.map((e) => /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { key: e, style: styles13.detailValue }, e))), platforms.length > 0 && /* @__PURE__ */ import_react16.default.createElement(import_react_native15.View, { style: styles13.detailBlock }, /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: styles13.detailLabel }, "Platforms"), /* @__PURE__ */ import_react16.default.createElement(import_react_native15.View, { style: styles13.platformTags }, platforms.map((p) => /* @__PURE__ */ import_react16.default.createElement(import_react_native15.View, { key: p, style: styles13.platformTag }, /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: styles13.platformTagText }, p === "ios" ? "\u{1F4F1} iOS" : p === "android" ? "\u{1F916} Android" : "\u{1F310} Web")))))), /* @__PURE__ */ import_react16.default.createElement(
15466
- import_react_native15.TouchableOpacity,
16132
+ return /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, null, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { style: styles13.profileCard }, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { style: styles13.avatar }, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: styles13.avatarText }, testerInfo.name.charAt(0).toUpperCase())), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: styles13.profileName }, testerInfo.name), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: styles13.profileEmail }, testerInfo.email)), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { style: styles13.statsRow }, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { style: styles13.statItem }, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: styles13.statNumber }, completedCount), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: styles13.statLabel }, "Completed")), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { style: styles13.statDivider }), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { style: styles13.statItem }, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: styles13.statNumber }, assignments.length), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: styles13.statLabel }, "Total Assigned"))), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.TouchableOpacity, { onPress: () => setShowDetails(!showDetails), style: styles13.detailsToggle }, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: styles13.detailsToggleText }, showDetails ? "\u25BC" : "\u25B6", " Details")), showDetails && /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { style: styles13.detailsSection }, additionalEmails.length > 0 && /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { style: styles13.detailBlock }, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: styles13.detailLabel }, "Additional Emails"), additionalEmails.map((e) => /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { key: e, style: styles13.detailValue }, e))), platforms.length > 0 && /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { style: styles13.detailBlock }, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: styles13.detailLabel }, "Platforms"), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { style: styles13.platformTags }, platforms.map((p) => /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { key: p, style: styles13.platformTag }, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: styles13.platformTagText }, p === "ios" ? "\u{1F4F1} iOS" : p === "android" ? "\u{1F916} Android" : "\u{1F310} Web")))))), /* @__PURE__ */ import_react17.default.createElement(
16133
+ import_react_native16.TouchableOpacity,
15467
16134
  {
15468
16135
  style: [shared.primaryButton, { marginTop: 20 }],
15469
16136
  onPress: () => setEditing(true)
15470
16137
  },
15471
- /* @__PURE__ */ import_react16.default.createElement(import_react_native15.Text, { style: shared.primaryButtonText }, "Edit Profile")
16138
+ /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: shared.primaryButtonText }, "Edit Profile")
15472
16139
  ));
15473
16140
  }
15474
- var styles13 = import_react_native15.StyleSheet.create({
16141
+ var styles13 = import_react_native16.StyleSheet.create({
15475
16142
  profileCard: { alignItems: "center", backgroundColor: colors.card, borderRadius: 16, padding: 24, marginBottom: 16 },
15476
16143
  avatar: { width: 64, height: 64, borderRadius: 32, backgroundColor: colors.blue, justifyContent: "center", alignItems: "center", marginBottom: 12 },
15477
16144
  avatarText: { fontSize: 28, fontWeight: "700", color: "#fff" },
@@ -15512,8 +16179,8 @@ var styles13 = import_react_native15.StyleSheet.create({
15512
16179
  });
15513
16180
 
15514
16181
  // src/widget/screens/IssueListScreen.tsx
15515
- var import_react17 = __toESM(require("react"));
15516
- var import_react_native16 = require("react-native");
16182
+ var import_react18 = __toESM(require("react"));
16183
+ var import_react_native17 = require("react-native");
15517
16184
  var CATEGORY_CONFIG = {
15518
16185
  open: { label: "Open Issues", accent: "#f97316", emptyIcon: "\u2705", emptyText: "No open issues" },
15519
16186
  done: { label: "Done", accent: "#22c55e", emptyIcon: "\u{1F389}", emptyText: "No completed issues yet" },
@@ -15527,10 +16194,10 @@ var SEVERITY_COLORS = {
15527
16194
  };
15528
16195
  function IssueListScreen({ nav, category }) {
15529
16196
  const { client } = useBugBear();
15530
- const [issues, setIssues] = (0, import_react17.useState)([]);
15531
- const [loading, setLoading] = (0, import_react17.useState)(true);
16197
+ const [issues, setIssues] = (0, import_react18.useState)([]);
16198
+ const [loading, setLoading] = (0, import_react18.useState)(true);
15532
16199
  const config = CATEGORY_CONFIG[category];
15533
- (0, import_react17.useEffect)(() => {
16200
+ (0, import_react18.useEffect)(() => {
15534
16201
  let cancelled = false;
15535
16202
  setLoading(true);
15536
16203
  (async () => {
@@ -15556,26 +16223,26 @@ function IssueListScreen({ nav, category }) {
15556
16223
  };
15557
16224
  }, [client, category]);
15558
16225
  if (loading) {
15559
- return /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { style: styles14.emptyContainer }, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.ActivityIndicator, { size: "small", color: colors.textMuted }), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: styles14.emptyText }, "Loading..."));
16226
+ return /* @__PURE__ */ import_react18.default.createElement(IssueListScreenSkeleton, null);
15560
16227
  }
15561
16228
  if (issues.length === 0) {
15562
- return /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { style: styles14.emptyContainer }, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: styles14.emptyIcon }, config.emptyIcon), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: styles14.emptyText }, config.emptyText));
16229
+ return /* @__PURE__ */ import_react18.default.createElement(import_react_native17.View, { style: styles14.emptyContainer }, /* @__PURE__ */ import_react18.default.createElement(import_react_native17.Text, { style: styles14.emptyIcon }, config.emptyIcon), /* @__PURE__ */ import_react18.default.createElement(import_react_native17.Text, { style: styles14.emptyText }, config.emptyText));
15563
16230
  }
15564
- return /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, null, issues.map((issue) => /* @__PURE__ */ import_react17.default.createElement(
15565
- import_react_native16.TouchableOpacity,
16231
+ return /* @__PURE__ */ import_react18.default.createElement(import_react_native17.View, null, issues.map((issue) => /* @__PURE__ */ import_react18.default.createElement(
16232
+ import_react_native17.TouchableOpacity,
15566
16233
  {
15567
16234
  key: issue.id,
15568
16235
  style: styles14.issueCard,
15569
16236
  onPress: () => nav.push({ name: "ISSUE_DETAIL", issue }),
15570
16237
  activeOpacity: 0.7
15571
16238
  },
15572
- /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { style: styles14.topRow }, issue.severity && /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { style: [styles14.severityDot, { backgroundColor: SEVERITY_COLORS[issue.severity] || colors.textDim }] }), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: styles14.issueTitle, numberOfLines: 1 }, issue.title)),
15573
- /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { style: styles14.bottomRow }, issue.route && /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: styles14.routeText, numberOfLines: 1 }, issue.route), /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: styles14.timeText }, formatRelativeTime(issue.updatedAt))),
15574
- category === "done" && issue.verifiedByName && /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { style: styles14.verifiedBadge }, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: styles14.verifiedBadgeText }, "\u2714", " Verified by ", issue.verifiedByName)),
15575
- category === "reopened" && issue.originalBugTitle && /* @__PURE__ */ import_react17.default.createElement(import_react_native16.View, { style: styles14.reopenedBadge }, /* @__PURE__ */ import_react17.default.createElement(import_react_native16.Text, { style: styles14.reopenedBadgeText, numberOfLines: 1 }, "\u{1F504}", " Retest of: ", issue.originalBugTitle))
16239
+ /* @__PURE__ */ import_react18.default.createElement(import_react_native17.View, { style: styles14.topRow }, issue.severity && /* @__PURE__ */ import_react18.default.createElement(import_react_native17.View, { style: [styles14.severityDot, { backgroundColor: SEVERITY_COLORS[issue.severity] || colors.textDim }] }), /* @__PURE__ */ import_react18.default.createElement(import_react_native17.Text, { style: styles14.issueTitle, numberOfLines: 1 }, issue.title)),
16240
+ /* @__PURE__ */ import_react18.default.createElement(import_react_native17.View, { style: styles14.bottomRow }, issue.route && /* @__PURE__ */ import_react18.default.createElement(import_react_native17.Text, { style: styles14.routeText, numberOfLines: 1 }, issue.route), /* @__PURE__ */ import_react18.default.createElement(import_react_native17.Text, { style: styles14.timeText }, formatRelativeTime(issue.updatedAt))),
16241
+ category === "done" && issue.verifiedByName && /* @__PURE__ */ import_react18.default.createElement(import_react_native17.View, { style: styles14.verifiedBadge }, /* @__PURE__ */ import_react18.default.createElement(import_react_native17.Text, { style: styles14.verifiedBadgeText }, "\u2714", " Verified by ", issue.verifiedByName)),
16242
+ category === "reopened" && issue.originalBugTitle && /* @__PURE__ */ import_react18.default.createElement(import_react_native17.View, { style: styles14.reopenedBadge }, /* @__PURE__ */ import_react18.default.createElement(import_react_native17.Text, { style: styles14.reopenedBadgeText, numberOfLines: 1 }, "\u{1F504}", " Retest of: ", issue.originalBugTitle))
15576
16243
  )));
15577
16244
  }
15578
- var styles14 = import_react_native16.StyleSheet.create({
16245
+ var styles14 = import_react_native17.StyleSheet.create({
15579
16246
  emptyContainer: {
15580
16247
  alignItems: "center",
15581
16248
  paddingVertical: 40
@@ -15666,8 +16333,8 @@ var styles14 = import_react_native16.StyleSheet.create({
15666
16333
  });
15667
16334
 
15668
16335
  // src/widget/screens/IssueDetailScreen.tsx
15669
- var import_react18 = __toESM(require("react"));
15670
- var import_react_native17 = require("react-native");
16336
+ var import_react19 = __toESM(require("react"));
16337
+ var import_react_native18 = require("react-native");
15671
16338
  var STATUS_LABELS = {
15672
16339
  new: { label: "New", bg: "#1e3a5f", color: "#60a5fa" },
15673
16340
  triaging: { label: "Triaging", bg: "#1e3a5f", color: "#60a5fa" },
@@ -15689,11 +16356,20 @@ var SEVERITY_CONFIG = {
15689
16356
  low: { label: "Low", color: "#71717a", bg: "#27272a" }
15690
16357
  };
15691
16358
  function IssueDetailScreen({ nav, issue }) {
16359
+ const { dashboardUrl } = useBugBear();
15692
16360
  const statusConfig = STATUS_LABELS[issue.status] || { label: issue.status, bg: "#27272a", color: "#a1a1aa" };
15693
16361
  const severityConfig = issue.severity ? SEVERITY_CONFIG[issue.severity] : null;
15694
- return /* @__PURE__ */ import_react18.default.createElement(import_react_native17.View, null, /* @__PURE__ */ import_react18.default.createElement(import_react_native17.View, { style: styles15.badgeRow }, /* @__PURE__ */ import_react18.default.createElement(import_react_native17.View, { style: [styles15.badge, { backgroundColor: statusConfig.bg }] }, /* @__PURE__ */ import_react18.default.createElement(import_react_native17.Text, { style: [styles15.badgeText, { color: statusConfig.color }] }, statusConfig.label)), severityConfig && /* @__PURE__ */ import_react18.default.createElement(import_react_native17.View, { style: [styles15.badge, { backgroundColor: severityConfig.bg }] }, /* @__PURE__ */ import_react18.default.createElement(import_react_native17.Text, { style: [styles15.badgeText, { color: severityConfig.color }] }, severityConfig.label))), /* @__PURE__ */ import_react18.default.createElement(import_react_native17.Text, { style: styles15.title }, issue.title), issue.route && /* @__PURE__ */ import_react18.default.createElement(import_react_native17.Text, { style: styles15.route }, issue.route), issue.description && /* @__PURE__ */ import_react18.default.createElement(import_react_native17.View, { style: styles15.descriptionCard }, /* @__PURE__ */ import_react18.default.createElement(import_react_native17.Text, { style: styles15.descriptionText }, issue.description)), issue.verifiedByName && /* @__PURE__ */ import_react18.default.createElement(import_react_native17.View, { style: styles15.verifiedCard }, /* @__PURE__ */ import_react18.default.createElement(import_react_native17.View, { style: styles15.verifiedHeader }, /* @__PURE__ */ import_react18.default.createElement(import_react_native17.Text, { style: styles15.verifiedIcon }, "\u2705"), /* @__PURE__ */ import_react18.default.createElement(import_react_native17.Text, { style: styles15.verifiedTitle }, "Retesting Proof")), /* @__PURE__ */ import_react18.default.createElement(import_react_native17.Text, { style: styles15.verifiedBody }, "Verified by ", issue.verifiedByName, issue.verifiedAt && ` on ${new Date(issue.verifiedAt).toLocaleDateString(void 0, { month: "short", day: "numeric", year: "numeric" })}`)), issue.originalBugTitle && /* @__PURE__ */ import_react18.default.createElement(import_react_native17.View, { style: styles15.originalBugCard }, /* @__PURE__ */ import_react18.default.createElement(import_react_native17.View, { style: styles15.originalBugHeader }, /* @__PURE__ */ import_react18.default.createElement(import_react_native17.Text, { style: styles15.originalBugIcon }, "\u{1F504}"), /* @__PURE__ */ import_react18.default.createElement(import_react_native17.Text, { style: styles15.originalBugTitle }, "Original Bug")), /* @__PURE__ */ import_react18.default.createElement(import_react_native17.Text, { style: styles15.originalBugBody }, "Retest of: ", issue.originalBugTitle)), issue.screenshotUrls && issue.screenshotUrls.length > 0 && /* @__PURE__ */ import_react18.default.createElement(import_react_native17.View, { style: styles15.screenshotSection }, /* @__PURE__ */ import_react18.default.createElement(import_react_native17.Text, { style: styles15.screenshotLabel }, "Screenshots (", issue.screenshotUrls.length, ")"), /* @__PURE__ */ import_react18.default.createElement(import_react_native17.View, { style: styles15.screenshotRow }, issue.screenshotUrls.map((url, i) => /* @__PURE__ */ import_react18.default.createElement(import_react_native17.TouchableOpacity, { key: i, onPress: () => import_react_native17.Linking.openURL(url), activeOpacity: 0.7 }, /* @__PURE__ */ import_react18.default.createElement(import_react_native17.Image, { source: { uri: url }, style: styles15.screenshotThumb }))))), /* @__PURE__ */ import_react18.default.createElement(import_react_native17.View, { style: styles15.metaSection }, issue.reporterName && /* @__PURE__ */ import_react18.default.createElement(import_react_native17.Text, { style: styles15.metaText }, "Reported by ", issue.reporterName), /* @__PURE__ */ import_react18.default.createElement(import_react_native17.Text, { style: styles15.metaTextSmall }, "Created ", formatRelativeTime(issue.createdAt), " ", "\xB7", " Updated ", formatRelativeTime(issue.updatedAt))));
16362
+ return /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, null, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: styles15.badgeRow }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: [styles15.badge, { backgroundColor: statusConfig.bg }] }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: [styles15.badgeText, { color: statusConfig.color }] }, statusConfig.label)), severityConfig && /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: [styles15.badge, { backgroundColor: severityConfig.bg }] }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: [styles15.badgeText, { color: severityConfig.color }] }, severityConfig.label))), /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.title }, issue.title), issue.route && /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.route }, issue.route), issue.description && /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: styles15.descriptionCard }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.descriptionText }, issue.description)), issue.verifiedByName && /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: styles15.verifiedCard }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: styles15.verifiedHeader }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.verifiedIcon }, "\u2705"), /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.verifiedTitle }, "Retesting Proof")), /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.verifiedBody }, "Verified by ", issue.verifiedByName, issue.verifiedAt && ` on ${new Date(issue.verifiedAt).toLocaleDateString(void 0, { month: "short", day: "numeric", year: "numeric" })}`)), issue.originalBugTitle && /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: styles15.originalBugCard }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: styles15.originalBugHeader }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.originalBugIcon }, "\u{1F504}"), /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.originalBugTitle }, "Original Bug")), /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.originalBugBody }, "Retest of: ", issue.originalBugTitle)), issue.screenshotUrls && issue.screenshotUrls.length > 0 && /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: styles15.screenshotSection }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.screenshotLabel }, "Screenshots (", issue.screenshotUrls.length, ")"), /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: styles15.screenshotRow }, issue.screenshotUrls.map((url, i) => /* @__PURE__ */ import_react19.default.createElement(import_react_native18.TouchableOpacity, { key: i, onPress: () => import_react_native18.Linking.openURL(url), activeOpacity: 0.7 }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Image, { source: { uri: url }, style: styles15.screenshotThumb }))))), /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: styles15.metaSection }, issue.reporterName && /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.metaText }, "Reported by ", issue.reporterName), /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.metaTextSmall }, "Created ", formatRelativeTime(issue.createdAt), " ", "\xB7", " Updated ", formatRelativeTime(issue.updatedAt))), dashboardUrl && /* @__PURE__ */ import_react19.default.createElement(
16363
+ import_react_native18.TouchableOpacity,
16364
+ {
16365
+ style: styles15.dashboardLink,
16366
+ onPress: () => import_react_native18.Linking.openURL(`${dashboardUrl}/reports`),
16367
+ activeOpacity: 0.7
16368
+ },
16369
+ /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles15.dashboardLinkText }, "\u{1F310}", " View on Dashboard ", "\u2192")
16370
+ ));
15695
16371
  }
15696
- var styles15 = import_react_native17.StyleSheet.create({
16372
+ var styles15 = import_react_native18.StyleSheet.create({
15697
16373
  badgeRow: {
15698
16374
  flexDirection: "row",
15699
16375
  gap: 8,
@@ -15820,12 +16496,24 @@ var styles15 = import_react_native17.StyleSheet.create({
15820
16496
  metaTextSmall: {
15821
16497
  fontSize: 11,
15822
16498
  color: colors.textDim
16499
+ },
16500
+ dashboardLink: {
16501
+ alignItems: "center",
16502
+ paddingVertical: 10,
16503
+ marginTop: 12,
16504
+ borderTopWidth: 1,
16505
+ borderTopColor: colors.border
16506
+ },
16507
+ dashboardLinkText: {
16508
+ fontSize: 13,
16509
+ fontWeight: "500",
16510
+ color: colors.blue
15823
16511
  }
15824
16512
  });
15825
16513
 
15826
16514
  // src/BugBearButton.tsx
15827
- var screenWidth = import_react_native18.Dimensions.get("window").width;
15828
- var screenHeight = import_react_native18.Dimensions.get("window").height;
16515
+ var screenWidth = import_react_native19.Dimensions.get("window").width;
16516
+ var screenHeight = import_react_native19.Dimensions.get("window").height;
15829
16517
  function BugBearButton({
15830
16518
  position = "bottom-right",
15831
16519
  buttonStyle,
@@ -15837,7 +16525,7 @@ function BugBearButton({
15837
16525
  }) {
15838
16526
  const { shouldShowWidget, testerInfo, isLoading, unreadCount, assignments } = useBugBear();
15839
16527
  const { currentScreen, canGoBack, push, pop, replace, reset } = useNavigation();
15840
- const [modalVisible, setModalVisible] = (0, import_react19.useState)(false);
16528
+ const [modalVisible, setModalVisible] = (0, import_react20.useState)(false);
15841
16529
  const getInitialPosition = () => {
15842
16530
  const buttonSize = 56;
15843
16531
  const margin = 16;
@@ -15849,10 +16537,10 @@ function BugBearButton({
15849
16537
  return { x, y };
15850
16538
  };
15851
16539
  const initialPos = getInitialPosition();
15852
- const pan = (0, import_react19.useRef)(new import_react_native18.Animated.ValueXY(initialPos)).current;
15853
- const isDragging = (0, import_react19.useRef)(false);
15854
- const panResponder = (0, import_react19.useRef)(
15855
- import_react_native18.PanResponder.create({
16540
+ const pan = (0, import_react20.useRef)(new import_react_native19.Animated.ValueXY(initialPos)).current;
16541
+ const isDragging = (0, import_react20.useRef)(false);
16542
+ const panResponder = (0, import_react20.useRef)(
16543
+ import_react_native19.PanResponder.create({
15856
16544
  onStartShouldSetPanResponder: () => draggable,
15857
16545
  onMoveShouldSetPanResponder: (_, gs) => draggable && (Math.abs(gs.dx) > 5 || Math.abs(gs.dy) > 5),
15858
16546
  onPanResponderGrant: () => {
@@ -15867,7 +16555,7 @@ function BugBearButton({
15867
16555
  if (Math.abs(gs.dx) > 5 || Math.abs(gs.dy) > 5) {
15868
16556
  isDragging.current = true;
15869
16557
  }
15870
- import_react_native18.Animated.event(
16558
+ import_react_native19.Animated.event(
15871
16559
  [null, { dx: pan.x, dy: pan.y }],
15872
16560
  { useNativeDriver: false }
15873
16561
  )(_, gs);
@@ -15880,7 +16568,7 @@ function BugBearButton({
15880
16568
  const margin = 16;
15881
16569
  const snapX = currentX < screenWidth / 2 ? margin : screenWidth - buttonSize - margin;
15882
16570
  const snapY = Math.max(minY, Math.min(currentY, screenHeight - maxYOffset));
15883
- import_react_native18.Animated.spring(pan, {
16571
+ import_react_native19.Animated.spring(pan, {
15884
16572
  toValue: { x: snapX, y: snapY },
15885
16573
  useNativeDriver: false,
15886
16574
  friction: 7,
@@ -15927,24 +16615,24 @@ function BugBearButton({
15927
16615
  }
15928
16616
  };
15929
16617
  const handleClose = () => {
15930
- import_react_native18.Keyboard.dismiss();
16618
+ import_react_native19.Keyboard.dismiss();
15931
16619
  setModalVisible(false);
15932
16620
  };
15933
16621
  const nav = {
15934
16622
  push: (screen) => {
15935
- import_react_native18.Keyboard.dismiss();
16623
+ import_react_native19.Keyboard.dismiss();
15936
16624
  push(screen);
15937
16625
  },
15938
16626
  pop: () => {
15939
- import_react_native18.Keyboard.dismiss();
16627
+ import_react_native19.Keyboard.dismiss();
15940
16628
  pop();
15941
16629
  },
15942
16630
  replace: (screen) => {
15943
- import_react_native18.Keyboard.dismiss();
16631
+ import_react_native19.Keyboard.dismiss();
15944
16632
  replace(screen);
15945
16633
  },
15946
16634
  reset: () => {
15947
- import_react_native18.Keyboard.dismiss();
16635
+ import_react_native19.Keyboard.dismiss();
15948
16636
  reset();
15949
16637
  },
15950
16638
  canGoBack,
@@ -15953,77 +16641,77 @@ function BugBearButton({
15953
16641
  const renderScreen = () => {
15954
16642
  switch (currentScreen.name) {
15955
16643
  case "HOME":
15956
- return /* @__PURE__ */ import_react19.default.createElement(HomeScreen, { nav });
16644
+ return /* @__PURE__ */ import_react20.default.createElement(HomeScreen, { nav });
15957
16645
  case "TEST_DETAIL":
15958
- return /* @__PURE__ */ import_react19.default.createElement(TestDetailScreen, { testId: currentScreen.testId, nav });
16646
+ return /* @__PURE__ */ import_react20.default.createElement(TestDetailScreen, { testId: currentScreen.testId, nav });
15959
16647
  case "TEST_LIST":
15960
- return /* @__PURE__ */ import_react19.default.createElement(TestListScreen, { nav });
16648
+ return /* @__PURE__ */ import_react20.default.createElement(TestListScreen, { nav });
15961
16649
  case "TEST_FEEDBACK":
15962
- return /* @__PURE__ */ import_react19.default.createElement(TestFeedbackScreen, { status: currentScreen.status, assignmentId: currentScreen.assignmentId, nav });
16650
+ return /* @__PURE__ */ import_react20.default.createElement(TestFeedbackScreen, { status: currentScreen.status, assignmentId: currentScreen.assignmentId, nav });
15963
16651
  case "REPORT":
15964
- return /* @__PURE__ */ import_react19.default.createElement(ReportScreen, { nav, prefill: currentScreen.prefill });
16652
+ return /* @__PURE__ */ import_react20.default.createElement(ReportScreen, { nav, prefill: currentScreen.prefill });
15965
16653
  case "REPORT_SUCCESS":
15966
- return /* @__PURE__ */ import_react19.default.createElement(ReportSuccessScreen, { nav });
16654
+ return /* @__PURE__ */ import_react20.default.createElement(ReportSuccessScreen, { nav });
15967
16655
  case "MESSAGE_LIST":
15968
- return /* @__PURE__ */ import_react19.default.createElement(MessageListScreen, { nav });
16656
+ return /* @__PURE__ */ import_react20.default.createElement(MessageListScreen, { nav });
15969
16657
  case "THREAD_DETAIL":
15970
- return /* @__PURE__ */ import_react19.default.createElement(ThreadDetailScreen, { thread: currentScreen.thread, nav });
16658
+ return /* @__PURE__ */ import_react20.default.createElement(ThreadDetailScreen, { thread: currentScreen.thread, nav });
15971
16659
  case "COMPOSE_MESSAGE":
15972
- return /* @__PURE__ */ import_react19.default.createElement(ComposeMessageScreen, { nav });
16660
+ return /* @__PURE__ */ import_react20.default.createElement(ComposeMessageScreen, { nav });
15973
16661
  case "ISSUE_LIST":
15974
- return /* @__PURE__ */ import_react19.default.createElement(IssueListScreen, { nav, category: currentScreen.category });
16662
+ return /* @__PURE__ */ import_react20.default.createElement(IssueListScreen, { nav, category: currentScreen.category });
15975
16663
  case "ISSUE_DETAIL":
15976
- return /* @__PURE__ */ import_react19.default.createElement(IssueDetailScreen, { nav, issue: currentScreen.issue });
16664
+ return /* @__PURE__ */ import_react20.default.createElement(IssueDetailScreen, { nav, issue: currentScreen.issue });
15977
16665
  case "PROFILE":
15978
- return /* @__PURE__ */ import_react19.default.createElement(ProfileScreen, { nav });
16666
+ return /* @__PURE__ */ import_react20.default.createElement(ProfileScreen, { nav });
15979
16667
  default:
15980
- return /* @__PURE__ */ import_react19.default.createElement(HomeScreen, { nav });
16668
+ return /* @__PURE__ */ import_react20.default.createElement(HomeScreen, { nav });
15981
16669
  }
15982
16670
  };
15983
- return /* @__PURE__ */ import_react19.default.createElement(import_react19.default.Fragment, null, /* @__PURE__ */ import_react19.default.createElement(
15984
- import_react_native18.Animated.View,
16671
+ return /* @__PURE__ */ import_react20.default.createElement(import_react20.default.Fragment, null, /* @__PURE__ */ import_react20.default.createElement(
16672
+ import_react_native19.Animated.View,
15985
16673
  {
15986
16674
  style: [styles16.fabContainer, { transform: pan.getTranslateTransform() }, buttonStyle],
15987
16675
  ...panResponder.panHandlers
15988
16676
  },
15989
- /* @__PURE__ */ import_react19.default.createElement(
15990
- import_react_native18.TouchableOpacity,
16677
+ /* @__PURE__ */ import_react20.default.createElement(
16678
+ import_react_native19.TouchableOpacity,
15991
16679
  {
15992
16680
  style: styles16.fab,
15993
16681
  onPress: () => setModalVisible(true),
15994
16682
  activeOpacity: draggable ? 1 : 0.7
15995
16683
  },
15996
- /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Image, { source: { uri: BUGBEAR_LOGO_BASE64 }, style: styles16.fabIcon }),
15997
- badgeCount > 0 && /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: styles16.badge }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles16.badgeText }, badgeCount > 9 ? "9+" : badgeCount))
16684
+ /* @__PURE__ */ import_react20.default.createElement(import_react_native19.Image, { source: { uri: BUGBEAR_LOGO_BASE64 }, style: styles16.fabIcon }),
16685
+ badgeCount > 0 && /* @__PURE__ */ import_react20.default.createElement(import_react_native19.View, { style: styles16.badge }, /* @__PURE__ */ import_react20.default.createElement(import_react_native19.Text, { style: styles16.badgeText }, badgeCount > 9 ? "9+" : badgeCount))
15998
16686
  )
15999
- ), /* @__PURE__ */ import_react19.default.createElement(
16000
- import_react_native18.Modal,
16687
+ ), /* @__PURE__ */ import_react20.default.createElement(
16688
+ import_react_native19.Modal,
16001
16689
  {
16002
16690
  visible: modalVisible,
16003
16691
  animationType: "slide",
16004
16692
  transparent: true,
16005
16693
  onRequestClose: handleClose
16006
16694
  },
16007
- /* @__PURE__ */ import_react19.default.createElement(
16008
- import_react_native18.KeyboardAvoidingView,
16695
+ /* @__PURE__ */ import_react20.default.createElement(
16696
+ import_react_native19.KeyboardAvoidingView,
16009
16697
  {
16010
- behavior: import_react_native18.Platform.OS === "ios" ? "padding" : "height",
16698
+ behavior: import_react_native19.Platform.OS === "ios" ? "padding" : "height",
16011
16699
  style: styles16.modalOverlay
16012
16700
  },
16013
- /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: styles16.modalContainer }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: styles16.header }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: styles16.headerLeft }, canGoBack ? /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: styles16.headerNavRow }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.TouchableOpacity, { onPress: () => nav.pop(), style: styles16.backButton }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles16.backText }, "\u2190 Back")), /* @__PURE__ */ import_react19.default.createElement(import_react_native18.TouchableOpacity, { onPress: () => nav.reset(), style: styles16.homeButton }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles16.homeText }, "\u{1F3E0}"))) : /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: styles16.headerTitleRow }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles16.headerTitle }, "BugBear"), testerInfo && /* @__PURE__ */ import_react19.default.createElement(import_react_native18.TouchableOpacity, { onPress: () => push({ name: "PROFILE" }) }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles16.headerName }, testerInfo.name, " \u270E")))), getHeaderTitle() ? /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles16.headerScreenTitle, numberOfLines: 1 }, getHeaderTitle()) : null, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.TouchableOpacity, { onPress: handleClose, style: styles16.closeButton }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles16.closeText }, "\u2715"))), /* @__PURE__ */ import_react19.default.createElement(
16014
- import_react_native18.ScrollView,
16701
+ /* @__PURE__ */ import_react20.default.createElement(import_react_native19.View, { style: styles16.modalContainer }, /* @__PURE__ */ import_react20.default.createElement(import_react_native19.View, { style: styles16.header }, /* @__PURE__ */ import_react20.default.createElement(import_react_native19.View, { style: styles16.headerLeft }, canGoBack ? /* @__PURE__ */ import_react20.default.createElement(import_react_native19.View, { style: styles16.headerNavRow }, /* @__PURE__ */ import_react20.default.createElement(import_react_native19.TouchableOpacity, { onPress: () => nav.pop(), style: styles16.backButton }, /* @__PURE__ */ import_react20.default.createElement(import_react_native19.Text, { style: styles16.backText }, "\u2190 Back")), /* @__PURE__ */ import_react20.default.createElement(import_react_native19.TouchableOpacity, { onPress: () => nav.reset(), style: styles16.homeButton }, /* @__PURE__ */ import_react20.default.createElement(import_react_native19.Text, { style: styles16.homeText }, "\u{1F3E0}"))) : /* @__PURE__ */ import_react20.default.createElement(import_react_native19.View, { style: styles16.headerTitleRow }, /* @__PURE__ */ import_react20.default.createElement(import_react_native19.Text, { style: styles16.headerTitle }, "BugBear"), testerInfo && /* @__PURE__ */ import_react20.default.createElement(import_react_native19.TouchableOpacity, { onPress: () => push({ name: "PROFILE" }) }, /* @__PURE__ */ import_react20.default.createElement(import_react_native19.Text, { style: styles16.headerName }, testerInfo.name, " \u270E")))), getHeaderTitle() ? /* @__PURE__ */ import_react20.default.createElement(import_react_native19.Text, { style: styles16.headerScreenTitle, numberOfLines: 1 }, getHeaderTitle()) : null, /* @__PURE__ */ import_react20.default.createElement(import_react_native19.TouchableOpacity, { onPress: handleClose, style: styles16.closeButton }, /* @__PURE__ */ import_react20.default.createElement(import_react_native19.Text, { style: styles16.closeText }, "\u2715"))), /* @__PURE__ */ import_react20.default.createElement(
16702
+ import_react_native19.ScrollView,
16015
16703
  {
16016
16704
  style: styles16.content,
16017
16705
  contentContainerStyle: styles16.contentContainer,
16018
16706
  keyboardShouldPersistTaps: "handled",
16019
16707
  showsVerticalScrollIndicator: false
16020
16708
  },
16021
- isLoading ? /* @__PURE__ */ import_react19.default.createElement(import_react_native18.View, { style: styles16.loadingContainer }, /* @__PURE__ */ import_react19.default.createElement(import_react_native18.ActivityIndicator, { size: "large", color: colors.blue }), /* @__PURE__ */ import_react19.default.createElement(import_react_native18.Text, { style: styles16.loadingText }, "Loading...")) : renderScreen()
16709
+ isLoading ? /* @__PURE__ */ import_react20.default.createElement(import_react_native19.View, { style: styles16.loadingContainer }, /* @__PURE__ */ import_react20.default.createElement(import_react_native19.ActivityIndicator, { size: "large", color: colors.blue }), /* @__PURE__ */ import_react20.default.createElement(import_react_native19.Text, { style: styles16.loadingText }, "Loading...")) : renderScreen()
16022
16710
  ))
16023
16711
  )
16024
16712
  ));
16025
16713
  }
16026
- var styles16 = import_react_native18.StyleSheet.create({
16714
+ var styles16 = import_react_native19.StyleSheet.create({
16027
16715
  // FAB
16028
16716
  fabContainer: {
16029
16717
  position: "absolute",
@@ -16165,9 +16853,9 @@ var styles16 = import_react_native18.StyleSheet.create({
16165
16853
  });
16166
16854
 
16167
16855
  // src/BugBearErrorBoundary.tsx
16168
- var import_react20 = __toESM(require("react"));
16169
- var import_react_native19 = require("react-native");
16170
- var BugBearErrorBoundary = class extends import_react20.Component {
16856
+ var import_react21 = __toESM(require("react"));
16857
+ var import_react_native20 = require("react-native");
16858
+ var BugBearErrorBoundary = class extends import_react21.Component {
16171
16859
  constructor(props) {
16172
16860
  super(props);
16173
16861
  this.reset = () => {
@@ -16211,7 +16899,7 @@ var BugBearErrorBoundary = class extends import_react20.Component {
16211
16899
  if (fallback) {
16212
16900
  return fallback;
16213
16901
  }
16214
- return /* @__PURE__ */ import_react20.default.createElement(import_react_native19.View, { style: styles17.container }, /* @__PURE__ */ import_react20.default.createElement(import_react_native19.Text, { style: styles17.title }, "Something went wrong"), /* @__PURE__ */ import_react20.default.createElement(import_react_native19.Text, { style: styles17.message }, error.message), /* @__PURE__ */ import_react20.default.createElement(import_react_native19.TouchableOpacity, { style: styles17.button, onPress: this.reset }, /* @__PURE__ */ import_react20.default.createElement(import_react_native19.Text, { style: styles17.buttonText }, "Try Again")), /* @__PURE__ */ import_react20.default.createElement(import_react_native19.Text, { style: styles17.caption }, "The error has been captured by BugBear"));
16902
+ return /* @__PURE__ */ import_react21.default.createElement(import_react_native20.View, { style: styles17.container }, /* @__PURE__ */ import_react21.default.createElement(import_react_native20.Text, { style: styles17.title }, "Something went wrong"), /* @__PURE__ */ import_react21.default.createElement(import_react_native20.Text, { style: styles17.message }, error.message), /* @__PURE__ */ import_react21.default.createElement(import_react_native20.TouchableOpacity, { style: styles17.button, onPress: this.reset }, /* @__PURE__ */ import_react21.default.createElement(import_react_native20.Text, { style: styles17.buttonText }, "Try Again")), /* @__PURE__ */ import_react21.default.createElement(import_react_native20.Text, { style: styles17.caption }, "The error has been captured by BugBear"));
16215
16903
  }
16216
16904
  return children;
16217
16905
  }
@@ -16222,7 +16910,7 @@ function useErrorContext() {
16222
16910
  getEnhancedContext: () => contextCapture.getEnhancedContext()
16223
16911
  };
16224
16912
  }
16225
- var styles17 = import_react_native19.StyleSheet.create({
16913
+ var styles17 = import_react_native20.StyleSheet.create({
16226
16914
  container: {
16227
16915
  padding: 20,
16228
16916
  margin: 20,