@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.mjs CHANGED
@@ -9,14 +9,14 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
9
9
  import React, { createContext, useContext, useEffect, useState, useCallback, useRef } from "react";
10
10
 
11
11
  // ../../node_modules/tslib/tslib.es6.mjs
12
- function __rest(s, e) {
12
+ function __rest(s2, e) {
13
13
  var t = {};
14
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
15
- t[p] = s[p];
16
- if (s != null && typeof Object.getOwnPropertySymbols === "function")
17
- for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
18
- if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
19
- t[p[i]] = s[p[i]];
14
+ for (var p in s2) if (Object.prototype.hasOwnProperty.call(s2, p) && e.indexOf(p) < 0)
15
+ t[p] = s2[p];
16
+ if (s2 != null && typeof Object.getOwnPropertySymbols === "function")
17
+ for (var i = 0, p = Object.getOwnPropertySymbols(s2); i < p.length; i++) {
18
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s2, p[i]))
19
+ t[p[i]] = s2[p[i]];
20
20
  }
21
21
  return t;
22
22
  }
@@ -831,9 +831,9 @@ var PostgrestFilterBuilder = class extends PostgrestTransformBuilder {
831
831
  * @param values - The values array to filter with
832
832
  */
833
833
  in(column, values) {
834
- const cleanedValues = Array.from(new Set(values)).map((s) => {
835
- if (typeof s === "string" && PostgrestReservedCharsRegexp.test(s)) return `"${s}"`;
836
- else return `${s}`;
834
+ const cleanedValues = Array.from(new Set(values)).map((s2) => {
835
+ if (typeof s2 === "string" && PostgrestReservedCharsRegexp.test(s2)) return `"${s2}"`;
836
+ else return `${s2}`;
837
837
  }).join(",");
838
838
  this.url.searchParams.append(column, `in.(${cleanedValues})`);
839
839
  return this;
@@ -845,9 +845,9 @@ var PostgrestFilterBuilder = class extends PostgrestTransformBuilder {
845
845
  * @param values - The values array to filter with
846
846
  */
847
847
  notIn(column, values) {
848
- const cleanedValues = Array.from(new Set(values)).map((s) => {
849
- if (typeof s === "string" && PostgrestReservedCharsRegexp.test(s)) return `"${s}"`;
850
- else return `${s}`;
848
+ const cleanedValues = Array.from(new Set(values)).map((s2) => {
849
+ if (typeof s2 === "string" && PostgrestReservedCharsRegexp.test(s2)) return `"${s2}"`;
850
+ else return `${s2}`;
851
851
  }).join(",");
852
852
  this.url.searchParams.append(column, `not.in.(${cleanedValues})`);
853
853
  return this;
@@ -11602,31 +11602,306 @@ function captureError(error, errorInfo) {
11602
11602
  componentStack: errorInfo?.componentStack
11603
11603
  };
11604
11604
  }
11605
+ var LocalStorageAdapter = class {
11606
+ constructor() {
11607
+ this.fallback = /* @__PURE__ */ new Map();
11608
+ }
11609
+ get isAvailable() {
11610
+ try {
11611
+ const key = "__bugbear_test__";
11612
+ localStorage.setItem(key, "1");
11613
+ localStorage.removeItem(key);
11614
+ return true;
11615
+ } catch {
11616
+ return false;
11617
+ }
11618
+ }
11619
+ async getItem(key) {
11620
+ if (this.isAvailable) return localStorage.getItem(key);
11621
+ return this.fallback.get(key) ?? null;
11622
+ }
11623
+ async setItem(key, value) {
11624
+ if (this.isAvailable) {
11625
+ localStorage.setItem(key, value);
11626
+ } else {
11627
+ this.fallback.set(key, value);
11628
+ }
11629
+ }
11630
+ async removeItem(key) {
11631
+ if (this.isAvailable) {
11632
+ localStorage.removeItem(key);
11633
+ } else {
11634
+ this.fallback.delete(key);
11635
+ }
11636
+ }
11637
+ };
11638
+ var OfflineQueue = class {
11639
+ constructor(config) {
11640
+ this.items = [];
11641
+ this.storageKey = "bugbear_offline_queue";
11642
+ this.flushing = false;
11643
+ this.handlers = /* @__PURE__ */ new Map();
11644
+ this.maxItems = config.maxItems ?? 50;
11645
+ this.maxRetries = config.maxRetries ?? 5;
11646
+ this.storage = config.storage ?? new LocalStorageAdapter();
11647
+ }
11648
+ // ── Flush handler registration ──────────────────────────────
11649
+ /** Register a handler that replays a queued operation. */
11650
+ registerHandler(type, handler) {
11651
+ this.handlers.set(type, handler);
11652
+ }
11653
+ // ── Change listener ─────────────────────────────────────────
11654
+ /** Subscribe to queue count changes (for UI badges). */
11655
+ onChange(callback) {
11656
+ this.listener = callback;
11657
+ }
11658
+ notify() {
11659
+ this.listener?.(this.items.length);
11660
+ }
11661
+ // ── Persistence ─────────────────────────────────────────────
11662
+ /** Load queue from persistent storage. Call once after construction. */
11663
+ async load() {
11664
+ try {
11665
+ const raw = await this.storage.getItem(this.storageKey);
11666
+ if (raw) {
11667
+ this.items = JSON.parse(raw);
11668
+ }
11669
+ } catch {
11670
+ this.items = [];
11671
+ }
11672
+ this.notify();
11673
+ }
11674
+ async save() {
11675
+ try {
11676
+ await this.storage.setItem(this.storageKey, JSON.stringify(this.items));
11677
+ } catch (err) {
11678
+ console.error("BugBear: Failed to persist offline queue", err);
11679
+ }
11680
+ this.notify();
11681
+ }
11682
+ // ── Enqueue ─────────────────────────────────────────────────
11683
+ /** Add a failed operation to the queue. Returns the item ID. */
11684
+ async enqueue(type, payload) {
11685
+ if (this.items.length >= this.maxItems) {
11686
+ this.items.shift();
11687
+ }
11688
+ const id = `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
11689
+ this.items.push({ id, type, payload, createdAt: Date.now(), retries: 0 });
11690
+ await this.save();
11691
+ return id;
11692
+ }
11693
+ // ── Accessors ───────────────────────────────────────────────
11694
+ /** Number of items waiting to be flushed. */
11695
+ get count() {
11696
+ return this.items.length;
11697
+ }
11698
+ /** Read-only snapshot of pending items. */
11699
+ get pending() {
11700
+ return [...this.items];
11701
+ }
11702
+ /** Whether a flush is currently in progress. */
11703
+ get isFlushing() {
11704
+ return this.flushing;
11705
+ }
11706
+ // ── Flush ───────────────────────────────────────────────────
11707
+ /**
11708
+ * Process all queued items in FIFO order.
11709
+ * Stops early if a network error is encountered (still offline).
11710
+ */
11711
+ async flush() {
11712
+ if (this.flushing || this.items.length === 0) {
11713
+ return { flushed: 0, failed: 0 };
11714
+ }
11715
+ this.flushing = true;
11716
+ let flushed = 0;
11717
+ let failed = 0;
11718
+ const snapshot = [...this.items];
11719
+ for (const item of snapshot) {
11720
+ const handler = this.handlers.get(item.type);
11721
+ if (!handler) {
11722
+ failed++;
11723
+ continue;
11724
+ }
11725
+ try {
11726
+ const result = await handler(item.payload);
11727
+ if (result.success) {
11728
+ this.items = this.items.filter((i) => i.id !== item.id);
11729
+ flushed++;
11730
+ } else if (isNetworkError(result.error)) {
11731
+ break;
11732
+ } else {
11733
+ const idx = this.items.findIndex((i) => i.id === item.id);
11734
+ if (idx !== -1) {
11735
+ this.items[idx].retries++;
11736
+ if (this.items[idx].retries >= this.maxRetries) {
11737
+ this.items.splice(idx, 1);
11738
+ }
11739
+ }
11740
+ failed++;
11741
+ }
11742
+ } catch {
11743
+ break;
11744
+ }
11745
+ }
11746
+ await this.save();
11747
+ this.flushing = false;
11748
+ return { flushed, failed };
11749
+ }
11750
+ // ── Clear ───────────────────────────────────────────────────
11751
+ /** Drop all queued items. */
11752
+ async clear() {
11753
+ this.items = [];
11754
+ await this.save();
11755
+ }
11756
+ };
11757
+ function isNetworkError(error) {
11758
+ if (!error) return false;
11759
+ const msg = error.toLowerCase();
11760
+ 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
11761
+ msg.includes("the internet connection appears to be offline") || msg.includes("a]server with the specified hostname could not be found");
11762
+ }
11605
11763
  var formatPgError = (e) => {
11606
11764
  if (!e || typeof e !== "object") return { raw: e };
11607
11765
  const { message, code, details, hint } = e;
11608
11766
  return { message, code, details, hint };
11609
11767
  };
11610
- var DEFAULT_SUPABASE_URL = "https://kyxgzjnqgvapvlnvqawz.supabase.co";
11611
- var getEnvVar = (key) => {
11612
- try {
11613
- if (typeof process !== "undefined" && process.env) {
11614
- return process.env[key];
11615
- }
11616
- } catch {
11617
- }
11618
- return void 0;
11619
- };
11620
- var HOSTED_BUGBEAR_ANON_KEY = getEnvVar("BUGBEAR_ANON_KEY") || getEnvVar("NEXT_PUBLIC_BUGBEAR_ANON_KEY") || "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Imt5eGd6am5xZ3ZhcHZsbnZxYXd6Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjkyNjgwNDIsImV4cCI6MjA4NDg0NDA0Mn0.NUkAlCHLFjeRoisbmNUVoGb4R6uQ8xs5LAEIX1BWTwU";
11621
11768
  var BugBearClient = class {
11622
11769
  constructor(config) {
11623
11770
  this.navigationHistory = [];
11624
11771
  this.reportSubmitInFlight = false;
11772
+ this._queue = null;
11773
+ this.realtimeChannels = [];
11774
+ if (!config.supabaseUrl) {
11775
+ throw new Error("BugBear: supabaseUrl is required. Get it from your BugBear project settings.");
11776
+ }
11777
+ if (!config.supabaseAnonKey) {
11778
+ throw new Error("BugBear: supabaseAnonKey is required. Get it from your BugBear project settings.");
11779
+ }
11625
11780
  this.config = config;
11626
- this.supabase = createClient(
11627
- config.supabaseUrl || DEFAULT_SUPABASE_URL,
11628
- config.supabaseAnonKey || HOSTED_BUGBEAR_ANON_KEY
11629
- );
11781
+ this.supabase = createClient(config.supabaseUrl, config.supabaseAnonKey);
11782
+ if (config.offlineQueue?.enabled) {
11783
+ this._queue = new OfflineQueue({
11784
+ enabled: true,
11785
+ maxItems: config.offlineQueue.maxItems,
11786
+ maxRetries: config.offlineQueue.maxRetries
11787
+ });
11788
+ this.registerQueueHandlers();
11789
+ }
11790
+ }
11791
+ // ── Offline Queue ─────────────────────────────────────────
11792
+ /**
11793
+ * Access the offline queue (if enabled).
11794
+ * Use this to check queue.count, subscribe to changes, or trigger flush.
11795
+ */
11796
+ get queue() {
11797
+ return this._queue;
11798
+ }
11799
+ /**
11800
+ * Initialize the offline queue with a platform-specific storage adapter.
11801
+ * Must be called after construction for React Native (which supplies AsyncStorage).
11802
+ * Web callers can skip this — LocalStorageAdapter is the default.
11803
+ */
11804
+ async initQueue(storage) {
11805
+ if (!this._queue) return;
11806
+ if (storage) {
11807
+ this._queue = new OfflineQueue({
11808
+ enabled: true,
11809
+ maxItems: this.config.offlineQueue?.maxItems,
11810
+ maxRetries: this.config.offlineQueue?.maxRetries,
11811
+ storage
11812
+ });
11813
+ this.registerQueueHandlers();
11814
+ }
11815
+ await this._queue.load();
11816
+ }
11817
+ registerQueueHandlers() {
11818
+ if (!this._queue) return;
11819
+ this._queue.registerHandler("report", async (payload) => {
11820
+ const { error } = await this.supabase.from("reports").insert(payload).select("id").single();
11821
+ if (error) return { success: false, error: error.message };
11822
+ return { success: true };
11823
+ });
11824
+ this._queue.registerHandler("message", async (payload) => {
11825
+ const { error } = await this.supabase.from("discussion_messages").insert(payload);
11826
+ if (error) return { success: false, error: error.message };
11827
+ return { success: true };
11828
+ });
11829
+ this._queue.registerHandler("feedback", async (payload) => {
11830
+ const { error } = await this.supabase.from("test_feedback").insert(payload);
11831
+ if (error) return { success: false, error: error.message };
11832
+ return { success: true };
11833
+ });
11834
+ }
11835
+ // ── Realtime Subscriptions ─────────────────────────────────
11836
+ /** Whether realtime is enabled in config. */
11837
+ get realtimeEnabled() {
11838
+ return !!this.config.realtime?.enabled;
11839
+ }
11840
+ /**
11841
+ * Subscribe to postgres_changes on relevant tables.
11842
+ * Each callback fires when the corresponding table has changes —
11843
+ * the provider should call its refresh function in response.
11844
+ * Returns a cleanup function that unsubscribes all channels.
11845
+ */
11846
+ subscribeToChanges(callbacks) {
11847
+ this.unsubscribeAll();
11848
+ const projectId = this.config.projectId;
11849
+ const debounce = (fn, ms = 500) => {
11850
+ let timer;
11851
+ return () => {
11852
+ clearTimeout(timer);
11853
+ timer = setTimeout(fn, ms);
11854
+ };
11855
+ };
11856
+ if (callbacks.onAssignmentChange) {
11857
+ const debouncedCb = debounce(callbacks.onAssignmentChange);
11858
+ const channel = this.supabase.channel("bugbear-assignments").on("postgres_changes", {
11859
+ event: "*",
11860
+ schema: "public",
11861
+ table: "test_assignments",
11862
+ filter: `project_id=eq.${projectId}`
11863
+ }, debouncedCb).subscribe((status) => {
11864
+ if (status === "CHANNEL_ERROR") {
11865
+ console.warn("BugBear: Realtime subscription failed for test_assignments");
11866
+ }
11867
+ });
11868
+ this.realtimeChannels.push(channel);
11869
+ }
11870
+ if (callbacks.onMessageChange) {
11871
+ const debouncedCb = debounce(callbacks.onMessageChange);
11872
+ const channel = this.supabase.channel("bugbear-messages").on("postgres_changes", {
11873
+ event: "INSERT",
11874
+ schema: "public",
11875
+ table: "discussion_messages"
11876
+ }, debouncedCb).subscribe((status) => {
11877
+ if (status === "CHANNEL_ERROR") {
11878
+ console.warn("BugBear: Realtime subscription failed for discussion_messages");
11879
+ }
11880
+ });
11881
+ this.realtimeChannels.push(channel);
11882
+ }
11883
+ if (callbacks.onReportChange) {
11884
+ const debouncedCb = debounce(callbacks.onReportChange);
11885
+ const channel = this.supabase.channel("bugbear-reports").on("postgres_changes", {
11886
+ event: "UPDATE",
11887
+ schema: "public",
11888
+ table: "reports",
11889
+ filter: `project_id=eq.${projectId}`
11890
+ }, debouncedCb).subscribe((status) => {
11891
+ if (status === "CHANNEL_ERROR") {
11892
+ console.warn("BugBear: Realtime subscription failed for reports");
11893
+ }
11894
+ });
11895
+ this.realtimeChannels.push(channel);
11896
+ }
11897
+ return () => this.unsubscribeAll();
11898
+ }
11899
+ /** Remove all active Realtime channels. */
11900
+ unsubscribeAll() {
11901
+ for (const channel of this.realtimeChannels) {
11902
+ this.supabase.removeChannel(channel);
11903
+ }
11904
+ this.realtimeChannels = [];
11630
11905
  }
11631
11906
  /**
11632
11907
  * Track navigation for context.
@@ -11686,6 +11961,7 @@ var BugBearClient = class {
11686
11961
  return { success: false, error: "A report is already being submitted" };
11687
11962
  }
11688
11963
  this.reportSubmitInFlight = true;
11964
+ let fullReport;
11689
11965
  try {
11690
11966
  const validationError = this.validateReport(report);
11691
11967
  if (validationError) {
@@ -11702,7 +11978,7 @@ var BugBearClient = class {
11702
11978
  return { success: false, error: "User not authenticated" };
11703
11979
  }
11704
11980
  const testerInfo = await this.getTesterInfo();
11705
- const fullReport = {
11981
+ fullReport = {
11706
11982
  project_id: this.config.projectId,
11707
11983
  reporter_id: userInfo.id,
11708
11984
  // User ID from host app (required)
@@ -11729,6 +12005,10 @@ var BugBearClient = class {
11729
12005
  };
11730
12006
  const { data, error } = await this.supabase.from("reports").insert(fullReport).select("id").single();
11731
12007
  if (error) {
12008
+ if (this._queue && isNetworkError(error.message)) {
12009
+ await this._queue.enqueue("report", fullReport);
12010
+ return { success: false, queued: true, error: "Queued \u2014 will send when online" };
12011
+ }
11732
12012
  console.error("BugBear: Failed to submit report", error.message);
11733
12013
  return { success: false, error: error.message };
11734
12014
  }
@@ -11738,6 +12018,10 @@ var BugBearClient = class {
11738
12018
  return { success: true, reportId: data.id };
11739
12019
  } catch (err) {
11740
12020
  const message = err instanceof Error ? err.message : "Unknown error";
12021
+ if (this._queue && fullReport && isNetworkError(message)) {
12022
+ await this._queue.enqueue("report", fullReport);
12023
+ return { success: false, queued: true, error: "Queued \u2014 will send when online" };
12024
+ }
11741
12025
  return { success: false, error: message };
11742
12026
  } finally {
11743
12027
  this.reportSubmitInFlight = false;
@@ -11747,14 +12031,18 @@ var BugBearClient = class {
11747
12031
  * Get assigned tests for current user
11748
12032
  * First looks up the tester by email, then fetches their assignments
11749
12033
  */
11750
- async getAssignedTests() {
12034
+ async getAssignedTests(options) {
11751
12035
  try {
11752
12036
  const testerInfo = await this.getTesterInfo();
11753
12037
  if (!testerInfo) return [];
11754
- const { data, error } = await this.supabase.from("test_assignments").select(`
12038
+ const pageSize = Math.min(options?.pageSize ?? 100, 100);
12039
+ const from = (options?.page ?? 0) * pageSize;
12040
+ const to = from + pageSize - 1;
12041
+ const selectFields = `
11755
12042
  id,
11756
12043
  status,
11757
12044
  started_at,
12045
+ completed_at,
11758
12046
  skip_reason,
11759
12047
  is_verification,
11760
12048
  original_report_id,
@@ -11789,20 +12077,24 @@ var BugBearClient = class {
11789
12077
  color,
11790
12078
  description,
11791
12079
  login_hint
11792
- )
12080
+ ),
12081
+ platforms
11793
12082
  )
11794
- `).eq("project_id", this.config.projectId).eq("tester_id", testerInfo.id).in("status", ["pending", "in_progress"]).order("created_at", { ascending: true }).limit(100);
11795
- if (error) {
11796
- console.error("BugBear: Failed to fetch assignments", formatPgError(error));
12083
+ `;
12084
+ const twentyFourHoursAgo = new Date(Date.now() - 24 * 60 * 60 * 1e3).toISOString();
12085
+ const [pendingResult, completedResult] = await Promise.all([
12086
+ 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),
12087
+ 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)
12088
+ ]);
12089
+ if (pendingResult.error) {
12090
+ console.error("BugBear: Failed to fetch assignments", formatPgError(pendingResult.error));
11797
12091
  return [];
11798
12092
  }
11799
- const mapped = (data || []).filter((item) => {
11800
- if (!item.test_case) {
11801
- console.warn("BugBear: Assignment returned without test_case", { id: item.id });
11802
- return false;
11803
- }
11804
- return true;
11805
- }).map((item) => ({
12093
+ const allData = [
12094
+ ...pendingResult.data || [],
12095
+ ...completedResult.data || []
12096
+ ];
12097
+ const mapItem = (item) => ({
11806
12098
  id: item.id,
11807
12099
  status: item.status,
11808
12100
  startedAt: item.started_at,
@@ -11840,12 +12132,24 @@ var BugBearClient = class {
11840
12132
  color: item.test_case.role.color,
11841
12133
  description: item.test_case.role.description,
11842
12134
  loginHint: item.test_case.role.login_hint
11843
- } : void 0
12135
+ } : void 0,
12136
+ platforms: item.test_case.platforms || void 0
11844
12137
  }
11845
- }));
12138
+ });
12139
+ const mapped = allData.filter((item) => {
12140
+ if (!item.test_case) {
12141
+ console.warn("BugBear: Assignment returned without test_case", { id: item.id });
12142
+ return false;
12143
+ }
12144
+ return true;
12145
+ }).map(mapItem);
11846
12146
  mapped.sort((a, b) => {
11847
12147
  if (a.isVerification && !b.isVerification) return -1;
11848
12148
  if (!a.isVerification && b.isVerification) return 1;
12149
+ const aActive = a.status === "pending" || a.status === "in_progress";
12150
+ const bActive = b.status === "pending" || b.status === "in_progress";
12151
+ if (aActive && !bActive) return -1;
12152
+ if (!aActive && bActive) return 1;
11849
12153
  return 0;
11850
12154
  });
11851
12155
  return mapped;
@@ -12010,6 +12314,36 @@ var BugBearClient = class {
12010
12314
  async failAssignment(assignmentId) {
12011
12315
  return this.updateAssignmentStatus(assignmentId, "failed");
12012
12316
  }
12317
+ /**
12318
+ * Reopen a completed assignment — sets it back to in_progress with a fresh timer.
12319
+ * Clears completed_at and duration_seconds so it can be re-evaluated.
12320
+ */
12321
+ async reopenAssignment(assignmentId) {
12322
+ try {
12323
+ const { data: current, error: fetchError } = await this.supabase.from("test_assignments").select("status").eq("id", assignmentId).single();
12324
+ if (fetchError || !current) {
12325
+ return { success: false, error: "Assignment not found" };
12326
+ }
12327
+ if (current.status === "pending" || current.status === "in_progress") {
12328
+ return { success: true };
12329
+ }
12330
+ const { error } = await this.supabase.from("test_assignments").update({
12331
+ status: "in_progress",
12332
+ started_at: (/* @__PURE__ */ new Date()).toISOString(),
12333
+ completed_at: null,
12334
+ duration_seconds: null,
12335
+ skip_reason: null
12336
+ }).eq("id", assignmentId).eq("status", current.status);
12337
+ if (error) {
12338
+ console.error("BugBear: Failed to reopen assignment", error);
12339
+ return { success: false, error: error.message };
12340
+ }
12341
+ return { success: true };
12342
+ } catch (err) {
12343
+ const message = err instanceof Error ? err.message : "Unknown error";
12344
+ return { success: false, error: message };
12345
+ }
12346
+ }
12013
12347
  /**
12014
12348
  * Skip a test assignment with a required reason
12015
12349
  * Marks the assignment as 'skipped' and records why it was skipped
@@ -12050,6 +12384,7 @@ var BugBearClient = class {
12050
12384
  * This empowers testers to shape better tests over time
12051
12385
  */
12052
12386
  async submitTestFeedback(options) {
12387
+ let feedbackPayload;
12053
12388
  try {
12054
12389
  const testerInfo = await this.getTesterInfo();
12055
12390
  if (!testerInfo) {
@@ -12069,7 +12404,7 @@ var BugBearClient = class {
12069
12404
  return { success: false, error: `${name} must be between 1 and 5` };
12070
12405
  }
12071
12406
  }
12072
- const { error: feedbackError } = await this.supabase.from("test_feedback").insert({
12407
+ feedbackPayload = {
12073
12408
  project_id: this.config.projectId,
12074
12409
  test_case_id: testCaseId,
12075
12410
  assignment_id: assignmentId || null,
@@ -12087,8 +12422,13 @@ var BugBearClient = class {
12087
12422
  platform: this.getDeviceInfo().platform,
12088
12423
  time_to_complete_seconds: timeToCompleteSeconds || null,
12089
12424
  screenshot_urls: screenshotUrls || []
12090
- });
12425
+ };
12426
+ const { error: feedbackError } = await this.supabase.from("test_feedback").insert(feedbackPayload);
12091
12427
  if (feedbackError) {
12428
+ if (this._queue && isNetworkError(feedbackError.message)) {
12429
+ await this._queue.enqueue("feedback", feedbackPayload);
12430
+ return { success: false, queued: true, error: "Queued \u2014 will send when online" };
12431
+ }
12092
12432
  console.error("BugBear: Failed to submit feedback", feedbackError);
12093
12433
  return { success: false, error: feedbackError.message };
12094
12434
  }
@@ -12105,6 +12445,10 @@ var BugBearClient = class {
12105
12445
  return { success: true };
12106
12446
  } catch (err) {
12107
12447
  const message = err instanceof Error ? err.message : "Unknown error";
12448
+ if (this._queue && feedbackPayload && isNetworkError(message)) {
12449
+ await this._queue.enqueue("feedback", feedbackPayload);
12450
+ return { success: false, queued: true, error: "Queued \u2014 will send when online" };
12451
+ }
12108
12452
  console.error("BugBear: Error submitting feedback", err);
12109
12453
  return { success: false, error: message };
12110
12454
  }
@@ -12739,6 +13083,7 @@ var BugBearClient = class {
12739
13083
  * Send a message to a thread
12740
13084
  */
12741
13085
  async sendMessage(threadId, content, attachments) {
13086
+ let insertData;
12742
13087
  try {
12743
13088
  const testerInfo = await this.getTesterInfo();
12744
13089
  if (!testerInfo) {
@@ -12750,7 +13095,7 @@ var BugBearClient = class {
12750
13095
  console.error("BugBear: Rate limit exceeded for messages");
12751
13096
  return false;
12752
13097
  }
12753
- const insertData = {
13098
+ insertData = {
12754
13099
  thread_id: threadId,
12755
13100
  sender_type: "tester",
12756
13101
  sender_tester_id: testerInfo.id,
@@ -12765,12 +13110,21 @@ var BugBearClient = class {
12765
13110
  }
12766
13111
  const { error } = await this.supabase.from("discussion_messages").insert(insertData);
12767
13112
  if (error) {
13113
+ if (this._queue && isNetworkError(error.message)) {
13114
+ await this._queue.enqueue("message", insertData);
13115
+ return false;
13116
+ }
12768
13117
  console.error("BugBear: Failed to send message", formatPgError(error));
12769
13118
  return false;
12770
13119
  }
12771
13120
  await this.markThreadAsRead(threadId);
12772
13121
  return true;
12773
13122
  } catch (err) {
13123
+ const message = err instanceof Error ? err.message : "Unknown error";
13124
+ if (this._queue && insertData && isNetworkError(message)) {
13125
+ await this._queue.enqueue("message", insertData);
13126
+ return false;
13127
+ }
12774
13128
  console.error("BugBear: Error sending message", err);
12775
13129
  return false;
12776
13130
  }
@@ -12957,7 +13311,7 @@ var BugBearClient = class {
12957
13311
  console.error("BugBear: Failed to fetch session history", formatPgError(error));
12958
13312
  return [];
12959
13313
  }
12960
- return (data || []).map((s) => this.transformSession(s));
13314
+ return (data || []).map((s2) => this.transformSession(s2));
12961
13315
  } catch (err) {
12962
13316
  console.error("BugBear: Error fetching session history", err);
12963
13317
  return [];
@@ -13105,7 +13459,7 @@ function createBugBear(config) {
13105
13459
  }
13106
13460
 
13107
13461
  // src/BugBearProvider.tsx
13108
- import { Platform, Dimensions } from "react-native";
13462
+ import { Platform, Dimensions, AppState } from "react-native";
13109
13463
  var BugBearContext = createContext({
13110
13464
  client: null,
13111
13465
  isTester: false,
@@ -13146,6 +13500,7 @@ var BugBearContext = createContext({
13146
13500
  issueCounts: { open: 0, done: 0, reopened: 0 },
13147
13501
  refreshIssueCounts: async () => {
13148
13502
  },
13503
+ queuedCount: 0,
13149
13504
  dashboardUrl: void 0,
13150
13505
  onError: void 0
13151
13506
  });
@@ -13162,6 +13517,7 @@ function BugBearProvider({ config, children, appVersion, enabled = true }) {
13162
13517
  const [threads, setThreads] = useState([]);
13163
13518
  const [unreadCount, setUnreadCount] = useState(0);
13164
13519
  const [issueCounts, setIssueCounts] = useState({ open: 0, done: 0, reopened: 0 });
13520
+ const [queuedCount, setQueuedCount] = useState(0);
13165
13521
  const [activeSession, setActiveSession] = useState(null);
13166
13522
  const [sessionFindings, setSessionFindings] = useState([]);
13167
13523
  const hasInitialized = useRef(false);
@@ -13321,18 +13677,46 @@ function BugBearProvider({ config, children, appVersion, enabled = true }) {
13321
13677
  hasInitialized.current = true;
13322
13678
  contextCapture.startCapture();
13323
13679
  const newClient = createBugBear(config);
13680
+ if (newClient.queue) {
13681
+ newClient.queue.onChange(setQueuedCount);
13682
+ newClient.initQueue();
13683
+ }
13324
13684
  setClient(newClient);
13325
13685
  initializeBugBear(newClient);
13326
13686
  }
13327
13687
  }, [enabled, config, initializeBugBear]);
13688
+ useEffect(() => {
13689
+ if (!client?.queue) return;
13690
+ const subscription = AppState.addEventListener("change", (state) => {
13691
+ if (state === "active" && client.queue && client.queue.count > 0) {
13692
+ client.queue.flush();
13693
+ }
13694
+ });
13695
+ if (client.queue.count > 0) {
13696
+ client.queue.flush();
13697
+ }
13698
+ return () => subscription.remove();
13699
+ }, [client]);
13328
13700
  useEffect(() => {
13329
13701
  if (!client || !isTester || !isQAEnabled) return;
13702
+ let unsubscribe;
13703
+ if (client.realtimeEnabled) {
13704
+ unsubscribe = client.subscribeToChanges({
13705
+ onAssignmentChange: refreshAssignments,
13706
+ onMessageChange: refreshThreads,
13707
+ onReportChange: refreshIssueCounts
13708
+ });
13709
+ }
13710
+ const pollInterval = client.realtimeEnabled ? 12e4 : 3e4;
13330
13711
  const interval = setInterval(() => {
13331
13712
  refreshThreads();
13332
13713
  refreshIssueCounts();
13333
- }, 3e4);
13334
- return () => clearInterval(interval);
13335
- }, [client, isTester, isQAEnabled, refreshThreads, refreshIssueCounts]);
13714
+ }, pollInterval);
13715
+ return () => {
13716
+ clearInterval(interval);
13717
+ unsubscribe?.();
13718
+ };
13719
+ }, [client, isTester, isQAEnabled, refreshThreads, refreshIssueCounts, refreshAssignments]);
13336
13720
  const currentAssignment = assignments.find(
13337
13721
  (a) => a.status === "in_progress"
13338
13722
  ) || assignments.find(
@@ -13375,6 +13759,7 @@ function BugBearProvider({ config, children, appVersion, enabled = true }) {
13375
13759
  // Issue tracking
13376
13760
  issueCounts,
13377
13761
  refreshIssueCounts,
13762
+ queuedCount,
13378
13763
  dashboardUrl: config.dashboardUrl,
13379
13764
  onError: config.onError
13380
13765
  }
@@ -13384,21 +13769,21 @@ function BugBearProvider({ config, children, appVersion, enabled = true }) {
13384
13769
  }
13385
13770
 
13386
13771
  // src/BugBearButton.tsx
13387
- import React17, { useState as useState12, useRef as useRef3 } from "react";
13772
+ import React18, { useState as useState12, useRef as useRef4 } from "react";
13388
13773
  import {
13389
- View as View16,
13774
+ View as View17,
13390
13775
  Text as Text16,
13391
13776
  Image as Image4,
13392
13777
  TouchableOpacity as TouchableOpacity15,
13393
13778
  Modal as Modal3,
13394
13779
  ScrollView as ScrollView3,
13395
- StyleSheet as StyleSheet17,
13780
+ StyleSheet as StyleSheet18,
13396
13781
  Dimensions as Dimensions2,
13397
13782
  KeyboardAvoidingView,
13398
- Platform as Platform4,
13783
+ Platform as Platform5,
13399
13784
  PanResponder,
13400
- Animated,
13401
- ActivityIndicator as ActivityIndicator3,
13785
+ Animated as Animated2,
13786
+ ActivityIndicator as ActivityIndicator2,
13402
13787
  Keyboard as Keyboard2
13403
13788
  } from "react-native";
13404
13789
 
@@ -13544,9 +13929,9 @@ var shared = StyleSheet.create({
13544
13929
  function formatElapsedTime(seconds) {
13545
13930
  const h = Math.floor(seconds / 3600);
13546
13931
  const m = Math.floor(seconds % 3600 / 60);
13547
- const s = seconds % 60;
13548
- if (h > 0) return `${h}:${m.toString().padStart(2, "0")}:${s.toString().padStart(2, "0")}`;
13549
- return `${m}:${s.toString().padStart(2, "0")}`;
13932
+ const s2 = seconds % 60;
13933
+ if (h > 0) return `${h}:${m.toString().padStart(2, "0")}:${s2.toString().padStart(2, "0")}`;
13934
+ return `${m}:${s2.toString().padStart(2, "0")}`;
13550
13935
  }
13551
13936
  function formatRelativeTime(dateString) {
13552
13937
  const date = new Date(dateString);
@@ -13618,11 +14003,90 @@ var templateInfo = {
13618
14003
  };
13619
14004
 
13620
14005
  // src/widget/screens/HomeScreen.tsx
13621
- import React2, { useEffect as useEffect2 } from "react";
13622
- import { View, Text, TouchableOpacity, StyleSheet as StyleSheet2, Linking } from "react-native";
13623
- function HomeScreen({ nav }) {
13624
- const { assignments, unreadCount, threads, refreshAssignments, refreshThreads, issueCounts, refreshIssueCounts, dashboardUrl } = useBugBear();
14006
+ import React3, { useEffect as useEffect3 } from "react";
14007
+ import { View as View2, Text, TouchableOpacity, StyleSheet as StyleSheet3, Linking } from "react-native";
14008
+
14009
+ // src/widget/Skeleton.tsx
14010
+ import React2, { useEffect as useEffect2, useRef as useRef2 } from "react";
14011
+ import { View, Animated, StyleSheet as StyleSheet2 } from "react-native";
14012
+ function usePulse(delay = 0) {
14013
+ const opacity = useRef2(new Animated.Value(0.6)).current;
13625
14014
  useEffect2(() => {
14015
+ const timeout = setTimeout(() => {
14016
+ Animated.loop(
14017
+ Animated.sequence([
14018
+ Animated.timing(opacity, { toValue: 0.25, duration: 750, useNativeDriver: true }),
14019
+ Animated.timing(opacity, { toValue: 0.6, duration: 750, useNativeDriver: true })
14020
+ ])
14021
+ ).start();
14022
+ }, delay);
14023
+ return () => clearTimeout(timeout);
14024
+ }, [opacity, delay]);
14025
+ return opacity;
14026
+ }
14027
+ function Bar({ width = "100%", height = 12, radius = 6, delay = 0 }) {
14028
+ const opacity = usePulse(delay);
14029
+ return /* @__PURE__ */ React2.createElement(
14030
+ Animated.View,
14031
+ {
14032
+ style: {
14033
+ width,
14034
+ height,
14035
+ borderRadius: radius,
14036
+ backgroundColor: colors.border,
14037
+ opacity
14038
+ }
14039
+ }
14040
+ );
14041
+ }
14042
+ function Circle({ size = 20, delay = 0 }) {
14043
+ const opacity = usePulse(delay);
14044
+ return /* @__PURE__ */ React2.createElement(
14045
+ Animated.View,
14046
+ {
14047
+ style: {
14048
+ width: size,
14049
+ height: size,
14050
+ borderRadius: size / 2,
14051
+ backgroundColor: colors.border,
14052
+ opacity
14053
+ }
14054
+ }
14055
+ );
14056
+ }
14057
+ function HomeScreenSkeleton() {
14058
+ return /* @__PURE__ */ React2.createElement(View, null, /* @__PURE__ */ React2.createElement(Bar, { width: "100%", height: 100, radius: 16 }), /* @__PURE__ */ React2.createElement(View, { style: { height: 20 } }), /* @__PURE__ */ React2.createElement(View, { style: s.actionGrid }, [0, 1, 2, 3].map((i) => /* @__PURE__ */ React2.createElement(View, { key: i, style: s.actionCard }, /* @__PURE__ */ React2.createElement(Circle, { size: 28, delay: i * 80 }), /* @__PURE__ */ React2.createElement(View, { style: { height: 8 } }), /* @__PURE__ */ React2.createElement(Bar, { width: 60, height: 10, delay: i * 80 })))), /* @__PURE__ */ React2.createElement(View, { style: s.issueGrid }, [0, 1, 2].map((i) => /* @__PURE__ */ React2.createElement(View, { key: i, style: s.issueCard }, /* @__PURE__ */ React2.createElement(Bar, { width: 30, height: 18, delay: i * 100 }), /* @__PURE__ */ React2.createElement(View, { style: { height: 6 } }), /* @__PURE__ */ React2.createElement(Bar, { width: 40, height: 8, delay: i * 100 })))), /* @__PURE__ */ React2.createElement(Bar, { width: "100%", height: 6, radius: 3 }), /* @__PURE__ */ React2.createElement(View, { style: { height: 8 } }), /* @__PURE__ */ React2.createElement(View, { style: { alignItems: "center" } }, /* @__PURE__ */ React2.createElement(Bar, { width: 120, height: 10 })));
14059
+ }
14060
+ function TestItemSkeleton({ delay = 0 }) {
14061
+ return /* @__PURE__ */ React2.createElement(View, { style: s.testItem }, /* @__PURE__ */ React2.createElement(Circle, { size: 18, delay }), /* @__PURE__ */ React2.createElement(View, { style: { flex: 1, marginLeft: 10 } }, /* @__PURE__ */ React2.createElement(Bar, { width: "70%", height: 11, delay }), /* @__PURE__ */ React2.createElement(View, { style: { height: 4 } }), /* @__PURE__ */ React2.createElement(Bar, { width: "45%", height: 8, delay })), /* @__PURE__ */ React2.createElement(Bar, { width: 50, height: 18, radius: 6, delay }));
14062
+ }
14063
+ function TestListScreenSkeleton() {
14064
+ return /* @__PURE__ */ React2.createElement(View, null, /* @__PURE__ */ React2.createElement(View, { style: s.filterRow }, [55, 50, 50, 70].map((w, i) => /* @__PURE__ */ React2.createElement(Bar, { key: i, width: w, height: 28, radius: 8, delay: i * 50 }))), /* @__PURE__ */ React2.createElement(Bar, { width: "100%", height: 36, radius: 8 }), /* @__PURE__ */ React2.createElement(View, { style: { height: 10 } }), [0, 1].map((g) => /* @__PURE__ */ React2.createElement(View, { key: g, style: { marginBottom: 12 } }, /* @__PURE__ */ React2.createElement(View, { style: s.folderHeader }, /* @__PURE__ */ React2.createElement(Bar, { width: 12, height: 10 }), /* @__PURE__ */ React2.createElement(Bar, { width: "40%", height: 12 }), /* @__PURE__ */ React2.createElement(View, { style: { flex: 1 } }), /* @__PURE__ */ React2.createElement(Bar, { width: 40, height: 4, radius: 2 }), /* @__PURE__ */ React2.createElement(Bar, { width: 24, height: 10 })), [0, 1, 2].map((i) => /* @__PURE__ */ React2.createElement(TestItemSkeleton, { key: i, delay: (g * 3 + i) * 80 })))));
14065
+ }
14066
+ function IssueListScreenSkeleton() {
14067
+ return /* @__PURE__ */ React2.createElement(View, null, [0, 1, 2, 3].map((i) => /* @__PURE__ */ React2.createElement(View, { key: i, style: s.issueRow }, /* @__PURE__ */ React2.createElement(View, { style: s.issueRowTop }, /* @__PURE__ */ React2.createElement(Circle, { size: 8, delay: i * 100 }), /* @__PURE__ */ React2.createElement(Bar, { width: "65%", height: 11, delay: i * 100 })), /* @__PURE__ */ React2.createElement(View, { style: s.issueRowBottom }, /* @__PURE__ */ React2.createElement(Bar, { width: "40%", height: 8, delay: i * 100 }), /* @__PURE__ */ React2.createElement(Bar, { width: 40, height: 8, delay: i * 100 })))));
14068
+ }
14069
+ function MessageListScreenSkeleton() {
14070
+ return /* @__PURE__ */ React2.createElement(View, null, /* @__PURE__ */ React2.createElement(Bar, { width: "100%", height: 44, radius: 12 }), /* @__PURE__ */ React2.createElement(View, { style: { height: 16 } }), [0, 1, 2, 3].map((i) => /* @__PURE__ */ React2.createElement(View, { key: i, style: s.threadRow }, /* @__PURE__ */ React2.createElement(Circle, { size: 20, delay: i * 100 }), /* @__PURE__ */ React2.createElement(View, { style: { flex: 1, marginLeft: 10 } }, /* @__PURE__ */ React2.createElement(Bar, { width: "55%", height: 11, delay: i * 100 }), /* @__PURE__ */ React2.createElement(View, { style: { height: 5 } }), /* @__PURE__ */ React2.createElement(Bar, { width: "80%", height: 9, delay: i * 100 })), /* @__PURE__ */ React2.createElement(Bar, { width: 30, height: 8, delay: i * 100 }))));
14071
+ }
14072
+ var s = StyleSheet2.create({
14073
+ actionGrid: { flexDirection: "row", flexWrap: "wrap", gap: 12, marginBottom: 20 },
14074
+ actionCard: { width: "47%", backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border, borderRadius: 12, padding: 16, alignItems: "center" },
14075
+ issueGrid: { flexDirection: "row", gap: 10, marginBottom: 20 },
14076
+ issueCard: { flex: 1, backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border, borderRadius: 10, paddingVertical: 12, paddingHorizontal: 8, alignItems: "center" },
14077
+ filterRow: { flexDirection: "row", gap: 8, marginBottom: 8 },
14078
+ folderHeader: { flexDirection: "row", alignItems: "center", gap: 8, paddingVertical: 8, paddingHorizontal: 4 },
14079
+ testItem: { flexDirection: "row", alignItems: "center", paddingVertical: 10, paddingHorizontal: 12, borderRadius: 8, marginBottom: 4, backgroundColor: colors.card },
14080
+ issueRow: { backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border, borderRadius: 10, padding: 14, marginBottom: 8 },
14081
+ issueRowTop: { flexDirection: "row", alignItems: "center", gap: 8 },
14082
+ issueRowBottom: { flexDirection: "row", justifyContent: "space-between", marginTop: 8 },
14083
+ threadRow: { flexDirection: "row", alignItems: "flex-start", padding: 12, borderRadius: 10, marginBottom: 4, backgroundColor: colors.card }
14084
+ });
14085
+
14086
+ // src/widget/screens/HomeScreen.tsx
14087
+ function HomeScreen({ nav }) {
14088
+ const { assignments, unreadCount, threads, refreshAssignments, refreshThreads, issueCounts, refreshIssueCounts, dashboardUrl, isLoading } = useBugBear();
14089
+ useEffect3(() => {
13626
14090
  refreshAssignments();
13627
14091
  refreshThreads();
13628
14092
  refreshIssueCounts();
@@ -13632,103 +14096,104 @@ function HomeScreen({ nav }) {
13632
14096
  const retestCount = pendingAssignments.filter((a) => a.isVerification).length;
13633
14097
  const completedCount = assignments.filter((a) => a.status === "passed" || a.status === "failed").length;
13634
14098
  const totalTests = assignments.length;
13635
- return /* @__PURE__ */ React2.createElement(View, null, pendingCount > 0 ? /* @__PURE__ */ React2.createElement(
14099
+ if (isLoading) return /* @__PURE__ */ React3.createElement(HomeScreenSkeleton, null);
14100
+ return /* @__PURE__ */ React3.createElement(View2, null, pendingCount > 0 ? /* @__PURE__ */ React3.createElement(
13636
14101
  TouchableOpacity,
13637
14102
  {
13638
14103
  style: [styles.heroBanner, styles.heroBannerTests],
13639
14104
  onPress: () => nav.push({ name: "TEST_DETAIL" }),
13640
14105
  activeOpacity: 0.8
13641
14106
  },
13642
- /* @__PURE__ */ React2.createElement(Text, { style: styles.heroCount }, pendingCount),
13643
- /* @__PURE__ */ React2.createElement(Text, { style: styles.heroLabel }, "test", pendingCount !== 1 ? "s" : "", " waiting"),
13644
- retestCount > 0 && /* @__PURE__ */ React2.createElement(View, { style: styles.retestPill }, /* @__PURE__ */ React2.createElement(Text, { style: styles.retestPillText }, "\u{1F504} ", retestCount, " retest", retestCount !== 1 ? "s" : "")),
13645
- /* @__PURE__ */ React2.createElement(Text, { style: styles.heroAction }, "Start Testing \u2192")
13646
- ) : unreadCount > 0 ? /* @__PURE__ */ React2.createElement(
14107
+ /* @__PURE__ */ React3.createElement(Text, { style: styles.heroCount }, pendingCount),
14108
+ /* @__PURE__ */ React3.createElement(Text, { style: styles.heroLabel }, "test", pendingCount !== 1 ? "s" : "", " waiting"),
14109
+ retestCount > 0 && /* @__PURE__ */ React3.createElement(View2, { style: styles.retestPill }, /* @__PURE__ */ React3.createElement(Text, { style: styles.retestPillText }, "\u{1F504} ", retestCount, " retest", retestCount !== 1 ? "s" : "")),
14110
+ /* @__PURE__ */ React3.createElement(Text, { style: styles.heroAction }, "Start Testing \u2192")
14111
+ ) : unreadCount > 0 ? /* @__PURE__ */ React3.createElement(
13647
14112
  TouchableOpacity,
13648
14113
  {
13649
14114
  style: [styles.heroBanner, styles.heroBannerMessages],
13650
14115
  onPress: () => nav.push({ name: "MESSAGE_LIST" }),
13651
14116
  activeOpacity: 0.8
13652
14117
  },
13653
- /* @__PURE__ */ React2.createElement(Text, { style: styles.heroCount }, unreadCount),
13654
- /* @__PURE__ */ React2.createElement(Text, { style: styles.heroLabel }, "unread message", unreadCount !== 1 ? "s" : ""),
13655
- /* @__PURE__ */ React2.createElement(Text, { style: styles.heroAction }, "View Messages \u2192")
13656
- ) : /* @__PURE__ */ React2.createElement(View, { style: [styles.heroBanner, styles.heroBannerClear] }, /* @__PURE__ */ React2.createElement(Text, { style: styles.heroClearEmoji }, "\u2705"), /* @__PURE__ */ React2.createElement(Text, { style: styles.heroClearTitle }, "All caught up!"), totalTests > 0 && /* @__PURE__ */ React2.createElement(Text, { style: styles.heroClearSub }, completedCount, "/", totalTests, " tests completed")), /* @__PURE__ */ React2.createElement(View, { style: styles.actionGrid }, /* @__PURE__ */ React2.createElement(
14118
+ /* @__PURE__ */ React3.createElement(Text, { style: styles.heroCount }, unreadCount),
14119
+ /* @__PURE__ */ React3.createElement(Text, { style: styles.heroLabel }, "unread message", unreadCount !== 1 ? "s" : ""),
14120
+ /* @__PURE__ */ React3.createElement(Text, { style: styles.heroAction }, "View Messages \u2192")
14121
+ ) : /* @__PURE__ */ React3.createElement(View2, { style: [styles.heroBanner, styles.heroBannerClear] }, /* @__PURE__ */ React3.createElement(Text, { style: styles.heroClearEmoji }, "\u2705"), /* @__PURE__ */ React3.createElement(Text, { style: styles.heroClearTitle }, "All caught up!"), totalTests > 0 && /* @__PURE__ */ React3.createElement(Text, { style: styles.heroClearSub }, completedCount, "/", totalTests, " tests completed")), /* @__PURE__ */ React3.createElement(View2, { style: styles.actionGrid }, /* @__PURE__ */ React3.createElement(
13657
14122
  TouchableOpacity,
13658
14123
  {
13659
14124
  style: styles.actionCard,
13660
14125
  onPress: () => nav.push({ name: "TEST_LIST" }),
13661
14126
  activeOpacity: 0.7
13662
14127
  },
13663
- /* @__PURE__ */ React2.createElement(Text, { style: styles.actionIcon }, "\u2705"),
13664
- /* @__PURE__ */ React2.createElement(Text, { style: styles.actionLabel }, "Tests"),
13665
- pendingCount > 0 && /* @__PURE__ */ React2.createElement(View, { style: styles.actionBadge }, /* @__PURE__ */ React2.createElement(Text, { style: styles.actionBadgeText }, pendingCount))
13666
- ), /* @__PURE__ */ React2.createElement(
14128
+ /* @__PURE__ */ React3.createElement(Text, { style: styles.actionIcon }, "\u2705"),
14129
+ /* @__PURE__ */ React3.createElement(Text, { style: styles.actionLabel }, "Tests"),
14130
+ pendingCount > 0 && /* @__PURE__ */ React3.createElement(View2, { style: styles.actionBadge }, /* @__PURE__ */ React3.createElement(Text, { style: styles.actionBadgeText }, pendingCount))
14131
+ ), /* @__PURE__ */ React3.createElement(
13667
14132
  TouchableOpacity,
13668
14133
  {
13669
14134
  style: styles.actionCard,
13670
14135
  onPress: () => nav.push({ name: "REPORT", prefill: { type: "bug" } }),
13671
14136
  activeOpacity: 0.7
13672
14137
  },
13673
- /* @__PURE__ */ React2.createElement(Text, { style: styles.actionIcon }, "\u{1F41B}"),
13674
- /* @__PURE__ */ React2.createElement(Text, { style: styles.actionLabel }, "Report Bug")
13675
- ), /* @__PURE__ */ React2.createElement(
14138
+ /* @__PURE__ */ React3.createElement(Text, { style: styles.actionIcon }, "\u{1F41B}"),
14139
+ /* @__PURE__ */ React3.createElement(Text, { style: styles.actionLabel }, "Report Bug")
14140
+ ), /* @__PURE__ */ React3.createElement(
13676
14141
  TouchableOpacity,
13677
14142
  {
13678
14143
  style: styles.actionCard,
13679
14144
  onPress: () => nav.push({ name: "REPORT", prefill: { type: "feedback" } }),
13680
14145
  activeOpacity: 0.7
13681
14146
  },
13682
- /* @__PURE__ */ React2.createElement(Text, { style: styles.actionIcon }, "\u{1F4A1}"),
13683
- /* @__PURE__ */ React2.createElement(Text, { style: styles.actionLabel }, "Feedback")
13684
- ), /* @__PURE__ */ React2.createElement(
14147
+ /* @__PURE__ */ React3.createElement(Text, { style: styles.actionIcon }, "\u{1F4A1}"),
14148
+ /* @__PURE__ */ React3.createElement(Text, { style: styles.actionLabel }, "Feedback")
14149
+ ), /* @__PURE__ */ React3.createElement(
13685
14150
  TouchableOpacity,
13686
14151
  {
13687
14152
  style: styles.actionCard,
13688
14153
  onPress: () => nav.push({ name: "MESSAGE_LIST" }),
13689
14154
  activeOpacity: 0.7
13690
14155
  },
13691
- /* @__PURE__ */ React2.createElement(Text, { style: styles.actionIcon }, "\u{1F4AC}"),
13692
- /* @__PURE__ */ React2.createElement(Text, { style: styles.actionLabel }, "Messages"),
13693
- unreadCount > 0 && /* @__PURE__ */ React2.createElement(View, { style: [styles.actionBadge, styles.actionBadgeMsg] }, /* @__PURE__ */ React2.createElement(Text, { style: styles.actionBadgeText }, unreadCount))
13694
- )), /* @__PURE__ */ React2.createElement(View, { style: styles.issueGrid }, /* @__PURE__ */ React2.createElement(
14156
+ /* @__PURE__ */ React3.createElement(Text, { style: styles.actionIcon }, "\u{1F4AC}"),
14157
+ /* @__PURE__ */ React3.createElement(Text, { style: styles.actionLabel }, "Messages"),
14158
+ unreadCount > 0 && /* @__PURE__ */ React3.createElement(View2, { style: [styles.actionBadge, styles.actionBadgeMsg] }, /* @__PURE__ */ React3.createElement(Text, { style: styles.actionBadgeText }, unreadCount))
14159
+ )), /* @__PURE__ */ React3.createElement(View2, { style: styles.issueGrid }, /* @__PURE__ */ React3.createElement(
13695
14160
  TouchableOpacity,
13696
14161
  {
13697
14162
  style: [styles.issueCard, styles.issueCardOpen],
13698
14163
  onPress: () => nav.push({ name: "ISSUE_LIST", category: "open" }),
13699
14164
  activeOpacity: 0.7
13700
14165
  },
13701
- /* @__PURE__ */ React2.createElement(Text, { style: styles.issueCountOpen }, issueCounts.open),
13702
- /* @__PURE__ */ React2.createElement(Text, { style: styles.issueLabel }, "Open")
13703
- ), /* @__PURE__ */ React2.createElement(
14166
+ /* @__PURE__ */ React3.createElement(Text, { style: styles.issueCountOpen }, issueCounts.open),
14167
+ /* @__PURE__ */ React3.createElement(Text, { style: styles.issueLabel }, "Open")
14168
+ ), /* @__PURE__ */ React3.createElement(
13704
14169
  TouchableOpacity,
13705
14170
  {
13706
14171
  style: [styles.issueCard, styles.issueCardDone],
13707
14172
  onPress: () => nav.push({ name: "ISSUE_LIST", category: "done" }),
13708
14173
  activeOpacity: 0.7
13709
14174
  },
13710
- /* @__PURE__ */ React2.createElement(Text, { style: styles.issueCountDone }, issueCounts.done),
13711
- /* @__PURE__ */ React2.createElement(Text, { style: styles.issueLabel }, "Done")
13712
- ), /* @__PURE__ */ React2.createElement(
14175
+ /* @__PURE__ */ React3.createElement(Text, { style: styles.issueCountDone }, issueCounts.done),
14176
+ /* @__PURE__ */ React3.createElement(Text, { style: styles.issueLabel }, "Done")
14177
+ ), /* @__PURE__ */ React3.createElement(
13713
14178
  TouchableOpacity,
13714
14179
  {
13715
14180
  style: [styles.issueCard, styles.issueCardReopened],
13716
14181
  onPress: () => nav.push({ name: "ISSUE_LIST", category: "reopened" }),
13717
14182
  activeOpacity: 0.7
13718
14183
  },
13719
- /* @__PURE__ */ React2.createElement(Text, { style: styles.issueCountReopened }, issueCounts.reopened),
13720
- /* @__PURE__ */ React2.createElement(Text, { style: styles.issueLabel }, "Reopened")
13721
- )), totalTests > 0 && /* @__PURE__ */ React2.createElement(View, { style: styles.progressSection }, /* @__PURE__ */ React2.createElement(View, { style: styles.progressBar }, /* @__PURE__ */ React2.createElement(View, { style: [styles.progressFill, { width: `${Math.round(completedCount / totalTests * 100)}%` }] })), /* @__PURE__ */ React2.createElement(Text, { style: styles.progressText }, completedCount, "/", totalTests, " tests completed")), dashboardUrl && /* @__PURE__ */ React2.createElement(
14184
+ /* @__PURE__ */ React3.createElement(Text, { style: styles.issueCountReopened }, issueCounts.reopened),
14185
+ /* @__PURE__ */ React3.createElement(Text, { style: styles.issueLabel }, "Reopened")
14186
+ )), totalTests > 0 && /* @__PURE__ */ React3.createElement(View2, { style: styles.progressSection }, /* @__PURE__ */ React3.createElement(View2, { style: styles.progressBar }, /* @__PURE__ */ React3.createElement(View2, { style: [styles.progressFill, { width: `${Math.round(completedCount / totalTests * 100)}%` }] })), /* @__PURE__ */ React3.createElement(Text, { style: styles.progressText }, completedCount, "/", totalTests, " tests completed")), dashboardUrl && /* @__PURE__ */ React3.createElement(
13722
14187
  TouchableOpacity,
13723
14188
  {
13724
14189
  style: styles.webAppLink,
13725
14190
  onPress: () => Linking.openURL(dashboardUrl),
13726
14191
  activeOpacity: 0.7
13727
14192
  },
13728
- /* @__PURE__ */ React2.createElement(Text, { style: styles.webAppIcon }, "\u{1F310}"),
13729
- /* @__PURE__ */ React2.createElement(View, { style: styles.webAppTextWrap }, /* @__PURE__ */ React2.createElement(Text, { style: styles.webAppTitle }, "Open Web Dashboard"), /* @__PURE__ */ React2.createElement(Text, { style: styles.webAppSub }, "View analytics, history & more")),
13730
- /* @__PURE__ */ React2.createElement(Text, { style: styles.webAppArrow }, "\u2192")
13731
- ), /* @__PURE__ */ React2.createElement(
14193
+ /* @__PURE__ */ React3.createElement(Text, { style: styles.webAppIcon }, "\u{1F310}"),
14194
+ /* @__PURE__ */ React3.createElement(View2, { style: styles.webAppTextWrap }, /* @__PURE__ */ React3.createElement(Text, { style: styles.webAppTitle }, "Open Web Dashboard"), /* @__PURE__ */ React3.createElement(Text, { style: styles.webAppSub }, "View analytics, history & more")),
14195
+ /* @__PURE__ */ React3.createElement(Text, { style: styles.webAppArrow }, "\u2192")
14196
+ ), /* @__PURE__ */ React3.createElement(
13732
14197
  TouchableOpacity,
13733
14198
  {
13734
14199
  style: styles.refreshButton,
@@ -13738,10 +14203,10 @@ function HomeScreen({ nav }) {
13738
14203
  refreshIssueCounts();
13739
14204
  }
13740
14205
  },
13741
- /* @__PURE__ */ React2.createElement(Text, { style: styles.refreshText }, "\u21BB Refresh")
14206
+ /* @__PURE__ */ React3.createElement(Text, { style: styles.refreshText }, "\u21BB Refresh")
13742
14207
  ));
13743
14208
  }
13744
- var styles = StyleSheet2.create({
14209
+ var styles = StyleSheet3.create({
13745
14210
  heroBanner: {
13746
14211
  borderRadius: 16,
13747
14212
  padding: 24,
@@ -13965,8 +14430,8 @@ var styles = StyleSheet2.create({
13965
14430
  });
13966
14431
 
13967
14432
  // src/widget/screens/TestDetailScreen.tsx
13968
- import React3, { useState as useState2, useEffect as useEffect3, useCallback as useCallback2 } from "react";
13969
- import { View as View2, Text as Text2, TouchableOpacity as TouchableOpacity2, StyleSheet as StyleSheet3, Modal, TextInput, Keyboard } from "react-native";
14433
+ import React4, { useState as useState2, useEffect as useEffect4, useCallback as useCallback2 } from "react";
14434
+ import { View as View3, Text as Text2, TouchableOpacity as TouchableOpacity2, StyleSheet as StyleSheet4, Modal, TextInput, Keyboard } from "react-native";
13970
14435
  function TestDetailScreen({ testId, nav }) {
13971
14436
  const { client, assignments, currentAssignment, refreshAssignments, getDeviceInfo, onNavigate } = useBugBear();
13972
14437
  const displayedAssignment = testId ? assignments.find((a) => a.id === testId) || currentAssignment : currentAssignment;
@@ -13979,12 +14444,12 @@ function TestDetailScreen({ testId, nav }) {
13979
14444
  const [skipNotes, setSkipNotes] = useState2("");
13980
14445
  const [skipping, setSkipping] = useState2(false);
13981
14446
  const [isSubmitting, setIsSubmitting] = useState2(false);
13982
- useEffect3(() => {
14447
+ useEffect4(() => {
13983
14448
  setCriteriaResults({});
13984
14449
  setShowSteps(true);
13985
14450
  setShowDetails(false);
13986
14451
  }, [displayedAssignment?.id]);
13987
- useEffect3(() => {
14452
+ useEffect4(() => {
13988
14453
  const active = displayedAssignment?.status === "in_progress" ? displayedAssignment : null;
13989
14454
  if (!active?.startedAt) {
13990
14455
  setAssignmentElapsedTime(0);
@@ -14034,6 +14499,39 @@ function TestDetailScreen({ testId, nav }) {
14034
14499
  setIsSubmitting(false);
14035
14500
  }
14036
14501
  }, [client, displayedAssignment, refreshAssignments, nav, isSubmitting]);
14502
+ const handleReopen = useCallback2(async () => {
14503
+ if (!client || !displayedAssignment || isSubmitting) return;
14504
+ Keyboard.dismiss();
14505
+ setIsSubmitting(true);
14506
+ try {
14507
+ await client.reopenAssignment(displayedAssignment.id);
14508
+ await refreshAssignments();
14509
+ } finally {
14510
+ setIsSubmitting(false);
14511
+ }
14512
+ }, [client, displayedAssignment, refreshAssignments, isSubmitting]);
14513
+ const handleChangeResult = useCallback2(async (newStatus) => {
14514
+ if (!client || !displayedAssignment || isSubmitting) return;
14515
+ Keyboard.dismiss();
14516
+ setIsSubmitting(true);
14517
+ try {
14518
+ await client.reopenAssignment(displayedAssignment.id);
14519
+ await client.updateAssignmentStatus(displayedAssignment.id, newStatus);
14520
+ await refreshAssignments();
14521
+ if (newStatus === "failed") {
14522
+ nav.replace({
14523
+ name: "REPORT",
14524
+ prefill: {
14525
+ type: "test_fail",
14526
+ assignmentId: displayedAssignment.id,
14527
+ testCaseId: displayedAssignment.testCase.id
14528
+ }
14529
+ });
14530
+ }
14531
+ } finally {
14532
+ setIsSubmitting(false);
14533
+ }
14534
+ }, [client, displayedAssignment, refreshAssignments, nav, isSubmitting]);
14037
14535
  const handleSkip = useCallback2(async () => {
14038
14536
  if (!client || !displayedAssignment || !selectedSkipReason) return;
14039
14537
  Keyboard.dismiss();
@@ -14060,14 +14558,14 @@ function TestDetailScreen({ testId, nav }) {
14060
14558
  }
14061
14559
  }, [client, displayedAssignment, selectedSkipReason, skipNotes, refreshAssignments, assignments, nav]);
14062
14560
  if (!displayedAssignment) {
14063
- return /* @__PURE__ */ React3.createElement(View2, { style: shared.emptyState }, /* @__PURE__ */ React3.createElement(Text2, { style: shared.emptyEmoji }, "\u2705"), /* @__PURE__ */ React3.createElement(Text2, { style: shared.emptyTitle }, "No tests assigned"), /* @__PURE__ */ React3.createElement(Text2, { style: shared.emptySubtitle }, "Check back later for new tests"), /* @__PURE__ */ React3.createElement(TouchableOpacity2, { style: [shared.primaryButton, { marginTop: 20, paddingHorizontal: 24 }], onPress: () => nav.reset() }, /* @__PURE__ */ React3.createElement(Text2, { style: shared.primaryButtonText }, "Go Home")));
14561
+ return /* @__PURE__ */ React4.createElement(View3, { style: shared.emptyState }, /* @__PURE__ */ React4.createElement(Text2, { style: shared.emptyEmoji }, "\u2705"), /* @__PURE__ */ React4.createElement(Text2, { style: shared.emptyTitle }, "No tests assigned"), /* @__PURE__ */ React4.createElement(Text2, { style: shared.emptySubtitle }, "Check back later for new tests"), /* @__PURE__ */ React4.createElement(TouchableOpacity2, { style: [shared.primaryButton, { marginTop: 20, paddingHorizontal: 24 }], onPress: () => nav.reset() }, /* @__PURE__ */ React4.createElement(Text2, { style: shared.primaryButtonText }, "Go Home")));
14064
14562
  }
14065
14563
  const testCase = displayedAssignment.testCase;
14066
14564
  const template = testCase.track?.testTemplate || "steps";
14067
14565
  const steps = testCase.steps;
14068
14566
  const info = templateInfo[template] || templateInfo.steps;
14069
14567
  const rubricMode = testCase.track?.rubricMode || "pass_fail";
14070
- return /* @__PURE__ */ React3.createElement(View2, { style: styles2.container }, /* @__PURE__ */ React3.createElement(View2, { style: styles2.topRow }, /* @__PURE__ */ React3.createElement(View2, { style: styles2.positionInfo }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.positionText }, "Test ", currentIndex + 1, " of ", allTests.length), displayedAssignment.status === "in_progress" && assignmentElapsedTime > 0 && /* @__PURE__ */ React3.createElement(View2, { style: styles2.timerBadge }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.timerText }, formatElapsedTime(assignmentElapsedTime)))), /* @__PURE__ */ React3.createElement(TouchableOpacity2, { onPress: () => nav.push({ name: "TEST_LIST" }) }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.viewAllLink }, "View All \u2192"))), displayedAssignment.isVerification && /* @__PURE__ */ React3.createElement(View2, { style: styles2.retestBanner }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.retestIcon }, "\u{1F504}"), /* @__PURE__ */ React3.createElement(Text2, { style: styles2.retestLabel }, "Retest"), /* @__PURE__ */ React3.createElement(Text2, { style: styles2.retestSub }, "\u2014 Verify bug fix")), /* @__PURE__ */ React3.createElement(Text2, { style: styles2.testTitle }, testCase.title), testCase.key && /* @__PURE__ */ React3.createElement(Text2, { style: styles2.testKey }, testCase.key), /* @__PURE__ */ React3.createElement(TouchableOpacity2, { onPress: () => setShowSteps(!showSteps), style: styles2.sectionHeader }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.sectionHeaderText }, showSteps ? "\u25BC" : "\u25B6", " ", info.icon, " ", template === "freeform" ? "Instructions" : `${steps.length} ${template === "checklist" ? "items" : template === "rubric" ? "criteria" : "steps"}`)), showSteps && /* @__PURE__ */ React3.createElement(View2, { style: styles2.templateContent }, template === "steps" && steps.map((step, idx) => /* @__PURE__ */ React3.createElement(View2, { key: idx, style: styles2.step }, /* @__PURE__ */ React3.createElement(View2, { style: styles2.stepNumber }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.stepNumberText }, step.stepNumber)), /* @__PURE__ */ React3.createElement(View2, { style: styles2.stepBody }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.stepAction }, step.action), step.expectedResult && /* @__PURE__ */ React3.createElement(Text2, { style: styles2.stepExpected }, "\u2192 ", step.expectedResult)))), template === "checklist" && /* @__PURE__ */ React3.createElement(React3.Fragment, null, steps.map((step, idx) => /* @__PURE__ */ React3.createElement(
14568
+ return /* @__PURE__ */ React4.createElement(View3, { style: styles2.container }, /* @__PURE__ */ React4.createElement(View3, { style: styles2.topRow }, /* @__PURE__ */ React4.createElement(View3, { style: styles2.positionInfo }, /* @__PURE__ */ React4.createElement(Text2, { style: styles2.positionText }, "Test ", currentIndex + 1, " of ", allTests.length), displayedAssignment.status === "in_progress" && assignmentElapsedTime > 0 && /* @__PURE__ */ React4.createElement(View3, { style: styles2.timerBadge }, /* @__PURE__ */ React4.createElement(Text2, { style: styles2.timerText }, formatElapsedTime(assignmentElapsedTime)))), /* @__PURE__ */ React4.createElement(TouchableOpacity2, { onPress: () => nav.push({ name: "TEST_LIST" }) }, /* @__PURE__ */ React4.createElement(Text2, { style: styles2.viewAllLink }, "View All \u2192"))), displayedAssignment.isVerification && /* @__PURE__ */ React4.createElement(View3, { style: styles2.retestBanner }, /* @__PURE__ */ React4.createElement(Text2, { style: styles2.retestIcon }, "\u{1F504}"), /* @__PURE__ */ React4.createElement(Text2, { style: styles2.retestLabel }, "Retest"), /* @__PURE__ */ React4.createElement(Text2, { style: styles2.retestSub }, "\u2014 Verify bug fix")), /* @__PURE__ */ React4.createElement(Text2, { style: styles2.testTitle }, testCase.title), testCase.key && /* @__PURE__ */ React4.createElement(Text2, { style: styles2.testKey }, testCase.key), /* @__PURE__ */ React4.createElement(TouchableOpacity2, { onPress: () => setShowSteps(!showSteps), style: styles2.sectionHeader }, /* @__PURE__ */ React4.createElement(Text2, { style: styles2.sectionHeaderText }, showSteps ? "\u25BC" : "\u25B6", " ", info.icon, " ", template === "freeform" ? "Instructions" : `${steps.length} ${template === "checklist" ? "items" : template === "rubric" ? "criteria" : "steps"}`)), showSteps && /* @__PURE__ */ React4.createElement(View3, { style: styles2.templateContent }, template === "steps" && steps.map((step, idx) => /* @__PURE__ */ React4.createElement(View3, { key: idx, style: styles2.step }, /* @__PURE__ */ React4.createElement(View3, { style: styles2.stepNumber }, /* @__PURE__ */ React4.createElement(Text2, { style: styles2.stepNumberText }, step.stepNumber)), /* @__PURE__ */ React4.createElement(View3, { style: styles2.stepBody }, /* @__PURE__ */ React4.createElement(Text2, { style: styles2.stepAction }, step.action), step.expectedResult && /* @__PURE__ */ React4.createElement(Text2, { style: styles2.stepExpected }, "\u2192 ", step.expectedResult)))), template === "checklist" && /* @__PURE__ */ React4.createElement(React4.Fragment, null, steps.map((step, idx) => /* @__PURE__ */ React4.createElement(
14071
14569
  TouchableOpacity2,
14072
14570
  {
14073
14571
  key: idx,
@@ -14079,31 +14577,31 @@ function TestDetailScreen({ testId, nav }) {
14079
14577
  }),
14080
14578
  style: [styles2.checklistItem, criteriaResults[idx] === true && styles2.checklistItemChecked]
14081
14579
  },
14082
- /* @__PURE__ */ React3.createElement(View2, { style: [styles2.checkbox, criteriaResults[idx] === true && styles2.checkboxChecked] }, criteriaResults[idx] === true && /* @__PURE__ */ React3.createElement(Text2, { style: styles2.checkmark }, "\u2713")),
14083
- /* @__PURE__ */ React3.createElement(Text2, { style: [styles2.checklistText, criteriaResults[idx] === true && styles2.checklistTextDone] }, step.action)
14084
- )), Object.keys(criteriaResults).length > 0 && /* @__PURE__ */ React3.createElement(TouchableOpacity2, { onPress: () => setCriteriaResults({}), style: styles2.resetRow }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.resetText }, "\u21BA Reset"))), template === "rubric" && steps.map((step, idx) => /* @__PURE__ */ React3.createElement(View2, { key: idx, style: styles2.rubricItem }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.rubricTitle }, idx + 1, ". ", step.action), step.expectedResult && /* @__PURE__ */ React3.createElement(Text2, { style: styles2.rubricExpected }, step.expectedResult), rubricMode === "pass_fail" ? /* @__PURE__ */ React3.createElement(View2, { style: styles2.passFailRow }, /* @__PURE__ */ React3.createElement(
14580
+ /* @__PURE__ */ React4.createElement(View3, { style: [styles2.checkbox, criteriaResults[idx] === true && styles2.checkboxChecked] }, criteriaResults[idx] === true && /* @__PURE__ */ React4.createElement(Text2, { style: styles2.checkmark }, "\u2713")),
14581
+ /* @__PURE__ */ React4.createElement(Text2, { style: [styles2.checklistText, criteriaResults[idx] === true && styles2.checklistTextDone] }, step.action)
14582
+ )), Object.keys(criteriaResults).length > 0 && /* @__PURE__ */ React4.createElement(TouchableOpacity2, { onPress: () => setCriteriaResults({}), style: styles2.resetRow }, /* @__PURE__ */ React4.createElement(Text2, { style: styles2.resetText }, "\u21BA Reset"))), template === "rubric" && steps.map((step, idx) => /* @__PURE__ */ React4.createElement(View3, { key: idx, style: styles2.rubricItem }, /* @__PURE__ */ React4.createElement(Text2, { style: styles2.rubricTitle }, idx + 1, ". ", step.action), step.expectedResult && /* @__PURE__ */ React4.createElement(Text2, { style: styles2.rubricExpected }, step.expectedResult), rubricMode === "pass_fail" ? /* @__PURE__ */ React4.createElement(View3, { style: styles2.passFailRow }, /* @__PURE__ */ React4.createElement(
14085
14583
  TouchableOpacity2,
14086
14584
  {
14087
14585
  onPress: () => setCriteriaResults((prev) => ({ ...prev, [idx]: true })),
14088
14586
  style: [styles2.pfButton, criteriaResults[idx] === true && styles2.pfButtonPass]
14089
14587
  },
14090
- /* @__PURE__ */ React3.createElement(Text2, { style: [styles2.pfButtonText, criteriaResults[idx] === true && styles2.pfButtonTextActive] }, "\u2713 Pass")
14091
- ), /* @__PURE__ */ React3.createElement(
14588
+ /* @__PURE__ */ React4.createElement(Text2, { style: [styles2.pfButtonText, criteriaResults[idx] === true && styles2.pfButtonTextActive] }, "\u2713 Pass")
14589
+ ), /* @__PURE__ */ React4.createElement(
14092
14590
  TouchableOpacity2,
14093
14591
  {
14094
14592
  onPress: () => setCriteriaResults((prev) => ({ ...prev, [idx]: false })),
14095
14593
  style: [styles2.pfButton, criteriaResults[idx] === false && styles2.pfButtonFail]
14096
14594
  },
14097
- /* @__PURE__ */ React3.createElement(Text2, { style: [styles2.pfButtonText, criteriaResults[idx] === false && styles2.pfButtonTextActive] }, "\u2717 Fail")
14098
- )) : /* @__PURE__ */ React3.createElement(View2, { style: styles2.ratingRow }, [1, 2, 3, 4, 5].map((n) => /* @__PURE__ */ React3.createElement(
14595
+ /* @__PURE__ */ React4.createElement(Text2, { style: [styles2.pfButtonText, criteriaResults[idx] === false && styles2.pfButtonTextActive] }, "\u2717 Fail")
14596
+ )) : /* @__PURE__ */ React4.createElement(View3, { style: styles2.ratingRow }, [1, 2, 3, 4, 5].map((n) => /* @__PURE__ */ React4.createElement(
14099
14597
  TouchableOpacity2,
14100
14598
  {
14101
14599
  key: n,
14102
14600
  onPress: () => setCriteriaResults((prev) => ({ ...prev, [idx]: n })),
14103
14601
  style: [styles2.ratingBtn, criteriaResults[idx] === n && styles2.ratingBtnActive]
14104
14602
  },
14105
- /* @__PURE__ */ React3.createElement(Text2, { style: [styles2.ratingBtnText, criteriaResults[idx] === n && styles2.ratingBtnTextActive] }, n)
14106
- ))))), template === "freeform" && /* @__PURE__ */ React3.createElement(View2, { style: styles2.freeformBox }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.freeformText }, "Review the area and note:"), /* @__PURE__ */ React3.createElement(Text2, { style: styles2.freeformBullet }, "\u2022 What works well"), /* @__PURE__ */ React3.createElement(Text2, { style: styles2.freeformBullet }, "\u2022 Issues or concerns"), /* @__PURE__ */ React3.createElement(Text2, { style: styles2.freeformBullet }, "\u2022 Suggestions"))), testCase.expectedResult && /* @__PURE__ */ React3.createElement(View2, { style: styles2.expectedBox }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.expectedLabel }, "\u2705 Expected Result"), /* @__PURE__ */ React3.createElement(Text2, { style: styles2.expectedText }, testCase.expectedResult)), testCase.targetRoute && onNavigate && /* @__PURE__ */ React3.createElement(
14603
+ /* @__PURE__ */ React4.createElement(Text2, { style: [styles2.ratingBtnText, criteriaResults[idx] === n && styles2.ratingBtnTextActive] }, n)
14604
+ ))))), template === "freeform" && /* @__PURE__ */ React4.createElement(View3, { style: styles2.freeformBox }, /* @__PURE__ */ React4.createElement(Text2, { style: styles2.freeformText }, "Review the area and note:"), /* @__PURE__ */ React4.createElement(Text2, { style: styles2.freeformBullet }, "\u2022 What works well"), /* @__PURE__ */ React4.createElement(Text2, { style: styles2.freeformBullet }, "\u2022 Issues or concerns"), /* @__PURE__ */ React4.createElement(Text2, { style: styles2.freeformBullet }, "\u2022 Suggestions"))), testCase.expectedResult && /* @__PURE__ */ React4.createElement(View3, { style: styles2.expectedBox }, /* @__PURE__ */ React4.createElement(Text2, { style: styles2.expectedLabel }, "\u2705 Expected Result"), /* @__PURE__ */ React4.createElement(Text2, { style: styles2.expectedText }, testCase.expectedResult)), testCase.targetRoute && onNavigate && /* @__PURE__ */ React4.createElement(
14107
14605
  TouchableOpacity2,
14108
14606
  {
14109
14607
  style: styles2.navigateButton,
@@ -14113,21 +14611,53 @@ function TestDetailScreen({ testId, nav }) {
14113
14611
  nav.closeWidget?.();
14114
14612
  }
14115
14613
  },
14116
- /* @__PURE__ */ React3.createElement(Text2, { style: styles2.navigateText }, "\u{1F9ED} Go to test location")
14117
- ), /* @__PURE__ */ React3.createElement(TouchableOpacity2, { onPress: () => setShowDetails(!showDetails), style: styles2.detailsToggle }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.detailsToggleText }, showDetails ? "\u25BC" : "\u25B6", " Details")), showDetails && /* @__PURE__ */ React3.createElement(View2, { style: styles2.detailsSection }, testCase.key && /* @__PURE__ */ React3.createElement(Text2, { style: styles2.detailMeta }, testCase.key, " \xB7 ", testCase.priority, " \xB7 ", info.name), testCase.description && /* @__PURE__ */ React3.createElement(Text2, { style: styles2.detailDesc }, testCase.description), testCase.group && /* @__PURE__ */ React3.createElement(View2, { style: styles2.folderProgress }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.folderName }, "\u{1F4C1} ", testCase.group.name))), /* @__PURE__ */ React3.createElement(View2, { style: styles2.actionButtons }, /* @__PURE__ */ React3.createElement(TouchableOpacity2, { style: [styles2.actionBtn, styles2.failBtn, isSubmitting && { opacity: 0.5 }], onPress: handleFail, disabled: isSubmitting }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.failBtnText }, isSubmitting ? "Failing..." : "Fail")), /* @__PURE__ */ React3.createElement(TouchableOpacity2, { style: [styles2.actionBtn, styles2.skipBtn, isSubmitting && { opacity: 0.5 }], onPress: () => setShowSkipModal(true), disabled: isSubmitting }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.skipBtnText }, "Skip")), /* @__PURE__ */ React3.createElement(TouchableOpacity2, { style: [styles2.actionBtn, styles2.passBtn, isSubmitting && { opacity: 0.5 }], onPress: handlePass, disabled: isSubmitting }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.passBtnText }, isSubmitting ? "Passing..." : "Pass"))), /* @__PURE__ */ React3.createElement(Modal, { visible: showSkipModal, transparent: true, animationType: "fade" }, /* @__PURE__ */ React3.createElement(View2, { style: styles2.modalOverlay }, /* @__PURE__ */ React3.createElement(View2, { style: styles2.modalContent }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.modalTitle }, "Skip this test?"), /* @__PURE__ */ React3.createElement(Text2, { style: styles2.modalSubtitle }, "Select a reason:"), [
14614
+ /* @__PURE__ */ React4.createElement(Text2, { style: styles2.navigateText }, "\u{1F9ED} Go to test location")
14615
+ ), /* @__PURE__ */ React4.createElement(TouchableOpacity2, { onPress: () => setShowDetails(!showDetails), style: styles2.detailsToggle }, /* @__PURE__ */ React4.createElement(Text2, { style: styles2.detailsToggleText }, showDetails ? "\u25BC" : "\u25B6", " Details")), showDetails && /* @__PURE__ */ React4.createElement(View3, { style: styles2.detailsSection }, testCase.key && /* @__PURE__ */ React4.createElement(Text2, { style: styles2.detailMeta }, testCase.key, " \xB7 ", testCase.priority, " \xB7 ", info.name), testCase.description && /* @__PURE__ */ React4.createElement(Text2, { style: styles2.detailDesc }, testCase.description), testCase.group && /* @__PURE__ */ React4.createElement(View3, { style: styles2.folderProgress }, /* @__PURE__ */ React4.createElement(Text2, { style: styles2.folderName }, "\u{1F4C1} ", testCase.group.name))), displayedAssignment.status === "passed" || displayedAssignment.status === "failed" || displayedAssignment.status === "skipped" || displayedAssignment.status === "blocked" ? /* @__PURE__ */ React4.createElement(React4.Fragment, null, /* @__PURE__ */ React4.createElement(View3, { style: [
14616
+ styles2.completedBanner,
14617
+ displayedAssignment.status === "passed" && styles2.completedBannerPass,
14618
+ displayedAssignment.status === "failed" && styles2.completedBannerFail
14619
+ ] }, /* @__PURE__ */ React4.createElement(Text2, { style: styles2.completedIcon }, displayedAssignment.status === "passed" ? "\u2705" : displayedAssignment.status === "failed" ? "\u274C" : displayedAssignment.status === "skipped" ? "\u23ED" : "\u{1F6AB}"), /* @__PURE__ */ React4.createElement(Text2, { style: [
14620
+ styles2.completedLabel,
14621
+ displayedAssignment.status === "passed" && { color: colors.green },
14622
+ displayedAssignment.status === "failed" && { color: "#fca5a5" }
14623
+ ] }, "Marked as ", displayedAssignment.status.charAt(0).toUpperCase() + displayedAssignment.status.slice(1))), /* @__PURE__ */ React4.createElement(View3, { style: [styles2.actionButtons, { marginTop: 4 }] }, /* @__PURE__ */ React4.createElement(
14624
+ TouchableOpacity2,
14625
+ {
14626
+ style: [styles2.actionBtn, styles2.reopenBtn, isSubmitting && { opacity: 0.5 }],
14627
+ onPress: handleReopen,
14628
+ disabled: isSubmitting
14629
+ },
14630
+ /* @__PURE__ */ React4.createElement(Text2, { style: styles2.reopenBtnText }, isSubmitting ? "Reopening..." : "\u{1F504} Reopen Test")
14631
+ ), displayedAssignment.status === "passed" && /* @__PURE__ */ React4.createElement(
14632
+ TouchableOpacity2,
14633
+ {
14634
+ style: [styles2.actionBtn, styles2.failBtn, isSubmitting && { opacity: 0.5 }],
14635
+ onPress: () => handleChangeResult("failed"),
14636
+ disabled: isSubmitting
14637
+ },
14638
+ /* @__PURE__ */ React4.createElement(Text2, { style: styles2.failBtnText }, "Change to Fail")
14639
+ ), displayedAssignment.status === "failed" && /* @__PURE__ */ React4.createElement(
14640
+ TouchableOpacity2,
14641
+ {
14642
+ style: [styles2.actionBtn, styles2.passBtn, isSubmitting && { opacity: 0.5 }],
14643
+ onPress: () => handleChangeResult("passed"),
14644
+ disabled: isSubmitting
14645
+ },
14646
+ /* @__PURE__ */ React4.createElement(Text2, { style: styles2.passBtnText }, "Change to Pass")
14647
+ ))) : /* @__PURE__ */ React4.createElement(View3, { style: styles2.actionButtons }, /* @__PURE__ */ React4.createElement(TouchableOpacity2, { style: [styles2.actionBtn, styles2.failBtn, isSubmitting && { opacity: 0.5 }], onPress: handleFail, disabled: isSubmitting }, /* @__PURE__ */ React4.createElement(Text2, { style: styles2.failBtnText }, isSubmitting ? "Failing..." : "Fail")), /* @__PURE__ */ React4.createElement(TouchableOpacity2, { style: [styles2.actionBtn, styles2.skipBtn, isSubmitting && { opacity: 0.5 }], onPress: () => setShowSkipModal(true), disabled: isSubmitting }, /* @__PURE__ */ React4.createElement(Text2, { style: styles2.skipBtnText }, "Skip")), /* @__PURE__ */ React4.createElement(TouchableOpacity2, { style: [styles2.actionBtn, styles2.passBtn, isSubmitting && { opacity: 0.5 }], onPress: handlePass, disabled: isSubmitting }, /* @__PURE__ */ React4.createElement(Text2, { style: styles2.passBtnText }, isSubmitting ? "Passing..." : "Pass"))), /* @__PURE__ */ React4.createElement(Modal, { visible: showSkipModal, transparent: true, animationType: "fade" }, /* @__PURE__ */ React4.createElement(View3, { style: styles2.modalOverlay }, /* @__PURE__ */ React4.createElement(View3, { style: styles2.modalContent }, /* @__PURE__ */ React4.createElement(Text2, { style: styles2.modalTitle }, "Skip this test?"), /* @__PURE__ */ React4.createElement(Text2, { style: styles2.modalSubtitle }, "Select a reason:"), [
14118
14648
  { reason: "blocked", label: "\u{1F6AB} Blocked by a bug" },
14119
14649
  { reason: "not_ready", label: "\u{1F6A7} Feature not ready" },
14120
14650
  { reason: "dependency", label: "\u{1F517} Needs another test first" },
14121
14651
  { reason: "other", label: "\u{1F4DD} Other reason" }
14122
- ].map(({ reason, label }) => /* @__PURE__ */ React3.createElement(
14652
+ ].map(({ reason, label }) => /* @__PURE__ */ React4.createElement(
14123
14653
  TouchableOpacity2,
14124
14654
  {
14125
14655
  key: reason,
14126
14656
  style: [styles2.skipOption, selectedSkipReason === reason && styles2.skipOptionActive],
14127
14657
  onPress: () => setSelectedSkipReason(reason)
14128
14658
  },
14129
- /* @__PURE__ */ React3.createElement(Text2, { style: [styles2.skipOptionText, selectedSkipReason === reason && styles2.skipOptionTextActive] }, label)
14130
- )), /* @__PURE__ */ React3.createElement(
14659
+ /* @__PURE__ */ React4.createElement(Text2, { style: [styles2.skipOptionText, selectedSkipReason === reason && styles2.skipOptionTextActive] }, label)
14660
+ )), /* @__PURE__ */ React4.createElement(
14131
14661
  TextInput,
14132
14662
  {
14133
14663
  style: styles2.skipNotes,
@@ -14137,21 +14667,21 @@ function TestDetailScreen({ testId, nav }) {
14137
14667
  placeholderTextColor: colors.textMuted,
14138
14668
  multiline: true
14139
14669
  }
14140
- ), /* @__PURE__ */ React3.createElement(View2, { style: styles2.skipActions }, /* @__PURE__ */ React3.createElement(TouchableOpacity2, { style: styles2.skipCancel, onPress: () => {
14670
+ ), /* @__PURE__ */ React4.createElement(View3, { style: styles2.skipActions }, /* @__PURE__ */ React4.createElement(TouchableOpacity2, { style: styles2.skipCancel, onPress: () => {
14141
14671
  setShowSkipModal(false);
14142
14672
  setSelectedSkipReason(null);
14143
14673
  setSkipNotes("");
14144
- } }, /* @__PURE__ */ React3.createElement(Text2, { style: styles2.skipCancelText }, "Cancel")), /* @__PURE__ */ React3.createElement(
14674
+ } }, /* @__PURE__ */ React4.createElement(Text2, { style: styles2.skipCancelText }, "Cancel")), /* @__PURE__ */ React4.createElement(
14145
14675
  TouchableOpacity2,
14146
14676
  {
14147
14677
  style: [styles2.skipConfirm, !selectedSkipReason && { opacity: 0.4 }],
14148
14678
  onPress: handleSkip,
14149
14679
  disabled: !selectedSkipReason || skipping
14150
14680
  },
14151
- /* @__PURE__ */ React3.createElement(Text2, { style: styles2.skipConfirmText }, skipping ? "Skipping..." : "Skip Test")
14681
+ /* @__PURE__ */ React4.createElement(Text2, { style: styles2.skipConfirmText }, skipping ? "Skipping..." : "Skip Test")
14152
14682
  ))))));
14153
14683
  }
14154
- var styles2 = StyleSheet3.create({
14684
+ var styles2 = StyleSheet4.create({
14155
14685
  container: { paddingBottom: 16 },
14156
14686
  retestBanner: { flexDirection: "row", alignItems: "center", gap: 6, backgroundColor: "#422006", borderWidth: 1, borderColor: "#854d0e", borderRadius: 8, paddingVertical: 6, paddingHorizontal: 10, marginBottom: 10 },
14157
14687
  retestIcon: { fontSize: 14 },
@@ -14219,6 +14749,14 @@ var styles2 = StyleSheet3.create({
14219
14749
  detailDesc: { fontSize: 13, color: colors.textSecondary, lineHeight: 18 },
14220
14750
  folderProgress: { marginTop: 8 },
14221
14751
  folderName: { fontSize: 12, color: colors.textMuted },
14752
+ // Completed state
14753
+ 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 },
14754
+ completedBannerPass: { backgroundColor: colors.greenDark, borderColor: colors.green },
14755
+ completedBannerFail: { backgroundColor: colors.redDark, borderColor: colors.red },
14756
+ completedIcon: { fontSize: 14 },
14757
+ completedLabel: { fontSize: 13, fontWeight: "600", color: colors.textSecondary },
14758
+ reopenBtn: { backgroundColor: colors.card, borderWidth: 1, borderColor: colors.blue },
14759
+ reopenBtnText: { fontSize: 14, fontWeight: "600", color: colors.blue },
14222
14760
  // Action buttons
14223
14761
  actionButtons: { flexDirection: "row", gap: 10, marginTop: 8 },
14224
14762
  actionBtn: { flex: 1, paddingVertical: 14, borderRadius: 12, alignItems: "center" },
@@ -14246,14 +14784,18 @@ var styles2 = StyleSheet3.create({
14246
14784
  });
14247
14785
 
14248
14786
  // src/widget/screens/TestListScreen.tsx
14249
- import React4, { useState as useState3, useMemo as useMemo2, useCallback as useCallback3, useEffect as useEffect4 } from "react";
14250
- import { View as View3, Text as Text3, TouchableOpacity as TouchableOpacity3, StyleSheet as StyleSheet4, ScrollView } from "react-native";
14787
+ import React5, { useState as useState3, useMemo as useMemo2, useCallback as useCallback3, useEffect as useEffect5 } from "react";
14788
+ import { View as View4, Text as Text3, TouchableOpacity as TouchableOpacity3, StyleSheet as StyleSheet5, ScrollView, TextInput as TextInput2, Platform as Platform3, Linking as Linking2 } from "react-native";
14251
14789
  function TestListScreen({ nav }) {
14252
- const { assignments, currentAssignment, refreshAssignments } = useBugBear();
14790
+ const { assignments, currentAssignment, refreshAssignments, dashboardUrl, isLoading } = useBugBear();
14253
14791
  const [filter, setFilter] = useState3("all");
14254
14792
  const [roleFilter, setRoleFilter] = useState3(null);
14793
+ const [trackFilter, setTrackFilter] = useState3(null);
14794
+ const [platformFilter, setPlatformFilter] = useState3(Platform3.OS === "android" ? "android" : "ios");
14795
+ const [searchQuery, setSearchQuery] = useState3("");
14796
+ const [sortMode, setSortMode] = useState3("priority");
14255
14797
  const [collapsedFolders, setCollapsedFolders] = useState3(/* @__PURE__ */ new Set());
14256
- useEffect4(() => {
14798
+ useEffect5(() => {
14257
14799
  refreshAssignments();
14258
14800
  }, []);
14259
14801
  const availableRoles = useMemo2(() => {
@@ -14263,6 +14805,20 @@ function TestListScreen({ nav }) {
14263
14805
  }
14264
14806
  return Array.from(roleMap.values());
14265
14807
  }, [assignments]);
14808
+ const availableTracks = useMemo2(() => {
14809
+ const trackMap = /* @__PURE__ */ new Map();
14810
+ for (const a of assignments) {
14811
+ if (a.testCase.track) trackMap.set(a.testCase.track.id, a.testCase.track);
14812
+ }
14813
+ return Array.from(trackMap.values());
14814
+ }, [assignments]);
14815
+ const availablePlatforms = useMemo2(() => {
14816
+ const set = /* @__PURE__ */ new Set();
14817
+ for (const a of assignments) {
14818
+ if (a.testCase.platforms) a.testCase.platforms.forEach((p) => set.add(p));
14819
+ }
14820
+ return Array.from(set).sort();
14821
+ }, [assignments]);
14266
14822
  const selectedRole = availableRoles.find((r) => r.id === roleFilter);
14267
14823
  const groupedAssignments = useMemo2(() => {
14268
14824
  const groups = /* @__PURE__ */ new Map();
@@ -14286,6 +14842,8 @@ function TestListScreen({ nav }) {
14286
14842
  folder.assignments.sort((a, b) => {
14287
14843
  if (a.isVerification && !b.isVerification) return -1;
14288
14844
  if (!a.isVerification && b.isVerification) return 1;
14845
+ if (sortMode === "alpha") return a.testCase.title.localeCompare(b.testCase.title);
14846
+ if (sortMode === "recent") return 0;
14289
14847
  const sd = (statusOrder[a.status] ?? 5) - (statusOrder[b.status] ?? 5);
14290
14848
  if (sd !== 0) return sd;
14291
14849
  return (priorityOrder[a.testCase.priority] ?? 4) - (priorityOrder[b.testCase.priority] ?? 4);
@@ -14297,7 +14855,7 @@ function TestListScreen({ nav }) {
14297
14855
  if (!b.group) return -1;
14298
14856
  return a.group.sortOrder - b.group.sortOrder;
14299
14857
  });
14300
- }, [assignments]);
14858
+ }, [assignments, sortMode]);
14301
14859
  const toggleFolder = useCallback3((id) => {
14302
14860
  setCollapsedFolders((prev) => {
14303
14861
  const next = new Set(prev);
@@ -14306,28 +14864,37 @@ function TestListScreen({ nav }) {
14306
14864
  return next;
14307
14865
  });
14308
14866
  }, []);
14309
- const filterAssignment = (a) => {
14867
+ const filterAssignment = useCallback3((a) => {
14868
+ if (platformFilter && a.testCase.platforms && !a.testCase.platforms.includes(platformFilter)) return false;
14310
14869
  if (roleFilter && a.testCase.role?.id !== roleFilter) return false;
14870
+ if (trackFilter && a.testCase.track?.id !== trackFilter) return false;
14871
+ if (searchQuery) {
14872
+ const q = searchQuery.toLowerCase();
14873
+ const titleMatch = a.testCase.title.toLowerCase().includes(q);
14874
+ const keyMatch = a.testCase.testKey.toLowerCase().includes(q);
14875
+ if (!titleMatch && !keyMatch) return false;
14876
+ }
14311
14877
  if (filter === "pending") return a.status === "pending" || a.status === "in_progress";
14312
14878
  if (filter === "done") return a.status === "passed";
14313
14879
  if (filter === "reopened") return a.status === "failed";
14314
14880
  return true;
14315
- };
14316
- return /* @__PURE__ */ React4.createElement(View3, null, /* @__PURE__ */ React4.createElement(View3, { style: styles3.filterBar }, [
14881
+ }, [platformFilter, roleFilter, trackFilter, searchQuery, filter]);
14882
+ if (isLoading) return /* @__PURE__ */ React5.createElement(TestListScreenSkeleton, null);
14883
+ return /* @__PURE__ */ React5.createElement(View4, null, /* @__PURE__ */ React5.createElement(View4, { style: styles3.filterBar }, [
14317
14884
  { key: "all", label: "All", count: assignments.length },
14318
14885
  { key: "pending", label: "To Do", count: assignments.filter((a) => a.status === "pending" || a.status === "in_progress").length },
14319
14886
  { key: "done", label: "Done", count: assignments.filter((a) => a.status === "passed").length },
14320
14887
  { key: "reopened", label: "Re Opened", count: assignments.filter((a) => a.status === "failed").length }
14321
- ].map((f) => /* @__PURE__ */ React4.createElement(TouchableOpacity3, { key: f.key, style: [styles3.filterBtn, filter === f.key && styles3.filterBtnActive], onPress: () => setFilter(f.key) }, /* @__PURE__ */ React4.createElement(Text3, { style: [styles3.filterBtnText, filter === f.key && styles3.filterBtnTextActive] }, f.label, " (", f.count, ")")))), availableRoles.length >= 2 && /* @__PURE__ */ React4.createElement(View3, { style: styles3.roleSection }, /* @__PURE__ */ React4.createElement(ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, style: styles3.roleBar }, /* @__PURE__ */ React4.createElement(
14888
+ ].map((f) => /* @__PURE__ */ React5.createElement(TouchableOpacity3, { key: f.key, style: [styles3.filterBtn, filter === f.key && styles3.filterBtnActive], onPress: () => setFilter(f.key) }, /* @__PURE__ */ React5.createElement(Text3, { style: [styles3.filterBtnText, filter === f.key && styles3.filterBtnTextActive] }, f.label, " (", f.count, ")")))), availableRoles.length >= 2 && /* @__PURE__ */ React5.createElement(View4, { style: styles3.roleSection }, /* @__PURE__ */ React5.createElement(ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, style: styles3.roleBar }, /* @__PURE__ */ React5.createElement(
14322
14889
  TouchableOpacity3,
14323
14890
  {
14324
14891
  style: [styles3.roleBtn, !roleFilter && styles3.roleBtnActive],
14325
14892
  onPress: () => setRoleFilter(null)
14326
14893
  },
14327
- /* @__PURE__ */ React4.createElement(Text3, { style: [styles3.roleBtnText, !roleFilter && styles3.roleBtnTextActive] }, "All Roles")
14894
+ /* @__PURE__ */ React5.createElement(Text3, { style: [styles3.roleBtnText, !roleFilter && styles3.roleBtnTextActive] }, "All Roles")
14328
14895
  ), availableRoles.map((role) => {
14329
14896
  const isActive = roleFilter === role.id;
14330
- return /* @__PURE__ */ React4.createElement(
14897
+ return /* @__PURE__ */ React5.createElement(
14331
14898
  TouchableOpacity3,
14332
14899
  {
14333
14900
  key: role.id,
@@ -14337,33 +14904,95 @@ function TestListScreen({ nav }) {
14337
14904
  ],
14338
14905
  onPress: () => setRoleFilter(isActive ? null : role.id)
14339
14906
  },
14340
- /* @__PURE__ */ React4.createElement(View3, { style: [styles3.roleDot, { backgroundColor: role.color }] }),
14341
- /* @__PURE__ */ React4.createElement(Text3, { style: [styles3.roleBtnText, isActive && { color: role.color, fontWeight: "600" }] }, role.name)
14907
+ /* @__PURE__ */ React5.createElement(View4, { style: [styles3.roleDot, { backgroundColor: role.color }] }),
14908
+ /* @__PURE__ */ React5.createElement(Text3, { style: [styles3.roleBtnText, isActive && { color: role.color, fontWeight: "600" }] }, role.name)
14342
14909
  );
14343
- })), selectedRole?.loginHint && /* @__PURE__ */ React4.createElement(View3, { style: [styles3.loginHint, { backgroundColor: selectedRole.color + "10", borderColor: selectedRole.color + "30" }] }, /* @__PURE__ */ React4.createElement(Text3, { style: styles3.loginHintText }, "Log in as: ", selectedRole.loginHint))), groupedAssignments.map((folder) => {
14910
+ })), selectedRole?.loginHint && /* @__PURE__ */ React5.createElement(View4, { style: [styles3.loginHint, { backgroundColor: selectedRole.color + "10", borderColor: selectedRole.color + "30" }] }, /* @__PURE__ */ React5.createElement(Text3, { style: styles3.loginHintText }, "Log in as: ", selectedRole.loginHint))), /* @__PURE__ */ React5.createElement(View4, { style: styles3.searchContainer }, /* @__PURE__ */ React5.createElement(
14911
+ TextInput2,
14912
+ {
14913
+ value: searchQuery,
14914
+ onChangeText: setSearchQuery,
14915
+ placeholder: "Search tests...",
14916
+ placeholderTextColor: colors.textMuted,
14917
+ style: styles3.searchInput
14918
+ }
14919
+ )), availablePlatforms.length >= 2 && /* @__PURE__ */ React5.createElement(ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, style: styles3.platformBar }, /* @__PURE__ */ React5.createElement(
14920
+ TouchableOpacity3,
14921
+ {
14922
+ style: [styles3.platformBtn, !platformFilter && styles3.platformBtnActive],
14923
+ onPress: () => setPlatformFilter(null)
14924
+ },
14925
+ /* @__PURE__ */ React5.createElement(Text3, { style: [styles3.platformBtnText, !platformFilter && styles3.platformBtnTextActive] }, "All Platforms")
14926
+ ), availablePlatforms.map((p) => {
14927
+ const isActive = platformFilter === p;
14928
+ const label = p === "ios" ? "iOS" : p === "android" ? "Android" : p === "web" ? "Web" : p;
14929
+ const icon = p === "ios" ? "\u{1F4F1}" : p === "android" ? "\u{1F916}" : p === "web" ? "\u{1F310}" : "\u{1F4CB}";
14930
+ return /* @__PURE__ */ React5.createElement(
14931
+ TouchableOpacity3,
14932
+ {
14933
+ key: p,
14934
+ style: [
14935
+ styles3.platformBtn,
14936
+ isActive && { backgroundColor: colors.blue + "20", borderColor: colors.blue + "60", borderWidth: 1 }
14937
+ ],
14938
+ onPress: () => setPlatformFilter(isActive ? null : p)
14939
+ },
14940
+ /* @__PURE__ */ React5.createElement(Text3, { style: [styles3.platformBtnText, isActive && { color: colors.blue, fontWeight: "600" }] }, icon, " ", label)
14941
+ );
14942
+ })), /* @__PURE__ */ React5.createElement(View4, { style: styles3.trackSortRow }, availableTracks.length >= 2 && /* @__PURE__ */ React5.createElement(ScrollView, { horizontal: true, showsHorizontalScrollIndicator: false, style: { flex: 1 } }, /* @__PURE__ */ React5.createElement(
14943
+ TouchableOpacity3,
14944
+ {
14945
+ style: [styles3.trackBtn, !trackFilter && styles3.trackBtnActive],
14946
+ onPress: () => setTrackFilter(null)
14947
+ },
14948
+ /* @__PURE__ */ React5.createElement(Text3, { style: [styles3.trackBtnText, !trackFilter && styles3.trackBtnTextActive] }, "All Tracks")
14949
+ ), availableTracks.map((track) => {
14950
+ const isActive = trackFilter === track.id;
14951
+ return /* @__PURE__ */ React5.createElement(
14952
+ TouchableOpacity3,
14953
+ {
14954
+ key: track.id,
14955
+ style: [styles3.trackBtn, isActive && { backgroundColor: track.color + "20", borderColor: track.color + "60", borderWidth: 1 }],
14956
+ onPress: () => setTrackFilter(isActive ? null : track.id)
14957
+ },
14958
+ /* @__PURE__ */ React5.createElement(Text3, { style: [styles3.trackBtnText, isActive && { color: track.color, fontWeight: "600" }] }, track.icon, " ", track.name)
14959
+ );
14960
+ })), /* @__PURE__ */ React5.createElement(View4, { style: styles3.sortGroup }, [
14961
+ { key: "priority", label: "\u2195" },
14962
+ { key: "recent", label: "\u{1F550}" },
14963
+ { key: "alpha", label: "AZ" }
14964
+ ].map((s2) => /* @__PURE__ */ React5.createElement(
14965
+ TouchableOpacity3,
14966
+ {
14967
+ key: s2.key,
14968
+ style: [styles3.sortBtn, sortMode === s2.key && styles3.sortBtnActive],
14969
+ onPress: () => setSortMode(s2.key)
14970
+ },
14971
+ /* @__PURE__ */ React5.createElement(Text3, { style: [styles3.sortBtnText, sortMode === s2.key && styles3.sortBtnTextActive] }, s2.label)
14972
+ )))), groupedAssignments.map((folder) => {
14344
14973
  const folderId = folder.group?.id || "ungrouped";
14345
14974
  const isCollapsed = collapsedFolders.has(folderId);
14346
14975
  const filtered = folder.assignments.filter(filterAssignment);
14347
14976
  if (filtered.length === 0 && filter !== "all") return null;
14348
- return /* @__PURE__ */ React4.createElement(View3, { key: folderId, style: styles3.folder }, /* @__PURE__ */ React4.createElement(TouchableOpacity3, { style: styles3.folderHeader, onPress: () => toggleFolder(folderId) }, /* @__PURE__ */ React4.createElement(Text3, { style: styles3.folderToggle }, isCollapsed ? "\u25B6" : "\u25BC"), /* @__PURE__ */ React4.createElement(Text3, { style: styles3.folderName, numberOfLines: 1 }, folder.group?.name || "Ungrouped"), /* @__PURE__ */ React4.createElement(View3, { style: styles3.folderProgress }, /* @__PURE__ */ React4.createElement(View3, { style: [styles3.folderProgressFill, { width: `${folder.stats.total > 0 ? Math.round((folder.stats.passed + folder.stats.failed) / folder.stats.total * 100) : 0}%` }] })), /* @__PURE__ */ React4.createElement(Text3, { style: styles3.folderCount }, folder.stats.passed + folder.stats.failed, "/", folder.stats.total)), !isCollapsed && filtered.map((assignment) => {
14977
+ return /* @__PURE__ */ React5.createElement(View4, { key: folderId, style: styles3.folder }, /* @__PURE__ */ React5.createElement(TouchableOpacity3, { style: styles3.folderHeader, onPress: () => toggleFolder(folderId) }, /* @__PURE__ */ React5.createElement(Text3, { style: styles3.folderToggle }, isCollapsed ? "\u25B6" : "\u25BC"), /* @__PURE__ */ React5.createElement(Text3, { style: styles3.folderName, numberOfLines: 1 }, folder.group?.name || "Ungrouped"), /* @__PURE__ */ React5.createElement(View4, { style: styles3.folderProgress }, /* @__PURE__ */ React5.createElement(View4, { style: [styles3.folderProgressFill, { width: `${folder.stats.total > 0 ? Math.round((folder.stats.passed + folder.stats.failed) / folder.stats.total * 100) : 0}%` }] })), /* @__PURE__ */ React5.createElement(Text3, { style: styles3.folderCount }, folder.stats.passed + folder.stats.failed, "/", folder.stats.total)), !isCollapsed && filtered.map((assignment) => {
14349
14978
  const badge = getStatusBadge(assignment.status);
14350
14979
  const isCurrent = currentAssignment?.id === assignment.id;
14351
- return /* @__PURE__ */ React4.createElement(
14980
+ return /* @__PURE__ */ React5.createElement(
14352
14981
  TouchableOpacity3,
14353
14982
  {
14354
14983
  key: assignment.id,
14355
14984
  style: [styles3.testItem, isCurrent && styles3.testItemCurrent],
14356
14985
  onPress: () => nav.push({ name: "TEST_DETAIL", testId: assignment.id })
14357
14986
  },
14358
- /* @__PURE__ */ React4.createElement(Text3, { style: styles3.testBadge }, badge.icon),
14359
- /* @__PURE__ */ React4.createElement(View3, { style: styles3.testInfo }, /* @__PURE__ */ React4.createElement(Text3, { style: styles3.testTitle, numberOfLines: 1 }, assignment.testCase.title), /* @__PURE__ */ React4.createElement(View3, { style: styles3.testMetaRow }, assignment.isVerification && /* @__PURE__ */ React4.createElement(View3, { style: styles3.retestTag }, /* @__PURE__ */ React4.createElement(Text3, { style: styles3.retestTagText }, "Retest")), /* @__PURE__ */ React4.createElement(Text3, { style: styles3.testMeta }, assignment.testCase.testKey, " \xB7 ", assignment.testCase.priority), assignment.testCase.role && /* @__PURE__ */ React4.createElement(View3, { style: styles3.roleBadgeRow }, /* @__PURE__ */ React4.createElement(Text3, { style: styles3.testMeta }, " \xB7 "), /* @__PURE__ */ React4.createElement(View3, { style: [styles3.roleBadgeDot, { backgroundColor: assignment.testCase.role.color }] }), /* @__PURE__ */ React4.createElement(Text3, { style: [styles3.testMeta, { color: assignment.testCase.role.color, fontWeight: "500" }] }, assignment.testCase.role.name)))),
14360
- /* @__PURE__ */ React4.createElement(View3, { style: [
14987
+ /* @__PURE__ */ React5.createElement(Text3, { style: styles3.testBadge }, badge.icon),
14988
+ /* @__PURE__ */ React5.createElement(View4, { style: styles3.testInfo }, /* @__PURE__ */ React5.createElement(Text3, { style: styles3.testTitle, numberOfLines: 1 }, assignment.testCase.title), /* @__PURE__ */ React5.createElement(View4, { style: styles3.testMetaRow }, assignment.isVerification && /* @__PURE__ */ React5.createElement(View4, { style: styles3.retestTag }, /* @__PURE__ */ React5.createElement(Text3, { style: styles3.retestTagText }, "Retest")), /* @__PURE__ */ React5.createElement(Text3, { style: styles3.testMeta }, assignment.testCase.testKey, " \xB7 ", assignment.testCase.priority), assignment.testCase.role && /* @__PURE__ */ React5.createElement(View4, { style: styles3.roleBadgeRow }, /* @__PURE__ */ React5.createElement(Text3, { style: styles3.testMeta }, " \xB7 "), /* @__PURE__ */ React5.createElement(View4, { style: [styles3.roleBadgeDot, { backgroundColor: assignment.testCase.role.color }] }), /* @__PURE__ */ React5.createElement(Text3, { style: [styles3.testMeta, { color: assignment.testCase.role.color, fontWeight: "500" }] }, assignment.testCase.role.name)))),
14989
+ /* @__PURE__ */ React5.createElement(View4, { style: [
14361
14990
  styles3.statusPill,
14362
14991
  {
14363
14992
  backgroundColor: assignment.status === "passed" ? "#14532d" : assignment.status === "failed" ? "#450a0a" : assignment.status === "in_progress" ? "#172554" : "#27272a",
14364
14993
  borderColor: assignment.status === "passed" ? "#166534" : assignment.status === "failed" ? "#7f1d1d" : assignment.status === "in_progress" ? "#1e3a5f" : "#3f3f46"
14365
14994
  }
14366
- ] }, /* @__PURE__ */ React4.createElement(Text3, { style: [
14995
+ ] }, /* @__PURE__ */ React5.createElement(Text3, { style: [
14367
14996
  styles3.statusPillText,
14368
14997
  {
14369
14998
  color: assignment.status === "passed" ? "#4ade80" : assignment.status === "failed" ? "#f87171" : assignment.status === "in_progress" ? "#60a5fa" : "#d4d4d8"
@@ -14371,9 +15000,17 @@ function TestListScreen({ nav }) {
14371
15000
  ] }, badge.label))
14372
15001
  );
14373
15002
  }));
14374
- }), /* @__PURE__ */ React4.createElement(TouchableOpacity3, { style: styles3.refreshBtn, onPress: refreshAssignments }, /* @__PURE__ */ React4.createElement(Text3, { style: styles3.refreshText }, "\u21BB", " Refresh")));
15003
+ }), dashboardUrl && /* @__PURE__ */ React5.createElement(
15004
+ TouchableOpacity3,
15005
+ {
15006
+ style: styles3.dashboardLink,
15007
+ onPress: () => Linking2.openURL(`${dashboardUrl}/test-cases`),
15008
+ activeOpacity: 0.7
15009
+ },
15010
+ /* @__PURE__ */ React5.createElement(Text3, { style: styles3.dashboardLinkText }, "\u{1F310}", " Manage on Dashboard ", "\u2192")
15011
+ ), /* @__PURE__ */ React5.createElement(TouchableOpacity3, { style: styles3.refreshBtn, onPress: refreshAssignments }, /* @__PURE__ */ React5.createElement(Text3, { style: styles3.refreshText }, "\u21BB", " Refresh")));
14375
15012
  }
14376
- var styles3 = StyleSheet4.create({
15013
+ var styles3 = StyleSheet5.create({
14377
15014
  filterBar: { flexDirection: "row", gap: 8, marginBottom: 8 },
14378
15015
  filterBtn: { paddingHorizontal: 12, paddingVertical: 6, borderRadius: 8, backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border },
14379
15016
  filterBtnActive: { backgroundColor: colors.blue, borderColor: colors.blue },
@@ -14408,13 +15045,32 @@ var styles3 = StyleSheet4.create({
14408
15045
  testMeta: { fontSize: 11, color: colors.textDim },
14409
15046
  statusPill: { paddingHorizontal: 8, paddingVertical: 3, borderRadius: 6, borderWidth: 1, marginLeft: 8 },
14410
15047
  statusPillText: { fontSize: 10, fontWeight: "600" },
14411
- refreshBtn: { alignItems: "center", paddingVertical: 12 },
15048
+ searchContainer: { marginBottom: 8 },
15049
+ searchInput: { backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border, borderRadius: 8, paddingHorizontal: 12, paddingVertical: 8, fontSize: 13, color: colors.textPrimary },
15050
+ platformBar: { flexDirection: "row", marginBottom: 8 },
15051
+ platformBtn: { flexDirection: "row", alignItems: "center", gap: 4, paddingHorizontal: 10, paddingVertical: 4, borderRadius: 6, marginRight: 6, borderWidth: 1, borderColor: "transparent" },
15052
+ platformBtnActive: { backgroundColor: colors.card, borderColor: colors.border },
15053
+ platformBtnText: { fontSize: 11, color: colors.textMuted },
15054
+ platformBtnTextActive: { color: colors.textPrimary, fontWeight: "600" },
15055
+ trackSortRow: { flexDirection: "row", alignItems: "center", marginBottom: 10, gap: 8 },
15056
+ trackBtn: { flexDirection: "row", alignItems: "center", gap: 4, paddingHorizontal: 10, paddingVertical: 4, borderRadius: 6, marginRight: 6, borderWidth: 1, borderColor: "transparent" },
15057
+ trackBtnActive: { backgroundColor: colors.card, borderColor: colors.border },
15058
+ trackBtnText: { fontSize: 11, color: colors.textMuted },
15059
+ trackBtnTextActive: { color: colors.textPrimary, fontWeight: "600" },
15060
+ sortGroup: { flexDirection: "row", gap: 2 },
15061
+ sortBtn: { paddingHorizontal: 8, paddingVertical: 4, borderRadius: 6, borderWidth: 1, borderColor: "transparent" },
15062
+ sortBtnActive: { backgroundColor: colors.card, borderColor: colors.border },
15063
+ sortBtnText: { fontSize: 11, color: colors.textMuted },
15064
+ sortBtnTextActive: { color: colors.textPrimary, fontWeight: "600" },
15065
+ dashboardLink: { alignItems: "center", paddingTop: 12 },
15066
+ dashboardLinkText: { fontSize: 13, fontWeight: "500", color: colors.blue },
15067
+ refreshBtn: { alignItems: "center", paddingVertical: 8 },
14412
15068
  refreshText: { fontSize: 13, color: colors.blue }
14413
15069
  });
14414
15070
 
14415
15071
  // src/widget/screens/TestFeedbackScreen.tsx
14416
- import React7, { useState as useState5 } from "react";
14417
- import { View as View6, Text as Text6, TouchableOpacity as TouchableOpacity6, TextInput as TextInput2, StyleSheet as StyleSheet7 } from "react-native";
15072
+ import React8, { useState as useState5 } from "react";
15073
+ import { View as View7, Text as Text6, TouchableOpacity as TouchableOpacity6, TextInput as TextInput3, StyleSheet as StyleSheet8 } from "react-native";
14418
15074
 
14419
15075
  // src/widget/useImageAttachments.ts
14420
15076
  import { useState as useState4, useCallback as useCallback4 } from "react";
@@ -14514,17 +15170,17 @@ function useImageAttachments(uploadFn, maxImages, bucket = "screenshots") {
14514
15170
  }
14515
15171
 
14516
15172
  // src/widget/ImagePickerButtons.tsx
14517
- import React6 from "react";
14518
- import { View as View5, Text as Text5, TouchableOpacity as TouchableOpacity5, StyleSheet as StyleSheet6 } from "react-native";
15173
+ import React7 from "react";
15174
+ import { View as View6, Text as Text5, TouchableOpacity as TouchableOpacity5, StyleSheet as StyleSheet7 } from "react-native";
14519
15175
 
14520
15176
  // src/widget/ImagePreviewStrip.tsx
14521
- import React5 from "react";
14522
- import { View as View4, Text as Text4, TouchableOpacity as TouchableOpacity4, ScrollView as ScrollView2, Image, ActivityIndicator, StyleSheet as StyleSheet5 } from "react-native";
15177
+ import React6 from "react";
15178
+ import { View as View5, Text as Text4, TouchableOpacity as TouchableOpacity4, ScrollView as ScrollView2, Image, ActivityIndicator, StyleSheet as StyleSheet6 } from "react-native";
14523
15179
  function ImagePreviewStrip({ images, onRemove }) {
14524
15180
  if (images.length === 0) return null;
14525
- return /* @__PURE__ */ React5.createElement(ScrollView2, { horizontal: true, showsHorizontalScrollIndicator: false, style: styles4.strip }, images.map((img) => /* @__PURE__ */ React5.createElement(View4, { key: img.id, style: styles4.thumbContainer }, /* @__PURE__ */ React5.createElement(Image, { source: { uri: img.localUri }, style: styles4.thumb }), img.status === "uploading" && /* @__PURE__ */ React5.createElement(View4, { style: styles4.thumbOverlay }, /* @__PURE__ */ React5.createElement(ActivityIndicator, { size: "small", color: "#fff" })), img.status === "error" && /* @__PURE__ */ React5.createElement(View4, { style: [styles4.thumbOverlay, styles4.thumbOverlayError] }, /* @__PURE__ */ React5.createElement(Text4, { style: styles4.thumbErrorText }, "!")), /* @__PURE__ */ React5.createElement(TouchableOpacity4, { style: styles4.thumbRemove, onPress: () => onRemove(img.id) }, /* @__PURE__ */ React5.createElement(Text4, { style: styles4.thumbRemoveText }, "\u2715")))));
15181
+ return /* @__PURE__ */ React6.createElement(ScrollView2, { horizontal: true, showsHorizontalScrollIndicator: false, style: styles4.strip }, images.map((img) => /* @__PURE__ */ React6.createElement(View5, { key: img.id, style: styles4.thumbContainer }, /* @__PURE__ */ React6.createElement(Image, { source: { uri: img.localUri }, style: styles4.thumb }), img.status === "uploading" && /* @__PURE__ */ React6.createElement(View5, { style: styles4.thumbOverlay }, /* @__PURE__ */ React6.createElement(ActivityIndicator, { size: "small", color: "#fff" })), img.status === "error" && /* @__PURE__ */ React6.createElement(View5, { style: [styles4.thumbOverlay, styles4.thumbOverlayError] }, /* @__PURE__ */ React6.createElement(Text4, { style: styles4.thumbErrorText }, "!")), /* @__PURE__ */ React6.createElement(TouchableOpacity4, { style: styles4.thumbRemove, onPress: () => onRemove(img.id) }, /* @__PURE__ */ React6.createElement(Text4, { style: styles4.thumbRemoveText }, "\u2715")))));
14526
15182
  }
14527
- var styles4 = StyleSheet5.create({
15183
+ var styles4 = StyleSheet6.create({
14528
15184
  strip: {
14529
15185
  flexDirection: "row",
14530
15186
  marginTop: 4
@@ -14543,7 +15199,7 @@ var styles4 = StyleSheet5.create({
14543
15199
  borderRadius: 8
14544
15200
  },
14545
15201
  thumbOverlay: {
14546
- ...StyleSheet5.absoluteFillObject,
15202
+ ...StyleSheet6.absoluteFillObject,
14547
15203
  backgroundColor: "rgba(0,0,0,0.5)",
14548
15204
  justifyContent: "center",
14549
15205
  alignItems: "center",
@@ -14578,25 +15234,25 @@ var styles4 = StyleSheet5.create({
14578
15234
  // src/widget/ImagePickerButtons.tsx
14579
15235
  function ImagePickerButtons({ images, maxImages, onPickGallery, onPickCamera, onRemove, label }) {
14580
15236
  if (!IMAGE_PICKER_AVAILABLE) return null;
14581
- return /* @__PURE__ */ React6.createElement(View5, { style: styles5.section }, label && /* @__PURE__ */ React6.createElement(Text5, { style: styles5.label }, label), /* @__PURE__ */ React6.createElement(View5, { style: styles5.buttonRow }, /* @__PURE__ */ React6.createElement(
15237
+ return /* @__PURE__ */ React7.createElement(View6, { style: styles5.section }, label && /* @__PURE__ */ React7.createElement(Text5, { style: styles5.label }, label), /* @__PURE__ */ React7.createElement(View6, { style: styles5.buttonRow }, /* @__PURE__ */ React7.createElement(
14582
15238
  TouchableOpacity5,
14583
15239
  {
14584
15240
  style: styles5.pickButton,
14585
15241
  onPress: onPickGallery,
14586
15242
  disabled: images.length >= maxImages
14587
15243
  },
14588
- /* @__PURE__ */ React6.createElement(Text5, { style: [styles5.pickButtonText, images.length >= maxImages && styles5.pickButtonDisabled] }, "Gallery")
14589
- ), /* @__PURE__ */ React6.createElement(
15244
+ /* @__PURE__ */ React7.createElement(Text5, { style: [styles5.pickButtonText, images.length >= maxImages && styles5.pickButtonDisabled] }, "Gallery")
15245
+ ), /* @__PURE__ */ React7.createElement(
14590
15246
  TouchableOpacity5,
14591
15247
  {
14592
15248
  style: styles5.pickButton,
14593
15249
  onPress: onPickCamera,
14594
15250
  disabled: images.length >= maxImages
14595
15251
  },
14596
- /* @__PURE__ */ React6.createElement(Text5, { style: [styles5.pickButtonText, images.length >= maxImages && styles5.pickButtonDisabled] }, "Camera")
14597
- ), /* @__PURE__ */ React6.createElement(Text5, { style: styles5.countText }, images.length, "/", maxImages)), /* @__PURE__ */ React6.createElement(ImagePreviewStrip, { images, onRemove }));
15252
+ /* @__PURE__ */ React7.createElement(Text5, { style: [styles5.pickButtonText, images.length >= maxImages && styles5.pickButtonDisabled] }, "Camera")
15253
+ ), /* @__PURE__ */ React7.createElement(Text5, { style: styles5.countText }, images.length, "/", maxImages)), /* @__PURE__ */ React7.createElement(ImagePreviewStrip, { images, onRemove }));
14598
15254
  }
14599
- var styles5 = StyleSheet6.create({
15255
+ var styles5 = StyleSheet7.create({
14600
15256
  section: {
14601
15257
  marginTop: 12,
14602
15258
  marginBottom: 4
@@ -14697,22 +15353,22 @@ function TestFeedbackScreen({ status, assignmentId, nav }) {
14697
15353
  }
14698
15354
  }
14699
15355
  };
14700
- return /* @__PURE__ */ React7.createElement(View6, { style: styles6.container }, /* @__PURE__ */ React7.createElement(Text6, { style: styles6.header }, status === "passed" ? "\u2705 Test Passed!" : "\u274C Test Failed"), /* @__PURE__ */ React7.createElement(Text6, { style: styles6.subheader }, "Rate this test case"), /* @__PURE__ */ React7.createElement(View6, { style: styles6.starRow }, [1, 2, 3, 4, 5].map((n) => /* @__PURE__ */ React7.createElement(TouchableOpacity6, { key: n, onPress: () => setRating(n), style: styles6.starButton }, /* @__PURE__ */ React7.createElement(Text6, { style: [styles6.star, n <= rating && styles6.starActive] }, n <= rating ? "\u2605" : "\u2606")))), showFlags && /* @__PURE__ */ React7.createElement(View6, { style: styles6.flagsSection }, /* @__PURE__ */ React7.createElement(Text6, { style: styles6.flagsLabel }, "What could be improved?"), [
15356
+ return /* @__PURE__ */ React8.createElement(View7, { style: styles6.container }, /* @__PURE__ */ React8.createElement(Text6, { style: styles6.header }, status === "passed" ? "\u2705 Test Passed!" : "\u274C Test Failed"), /* @__PURE__ */ React8.createElement(Text6, { style: styles6.subheader }, "Rate this test case"), /* @__PURE__ */ React8.createElement(View7, { style: styles6.starRow }, [1, 2, 3, 4, 5].map((n) => /* @__PURE__ */ React8.createElement(TouchableOpacity6, { key: n, onPress: () => setRating(n), style: styles6.starButton }, /* @__PURE__ */ React8.createElement(Text6, { style: [styles6.star, n <= rating && styles6.starActive] }, n <= rating ? "\u2605" : "\u2606")))), showFlags && /* @__PURE__ */ React8.createElement(View7, { style: styles6.flagsSection }, /* @__PURE__ */ React8.createElement(Text6, { style: styles6.flagsLabel }, "What could be improved?"), [
14701
15357
  { key: "isOutdated", label: "Test is outdated" },
14702
15358
  { key: "needsMoreDetail", label: "Needs more detail" },
14703
15359
  { key: "stepsUnclear", label: "Steps are unclear" },
14704
15360
  { key: "expectedResultUnclear", label: "Expected result unclear" }
14705
- ].map(({ key, label }) => /* @__PURE__ */ React7.createElement(
15361
+ ].map(({ key, label }) => /* @__PURE__ */ React8.createElement(
14706
15362
  TouchableOpacity6,
14707
15363
  {
14708
15364
  key,
14709
15365
  style: [styles6.flagItem, flags[key] && styles6.flagItemActive],
14710
15366
  onPress: () => setFlags((prev) => ({ ...prev, [key]: !prev[key] }))
14711
15367
  },
14712
- /* @__PURE__ */ React7.createElement(View6, { style: [styles6.flagCheck, flags[key] && styles6.flagCheckActive] }, flags[key] && /* @__PURE__ */ React7.createElement(Text6, { style: styles6.flagCheckmark }, "\u2713")),
14713
- /* @__PURE__ */ React7.createElement(Text6, { style: [styles6.flagText, flags[key] && styles6.flagTextActive] }, label)
14714
- ))), /* @__PURE__ */ React7.createElement(
14715
- TextInput2,
15368
+ /* @__PURE__ */ React8.createElement(View7, { style: [styles6.flagCheck, flags[key] && styles6.flagCheckActive] }, flags[key] && /* @__PURE__ */ React8.createElement(Text6, { style: styles6.flagCheckmark }, "\u2713")),
15369
+ /* @__PURE__ */ React8.createElement(Text6, { style: [styles6.flagText, flags[key] && styles6.flagTextActive] }, label)
15370
+ ))), /* @__PURE__ */ React8.createElement(
15371
+ TextInput3,
14716
15372
  {
14717
15373
  style: styles6.noteInput,
14718
15374
  value: note,
@@ -14721,7 +15377,7 @@ function TestFeedbackScreen({ status, assignmentId, nav }) {
14721
15377
  placeholderTextColor: colors.textMuted,
14722
15378
  multiline: true
14723
15379
  }
14724
- ), /* @__PURE__ */ React7.createElement(
15380
+ ), /* @__PURE__ */ React8.createElement(
14725
15381
  ImagePickerButtons,
14726
15382
  {
14727
15383
  images: images.images,
@@ -14731,9 +15387,9 @@ function TestFeedbackScreen({ status, assignmentId, nav }) {
14731
15387
  onRemove: images.removeImage,
14732
15388
  label: "Screenshots (optional)"
14733
15389
  }
14734
- ), /* @__PURE__ */ React7.createElement(View6, { style: styles6.actions }, /* @__PURE__ */ React7.createElement(TouchableOpacity6, { style: styles6.skipButton, onPress: handleSkip }, /* @__PURE__ */ React7.createElement(Text6, { style: styles6.skipText }, "Skip")), /* @__PURE__ */ React7.createElement(TouchableOpacity6, { style: [shared.primaryButton, { flex: 2, opacity: submitting || images.isUploading ? 0.5 : 1 }], onPress: handleSubmit, disabled: submitting || images.isUploading }, /* @__PURE__ */ React7.createElement(Text6, { style: shared.primaryButtonText }, images.isUploading ? "Uploading..." : submitting ? "Submitting..." : "Submit"))));
15390
+ ), /* @__PURE__ */ React8.createElement(View7, { style: styles6.actions }, /* @__PURE__ */ React8.createElement(TouchableOpacity6, { style: styles6.skipButton, onPress: handleSkip }, /* @__PURE__ */ React8.createElement(Text6, { style: styles6.skipText }, "Skip")), /* @__PURE__ */ React8.createElement(TouchableOpacity6, { style: [shared.primaryButton, { flex: 2, opacity: submitting || images.isUploading ? 0.5 : 1 }], onPress: handleSubmit, disabled: submitting || images.isUploading }, /* @__PURE__ */ React8.createElement(Text6, { style: shared.primaryButtonText }, images.isUploading ? "Uploading..." : submitting ? "Submitting..." : "Submit"))));
14735
15391
  }
14736
- var styles6 = StyleSheet7.create({
15392
+ var styles6 = StyleSheet8.create({
14737
15393
  container: { paddingTop: 8 },
14738
15394
  header: { fontSize: 22, fontWeight: "700", color: colors.textPrimary, textAlign: "center", marginBottom: 4 },
14739
15395
  subheader: { fontSize: 14, color: colors.textMuted, textAlign: "center", marginBottom: 20 },
@@ -14757,12 +15413,12 @@ var styles6 = StyleSheet7.create({
14757
15413
  });
14758
15414
 
14759
15415
  // src/widget/screens/ReportScreen.tsx
14760
- import React9, { useState as useState7, useRef as useRef2, useEffect as useEffect5 } from "react";
14761
- import { View as View8, Text as Text8, TouchableOpacity as TouchableOpacity8, TextInput as TextInput3, StyleSheet as StyleSheet9 } from "react-native";
15416
+ import React10, { useState as useState7, useRef as useRef3, useEffect as useEffect6 } from "react";
15417
+ import { View as View9, Text as Text8, TouchableOpacity as TouchableOpacity8, TextInput as TextInput4, StyleSheet as StyleSheet10 } from "react-native";
14762
15418
 
14763
15419
  // src/widget/CategoryPicker.tsx
14764
- import React8, { useState as useState6 } from "react";
14765
- import { View as View7, Text as Text7, TouchableOpacity as TouchableOpacity7, Modal as Modal2, StyleSheet as StyleSheet8 } from "react-native";
15420
+ import React9, { useState as useState6 } from "react";
15421
+ import { View as View8, Text as Text7, TouchableOpacity as TouchableOpacity7, Modal as Modal2, StyleSheet as StyleSheet9 } from "react-native";
14766
15422
  var categoryOptions = [
14767
15423
  { value: "ui_ux", label: "UI/UX", icon: "\u{1F3A8}" },
14768
15424
  { value: "functional", label: "Functional", icon: "\u2699\uFE0F" },
@@ -14777,15 +15433,15 @@ function CategoryPicker({ value, onChange, optional = true }) {
14777
15433
  onChange(category);
14778
15434
  setModalVisible(false);
14779
15435
  };
14780
- return /* @__PURE__ */ React8.createElement(React8.Fragment, null, /* @__PURE__ */ React8.createElement(
15436
+ return /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement(
14781
15437
  TouchableOpacity7,
14782
15438
  {
14783
15439
  style: styles7.trigger,
14784
15440
  onPress: () => setModalVisible(true)
14785
15441
  },
14786
- /* @__PURE__ */ React8.createElement(Text7, { style: selectedOption ? styles7.triggerTextSelected : styles7.triggerTextPlaceholder }, selectedOption ? `${selectedOption.icon} ${selectedOption.label}` : optional ? "Select category (optional)" : "Select category"),
14787
- /* @__PURE__ */ React8.createElement(Text7, { style: styles7.chevron }, "\u25BC")
14788
- ), /* @__PURE__ */ React8.createElement(
15442
+ /* @__PURE__ */ React9.createElement(Text7, { style: selectedOption ? styles7.triggerTextSelected : styles7.triggerTextPlaceholder }, selectedOption ? `${selectedOption.icon} ${selectedOption.label}` : optional ? "Select category (optional)" : "Select category"),
15443
+ /* @__PURE__ */ React9.createElement(Text7, { style: styles7.chevron }, "\u25BC")
15444
+ ), /* @__PURE__ */ React9.createElement(
14789
15445
  Modal2,
14790
15446
  {
14791
15447
  visible: modalVisible,
@@ -14793,42 +15449,42 @@ function CategoryPicker({ value, onChange, optional = true }) {
14793
15449
  animationType: "fade",
14794
15450
  onRequestClose: () => setModalVisible(false)
14795
15451
  },
14796
- /* @__PURE__ */ React8.createElement(
15452
+ /* @__PURE__ */ React9.createElement(
14797
15453
  TouchableOpacity7,
14798
15454
  {
14799
15455
  style: styles7.overlay,
14800
15456
  activeOpacity: 1,
14801
15457
  onPress: () => setModalVisible(false)
14802
15458
  },
14803
- /* @__PURE__ */ React8.createElement(View7, { style: styles7.modal, onStartShouldSetResponder: () => true }, /* @__PURE__ */ React8.createElement(Text7, { style: styles7.modalTitle }, "Select Category"), optional && /* @__PURE__ */ React8.createElement(
15459
+ /* @__PURE__ */ React9.createElement(View8, { style: styles7.modal, onStartShouldSetResponder: () => true }, /* @__PURE__ */ React9.createElement(Text7, { style: styles7.modalTitle }, "Select Category"), optional && /* @__PURE__ */ React9.createElement(
14804
15460
  TouchableOpacity7,
14805
15461
  {
14806
15462
  style: [styles7.option, !value && styles7.optionSelected],
14807
15463
  onPress: () => handleSelect(null)
14808
15464
  },
14809
- /* @__PURE__ */ React8.createElement(Text7, { style: styles7.optionText }, "\u2014 None \u2014")
14810
- ), categoryOptions.map(({ value: optValue, label, icon }) => /* @__PURE__ */ React8.createElement(
15465
+ /* @__PURE__ */ React9.createElement(Text7, { style: styles7.optionText }, "\u2014 None \u2014")
15466
+ ), categoryOptions.map(({ value: optValue, label, icon }) => /* @__PURE__ */ React9.createElement(
14811
15467
  TouchableOpacity7,
14812
15468
  {
14813
15469
  key: optValue,
14814
15470
  style: [styles7.option, value === optValue && styles7.optionSelected],
14815
15471
  onPress: () => handleSelect(optValue)
14816
15472
  },
14817
- /* @__PURE__ */ React8.createElement(Text7, { style: styles7.optionIcon }, icon),
14818
- /* @__PURE__ */ React8.createElement(Text7, { style: styles7.optionText }, label),
14819
- value === optValue && /* @__PURE__ */ React8.createElement(Text7, { style: styles7.checkmark }, "\u2713")
14820
- )), /* @__PURE__ */ React8.createElement(
15473
+ /* @__PURE__ */ React9.createElement(Text7, { style: styles7.optionIcon }, icon),
15474
+ /* @__PURE__ */ React9.createElement(Text7, { style: styles7.optionText }, label),
15475
+ value === optValue && /* @__PURE__ */ React9.createElement(Text7, { style: styles7.checkmark }, "\u2713")
15476
+ )), /* @__PURE__ */ React9.createElement(
14821
15477
  TouchableOpacity7,
14822
15478
  {
14823
15479
  style: styles7.cancelButton,
14824
15480
  onPress: () => setModalVisible(false)
14825
15481
  },
14826
- /* @__PURE__ */ React8.createElement(Text7, { style: styles7.cancelText }, "Cancel")
15482
+ /* @__PURE__ */ React9.createElement(Text7, { style: styles7.cancelText }, "Cancel")
14827
15483
  ))
14828
15484
  )
14829
15485
  ));
14830
15486
  }
14831
- var styles7 = StyleSheet8.create({
15487
+ var styles7 = StyleSheet9.create({
14832
15488
  trigger: {
14833
15489
  flexDirection: "row",
14834
15490
  alignItems: "center",
@@ -14925,11 +15581,11 @@ function ReportScreen({ nav, prefill }) {
14925
15581
  const [affectedScreen, setAffectedScreen] = useState7("");
14926
15582
  const [submitting, setSubmitting] = useState7(false);
14927
15583
  const [error, setError] = useState7(null);
14928
- const submittingRef = useRef2(false);
15584
+ const submittingRef = useRef3(false);
14929
15585
  const images = useImageAttachments(uploadImage, 5, "screenshots");
14930
15586
  const isRetestFailure = prefill?.type === "test_fail";
14931
15587
  const isBugType = reportType === "bug" || reportType === "test_fail";
14932
- useEffect5(() => {
15588
+ useEffect6(() => {
14933
15589
  if (reportType === "feedback" || reportType === "suggestion") {
14934
15590
  setCategory("other");
14935
15591
  } else {
@@ -14978,21 +15634,21 @@ function ReportScreen({ nav, prefill }) {
14978
15634
  submittingRef.current = false;
14979
15635
  }
14980
15636
  };
14981
- return /* @__PURE__ */ React9.createElement(View8, null, isRetestFailure ? /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement(View8, { style: styles8.retestBanner }, /* @__PURE__ */ React9.createElement(Text8, { style: styles8.retestIcon }, "\u{1F504}"), /* @__PURE__ */ React9.createElement(View8, null, /* @__PURE__ */ React9.createElement(Text8, { style: styles8.retestTitle }, "Bug Still Present"), /* @__PURE__ */ React9.createElement(Text8, { style: styles8.retestSubtitle }, "The fix did not resolve this issue"))), /* @__PURE__ */ React9.createElement(View8, { style: styles8.section }, /* @__PURE__ */ React9.createElement(Text8, { style: shared.label }, "Severity"), /* @__PURE__ */ React9.createElement(View8, { style: styles8.severityRow }, [
15637
+ return /* @__PURE__ */ React10.createElement(View9, null, isRetestFailure ? /* @__PURE__ */ React10.createElement(React10.Fragment, null, /* @__PURE__ */ React10.createElement(View9, { style: styles8.retestBanner }, /* @__PURE__ */ React10.createElement(Text8, { style: styles8.retestIcon }, "\u{1F504}"), /* @__PURE__ */ React10.createElement(View9, null, /* @__PURE__ */ React10.createElement(Text8, { style: styles8.retestTitle }, "Bug Still Present"), /* @__PURE__ */ React10.createElement(Text8, { style: styles8.retestSubtitle }, "The fix did not resolve this issue"))), /* @__PURE__ */ React10.createElement(View9, { style: styles8.section }, /* @__PURE__ */ React10.createElement(Text8, { style: shared.label }, "Severity"), /* @__PURE__ */ React10.createElement(View9, { style: styles8.severityRow }, [
14982
15638
  { sev: "critical", color: "#ef4444" },
14983
15639
  { sev: "high", color: "#f97316" },
14984
15640
  { sev: "medium", color: "#eab308" },
14985
15641
  { sev: "low", color: "#6b7280" }
14986
- ].map(({ sev, color }) => /* @__PURE__ */ React9.createElement(
15642
+ ].map(({ sev, color }) => /* @__PURE__ */ React10.createElement(
14987
15643
  TouchableOpacity8,
14988
15644
  {
14989
15645
  key: sev,
14990
15646
  style: [styles8.sevButton, severity === sev && { backgroundColor: `${color}30`, borderColor: color }],
14991
15647
  onPress: () => setSeverity(sev)
14992
15648
  },
14993
- /* @__PURE__ */ React9.createElement(Text8, { style: [styles8.sevText, severity === sev && { color }] }, sev)
14994
- )))), /* @__PURE__ */ React9.createElement(View8, { style: styles8.section }, /* @__PURE__ */ React9.createElement(Text8, { style: shared.label }, "Category (optional)"), /* @__PURE__ */ React9.createElement(CategoryPicker, { value: category, onChange: setCategory, optional: true })), /* @__PURE__ */ React9.createElement(View8, { style: styles8.section }, /* @__PURE__ */ React9.createElement(Text8, { style: shared.label }, "What went wrong?"), /* @__PURE__ */ React9.createElement(
14995
- TextInput3,
15649
+ /* @__PURE__ */ React10.createElement(Text8, { style: [styles8.sevText, severity === sev && { color }] }, sev)
15650
+ )))), /* @__PURE__ */ React10.createElement(View9, { style: styles8.section }, /* @__PURE__ */ React10.createElement(Text8, { style: shared.label }, "Category (optional)"), /* @__PURE__ */ React10.createElement(CategoryPicker, { value: category, onChange: setCategory, optional: true })), /* @__PURE__ */ React10.createElement(View9, { style: styles8.section }, /* @__PURE__ */ React10.createElement(Text8, { style: shared.label }, "What went wrong?"), /* @__PURE__ */ React10.createElement(
15651
+ TextInput4,
14996
15652
  {
14997
15653
  style: styles8.descInput,
14998
15654
  value: description,
@@ -15003,7 +15659,7 @@ function ReportScreen({ nav, prefill }) {
15003
15659
  numberOfLines: 4,
15004
15660
  textAlignVertical: "top"
15005
15661
  }
15006
- )), /* @__PURE__ */ React9.createElement(
15662
+ )), /* @__PURE__ */ React10.createElement(
15007
15663
  ImagePickerButtons,
15008
15664
  {
15009
15665
  images: images.images,
@@ -15013,42 +15669,42 @@ function ReportScreen({ nav, prefill }) {
15013
15669
  onRemove: images.removeImage,
15014
15670
  label: "Attachments (optional)"
15015
15671
  }
15016
- ), error && /* @__PURE__ */ React9.createElement(View8, { style: styles8.errorBanner }, /* @__PURE__ */ React9.createElement(Text8, { style: styles8.errorText }, error)), /* @__PURE__ */ React9.createElement(
15672
+ ), error && /* @__PURE__ */ React10.createElement(View9, { style: styles8.errorBanner }, /* @__PURE__ */ React10.createElement(Text8, { style: styles8.errorText }, error)), /* @__PURE__ */ React10.createElement(
15017
15673
  TouchableOpacity8,
15018
15674
  {
15019
15675
  style: [shared.primaryButton, styles8.retestSubmitButton, (!description.trim() || submitting || images.isUploading) && shared.primaryButtonDisabled, { marginTop: 20 }],
15020
15676
  onPress: handleSubmit,
15021
15677
  disabled: !description.trim() || submitting || images.isUploading
15022
15678
  },
15023
- /* @__PURE__ */ React9.createElement(Text8, { style: shared.primaryButtonText }, images.isUploading ? "Uploading images..." : submitting ? "Submitting..." : error ? "Retry" : "Submit Failed Retest")
15024
- )) : /* @__PURE__ */ React9.createElement(React9.Fragment, null, /* @__PURE__ */ React9.createElement(Text8, { style: shared.label }, "What are you reporting?"), /* @__PURE__ */ React9.createElement(View8, { style: styles8.typeRow }, [
15679
+ /* @__PURE__ */ React10.createElement(Text8, { style: shared.primaryButtonText }, images.isUploading ? "Uploading images..." : submitting ? "Submitting..." : error ? "Retry" : "Submit Failed Retest")
15680
+ )) : /* @__PURE__ */ React10.createElement(React10.Fragment, null, /* @__PURE__ */ React10.createElement(Text8, { style: shared.label }, "What are you reporting?"), /* @__PURE__ */ React10.createElement(View9, { style: styles8.typeRow }, [
15025
15681
  { type: "bug", label: "Bug", icon: "\u{1F41B}" },
15026
15682
  { type: "feedback", label: "Feedback", icon: "\u{1F4A1}" },
15027
15683
  { type: "suggestion", label: "Idea", icon: "\u2728" }
15028
- ].map(({ type, label, icon }) => /* @__PURE__ */ React9.createElement(
15684
+ ].map(({ type, label, icon }) => /* @__PURE__ */ React10.createElement(
15029
15685
  TouchableOpacity8,
15030
15686
  {
15031
15687
  key: type,
15032
15688
  style: [styles8.typeCard, reportType === type && styles8.typeCardActive],
15033
15689
  onPress: () => setReportType(type)
15034
15690
  },
15035
- /* @__PURE__ */ React9.createElement(Text8, { style: styles8.typeIcon }, icon),
15036
- /* @__PURE__ */ React9.createElement(Text8, { style: [styles8.typeLabel, reportType === type && styles8.typeLabelActive] }, label)
15037
- ))), isBugType && /* @__PURE__ */ React9.createElement(View8, { style: styles8.section }, /* @__PURE__ */ React9.createElement(Text8, { style: shared.label }, "Severity"), /* @__PURE__ */ React9.createElement(View8, { style: styles8.severityRow }, [
15691
+ /* @__PURE__ */ React10.createElement(Text8, { style: styles8.typeIcon }, icon),
15692
+ /* @__PURE__ */ React10.createElement(Text8, { style: [styles8.typeLabel, reportType === type && styles8.typeLabelActive] }, label)
15693
+ ))), isBugType && /* @__PURE__ */ React10.createElement(View9, { style: styles8.section }, /* @__PURE__ */ React10.createElement(Text8, { style: shared.label }, "Severity"), /* @__PURE__ */ React10.createElement(View9, { style: styles8.severityRow }, [
15038
15694
  { sev: "critical", color: "#ef4444" },
15039
15695
  { sev: "high", color: "#f97316" },
15040
15696
  { sev: "medium", color: "#eab308" },
15041
15697
  { sev: "low", color: "#6b7280" }
15042
- ].map(({ sev, color }) => /* @__PURE__ */ React9.createElement(
15698
+ ].map(({ sev, color }) => /* @__PURE__ */ React10.createElement(
15043
15699
  TouchableOpacity8,
15044
15700
  {
15045
15701
  key: sev,
15046
15702
  style: [styles8.sevButton, severity === sev && { backgroundColor: `${color}30`, borderColor: color }],
15047
15703
  onPress: () => setSeverity(sev)
15048
15704
  },
15049
- /* @__PURE__ */ React9.createElement(Text8, { style: [styles8.sevText, severity === sev && { color }] }, sev)
15050
- )))), isBugType && /* @__PURE__ */ React9.createElement(View8, { style: styles8.section }, /* @__PURE__ */ React9.createElement(Text8, { style: shared.label }, "Category (optional)"), /* @__PURE__ */ React9.createElement(CategoryPicker, { value: category, onChange: setCategory, optional: true })), /* @__PURE__ */ React9.createElement(View8, { style: styles8.section }, /* @__PURE__ */ React9.createElement(Text8, { style: shared.label }, "What happened?"), /* @__PURE__ */ React9.createElement(
15051
- TextInput3,
15705
+ /* @__PURE__ */ React10.createElement(Text8, { style: [styles8.sevText, severity === sev && { color }] }, sev)
15706
+ )))), isBugType && /* @__PURE__ */ React10.createElement(View9, { style: styles8.section }, /* @__PURE__ */ React10.createElement(Text8, { style: shared.label }, "Category (optional)"), /* @__PURE__ */ React10.createElement(CategoryPicker, { value: category, onChange: setCategory, optional: true })), /* @__PURE__ */ React10.createElement(View9, { style: styles8.section }, /* @__PURE__ */ React10.createElement(Text8, { style: shared.label }, "What happened?"), /* @__PURE__ */ React10.createElement(
15707
+ TextInput4,
15052
15708
  {
15053
15709
  style: styles8.descInput,
15054
15710
  value: description,
@@ -15059,8 +15715,8 @@ function ReportScreen({ nav, prefill }) {
15059
15715
  numberOfLines: 4,
15060
15716
  textAlignVertical: "top"
15061
15717
  }
15062
- )), isBugType && /* @__PURE__ */ React9.createElement(View8, { style: styles8.section }, /* @__PURE__ */ React9.createElement(Text8, { style: shared.label }, "Which screen?"), /* @__PURE__ */ React9.createElement(
15063
- TextInput3,
15718
+ )), isBugType && /* @__PURE__ */ React10.createElement(View9, { style: styles8.section }, /* @__PURE__ */ React10.createElement(Text8, { style: shared.label }, "Which screen?"), /* @__PURE__ */ React10.createElement(
15719
+ TextInput4,
15064
15720
  {
15065
15721
  style: styles8.screenInput,
15066
15722
  value: affectedScreen,
@@ -15068,7 +15724,7 @@ function ReportScreen({ nav, prefill }) {
15068
15724
  placeholder: "e.g. Reservations, Settings...",
15069
15725
  placeholderTextColor: colors.textMuted
15070
15726
  }
15071
- ), /* @__PURE__ */ React9.createElement(Text8, { style: styles8.screenHint }, "Which screen or area was the bug on? (optional)")), /* @__PURE__ */ React9.createElement(
15727
+ ), /* @__PURE__ */ React10.createElement(Text8, { style: styles8.screenHint }, "Which screen or area was the bug on? (optional)")), /* @__PURE__ */ React10.createElement(
15072
15728
  ImagePickerButtons,
15073
15729
  {
15074
15730
  images: images.images,
@@ -15078,17 +15734,17 @@ function ReportScreen({ nav, prefill }) {
15078
15734
  onRemove: images.removeImage,
15079
15735
  label: "Screenshots (optional)"
15080
15736
  }
15081
- ), error && /* @__PURE__ */ React9.createElement(View8, { style: styles8.errorBanner }, /* @__PURE__ */ React9.createElement(Text8, { style: styles8.errorText }, error)), /* @__PURE__ */ React9.createElement(
15737
+ ), error && /* @__PURE__ */ React10.createElement(View9, { style: styles8.errorBanner }, /* @__PURE__ */ React10.createElement(Text8, { style: styles8.errorText }, error)), /* @__PURE__ */ React10.createElement(
15082
15738
  TouchableOpacity8,
15083
15739
  {
15084
15740
  style: [shared.primaryButton, (!description.trim() || submitting || images.isUploading) && shared.primaryButtonDisabled, { marginTop: 20 }],
15085
15741
  onPress: handleSubmit,
15086
15742
  disabled: !description.trim() || submitting || images.isUploading
15087
15743
  },
15088
- /* @__PURE__ */ React9.createElement(Text8, { style: shared.primaryButtonText }, images.isUploading ? "Uploading images..." : submitting ? "Submitting..." : error ? "Retry" : "Submit Report")
15744
+ /* @__PURE__ */ React10.createElement(Text8, { style: shared.primaryButtonText }, images.isUploading ? "Uploading images..." : submitting ? "Submitting..." : error ? "Retry" : "Submit Report")
15089
15745
  )));
15090
15746
  }
15091
- var styles8 = StyleSheet9.create({
15747
+ var styles8 = StyleSheet10.create({
15092
15748
  typeRow: { flexDirection: "row", gap: 10, marginBottom: 20 },
15093
15749
  typeCard: { flex: 1, alignItems: "center", paddingVertical: 16, borderRadius: 12, backgroundColor: colors.card, borderWidth: 1, borderColor: colors.border },
15094
15750
  typeCardActive: { borderColor: colors.blue, backgroundColor: "#172554" },
@@ -15112,16 +15768,16 @@ var styles8 = StyleSheet9.create({
15112
15768
  });
15113
15769
 
15114
15770
  // src/widget/screens/ReportSuccessScreen.tsx
15115
- import React10, { useEffect as useEffect6 } from "react";
15116
- import { View as View9, Text as Text9, StyleSheet as StyleSheet10 } from "react-native";
15771
+ import React11, { useEffect as useEffect7 } from "react";
15772
+ import { View as View10, Text as Text9, StyleSheet as StyleSheet11 } from "react-native";
15117
15773
  function ReportSuccessScreen({ nav }) {
15118
- useEffect6(() => {
15774
+ useEffect7(() => {
15119
15775
  const timer = setTimeout(() => nav.reset(), 2e3);
15120
15776
  return () => clearTimeout(timer);
15121
15777
  }, [nav]);
15122
- return /* @__PURE__ */ React10.createElement(View9, { style: styles9.container }, /* @__PURE__ */ React10.createElement(Text9, { style: styles9.emoji }, "\u{1F389}"), /* @__PURE__ */ React10.createElement(Text9, { style: styles9.title }, "Report submitted!"), /* @__PURE__ */ React10.createElement(Text9, { style: styles9.subtitle }, "Thank you for your feedback"));
15778
+ return /* @__PURE__ */ React11.createElement(View10, { style: styles9.container }, /* @__PURE__ */ React11.createElement(Text9, { style: styles9.emoji }, "\u{1F389}"), /* @__PURE__ */ React11.createElement(Text9, { style: styles9.title }, "Report submitted!"), /* @__PURE__ */ React11.createElement(Text9, { style: styles9.subtitle }, "Thank you for your feedback"));
15123
15779
  }
15124
- var styles9 = StyleSheet10.create({
15780
+ var styles9 = StyleSheet11.create({
15125
15781
  container: { alignItems: "center", paddingVertical: 60 },
15126
15782
  emoji: { fontSize: 48, marginBottom: 16 },
15127
15783
  title: { fontSize: 22, fontWeight: "700", color: colors.textPrimary, marginBottom: 6 },
@@ -15129,29 +15785,38 @@ var styles9 = StyleSheet10.create({
15129
15785
  });
15130
15786
 
15131
15787
  // src/widget/screens/MessageListScreen.tsx
15132
- import React11 from "react";
15133
- import { View as View10, Text as Text10, TouchableOpacity as TouchableOpacity9, StyleSheet as StyleSheet11 } from "react-native";
15788
+ import React12 from "react";
15789
+ import { View as View11, Text as Text10, TouchableOpacity as TouchableOpacity9, StyleSheet as StyleSheet12, Linking as Linking3 } from "react-native";
15134
15790
  function MessageListScreen({ nav }) {
15135
- const { threads, unreadCount, refreshThreads } = useBugBear();
15136
- return /* @__PURE__ */ React11.createElement(View10, null, /* @__PURE__ */ React11.createElement(
15791
+ const { threads, unreadCount, refreshThreads, dashboardUrl, isLoading } = useBugBear();
15792
+ if (isLoading) return /* @__PURE__ */ React12.createElement(MessageListScreenSkeleton, null);
15793
+ return /* @__PURE__ */ React12.createElement(View11, null, /* @__PURE__ */ React12.createElement(
15137
15794
  TouchableOpacity9,
15138
15795
  {
15139
15796
  style: styles10.newMsgButton,
15140
15797
  onPress: () => nav.push({ name: "COMPOSE_MESSAGE" })
15141
15798
  },
15142
- /* @__PURE__ */ React11.createElement(Text10, { style: styles10.newMsgText }, "\u2709\uFE0F New Message")
15143
- ), threads.length === 0 ? /* @__PURE__ */ React11.createElement(View10, { style: shared.emptyState }, /* @__PURE__ */ React11.createElement(Text10, { style: shared.emptyEmoji }, "\u{1F4AC}"), /* @__PURE__ */ React11.createElement(Text10, { style: shared.emptyTitle }, "No messages yet"), /* @__PURE__ */ React11.createElement(Text10, { style: shared.emptySubtitle }, "Start a conversation or wait for messages from admins")) : /* @__PURE__ */ React11.createElement(View10, null, threads.map((thread) => /* @__PURE__ */ React11.createElement(
15799
+ /* @__PURE__ */ React12.createElement(Text10, { style: styles10.newMsgText }, "\u2709\uFE0F New Message")
15800
+ ), threads.length === 0 ? /* @__PURE__ */ React12.createElement(View11, { style: shared.emptyState }, /* @__PURE__ */ React12.createElement(Text10, { style: shared.emptyEmoji }, "\u{1F4AC}"), /* @__PURE__ */ React12.createElement(Text10, { style: shared.emptyTitle }, "No messages yet"), /* @__PURE__ */ React12.createElement(Text10, { style: shared.emptySubtitle }, "Start a conversation or wait for messages from admins")) : /* @__PURE__ */ React12.createElement(View11, null, threads.map((thread) => /* @__PURE__ */ React12.createElement(
15144
15801
  TouchableOpacity9,
15145
15802
  {
15146
15803
  key: thread.id,
15147
15804
  style: [styles10.threadItem, thread.unreadCount > 0 && styles10.threadItemUnread],
15148
15805
  onPress: () => nav.push({ name: "THREAD_DETAIL", thread })
15149
15806
  },
15150
- /* @__PURE__ */ React11.createElement(View10, { style: styles10.threadLeft }, /* @__PURE__ */ React11.createElement(Text10, { style: styles10.threadIcon }, getThreadTypeIcon(thread.threadType)), /* @__PURE__ */ React11.createElement(View10, { style: styles10.threadInfo }, /* @__PURE__ */ React11.createElement(View10, { style: styles10.threadTitleRow }, thread.isPinned && /* @__PURE__ */ React11.createElement(Text10, { style: styles10.pinIcon }, "\u{1F4CC}"), /* @__PURE__ */ React11.createElement(Text10, { style: styles10.threadSubject, numberOfLines: 1 }, thread.subject || "No subject")), thread.lastMessage && /* @__PURE__ */ React11.createElement(Text10, { style: styles10.threadPreview, numberOfLines: 1 }, thread.lastMessage.senderName, ": ", thread.lastMessage.content))),
15151
- /* @__PURE__ */ React11.createElement(View10, { style: styles10.threadRight }, /* @__PURE__ */ React11.createElement(Text10, { style: styles10.threadTime }, formatRelativeTime(thread.lastMessageAt)), thread.unreadCount > 0 && /* @__PURE__ */ React11.createElement(View10, { style: styles10.unreadBadge }, /* @__PURE__ */ React11.createElement(Text10, { style: styles10.unreadText }, thread.unreadCount)), thread.priority !== "normal" && /* @__PURE__ */ React11.createElement(View10, { style: [styles10.priorityDot, { backgroundColor: getPriorityColor(thread.priority) }] }))
15152
- ))), /* @__PURE__ */ React11.createElement(View10, { style: styles10.footer }, /* @__PURE__ */ React11.createElement(Text10, { style: styles10.footerText }, threads.length, " thread", threads.length !== 1 ? "s" : "", " \xB7 ", unreadCount, " unread"), /* @__PURE__ */ React11.createElement(TouchableOpacity9, { onPress: refreshThreads }, /* @__PURE__ */ React11.createElement(Text10, { style: styles10.refreshText }, "\u21BB Refresh"))));
15807
+ /* @__PURE__ */ React12.createElement(View11, { style: styles10.threadLeft }, /* @__PURE__ */ React12.createElement(Text10, { style: styles10.threadIcon }, getThreadTypeIcon(thread.threadType)), /* @__PURE__ */ React12.createElement(View11, { style: styles10.threadInfo }, /* @__PURE__ */ React12.createElement(View11, { style: styles10.threadTitleRow }, thread.isPinned && /* @__PURE__ */ React12.createElement(Text10, { style: styles10.pinIcon }, "\u{1F4CC}"), /* @__PURE__ */ React12.createElement(Text10, { style: styles10.threadSubject, numberOfLines: 1 }, thread.subject || "No subject")), thread.lastMessage && /* @__PURE__ */ React12.createElement(Text10, { style: styles10.threadPreview, numberOfLines: 1 }, thread.lastMessage.senderName, ": ", thread.lastMessage.content))),
15808
+ /* @__PURE__ */ React12.createElement(View11, { style: styles10.threadRight }, /* @__PURE__ */ React12.createElement(Text10, { style: styles10.threadTime }, formatRelativeTime(thread.lastMessageAt)), thread.unreadCount > 0 && /* @__PURE__ */ React12.createElement(View11, { style: styles10.unreadBadge }, /* @__PURE__ */ React12.createElement(Text10, { style: styles10.unreadText }, thread.unreadCount)), thread.priority !== "normal" && /* @__PURE__ */ React12.createElement(View11, { style: [styles10.priorityDot, { backgroundColor: getPriorityColor(thread.priority) }] }))
15809
+ ))), dashboardUrl && /* @__PURE__ */ React12.createElement(
15810
+ TouchableOpacity9,
15811
+ {
15812
+ style: styles10.dashboardLink,
15813
+ onPress: () => Linking3.openURL(`${dashboardUrl}/discussions`),
15814
+ activeOpacity: 0.7
15815
+ },
15816
+ /* @__PURE__ */ React12.createElement(Text10, { style: styles10.dashboardLinkText }, "\u{1F310}", " View on Dashboard ", "\u2192")
15817
+ ), /* @__PURE__ */ React12.createElement(View11, { style: styles10.footer }, /* @__PURE__ */ React12.createElement(Text10, { style: styles10.footerText }, threads.length, " thread", threads.length !== 1 ? "s" : "", " \xB7 ", unreadCount, " unread"), /* @__PURE__ */ React12.createElement(TouchableOpacity9, { onPress: refreshThreads }, /* @__PURE__ */ React12.createElement(Text10, { style: styles10.refreshText }, "\u21BB Refresh"))));
15153
15818
  }
15154
- var styles10 = StyleSheet11.create({
15819
+ var styles10 = StyleSheet12.create({
15155
15820
  newMsgButton: { backgroundColor: colors.blue, paddingVertical: 12, borderRadius: 12, alignItems: "center", marginBottom: 16 },
15156
15821
  newMsgText: { fontSize: 15, fontWeight: "600", color: "#fff" },
15157
15822
  threadItem: { flexDirection: "row", justifyContent: "space-between", paddingVertical: 12, paddingHorizontal: 12, borderRadius: 10, marginBottom: 4, backgroundColor: colors.card },
@@ -15168,14 +15833,16 @@ var styles10 = StyleSheet11.create({
15168
15833
  unreadBadge: { backgroundColor: colors.blue, borderRadius: 10, minWidth: 20, height: 20, justifyContent: "center", alignItems: "center", paddingHorizontal: 6 },
15169
15834
  unreadText: { fontSize: 11, fontWeight: "bold", color: "#fff" },
15170
15835
  priorityDot: { width: 8, height: 8, borderRadius: 4 },
15171
- footer: { flexDirection: "row", justifyContent: "space-between", alignItems: "center", paddingTop: 12, paddingHorizontal: 4 },
15836
+ dashboardLink: { alignItems: "center", paddingTop: 12 },
15837
+ dashboardLinkText: { fontSize: 13, fontWeight: "500", color: colors.blue },
15838
+ footer: { flexDirection: "row", justifyContent: "space-between", alignItems: "center", paddingTop: 8, paddingHorizontal: 4 },
15172
15839
  footerText: { fontSize: 12, color: colors.textMuted },
15173
15840
  refreshText: { fontSize: 13, color: colors.blue }
15174
15841
  });
15175
15842
 
15176
15843
  // src/widget/screens/ThreadDetailScreen.tsx
15177
- import React12, { useState as useState8, useEffect as useEffect7 } from "react";
15178
- import { View as View11, Text as Text11, TouchableOpacity as TouchableOpacity10, TextInput as TextInput4, StyleSheet as StyleSheet12, Image as Image2 } from "react-native";
15844
+ import React13, { useState as useState8, useEffect as useEffect8 } from "react";
15845
+ import { View as View12, Text as Text11, TouchableOpacity as TouchableOpacity10, TextInput as TextInput5, StyleSheet as StyleSheet13, Image as Image2 } from "react-native";
15179
15846
  function ThreadDetailScreen({ thread, nav }) {
15180
15847
  const { getThreadMessages, sendMessage, markAsRead, uploadImage } = useBugBear();
15181
15848
  const [messages, setMessages] = useState8([]);
@@ -15184,7 +15851,7 @@ function ThreadDetailScreen({ thread, nav }) {
15184
15851
  const [sending, setSending] = useState8(false);
15185
15852
  const [sendError, setSendError] = useState8(false);
15186
15853
  const replyImages = useImageAttachments(uploadImage, 3, "discussion-attachments");
15187
- useEffect7(() => {
15854
+ useEffect8(() => {
15188
15855
  let cancelled = false;
15189
15856
  setLoading(true);
15190
15857
  (async () => {
@@ -15229,18 +15896,18 @@ function ThreadDetailScreen({ thread, nav }) {
15229
15896
  }
15230
15897
  setSending(false);
15231
15898
  };
15232
- return /* @__PURE__ */ React12.createElement(View11, { style: styles11.container }, /* @__PURE__ */ React12.createElement(View11, { style: styles11.header }, /* @__PURE__ */ React12.createElement(Text11, { style: styles11.headerIcon }, getThreadTypeIcon(thread.threadType)), /* @__PURE__ */ React12.createElement(Text11, { style: styles11.headerSubject, numberOfLines: 2 }, thread.subject || "No subject")), loading ? /* @__PURE__ */ React12.createElement(View11, { style: styles11.loadingContainer }, /* @__PURE__ */ React12.createElement(Text11, { style: styles11.loadingText }, "Loading messages...")) : /* @__PURE__ */ React12.createElement(View11, { style: styles11.messagesContainer }, messages.map((msg) => /* @__PURE__ */ React12.createElement(
15233
- View11,
15899
+ return /* @__PURE__ */ React13.createElement(View12, { style: styles11.container }, /* @__PURE__ */ React13.createElement(View12, { style: styles11.header }, /* @__PURE__ */ React13.createElement(Text11, { style: styles11.headerIcon }, getThreadTypeIcon(thread.threadType)), /* @__PURE__ */ React13.createElement(Text11, { style: styles11.headerSubject, numberOfLines: 2 }, thread.subject || "No subject")), loading ? /* @__PURE__ */ React13.createElement(View12, { style: styles11.loadingContainer }, /* @__PURE__ */ React13.createElement(Text11, { style: styles11.loadingText }, "Loading messages...")) : /* @__PURE__ */ React13.createElement(View12, { style: styles11.messagesContainer }, messages.map((msg) => /* @__PURE__ */ React13.createElement(
15900
+ View12,
15234
15901
  {
15235
15902
  key: msg.id,
15236
15903
  style: [styles11.bubble, msg.senderType === "tester" ? styles11.bubbleTester : styles11.bubbleAdmin]
15237
15904
  },
15238
- /* @__PURE__ */ React12.createElement(Text11, { style: [styles11.sender, msg.senderType === "tester" && styles11.senderTester] }, msg.senderType === "tester" ? "You" : msg.senderName),
15239
- /* @__PURE__ */ React12.createElement(Text11, { style: [styles11.content, msg.senderType === "tester" && styles11.contentTester] }, msg.content),
15240
- msg.attachments && msg.attachments.length > 0 && /* @__PURE__ */ React12.createElement(View11, { style: styles11.attachments }, msg.attachments.filter((a) => a.type === "image").map((att, idx) => /* @__PURE__ */ React12.createElement(Image2, { key: idx, source: { uri: att.url }, style: styles11.attachmentImage, resizeMode: "cover" }))),
15241
- /* @__PURE__ */ React12.createElement(Text11, { style: [styles11.time, msg.senderType === "tester" && styles11.timeTester] }, formatMessageTime(msg.createdAt))
15242
- ))), sendError && /* @__PURE__ */ React12.createElement(View11, { style: styles11.errorBar }, /* @__PURE__ */ React12.createElement(Text11, { style: styles11.errorText }, "Failed to send. Tap Send to retry.")), replyImages.images.length > 0 && /* @__PURE__ */ React12.createElement(View11, { style: styles11.replyPreview }, /* @__PURE__ */ React12.createElement(ImagePreviewStrip, { images: replyImages.images, onRemove: replyImages.removeImage })), /* @__PURE__ */ React12.createElement(View11, { style: styles11.composer }, IMAGE_PICKER_AVAILABLE && /* @__PURE__ */ React12.createElement(TouchableOpacity10, { style: styles11.attachBtn, onPress: replyImages.pickFromGallery, disabled: replyImages.images.length >= 3 }, /* @__PURE__ */ React12.createElement(Text11, { style: styles11.attachBtnText }, "\u{1F4CE}")), /* @__PURE__ */ React12.createElement(
15243
- TextInput4,
15905
+ /* @__PURE__ */ React13.createElement(Text11, { style: [styles11.sender, msg.senderType === "tester" && styles11.senderTester] }, msg.senderType === "tester" ? "You" : msg.senderName),
15906
+ /* @__PURE__ */ React13.createElement(Text11, { style: [styles11.content, msg.senderType === "tester" && styles11.contentTester] }, msg.content),
15907
+ msg.attachments && msg.attachments.length > 0 && /* @__PURE__ */ React13.createElement(View12, { style: styles11.attachments }, msg.attachments.filter((a) => a.type === "image").map((att, idx) => /* @__PURE__ */ React13.createElement(Image2, { key: idx, source: { uri: att.url }, style: styles11.attachmentImage, resizeMode: "cover" }))),
15908
+ /* @__PURE__ */ React13.createElement(Text11, { style: [styles11.time, msg.senderType === "tester" && styles11.timeTester] }, formatMessageTime(msg.createdAt))
15909
+ ))), sendError && /* @__PURE__ */ React13.createElement(View12, { style: styles11.errorBar }, /* @__PURE__ */ React13.createElement(Text11, { style: styles11.errorText }, "Failed to send. Tap Send to retry.")), replyImages.images.length > 0 && /* @__PURE__ */ React13.createElement(View12, { style: styles11.replyPreview }, /* @__PURE__ */ React13.createElement(ImagePreviewStrip, { images: replyImages.images, onRemove: replyImages.removeImage })), /* @__PURE__ */ React13.createElement(View12, { style: styles11.composer }, IMAGE_PICKER_AVAILABLE && /* @__PURE__ */ React13.createElement(TouchableOpacity10, { style: styles11.attachBtn, onPress: replyImages.pickFromGallery, disabled: replyImages.images.length >= 3 }, /* @__PURE__ */ React13.createElement(Text11, { style: styles11.attachBtnText }, "\u{1F4CE}")), /* @__PURE__ */ React13.createElement(
15910
+ TextInput5,
15244
15911
  {
15245
15912
  style: styles11.replyInput,
15246
15913
  value: replyText,
@@ -15250,17 +15917,17 @@ function ThreadDetailScreen({ thread, nav }) {
15250
15917
  multiline: true,
15251
15918
  maxLength: 1e3
15252
15919
  }
15253
- ), /* @__PURE__ */ React12.createElement(
15920
+ ), /* @__PURE__ */ React13.createElement(
15254
15921
  TouchableOpacity10,
15255
15922
  {
15256
15923
  style: [styles11.sendBtn, (!replyText.trim() && replyImages.images.length === 0 || sending || replyImages.isUploading) && styles11.sendBtnDisabled],
15257
15924
  onPress: handleSend,
15258
15925
  disabled: !replyText.trim() && replyImages.images.length === 0 || sending || replyImages.isUploading
15259
15926
  },
15260
- /* @__PURE__ */ React12.createElement(Text11, { style: styles11.sendBtnText }, sending ? "..." : "Send")
15927
+ /* @__PURE__ */ React13.createElement(Text11, { style: styles11.sendBtnText }, sending ? "..." : "Send")
15261
15928
  )));
15262
15929
  }
15263
- var styles11 = StyleSheet12.create({
15930
+ var styles11 = StyleSheet13.create({
15264
15931
  container: { flex: 1 },
15265
15932
  header: { flexDirection: "row", alignItems: "center", gap: 8, marginBottom: 16, paddingBottom: 12, borderBottomWidth: 1, borderBottomColor: colors.border },
15266
15933
  headerIcon: { fontSize: 20 },
@@ -15292,8 +15959,8 @@ var styles11 = StyleSheet12.create({
15292
15959
  });
15293
15960
 
15294
15961
  // src/widget/screens/ComposeMessageScreen.tsx
15295
- import React13, { useState as useState9 } from "react";
15296
- import { View as View12, Text as Text12, TextInput as TextInput5, TouchableOpacity as TouchableOpacity11, StyleSheet as StyleSheet13 } from "react-native";
15962
+ import React14, { useState as useState9 } from "react";
15963
+ import { View as View13, Text as Text12, TextInput as TextInput6, TouchableOpacity as TouchableOpacity11, StyleSheet as StyleSheet14 } from "react-native";
15297
15964
  function ComposeMessageScreen({ nav }) {
15298
15965
  const { createThread, uploadImage } = useBugBear();
15299
15966
  const [subject, setSubject] = useState9("");
@@ -15314,8 +15981,8 @@ function ComposeMessageScreen({ nav }) {
15314
15981
  nav.pop();
15315
15982
  }
15316
15983
  };
15317
- return /* @__PURE__ */ React13.createElement(View12, null, /* @__PURE__ */ React13.createElement(View12, { style: styles12.header }, /* @__PURE__ */ React13.createElement(Text12, { style: styles12.title }, "New Message"), /* @__PURE__ */ React13.createElement(Text12, { style: styles12.subtitle }, "Send a message to the QA team")), /* @__PURE__ */ React13.createElement(View12, { style: styles12.form }, /* @__PURE__ */ React13.createElement(Text12, { style: shared.label }, "Subject"), /* @__PURE__ */ React13.createElement(
15318
- TextInput5,
15984
+ return /* @__PURE__ */ React14.createElement(View13, null, /* @__PURE__ */ React14.createElement(View13, { style: styles12.header }, /* @__PURE__ */ React14.createElement(Text12, { style: styles12.title }, "New Message"), /* @__PURE__ */ React14.createElement(Text12, { style: styles12.subtitle }, "Send a message to the QA team")), /* @__PURE__ */ React14.createElement(View13, { style: styles12.form }, /* @__PURE__ */ React14.createElement(Text12, { style: shared.label }, "Subject"), /* @__PURE__ */ React14.createElement(
15985
+ TextInput6,
15319
15986
  {
15320
15987
  style: styles12.subjectInput,
15321
15988
  value: subject,
@@ -15324,8 +15991,8 @@ function ComposeMessageScreen({ nav }) {
15324
15991
  placeholderTextColor: colors.textMuted,
15325
15992
  maxLength: 100
15326
15993
  }
15327
- ), /* @__PURE__ */ React13.createElement(Text12, { style: [shared.label, { marginTop: 16 }] }, "Message"), /* @__PURE__ */ React13.createElement(
15328
- TextInput5,
15994
+ ), /* @__PURE__ */ React14.createElement(Text12, { style: [shared.label, { marginTop: 16 }] }, "Message"), /* @__PURE__ */ React14.createElement(
15995
+ TextInput6,
15329
15996
  {
15330
15997
  style: styles12.messageInput,
15331
15998
  value: message,
@@ -15337,7 +16004,7 @@ function ComposeMessageScreen({ nav }) {
15337
16004
  textAlignVertical: "top",
15338
16005
  maxLength: 2e3
15339
16006
  }
15340
- ), /* @__PURE__ */ React13.createElement(
16007
+ ), /* @__PURE__ */ React14.createElement(
15341
16008
  ImagePickerButtons,
15342
16009
  {
15343
16010
  images: images.images,
@@ -15346,17 +16013,17 @@ function ComposeMessageScreen({ nav }) {
15346
16013
  onPickCamera: images.pickFromCamera,
15347
16014
  onRemove: images.removeImage
15348
16015
  }
15349
- ), /* @__PURE__ */ React13.createElement(
16016
+ ), /* @__PURE__ */ React14.createElement(
15350
16017
  TouchableOpacity11,
15351
16018
  {
15352
16019
  style: [shared.primaryButton, (!subject.trim() || !message.trim() || sending || images.isUploading) && shared.primaryButtonDisabled, { marginTop: 20 }],
15353
16020
  onPress: handleSend,
15354
16021
  disabled: !subject.trim() || !message.trim() || sending || images.isUploading
15355
16022
  },
15356
- /* @__PURE__ */ React13.createElement(Text12, { style: shared.primaryButtonText }, images.isUploading ? "Uploading..." : sending ? "Sending..." : "Send Message")
16023
+ /* @__PURE__ */ React14.createElement(Text12, { style: shared.primaryButtonText }, images.isUploading ? "Uploading..." : sending ? "Sending..." : "Send Message")
15357
16024
  )));
15358
16025
  }
15359
- var styles12 = StyleSheet13.create({
16026
+ var styles12 = StyleSheet14.create({
15360
16027
  header: { marginBottom: 20 },
15361
16028
  title: { fontSize: 20, fontWeight: "600", color: colors.textPrimary, marginBottom: 4 },
15362
16029
  subtitle: { fontSize: 14, color: colors.textMuted },
@@ -15366,8 +16033,8 @@ var styles12 = StyleSheet13.create({
15366
16033
  });
15367
16034
 
15368
16035
  // src/widget/screens/ProfileScreen.tsx
15369
- import React14, { useState as useState10, useEffect as useEffect8 } from "react";
15370
- import { View as View13, Text as Text13, TouchableOpacity as TouchableOpacity12, TextInput as TextInput6, StyleSheet as StyleSheet14 } from "react-native";
16036
+ import React15, { useState as useState10, useEffect as useEffect9 } from "react";
16037
+ import { View as View14, Text as Text13, TouchableOpacity as TouchableOpacity12, TextInput as TextInput7, StyleSheet as StyleSheet15 } from "react-native";
15371
16038
  function ProfileScreen({ nav }) {
15372
16039
  const { testerInfo, assignments, updateTesterProfile, refreshTesterInfo } = useBugBear();
15373
16040
  const [editing, setEditing] = useState10(false);
@@ -15379,7 +16046,7 @@ function ProfileScreen({ nav }) {
15379
16046
  const [saved, setSaved] = useState10(false);
15380
16047
  const [showDetails, setShowDetails] = useState10(false);
15381
16048
  const completedCount = assignments.filter((a) => a.status === "passed" || a.status === "failed").length;
15382
- useEffect8(() => {
16049
+ useEffect9(() => {
15383
16050
  if (testerInfo) {
15384
16051
  setName(testerInfo.name);
15385
16052
  setAdditionalEmails(testerInfo.additionalEmails || []);
@@ -15414,17 +16081,17 @@ function ProfileScreen({ nav }) {
15414
16081
  }
15415
16082
  };
15416
16083
  if (saved) {
15417
- return /* @__PURE__ */ React14.createElement(View13, { style: shared.emptyState }, /* @__PURE__ */ React14.createElement(Text13, { style: shared.emptyEmoji }, "\u2705"), /* @__PURE__ */ React14.createElement(Text13, { style: shared.emptyTitle }, "Profile saved!"));
16084
+ return /* @__PURE__ */ React15.createElement(View14, { style: shared.emptyState }, /* @__PURE__ */ React15.createElement(Text13, { style: shared.emptyEmoji }, "\u2705"), /* @__PURE__ */ React15.createElement(Text13, { style: shared.emptyTitle }, "Profile saved!"));
15418
16085
  }
15419
16086
  if (!testerInfo) {
15420
- return /* @__PURE__ */ React14.createElement(View13, { style: shared.emptyState }, /* @__PURE__ */ React14.createElement(Text13, { style: shared.emptyEmoji }, "\u{1F464}"), /* @__PURE__ */ React14.createElement(Text13, { style: shared.emptyTitle }, "No profile found"));
16087
+ return /* @__PURE__ */ React15.createElement(View14, { style: shared.emptyState }, /* @__PURE__ */ React15.createElement(Text13, { style: shared.emptyEmoji }, "\u{1F464}"), /* @__PURE__ */ React15.createElement(Text13, { style: shared.emptyTitle }, "No profile found"));
15421
16088
  }
15422
16089
  if (editing) {
15423
- return /* @__PURE__ */ React14.createElement(View13, null, /* @__PURE__ */ React14.createElement(View13, { style: styles13.editHeader }, /* @__PURE__ */ React14.createElement(Text13, { style: styles13.editTitle }, "Edit Profile"), /* @__PURE__ */ React14.createElement(TouchableOpacity12, { onPress: () => {
16090
+ return /* @__PURE__ */ React15.createElement(View14, null, /* @__PURE__ */ React15.createElement(View14, { style: styles13.editHeader }, /* @__PURE__ */ React15.createElement(Text13, { style: styles13.editTitle }, "Edit Profile"), /* @__PURE__ */ React15.createElement(TouchableOpacity12, { onPress: () => {
15424
16091
  setEditing(false);
15425
16092
  setNewEmailInput("");
15426
- } }, /* @__PURE__ */ React14.createElement(Text13, { style: styles13.cancelText }, "Cancel"))), /* @__PURE__ */ React14.createElement(View13, { style: styles13.field }, /* @__PURE__ */ React14.createElement(Text13, { style: shared.label }, "Name"), /* @__PURE__ */ React14.createElement(TextInput6, { style: styles13.input, value: name, onChangeText: setName, placeholder: "Your name", placeholderTextColor: colors.textMuted })), /* @__PURE__ */ React14.createElement(View13, { style: styles13.field }, /* @__PURE__ */ React14.createElement(Text13, { style: shared.label }, "Primary Email"), /* @__PURE__ */ React14.createElement(Text13, { style: styles13.emailFixed }, testerInfo.email)), /* @__PURE__ */ React14.createElement(View13, { style: styles13.field }, /* @__PURE__ */ React14.createElement(Text13, { style: shared.label }, "Additional Emails"), additionalEmails.map((email) => /* @__PURE__ */ React14.createElement(View13, { key: email, style: styles13.emailRow }, /* @__PURE__ */ React14.createElement(Text13, { style: styles13.emailText }, email), /* @__PURE__ */ React14.createElement(TouchableOpacity12, { onPress: () => setAdditionalEmails(additionalEmails.filter((e) => e !== email)) }, /* @__PURE__ */ React14.createElement(Text13, { style: styles13.removeEmail }, "\u2715")))), /* @__PURE__ */ React14.createElement(View13, { style: styles13.addEmailRow }, /* @__PURE__ */ React14.createElement(
15427
- TextInput6,
16093
+ } }, /* @__PURE__ */ React15.createElement(Text13, { style: styles13.cancelText }, "Cancel"))), /* @__PURE__ */ React15.createElement(View14, { style: styles13.field }, /* @__PURE__ */ React15.createElement(Text13, { style: shared.label }, "Name"), /* @__PURE__ */ React15.createElement(TextInput7, { style: styles13.input, value: name, onChangeText: setName, placeholder: "Your name", placeholderTextColor: colors.textMuted })), /* @__PURE__ */ React15.createElement(View14, { style: styles13.field }, /* @__PURE__ */ React15.createElement(Text13, { style: shared.label }, "Primary Email"), /* @__PURE__ */ React15.createElement(Text13, { style: styles13.emailFixed }, testerInfo.email)), /* @__PURE__ */ React15.createElement(View14, { style: styles13.field }, /* @__PURE__ */ React15.createElement(Text13, { style: shared.label }, "Additional Emails"), additionalEmails.map((email) => /* @__PURE__ */ React15.createElement(View14, { key: email, style: styles13.emailRow }, /* @__PURE__ */ React15.createElement(Text13, { style: styles13.emailText }, email), /* @__PURE__ */ React15.createElement(TouchableOpacity12, { onPress: () => setAdditionalEmails(additionalEmails.filter((e) => e !== email)) }, /* @__PURE__ */ React15.createElement(Text13, { style: styles13.removeEmail }, "\u2715")))), /* @__PURE__ */ React15.createElement(View14, { style: styles13.addEmailRow }, /* @__PURE__ */ React15.createElement(
16094
+ TextInput7,
15428
16095
  {
15429
16096
  style: [styles13.input, { flex: 1, marginRight: 8 }],
15430
16097
  value: newEmailInput,
@@ -15434,26 +16101,26 @@ function ProfileScreen({ nav }) {
15434
16101
  keyboardType: "email-address",
15435
16102
  autoCapitalize: "none"
15436
16103
  }
15437
- ), /* @__PURE__ */ React14.createElement(TouchableOpacity12, { style: styles13.addButton, onPress: handleAddEmail }, /* @__PURE__ */ React14.createElement(Text13, { style: styles13.addButtonText }, "Add")))), /* @__PURE__ */ React14.createElement(View13, { style: styles13.field }, /* @__PURE__ */ React14.createElement(Text13, { style: shared.label }, "Testing Platforms"), /* @__PURE__ */ React14.createElement(View13, { 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__ */ React14.createElement(
16104
+ ), /* @__PURE__ */ React15.createElement(TouchableOpacity12, { style: styles13.addButton, onPress: handleAddEmail }, /* @__PURE__ */ React15.createElement(Text13, { style: styles13.addButtonText }, "Add")))), /* @__PURE__ */ React15.createElement(View14, { style: styles13.field }, /* @__PURE__ */ React15.createElement(Text13, { style: shared.label }, "Testing Platforms"), /* @__PURE__ */ React15.createElement(View14, { 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__ */ React15.createElement(
15438
16105
  TouchableOpacity12,
15439
16106
  {
15440
16107
  key,
15441
16108
  style: [styles13.platformBtn, platforms.includes(key) && styles13.platformBtnActive],
15442
16109
  onPress: () => setPlatforms((prev) => prev.includes(key) ? prev.filter((p) => p !== key) : [...prev, key])
15443
16110
  },
15444
- /* @__PURE__ */ React14.createElement(Text13, { style: [styles13.platformText, platforms.includes(key) && styles13.platformTextActive] }, label)
15445
- )))), /* @__PURE__ */ React14.createElement(TouchableOpacity12, { style: [shared.primaryButton, { marginTop: 20 }], onPress: handleSave, disabled: saving }, /* @__PURE__ */ React14.createElement(Text13, { style: shared.primaryButtonText }, saving ? "Saving..." : "Save Profile")));
16111
+ /* @__PURE__ */ React15.createElement(Text13, { style: [styles13.platformText, platforms.includes(key) && styles13.platformTextActive] }, label)
16112
+ )))), /* @__PURE__ */ React15.createElement(TouchableOpacity12, { style: [shared.primaryButton, { marginTop: 20 }], onPress: handleSave, disabled: saving }, /* @__PURE__ */ React15.createElement(Text13, { style: shared.primaryButtonText }, saving ? "Saving..." : "Save Profile")));
15446
16113
  }
15447
- return /* @__PURE__ */ React14.createElement(View13, null, /* @__PURE__ */ React14.createElement(View13, { style: styles13.profileCard }, /* @__PURE__ */ React14.createElement(View13, { style: styles13.avatar }, /* @__PURE__ */ React14.createElement(Text13, { style: styles13.avatarText }, testerInfo.name.charAt(0).toUpperCase())), /* @__PURE__ */ React14.createElement(Text13, { style: styles13.profileName }, testerInfo.name), /* @__PURE__ */ React14.createElement(Text13, { style: styles13.profileEmail }, testerInfo.email)), /* @__PURE__ */ React14.createElement(View13, { style: styles13.statsRow }, /* @__PURE__ */ React14.createElement(View13, { style: styles13.statItem }, /* @__PURE__ */ React14.createElement(Text13, { style: styles13.statNumber }, completedCount), /* @__PURE__ */ React14.createElement(Text13, { style: styles13.statLabel }, "Completed")), /* @__PURE__ */ React14.createElement(View13, { style: styles13.statDivider }), /* @__PURE__ */ React14.createElement(View13, { style: styles13.statItem }, /* @__PURE__ */ React14.createElement(Text13, { style: styles13.statNumber }, assignments.length), /* @__PURE__ */ React14.createElement(Text13, { style: styles13.statLabel }, "Total Assigned"))), /* @__PURE__ */ React14.createElement(TouchableOpacity12, { onPress: () => setShowDetails(!showDetails), style: styles13.detailsToggle }, /* @__PURE__ */ React14.createElement(Text13, { style: styles13.detailsToggleText }, showDetails ? "\u25BC" : "\u25B6", " Details")), showDetails && /* @__PURE__ */ React14.createElement(View13, { style: styles13.detailsSection }, additionalEmails.length > 0 && /* @__PURE__ */ React14.createElement(View13, { style: styles13.detailBlock }, /* @__PURE__ */ React14.createElement(Text13, { style: styles13.detailLabel }, "Additional Emails"), additionalEmails.map((e) => /* @__PURE__ */ React14.createElement(Text13, { key: e, style: styles13.detailValue }, e))), platforms.length > 0 && /* @__PURE__ */ React14.createElement(View13, { style: styles13.detailBlock }, /* @__PURE__ */ React14.createElement(Text13, { style: styles13.detailLabel }, "Platforms"), /* @__PURE__ */ React14.createElement(View13, { style: styles13.platformTags }, platforms.map((p) => /* @__PURE__ */ React14.createElement(View13, { key: p, style: styles13.platformTag }, /* @__PURE__ */ React14.createElement(Text13, { style: styles13.platformTagText }, p === "ios" ? "\u{1F4F1} iOS" : p === "android" ? "\u{1F916} Android" : "\u{1F310} Web")))))), /* @__PURE__ */ React14.createElement(
16114
+ return /* @__PURE__ */ React15.createElement(View14, null, /* @__PURE__ */ React15.createElement(View14, { style: styles13.profileCard }, /* @__PURE__ */ React15.createElement(View14, { style: styles13.avatar }, /* @__PURE__ */ React15.createElement(Text13, { style: styles13.avatarText }, testerInfo.name.charAt(0).toUpperCase())), /* @__PURE__ */ React15.createElement(Text13, { style: styles13.profileName }, testerInfo.name), /* @__PURE__ */ React15.createElement(Text13, { style: styles13.profileEmail }, testerInfo.email)), /* @__PURE__ */ React15.createElement(View14, { style: styles13.statsRow }, /* @__PURE__ */ React15.createElement(View14, { style: styles13.statItem }, /* @__PURE__ */ React15.createElement(Text13, { style: styles13.statNumber }, completedCount), /* @__PURE__ */ React15.createElement(Text13, { style: styles13.statLabel }, "Completed")), /* @__PURE__ */ React15.createElement(View14, { style: styles13.statDivider }), /* @__PURE__ */ React15.createElement(View14, { style: styles13.statItem }, /* @__PURE__ */ React15.createElement(Text13, { style: styles13.statNumber }, assignments.length), /* @__PURE__ */ React15.createElement(Text13, { style: styles13.statLabel }, "Total Assigned"))), /* @__PURE__ */ React15.createElement(TouchableOpacity12, { onPress: () => setShowDetails(!showDetails), style: styles13.detailsToggle }, /* @__PURE__ */ React15.createElement(Text13, { style: styles13.detailsToggleText }, showDetails ? "\u25BC" : "\u25B6", " Details")), showDetails && /* @__PURE__ */ React15.createElement(View14, { style: styles13.detailsSection }, additionalEmails.length > 0 && /* @__PURE__ */ React15.createElement(View14, { style: styles13.detailBlock }, /* @__PURE__ */ React15.createElement(Text13, { style: styles13.detailLabel }, "Additional Emails"), additionalEmails.map((e) => /* @__PURE__ */ React15.createElement(Text13, { key: e, style: styles13.detailValue }, e))), platforms.length > 0 && /* @__PURE__ */ React15.createElement(View14, { style: styles13.detailBlock }, /* @__PURE__ */ React15.createElement(Text13, { style: styles13.detailLabel }, "Platforms"), /* @__PURE__ */ React15.createElement(View14, { style: styles13.platformTags }, platforms.map((p) => /* @__PURE__ */ React15.createElement(View14, { key: p, style: styles13.platformTag }, /* @__PURE__ */ React15.createElement(Text13, { style: styles13.platformTagText }, p === "ios" ? "\u{1F4F1} iOS" : p === "android" ? "\u{1F916} Android" : "\u{1F310} Web")))))), /* @__PURE__ */ React15.createElement(
15448
16115
  TouchableOpacity12,
15449
16116
  {
15450
16117
  style: [shared.primaryButton, { marginTop: 20 }],
15451
16118
  onPress: () => setEditing(true)
15452
16119
  },
15453
- /* @__PURE__ */ React14.createElement(Text13, { style: shared.primaryButtonText }, "Edit Profile")
16120
+ /* @__PURE__ */ React15.createElement(Text13, { style: shared.primaryButtonText }, "Edit Profile")
15454
16121
  ));
15455
16122
  }
15456
- var styles13 = StyleSheet14.create({
16123
+ var styles13 = StyleSheet15.create({
15457
16124
  profileCard: { alignItems: "center", backgroundColor: colors.card, borderRadius: 16, padding: 24, marginBottom: 16 },
15458
16125
  avatar: { width: 64, height: 64, borderRadius: 32, backgroundColor: colors.blue, justifyContent: "center", alignItems: "center", marginBottom: 12 },
15459
16126
  avatarText: { fontSize: 28, fontWeight: "700", color: "#fff" },
@@ -15494,8 +16161,8 @@ var styles13 = StyleSheet14.create({
15494
16161
  });
15495
16162
 
15496
16163
  // src/widget/screens/IssueListScreen.tsx
15497
- import React15, { useState as useState11, useEffect as useEffect9 } from "react";
15498
- import { View as View14, Text as Text14, TouchableOpacity as TouchableOpacity13, StyleSheet as StyleSheet15, ActivityIndicator as ActivityIndicator2 } from "react-native";
16164
+ import React16, { useState as useState11, useEffect as useEffect10 } from "react";
16165
+ import { View as View15, Text as Text14, TouchableOpacity as TouchableOpacity13, StyleSheet as StyleSheet16 } from "react-native";
15499
16166
  var CATEGORY_CONFIG = {
15500
16167
  open: { label: "Open Issues", accent: "#f97316", emptyIcon: "\u2705", emptyText: "No open issues" },
15501
16168
  done: { label: "Done", accent: "#22c55e", emptyIcon: "\u{1F389}", emptyText: "No completed issues yet" },
@@ -15512,7 +16179,7 @@ function IssueListScreen({ nav, category }) {
15512
16179
  const [issues, setIssues] = useState11([]);
15513
16180
  const [loading, setLoading] = useState11(true);
15514
16181
  const config = CATEGORY_CONFIG[category];
15515
- useEffect9(() => {
16182
+ useEffect10(() => {
15516
16183
  let cancelled = false;
15517
16184
  setLoading(true);
15518
16185
  (async () => {
@@ -15538,12 +16205,12 @@ function IssueListScreen({ nav, category }) {
15538
16205
  };
15539
16206
  }, [client, category]);
15540
16207
  if (loading) {
15541
- return /* @__PURE__ */ React15.createElement(View14, { style: styles14.emptyContainer }, /* @__PURE__ */ React15.createElement(ActivityIndicator2, { size: "small", color: colors.textMuted }), /* @__PURE__ */ React15.createElement(Text14, { style: styles14.emptyText }, "Loading..."));
16208
+ return /* @__PURE__ */ React16.createElement(IssueListScreenSkeleton, null);
15542
16209
  }
15543
16210
  if (issues.length === 0) {
15544
- return /* @__PURE__ */ React15.createElement(View14, { style: styles14.emptyContainer }, /* @__PURE__ */ React15.createElement(Text14, { style: styles14.emptyIcon }, config.emptyIcon), /* @__PURE__ */ React15.createElement(Text14, { style: styles14.emptyText }, config.emptyText));
16211
+ return /* @__PURE__ */ React16.createElement(View15, { style: styles14.emptyContainer }, /* @__PURE__ */ React16.createElement(Text14, { style: styles14.emptyIcon }, config.emptyIcon), /* @__PURE__ */ React16.createElement(Text14, { style: styles14.emptyText }, config.emptyText));
15545
16212
  }
15546
- return /* @__PURE__ */ React15.createElement(View14, null, issues.map((issue) => /* @__PURE__ */ React15.createElement(
16213
+ return /* @__PURE__ */ React16.createElement(View15, null, issues.map((issue) => /* @__PURE__ */ React16.createElement(
15547
16214
  TouchableOpacity13,
15548
16215
  {
15549
16216
  key: issue.id,
@@ -15551,13 +16218,13 @@ function IssueListScreen({ nav, category }) {
15551
16218
  onPress: () => nav.push({ name: "ISSUE_DETAIL", issue }),
15552
16219
  activeOpacity: 0.7
15553
16220
  },
15554
- /* @__PURE__ */ React15.createElement(View14, { style: styles14.topRow }, issue.severity && /* @__PURE__ */ React15.createElement(View14, { style: [styles14.severityDot, { backgroundColor: SEVERITY_COLORS[issue.severity] || colors.textDim }] }), /* @__PURE__ */ React15.createElement(Text14, { style: styles14.issueTitle, numberOfLines: 1 }, issue.title)),
15555
- /* @__PURE__ */ React15.createElement(View14, { style: styles14.bottomRow }, issue.route && /* @__PURE__ */ React15.createElement(Text14, { style: styles14.routeText, numberOfLines: 1 }, issue.route), /* @__PURE__ */ React15.createElement(Text14, { style: styles14.timeText }, formatRelativeTime(issue.updatedAt))),
15556
- category === "done" && issue.verifiedByName && /* @__PURE__ */ React15.createElement(View14, { style: styles14.verifiedBadge }, /* @__PURE__ */ React15.createElement(Text14, { style: styles14.verifiedBadgeText }, "\u2714", " Verified by ", issue.verifiedByName)),
15557
- category === "reopened" && issue.originalBugTitle && /* @__PURE__ */ React15.createElement(View14, { style: styles14.reopenedBadge }, /* @__PURE__ */ React15.createElement(Text14, { style: styles14.reopenedBadgeText, numberOfLines: 1 }, "\u{1F504}", " Retest of: ", issue.originalBugTitle))
16221
+ /* @__PURE__ */ React16.createElement(View15, { style: styles14.topRow }, issue.severity && /* @__PURE__ */ React16.createElement(View15, { style: [styles14.severityDot, { backgroundColor: SEVERITY_COLORS[issue.severity] || colors.textDim }] }), /* @__PURE__ */ React16.createElement(Text14, { style: styles14.issueTitle, numberOfLines: 1 }, issue.title)),
16222
+ /* @__PURE__ */ React16.createElement(View15, { style: styles14.bottomRow }, issue.route && /* @__PURE__ */ React16.createElement(Text14, { style: styles14.routeText, numberOfLines: 1 }, issue.route), /* @__PURE__ */ React16.createElement(Text14, { style: styles14.timeText }, formatRelativeTime(issue.updatedAt))),
16223
+ category === "done" && issue.verifiedByName && /* @__PURE__ */ React16.createElement(View15, { style: styles14.verifiedBadge }, /* @__PURE__ */ React16.createElement(Text14, { style: styles14.verifiedBadgeText }, "\u2714", " Verified by ", issue.verifiedByName)),
16224
+ category === "reopened" && issue.originalBugTitle && /* @__PURE__ */ React16.createElement(View15, { style: styles14.reopenedBadge }, /* @__PURE__ */ React16.createElement(Text14, { style: styles14.reopenedBadgeText, numberOfLines: 1 }, "\u{1F504}", " Retest of: ", issue.originalBugTitle))
15558
16225
  )));
15559
16226
  }
15560
- var styles14 = StyleSheet15.create({
16227
+ var styles14 = StyleSheet16.create({
15561
16228
  emptyContainer: {
15562
16229
  alignItems: "center",
15563
16230
  paddingVertical: 40
@@ -15648,8 +16315,8 @@ var styles14 = StyleSheet15.create({
15648
16315
  });
15649
16316
 
15650
16317
  // src/widget/screens/IssueDetailScreen.tsx
15651
- import React16 from "react";
15652
- import { View as View15, Text as Text15, Image as Image3, StyleSheet as StyleSheet16, Linking as Linking2, TouchableOpacity as TouchableOpacity14 } from "react-native";
16318
+ import React17 from "react";
16319
+ import { View as View16, Text as Text15, Image as Image3, StyleSheet as StyleSheet17, Linking as Linking4, TouchableOpacity as TouchableOpacity14 } from "react-native";
15653
16320
  var STATUS_LABELS = {
15654
16321
  new: { label: "New", bg: "#1e3a5f", color: "#60a5fa" },
15655
16322
  triaging: { label: "Triaging", bg: "#1e3a5f", color: "#60a5fa" },
@@ -15671,11 +16338,20 @@ var SEVERITY_CONFIG = {
15671
16338
  low: { label: "Low", color: "#71717a", bg: "#27272a" }
15672
16339
  };
15673
16340
  function IssueDetailScreen({ nav, issue }) {
16341
+ const { dashboardUrl } = useBugBear();
15674
16342
  const statusConfig = STATUS_LABELS[issue.status] || { label: issue.status, bg: "#27272a", color: "#a1a1aa" };
15675
16343
  const severityConfig = issue.severity ? SEVERITY_CONFIG[issue.severity] : null;
15676
- return /* @__PURE__ */ React16.createElement(View15, null, /* @__PURE__ */ React16.createElement(View15, { style: styles15.badgeRow }, /* @__PURE__ */ React16.createElement(View15, { style: [styles15.badge, { backgroundColor: statusConfig.bg }] }, /* @__PURE__ */ React16.createElement(Text15, { style: [styles15.badgeText, { color: statusConfig.color }] }, statusConfig.label)), severityConfig && /* @__PURE__ */ React16.createElement(View15, { style: [styles15.badge, { backgroundColor: severityConfig.bg }] }, /* @__PURE__ */ React16.createElement(Text15, { style: [styles15.badgeText, { color: severityConfig.color }] }, severityConfig.label))), /* @__PURE__ */ React16.createElement(Text15, { style: styles15.title }, issue.title), issue.route && /* @__PURE__ */ React16.createElement(Text15, { style: styles15.route }, issue.route), issue.description && /* @__PURE__ */ React16.createElement(View15, { style: styles15.descriptionCard }, /* @__PURE__ */ React16.createElement(Text15, { style: styles15.descriptionText }, issue.description)), issue.verifiedByName && /* @__PURE__ */ React16.createElement(View15, { style: styles15.verifiedCard }, /* @__PURE__ */ React16.createElement(View15, { style: styles15.verifiedHeader }, /* @__PURE__ */ React16.createElement(Text15, { style: styles15.verifiedIcon }, "\u2705"), /* @__PURE__ */ React16.createElement(Text15, { style: styles15.verifiedTitle }, "Retesting Proof")), /* @__PURE__ */ React16.createElement(Text15, { 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__ */ React16.createElement(View15, { style: styles15.originalBugCard }, /* @__PURE__ */ React16.createElement(View15, { style: styles15.originalBugHeader }, /* @__PURE__ */ React16.createElement(Text15, { style: styles15.originalBugIcon }, "\u{1F504}"), /* @__PURE__ */ React16.createElement(Text15, { style: styles15.originalBugTitle }, "Original Bug")), /* @__PURE__ */ React16.createElement(Text15, { style: styles15.originalBugBody }, "Retest of: ", issue.originalBugTitle)), issue.screenshotUrls && issue.screenshotUrls.length > 0 && /* @__PURE__ */ React16.createElement(View15, { style: styles15.screenshotSection }, /* @__PURE__ */ React16.createElement(Text15, { style: styles15.screenshotLabel }, "Screenshots (", issue.screenshotUrls.length, ")"), /* @__PURE__ */ React16.createElement(View15, { style: styles15.screenshotRow }, issue.screenshotUrls.map((url, i) => /* @__PURE__ */ React16.createElement(TouchableOpacity14, { key: i, onPress: () => Linking2.openURL(url), activeOpacity: 0.7 }, /* @__PURE__ */ React16.createElement(Image3, { source: { uri: url }, style: styles15.screenshotThumb }))))), /* @__PURE__ */ React16.createElement(View15, { style: styles15.metaSection }, issue.reporterName && /* @__PURE__ */ React16.createElement(Text15, { style: styles15.metaText }, "Reported by ", issue.reporterName), /* @__PURE__ */ React16.createElement(Text15, { style: styles15.metaTextSmall }, "Created ", formatRelativeTime(issue.createdAt), " ", "\xB7", " Updated ", formatRelativeTime(issue.updatedAt))));
16344
+ return /* @__PURE__ */ React17.createElement(View16, null, /* @__PURE__ */ React17.createElement(View16, { style: styles15.badgeRow }, /* @__PURE__ */ React17.createElement(View16, { style: [styles15.badge, { backgroundColor: statusConfig.bg }] }, /* @__PURE__ */ React17.createElement(Text15, { style: [styles15.badgeText, { color: statusConfig.color }] }, statusConfig.label)), severityConfig && /* @__PURE__ */ React17.createElement(View16, { style: [styles15.badge, { backgroundColor: severityConfig.bg }] }, /* @__PURE__ */ React17.createElement(Text15, { style: [styles15.badgeText, { color: severityConfig.color }] }, severityConfig.label))), /* @__PURE__ */ React17.createElement(Text15, { style: styles15.title }, issue.title), issue.route && /* @__PURE__ */ React17.createElement(Text15, { style: styles15.route }, issue.route), issue.description && /* @__PURE__ */ React17.createElement(View16, { style: styles15.descriptionCard }, /* @__PURE__ */ React17.createElement(Text15, { style: styles15.descriptionText }, issue.description)), issue.verifiedByName && /* @__PURE__ */ React17.createElement(View16, { style: styles15.verifiedCard }, /* @__PURE__ */ React17.createElement(View16, { style: styles15.verifiedHeader }, /* @__PURE__ */ React17.createElement(Text15, { style: styles15.verifiedIcon }, "\u2705"), /* @__PURE__ */ React17.createElement(Text15, { style: styles15.verifiedTitle }, "Retesting Proof")), /* @__PURE__ */ React17.createElement(Text15, { 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__ */ React17.createElement(View16, { style: styles15.originalBugCard }, /* @__PURE__ */ React17.createElement(View16, { style: styles15.originalBugHeader }, /* @__PURE__ */ React17.createElement(Text15, { style: styles15.originalBugIcon }, "\u{1F504}"), /* @__PURE__ */ React17.createElement(Text15, { style: styles15.originalBugTitle }, "Original Bug")), /* @__PURE__ */ React17.createElement(Text15, { style: styles15.originalBugBody }, "Retest of: ", issue.originalBugTitle)), issue.screenshotUrls && issue.screenshotUrls.length > 0 && /* @__PURE__ */ React17.createElement(View16, { style: styles15.screenshotSection }, /* @__PURE__ */ React17.createElement(Text15, { style: styles15.screenshotLabel }, "Screenshots (", issue.screenshotUrls.length, ")"), /* @__PURE__ */ React17.createElement(View16, { style: styles15.screenshotRow }, issue.screenshotUrls.map((url, i) => /* @__PURE__ */ React17.createElement(TouchableOpacity14, { key: i, onPress: () => Linking4.openURL(url), activeOpacity: 0.7 }, /* @__PURE__ */ React17.createElement(Image3, { source: { uri: url }, style: styles15.screenshotThumb }))))), /* @__PURE__ */ React17.createElement(View16, { style: styles15.metaSection }, issue.reporterName && /* @__PURE__ */ React17.createElement(Text15, { style: styles15.metaText }, "Reported by ", issue.reporterName), /* @__PURE__ */ React17.createElement(Text15, { style: styles15.metaTextSmall }, "Created ", formatRelativeTime(issue.createdAt), " ", "\xB7", " Updated ", formatRelativeTime(issue.updatedAt))), dashboardUrl && /* @__PURE__ */ React17.createElement(
16345
+ TouchableOpacity14,
16346
+ {
16347
+ style: styles15.dashboardLink,
16348
+ onPress: () => Linking4.openURL(`${dashboardUrl}/reports`),
16349
+ activeOpacity: 0.7
16350
+ },
16351
+ /* @__PURE__ */ React17.createElement(Text15, { style: styles15.dashboardLinkText }, "\u{1F310}", " View on Dashboard ", "\u2192")
16352
+ ));
15677
16353
  }
15678
- var styles15 = StyleSheet16.create({
16354
+ var styles15 = StyleSheet17.create({
15679
16355
  badgeRow: {
15680
16356
  flexDirection: "row",
15681
16357
  gap: 8,
@@ -15802,6 +16478,18 @@ var styles15 = StyleSheet16.create({
15802
16478
  metaTextSmall: {
15803
16479
  fontSize: 11,
15804
16480
  color: colors.textDim
16481
+ },
16482
+ dashboardLink: {
16483
+ alignItems: "center",
16484
+ paddingVertical: 10,
16485
+ marginTop: 12,
16486
+ borderTopWidth: 1,
16487
+ borderTopColor: colors.border
16488
+ },
16489
+ dashboardLinkText: {
16490
+ fontSize: 13,
16491
+ fontWeight: "500",
16492
+ color: colors.blue
15805
16493
  }
15806
16494
  });
15807
16495
 
@@ -15831,9 +16519,9 @@ function BugBearButton({
15831
16519
  return { x, y };
15832
16520
  };
15833
16521
  const initialPos = getInitialPosition();
15834
- const pan = useRef3(new Animated.ValueXY(initialPos)).current;
15835
- const isDragging = useRef3(false);
15836
- const panResponder = useRef3(
16522
+ const pan = useRef4(new Animated2.ValueXY(initialPos)).current;
16523
+ const isDragging = useRef4(false);
16524
+ const panResponder = useRef4(
15837
16525
  PanResponder.create({
15838
16526
  onStartShouldSetPanResponder: () => draggable,
15839
16527
  onMoveShouldSetPanResponder: (_, gs) => draggable && (Math.abs(gs.dx) > 5 || Math.abs(gs.dy) > 5),
@@ -15849,7 +16537,7 @@ function BugBearButton({
15849
16537
  if (Math.abs(gs.dx) > 5 || Math.abs(gs.dy) > 5) {
15850
16538
  isDragging.current = true;
15851
16539
  }
15852
- Animated.event(
16540
+ Animated2.event(
15853
16541
  [null, { dx: pan.x, dy: pan.y }],
15854
16542
  { useNativeDriver: false }
15855
16543
  )(_, gs);
@@ -15862,7 +16550,7 @@ function BugBearButton({
15862
16550
  const margin = 16;
15863
16551
  const snapX = currentX < screenWidth / 2 ? margin : screenWidth - buttonSize - margin;
15864
16552
  const snapY = Math.max(minY, Math.min(currentY, screenHeight - maxYOffset));
15865
- Animated.spring(pan, {
16553
+ Animated2.spring(pan, {
15866
16554
  toValue: { x: snapX, y: snapY },
15867
16555
  useNativeDriver: false,
15868
16556
  friction: 7,
@@ -15935,50 +16623,50 @@ function BugBearButton({
15935
16623
  const renderScreen = () => {
15936
16624
  switch (currentScreen.name) {
15937
16625
  case "HOME":
15938
- return /* @__PURE__ */ React17.createElement(HomeScreen, { nav });
16626
+ return /* @__PURE__ */ React18.createElement(HomeScreen, { nav });
15939
16627
  case "TEST_DETAIL":
15940
- return /* @__PURE__ */ React17.createElement(TestDetailScreen, { testId: currentScreen.testId, nav });
16628
+ return /* @__PURE__ */ React18.createElement(TestDetailScreen, { testId: currentScreen.testId, nav });
15941
16629
  case "TEST_LIST":
15942
- return /* @__PURE__ */ React17.createElement(TestListScreen, { nav });
16630
+ return /* @__PURE__ */ React18.createElement(TestListScreen, { nav });
15943
16631
  case "TEST_FEEDBACK":
15944
- return /* @__PURE__ */ React17.createElement(TestFeedbackScreen, { status: currentScreen.status, assignmentId: currentScreen.assignmentId, nav });
16632
+ return /* @__PURE__ */ React18.createElement(TestFeedbackScreen, { status: currentScreen.status, assignmentId: currentScreen.assignmentId, nav });
15945
16633
  case "REPORT":
15946
- return /* @__PURE__ */ React17.createElement(ReportScreen, { nav, prefill: currentScreen.prefill });
16634
+ return /* @__PURE__ */ React18.createElement(ReportScreen, { nav, prefill: currentScreen.prefill });
15947
16635
  case "REPORT_SUCCESS":
15948
- return /* @__PURE__ */ React17.createElement(ReportSuccessScreen, { nav });
16636
+ return /* @__PURE__ */ React18.createElement(ReportSuccessScreen, { nav });
15949
16637
  case "MESSAGE_LIST":
15950
- return /* @__PURE__ */ React17.createElement(MessageListScreen, { nav });
16638
+ return /* @__PURE__ */ React18.createElement(MessageListScreen, { nav });
15951
16639
  case "THREAD_DETAIL":
15952
- return /* @__PURE__ */ React17.createElement(ThreadDetailScreen, { thread: currentScreen.thread, nav });
16640
+ return /* @__PURE__ */ React18.createElement(ThreadDetailScreen, { thread: currentScreen.thread, nav });
15953
16641
  case "COMPOSE_MESSAGE":
15954
- return /* @__PURE__ */ React17.createElement(ComposeMessageScreen, { nav });
16642
+ return /* @__PURE__ */ React18.createElement(ComposeMessageScreen, { nav });
15955
16643
  case "ISSUE_LIST":
15956
- return /* @__PURE__ */ React17.createElement(IssueListScreen, { nav, category: currentScreen.category });
16644
+ return /* @__PURE__ */ React18.createElement(IssueListScreen, { nav, category: currentScreen.category });
15957
16645
  case "ISSUE_DETAIL":
15958
- return /* @__PURE__ */ React17.createElement(IssueDetailScreen, { nav, issue: currentScreen.issue });
16646
+ return /* @__PURE__ */ React18.createElement(IssueDetailScreen, { nav, issue: currentScreen.issue });
15959
16647
  case "PROFILE":
15960
- return /* @__PURE__ */ React17.createElement(ProfileScreen, { nav });
16648
+ return /* @__PURE__ */ React18.createElement(ProfileScreen, { nav });
15961
16649
  default:
15962
- return /* @__PURE__ */ React17.createElement(HomeScreen, { nav });
16650
+ return /* @__PURE__ */ React18.createElement(HomeScreen, { nav });
15963
16651
  }
15964
16652
  };
15965
- return /* @__PURE__ */ React17.createElement(React17.Fragment, null, /* @__PURE__ */ React17.createElement(
15966
- Animated.View,
16653
+ return /* @__PURE__ */ React18.createElement(React18.Fragment, null, /* @__PURE__ */ React18.createElement(
16654
+ Animated2.View,
15967
16655
  {
15968
16656
  style: [styles16.fabContainer, { transform: pan.getTranslateTransform() }, buttonStyle],
15969
16657
  ...panResponder.panHandlers
15970
16658
  },
15971
- /* @__PURE__ */ React17.createElement(
16659
+ /* @__PURE__ */ React18.createElement(
15972
16660
  TouchableOpacity15,
15973
16661
  {
15974
16662
  style: styles16.fab,
15975
16663
  onPress: () => setModalVisible(true),
15976
16664
  activeOpacity: draggable ? 1 : 0.7
15977
16665
  },
15978
- /* @__PURE__ */ React17.createElement(Image4, { source: { uri: BUGBEAR_LOGO_BASE64 }, style: styles16.fabIcon }),
15979
- badgeCount > 0 && /* @__PURE__ */ React17.createElement(View16, { style: styles16.badge }, /* @__PURE__ */ React17.createElement(Text16, { style: styles16.badgeText }, badgeCount > 9 ? "9+" : badgeCount))
16666
+ /* @__PURE__ */ React18.createElement(Image4, { source: { uri: BUGBEAR_LOGO_BASE64 }, style: styles16.fabIcon }),
16667
+ badgeCount > 0 && /* @__PURE__ */ React18.createElement(View17, { style: styles16.badge }, /* @__PURE__ */ React18.createElement(Text16, { style: styles16.badgeText }, badgeCount > 9 ? "9+" : badgeCount))
15980
16668
  )
15981
- ), /* @__PURE__ */ React17.createElement(
16669
+ ), /* @__PURE__ */ React18.createElement(
15982
16670
  Modal3,
15983
16671
  {
15984
16672
  visible: modalVisible,
@@ -15986,13 +16674,13 @@ function BugBearButton({
15986
16674
  transparent: true,
15987
16675
  onRequestClose: handleClose
15988
16676
  },
15989
- /* @__PURE__ */ React17.createElement(
16677
+ /* @__PURE__ */ React18.createElement(
15990
16678
  KeyboardAvoidingView,
15991
16679
  {
15992
- behavior: Platform4.OS === "ios" ? "padding" : "height",
16680
+ behavior: Platform5.OS === "ios" ? "padding" : "height",
15993
16681
  style: styles16.modalOverlay
15994
16682
  },
15995
- /* @__PURE__ */ React17.createElement(View16, { style: styles16.modalContainer }, /* @__PURE__ */ React17.createElement(View16, { style: styles16.header }, /* @__PURE__ */ React17.createElement(View16, { style: styles16.headerLeft }, canGoBack ? /* @__PURE__ */ React17.createElement(View16, { style: styles16.headerNavRow }, /* @__PURE__ */ React17.createElement(TouchableOpacity15, { onPress: () => nav.pop(), style: styles16.backButton }, /* @__PURE__ */ React17.createElement(Text16, { style: styles16.backText }, "\u2190 Back")), /* @__PURE__ */ React17.createElement(TouchableOpacity15, { onPress: () => nav.reset(), style: styles16.homeButton }, /* @__PURE__ */ React17.createElement(Text16, { style: styles16.homeText }, "\u{1F3E0}"))) : /* @__PURE__ */ React17.createElement(View16, { style: styles16.headerTitleRow }, /* @__PURE__ */ React17.createElement(Text16, { style: styles16.headerTitle }, "BugBear"), testerInfo && /* @__PURE__ */ React17.createElement(TouchableOpacity15, { onPress: () => push({ name: "PROFILE" }) }, /* @__PURE__ */ React17.createElement(Text16, { style: styles16.headerName }, testerInfo.name, " \u270E")))), getHeaderTitle() ? /* @__PURE__ */ React17.createElement(Text16, { style: styles16.headerScreenTitle, numberOfLines: 1 }, getHeaderTitle()) : null, /* @__PURE__ */ React17.createElement(TouchableOpacity15, { onPress: handleClose, style: styles16.closeButton }, /* @__PURE__ */ React17.createElement(Text16, { style: styles16.closeText }, "\u2715"))), /* @__PURE__ */ React17.createElement(
16683
+ /* @__PURE__ */ React18.createElement(View17, { style: styles16.modalContainer }, /* @__PURE__ */ React18.createElement(View17, { style: styles16.header }, /* @__PURE__ */ React18.createElement(View17, { style: styles16.headerLeft }, canGoBack ? /* @__PURE__ */ React18.createElement(View17, { style: styles16.headerNavRow }, /* @__PURE__ */ React18.createElement(TouchableOpacity15, { onPress: () => nav.pop(), style: styles16.backButton }, /* @__PURE__ */ React18.createElement(Text16, { style: styles16.backText }, "\u2190 Back")), /* @__PURE__ */ React18.createElement(TouchableOpacity15, { onPress: () => nav.reset(), style: styles16.homeButton }, /* @__PURE__ */ React18.createElement(Text16, { style: styles16.homeText }, "\u{1F3E0}"))) : /* @__PURE__ */ React18.createElement(View17, { style: styles16.headerTitleRow }, /* @__PURE__ */ React18.createElement(Text16, { style: styles16.headerTitle }, "BugBear"), testerInfo && /* @__PURE__ */ React18.createElement(TouchableOpacity15, { onPress: () => push({ name: "PROFILE" }) }, /* @__PURE__ */ React18.createElement(Text16, { style: styles16.headerName }, testerInfo.name, " \u270E")))), getHeaderTitle() ? /* @__PURE__ */ React18.createElement(Text16, { style: styles16.headerScreenTitle, numberOfLines: 1 }, getHeaderTitle()) : null, /* @__PURE__ */ React18.createElement(TouchableOpacity15, { onPress: handleClose, style: styles16.closeButton }, /* @__PURE__ */ React18.createElement(Text16, { style: styles16.closeText }, "\u2715"))), /* @__PURE__ */ React18.createElement(
15996
16684
  ScrollView3,
15997
16685
  {
15998
16686
  style: styles16.content,
@@ -16000,12 +16688,12 @@ function BugBearButton({
16000
16688
  keyboardShouldPersistTaps: "handled",
16001
16689
  showsVerticalScrollIndicator: false
16002
16690
  },
16003
- isLoading ? /* @__PURE__ */ React17.createElement(View16, { style: styles16.loadingContainer }, /* @__PURE__ */ React17.createElement(ActivityIndicator3, { size: "large", color: colors.blue }), /* @__PURE__ */ React17.createElement(Text16, { style: styles16.loadingText }, "Loading...")) : renderScreen()
16691
+ isLoading ? /* @__PURE__ */ React18.createElement(View17, { style: styles16.loadingContainer }, /* @__PURE__ */ React18.createElement(ActivityIndicator2, { size: "large", color: colors.blue }), /* @__PURE__ */ React18.createElement(Text16, { style: styles16.loadingText }, "Loading...")) : renderScreen()
16004
16692
  ))
16005
16693
  )
16006
16694
  ));
16007
16695
  }
16008
- var styles16 = StyleSheet17.create({
16696
+ var styles16 = StyleSheet18.create({
16009
16697
  // FAB
16010
16698
  fabContainer: {
16011
16699
  position: "absolute",
@@ -16147,8 +16835,8 @@ var styles16 = StyleSheet17.create({
16147
16835
  });
16148
16836
 
16149
16837
  // src/BugBearErrorBoundary.tsx
16150
- import React18, { Component } from "react";
16151
- import { View as View17, Text as Text17, TouchableOpacity as TouchableOpacity16, StyleSheet as StyleSheet18 } from "react-native";
16838
+ import React19, { Component } from "react";
16839
+ import { View as View18, Text as Text17, TouchableOpacity as TouchableOpacity16, StyleSheet as StyleSheet19 } from "react-native";
16152
16840
  var BugBearErrorBoundary = class extends Component {
16153
16841
  constructor(props) {
16154
16842
  super(props);
@@ -16193,7 +16881,7 @@ var BugBearErrorBoundary = class extends Component {
16193
16881
  if (fallback) {
16194
16882
  return fallback;
16195
16883
  }
16196
- return /* @__PURE__ */ React18.createElement(View17, { style: styles17.container }, /* @__PURE__ */ React18.createElement(Text17, { style: styles17.title }, "Something went wrong"), /* @__PURE__ */ React18.createElement(Text17, { style: styles17.message }, error.message), /* @__PURE__ */ React18.createElement(TouchableOpacity16, { style: styles17.button, onPress: this.reset }, /* @__PURE__ */ React18.createElement(Text17, { style: styles17.buttonText }, "Try Again")), /* @__PURE__ */ React18.createElement(Text17, { style: styles17.caption }, "The error has been captured by BugBear"));
16884
+ return /* @__PURE__ */ React19.createElement(View18, { style: styles17.container }, /* @__PURE__ */ React19.createElement(Text17, { style: styles17.title }, "Something went wrong"), /* @__PURE__ */ React19.createElement(Text17, { style: styles17.message }, error.message), /* @__PURE__ */ React19.createElement(TouchableOpacity16, { style: styles17.button, onPress: this.reset }, /* @__PURE__ */ React19.createElement(Text17, { style: styles17.buttonText }, "Try Again")), /* @__PURE__ */ React19.createElement(Text17, { style: styles17.caption }, "The error has been captured by BugBear"));
16197
16885
  }
16198
16886
  return children;
16199
16887
  }
@@ -16204,7 +16892,7 @@ function useErrorContext() {
16204
16892
  getEnhancedContext: () => contextCapture.getEnhancedContext()
16205
16893
  };
16206
16894
  }
16207
- var styles17 = StyleSheet18.create({
16895
+ var styles17 = StyleSheet19.create({
16208
16896
  container: {
16209
16897
  padding: 20,
16210
16898
  margin: 20,