@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.d.mts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1119 -431
- package/dist/index.mjs +1008 -320
- package/package.json +12 -3
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(
|
|
12
|
+
function __rest(s2, e) {
|
|
13
13
|
var t = {};
|
|
14
|
-
for (var p in
|
|
15
|
-
t[p] =
|
|
16
|
-
if (
|
|
17
|
-
for (var i = 0, p = Object.getOwnPropertySymbols(
|
|
18
|
-
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(
|
|
19
|
-
t[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((
|
|
835
|
-
if (typeof
|
|
836
|
-
else return `${
|
|
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((
|
|
849
|
-
if (typeof
|
|
850
|
-
else return `${
|
|
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
|
-
|
|
11628
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
11795
|
-
|
|
11796
|
-
|
|
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
|
|
11800
|
-
|
|
11801
|
-
|
|
11802
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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((
|
|
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
|
-
},
|
|
13334
|
-
return () =>
|
|
13335
|
-
|
|
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
|
|
13772
|
+
import React18, { useState as useState12, useRef as useRef4 } from "react";
|
|
13388
13773
|
import {
|
|
13389
|
-
View as
|
|
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
|
|
13780
|
+
StyleSheet as StyleSheet18,
|
|
13396
13781
|
Dimensions as Dimensions2,
|
|
13397
13782
|
KeyboardAvoidingView,
|
|
13398
|
-
Platform as
|
|
13783
|
+
Platform as Platform5,
|
|
13399
13784
|
PanResponder,
|
|
13400
|
-
Animated,
|
|
13401
|
-
ActivityIndicator as
|
|
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
|
|
13548
|
-
if (h > 0) return `${h}:${m.toString().padStart(2, "0")}:${
|
|
13549
|
-
return `${m}:${
|
|
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
|
|
13622
|
-
import { View, Text, TouchableOpacity, StyleSheet as
|
|
13623
|
-
|
|
13624
|
-
|
|
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__ */
|
|
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__ */
|
|
13643
|
-
/* @__PURE__ */
|
|
13644
|
-
retestCount > 0 && /* @__PURE__ */
|
|
13645
|
-
/* @__PURE__ */
|
|
13646
|
-
) : unreadCount > 0 ? /* @__PURE__ */
|
|
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__ */
|
|
13654
|
-
/* @__PURE__ */
|
|
13655
|
-
/* @__PURE__ */
|
|
13656
|
-
) : /* @__PURE__ */
|
|
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__ */
|
|
13664
|
-
/* @__PURE__ */
|
|
13665
|
-
pendingCount > 0 && /* @__PURE__ */
|
|
13666
|
-
), /* @__PURE__ */
|
|
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__ */
|
|
13674
|
-
/* @__PURE__ */
|
|
13675
|
-
), /* @__PURE__ */
|
|
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__ */
|
|
13683
|
-
/* @__PURE__ */
|
|
13684
|
-
), /* @__PURE__ */
|
|
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__ */
|
|
13692
|
-
/* @__PURE__ */
|
|
13693
|
-
unreadCount > 0 && /* @__PURE__ */
|
|
13694
|
-
)), /* @__PURE__ */
|
|
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__ */
|
|
13702
|
-
/* @__PURE__ */
|
|
13703
|
-
), /* @__PURE__ */
|
|
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__ */
|
|
13711
|
-
/* @__PURE__ */
|
|
13712
|
-
), /* @__PURE__ */
|
|
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__ */
|
|
13720
|
-
/* @__PURE__ */
|
|
13721
|
-
)), totalTests > 0 && /* @__PURE__ */
|
|
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__ */
|
|
13729
|
-
/* @__PURE__ */
|
|
13730
|
-
/* @__PURE__ */
|
|
13731
|
-
), /* @__PURE__ */
|
|
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__ */
|
|
14206
|
+
/* @__PURE__ */ React3.createElement(Text, { style: styles.refreshText }, "\u21BB Refresh")
|
|
13742
14207
|
));
|
|
13743
14208
|
}
|
|
13744
|
-
var styles =
|
|
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
|
|
13969
|
-
import { View as
|
|
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
|
-
|
|
14447
|
+
useEffect4(() => {
|
|
13983
14448
|
setCriteriaResults({});
|
|
13984
14449
|
setShowSteps(true);
|
|
13985
14450
|
setShowDetails(false);
|
|
13986
14451
|
}, [displayedAssignment?.id]);
|
|
13987
|
-
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
14083
|
-
/* @__PURE__ */
|
|
14084
|
-
)), Object.keys(criteriaResults).length > 0 && /* @__PURE__ */
|
|
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__ */
|
|
14091
|
-
), /* @__PURE__ */
|
|
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__ */
|
|
14098
|
-
)) : /* @__PURE__ */
|
|
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__ */
|
|
14106
|
-
))))), template === "freeform" && /* @__PURE__ */
|
|
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__ */
|
|
14117
|
-
), /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
14130
|
-
)), /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
14681
|
+
/* @__PURE__ */ React4.createElement(Text2, { style: styles2.skipConfirmText }, skipping ? "Skipping..." : "Skip Test")
|
|
14152
14682
|
))))));
|
|
14153
14683
|
}
|
|
14154
|
-
var styles2 =
|
|
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
|
|
14250
|
-
import { View as
|
|
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
|
-
|
|
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
|
-
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
14341
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
14359
|
-
/* @__PURE__ */
|
|
14360
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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
|
-
}),
|
|
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 =
|
|
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
|
-
|
|
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
|
|
14417
|
-
import { View as
|
|
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
|
|
14518
|
-
import { View as
|
|
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
|
|
14522
|
-
import { View as
|
|
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__ */
|
|
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 =
|
|
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
|
-
...
|
|
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__ */
|
|
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__ */
|
|
14589
|
-
), /* @__PURE__ */
|
|
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__ */
|
|
14597
|
-
), /* @__PURE__ */
|
|
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 =
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
14713
|
-
/* @__PURE__ */
|
|
14714
|
-
))), /* @__PURE__ */
|
|
14715
|
-
|
|
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__ */
|
|
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__ */
|
|
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 =
|
|
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
|
|
14761
|
-
import { View as
|
|
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
|
|
14765
|
-
import { View as
|
|
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__ */
|
|
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__ */
|
|
14787
|
-
/* @__PURE__ */
|
|
14788
|
-
), /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
14810
|
-
), categoryOptions.map(({ value: optValue, label, icon }) => /* @__PURE__ */
|
|
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__ */
|
|
14818
|
-
/* @__PURE__ */
|
|
14819
|
-
value === optValue && /* @__PURE__ */
|
|
14820
|
-
)), /* @__PURE__ */
|
|
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__ */
|
|
15482
|
+
/* @__PURE__ */ React9.createElement(Text7, { style: styles7.cancelText }, "Cancel")
|
|
14827
15483
|
))
|
|
14828
15484
|
)
|
|
14829
15485
|
));
|
|
14830
15486
|
}
|
|
14831
|
-
var styles7 =
|
|
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 =
|
|
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
|
-
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
14994
|
-
)))), /* @__PURE__ */
|
|
14995
|
-
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
15024
|
-
)) : /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
15036
|
-
/* @__PURE__ */
|
|
15037
|
-
))), isBugType && /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
15050
|
-
)))), isBugType && /* @__PURE__ */
|
|
15051
|
-
|
|
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__ */
|
|
15063
|
-
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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 =
|
|
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
|
|
15116
|
-
import { View as
|
|
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
|
-
|
|
15774
|
+
useEffect7(() => {
|
|
15119
15775
|
const timer = setTimeout(() => nav.reset(), 2e3);
|
|
15120
15776
|
return () => clearTimeout(timer);
|
|
15121
15777
|
}, [nav]);
|
|
15122
|
-
return /* @__PURE__ */
|
|
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 =
|
|
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
|
|
15133
|
-
import { View as
|
|
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__ */
|
|
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__ */
|
|
15143
|
-
), threads.length === 0 ? /* @__PURE__ */
|
|
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__ */
|
|
15151
|
-
/* @__PURE__ */
|
|
15152
|
-
))),
|
|
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 =
|
|
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
|
-
|
|
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
|
|
15178
|
-
import { View as
|
|
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
|
-
|
|
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__ */
|
|
15233
|
-
|
|
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__ */
|
|
15239
|
-
/* @__PURE__ */
|
|
15240
|
-
msg.attachments && msg.attachments.length > 0 && /* @__PURE__ */
|
|
15241
|
-
/* @__PURE__ */
|
|
15242
|
-
))), sendError && /* @__PURE__ */
|
|
15243
|
-
|
|
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__ */
|
|
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__ */
|
|
15927
|
+
/* @__PURE__ */ React13.createElement(Text11, { style: styles11.sendBtnText }, sending ? "..." : "Send")
|
|
15261
15928
|
)));
|
|
15262
15929
|
}
|
|
15263
|
-
var styles11 =
|
|
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
|
|
15296
|
-
import { View as
|
|
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__ */
|
|
15318
|
-
|
|
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__ */
|
|
15328
|
-
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
16023
|
+
/* @__PURE__ */ React14.createElement(Text12, { style: shared.primaryButtonText }, images.isUploading ? "Uploading..." : sending ? "Sending..." : "Send Message")
|
|
15357
16024
|
)));
|
|
15358
16025
|
}
|
|
15359
|
-
var styles12 =
|
|
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
|
|
15370
|
-
import { View as
|
|
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
|
-
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
15427
|
-
|
|
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__ */
|
|
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__ */
|
|
15445
|
-
)))), /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
16120
|
+
/* @__PURE__ */ React15.createElement(Text13, { style: shared.primaryButtonText }, "Edit Profile")
|
|
15454
16121
|
));
|
|
15455
16122
|
}
|
|
15456
|
-
var styles13 =
|
|
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
|
|
15498
|
-
import { View as
|
|
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
|
-
|
|
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__ */
|
|
16208
|
+
return /* @__PURE__ */ React16.createElement(IssueListScreenSkeleton, null);
|
|
15542
16209
|
}
|
|
15543
16210
|
if (issues.length === 0) {
|
|
15544
|
-
return /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
15555
|
-
/* @__PURE__ */
|
|
15556
|
-
category === "done" && issue.verifiedByName && /* @__PURE__ */
|
|
15557
|
-
category === "reopened" && issue.originalBugTitle && /* @__PURE__ */
|
|
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 =
|
|
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
|
|
15652
|
-
import { View as
|
|
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__ */
|
|
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 =
|
|
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 =
|
|
15835
|
-
const isDragging =
|
|
15836
|
-
const panResponder =
|
|
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
|
-
|
|
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
|
-
|
|
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__ */
|
|
16626
|
+
return /* @__PURE__ */ React18.createElement(HomeScreen, { nav });
|
|
15939
16627
|
case "TEST_DETAIL":
|
|
15940
|
-
return /* @__PURE__ */
|
|
16628
|
+
return /* @__PURE__ */ React18.createElement(TestDetailScreen, { testId: currentScreen.testId, nav });
|
|
15941
16629
|
case "TEST_LIST":
|
|
15942
|
-
return /* @__PURE__ */
|
|
16630
|
+
return /* @__PURE__ */ React18.createElement(TestListScreen, { nav });
|
|
15943
16631
|
case "TEST_FEEDBACK":
|
|
15944
|
-
return /* @__PURE__ */
|
|
16632
|
+
return /* @__PURE__ */ React18.createElement(TestFeedbackScreen, { status: currentScreen.status, assignmentId: currentScreen.assignmentId, nav });
|
|
15945
16633
|
case "REPORT":
|
|
15946
|
-
return /* @__PURE__ */
|
|
16634
|
+
return /* @__PURE__ */ React18.createElement(ReportScreen, { nav, prefill: currentScreen.prefill });
|
|
15947
16635
|
case "REPORT_SUCCESS":
|
|
15948
|
-
return /* @__PURE__ */
|
|
16636
|
+
return /* @__PURE__ */ React18.createElement(ReportSuccessScreen, { nav });
|
|
15949
16637
|
case "MESSAGE_LIST":
|
|
15950
|
-
return /* @__PURE__ */
|
|
16638
|
+
return /* @__PURE__ */ React18.createElement(MessageListScreen, { nav });
|
|
15951
16639
|
case "THREAD_DETAIL":
|
|
15952
|
-
return /* @__PURE__ */
|
|
16640
|
+
return /* @__PURE__ */ React18.createElement(ThreadDetailScreen, { thread: currentScreen.thread, nav });
|
|
15953
16641
|
case "COMPOSE_MESSAGE":
|
|
15954
|
-
return /* @__PURE__ */
|
|
16642
|
+
return /* @__PURE__ */ React18.createElement(ComposeMessageScreen, { nav });
|
|
15955
16643
|
case "ISSUE_LIST":
|
|
15956
|
-
return /* @__PURE__ */
|
|
16644
|
+
return /* @__PURE__ */ React18.createElement(IssueListScreen, { nav, category: currentScreen.category });
|
|
15957
16645
|
case "ISSUE_DETAIL":
|
|
15958
|
-
return /* @__PURE__ */
|
|
16646
|
+
return /* @__PURE__ */ React18.createElement(IssueDetailScreen, { nav, issue: currentScreen.issue });
|
|
15959
16647
|
case "PROFILE":
|
|
15960
|
-
return /* @__PURE__ */
|
|
16648
|
+
return /* @__PURE__ */ React18.createElement(ProfileScreen, { nav });
|
|
15961
16649
|
default:
|
|
15962
|
-
return /* @__PURE__ */
|
|
16650
|
+
return /* @__PURE__ */ React18.createElement(HomeScreen, { nav });
|
|
15963
16651
|
}
|
|
15964
16652
|
};
|
|
15965
|
-
return /* @__PURE__ */
|
|
15966
|
-
|
|
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__ */
|
|
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__ */
|
|
15979
|
-
badgeCount > 0 && /* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
16677
|
+
/* @__PURE__ */ React18.createElement(
|
|
15990
16678
|
KeyboardAvoidingView,
|
|
15991
16679
|
{
|
|
15992
|
-
behavior:
|
|
16680
|
+
behavior: Platform5.OS === "ios" ? "padding" : "height",
|
|
15993
16681
|
style: styles16.modalOverlay
|
|
15994
16682
|
},
|
|
15995
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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 =
|
|
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
|
|
16151
|
-
import { View as
|
|
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__ */
|
|
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 =
|
|
16895
|
+
var styles17 = StyleSheet19.create({
|
|
16208
16896
|
container: {
|
|
16209
16897
|
padding: 20,
|
|
16210
16898
|
margin: 20,
|