@bbearai/react-native 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,1664 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.tsx
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ BugBearButton: () => BugBearButton,
34
+ BugBearProvider: () => BugBearProvider,
35
+ useBugBear: () => useBugBear
36
+ });
37
+ module.exports = __toCommonJS(index_exports);
38
+
39
+ // src/BugBearProvider.tsx
40
+ var import_react = __toESM(require("react"));
41
+ var import_core = require("@bbearai/core");
42
+ var import_react_native = require("react-native");
43
+ var BugBearContext = (0, import_react.createContext)({
44
+ client: null,
45
+ isTester: false,
46
+ isQAEnabled: false,
47
+ shouldShowWidget: false,
48
+ testerInfo: null,
49
+ assignments: [],
50
+ currentAssignment: null,
51
+ refreshAssignments: async () => {
52
+ },
53
+ isLoading: true,
54
+ getDeviceInfo: () => ({ platform: "ios" }),
55
+ onNavigate: void 0,
56
+ threads: [],
57
+ unreadCount: 0,
58
+ refreshThreads: async () => {
59
+ },
60
+ getThreadMessages: async () => [],
61
+ sendMessage: async () => false,
62
+ markAsRead: async () => {
63
+ }
64
+ });
65
+ function useBugBear() {
66
+ return (0, import_react.useContext)(BugBearContext);
67
+ }
68
+ function BugBearProvider({ config, children, appVersion }) {
69
+ const [client] = (0, import_react.useState)(() => (0, import_core.createBugBear)(config));
70
+ const [isTester, setIsTester] = (0, import_react.useState)(false);
71
+ const [isQAEnabled, setIsQAEnabled] = (0, import_react.useState)(false);
72
+ const [testerInfo, setTesterInfo] = (0, import_react.useState)(null);
73
+ const [assignments, setAssignments] = (0, import_react.useState)([]);
74
+ const [isLoading, setIsLoading] = (0, import_react.useState)(true);
75
+ const [threads, setThreads] = (0, import_react.useState)([]);
76
+ const [unreadCount, setUnreadCount] = (0, import_react.useState)(0);
77
+ const getDeviceInfo = () => {
78
+ const { width, height } = import_react_native.Dimensions.get("window");
79
+ return {
80
+ platform: import_react_native.Platform.OS,
81
+ osVersion: import_react_native.Platform.Version?.toString(),
82
+ appVersion,
83
+ screenSize: { width, height }
84
+ };
85
+ };
86
+ const refreshAssignments = async () => {
87
+ const newAssignments = await client.getAssignedTests();
88
+ setAssignments(newAssignments);
89
+ };
90
+ const refreshThreads = async () => {
91
+ const newThreads = await client.getThreadsForTester();
92
+ setThreads(newThreads);
93
+ const totalUnread = newThreads.reduce((sum, t) => sum + t.unreadCount, 0);
94
+ setUnreadCount(totalUnread);
95
+ };
96
+ const getThreadMessages = async (threadId) => {
97
+ return client.getThreadMessages(threadId);
98
+ };
99
+ const sendMessage = async (threadId, content) => {
100
+ const success = await client.sendMessage(threadId, content);
101
+ if (success) {
102
+ await refreshThreads();
103
+ }
104
+ return success;
105
+ };
106
+ const markAsRead = async (threadId) => {
107
+ await client.markThreadAsRead(threadId);
108
+ await refreshThreads();
109
+ };
110
+ (0, import_react.useEffect)(() => {
111
+ const init = async () => {
112
+ setIsLoading(true);
113
+ try {
114
+ const [qaEnabled, info] = await Promise.all([
115
+ client.isQAEnabled(),
116
+ client.getTesterInfo()
117
+ ]);
118
+ setIsQAEnabled(qaEnabled);
119
+ setTesterInfo(info);
120
+ setIsTester(!!info);
121
+ if (info && qaEnabled) {
122
+ await Promise.all([
123
+ refreshAssignments(),
124
+ refreshThreads()
125
+ ]);
126
+ }
127
+ } catch (err) {
128
+ console.error("BugBear: Init error", err);
129
+ } finally {
130
+ setIsLoading(false);
131
+ }
132
+ };
133
+ init();
134
+ }, [client]);
135
+ const currentAssignment = assignments.find(
136
+ (a) => a.status === "in_progress"
137
+ ) || assignments[0] || null;
138
+ const shouldShowWidget = isQAEnabled && isTester;
139
+ return /* @__PURE__ */ import_react.default.createElement(
140
+ BugBearContext.Provider,
141
+ {
142
+ value: {
143
+ client,
144
+ isTester,
145
+ isQAEnabled,
146
+ shouldShowWidget,
147
+ testerInfo,
148
+ assignments,
149
+ currentAssignment,
150
+ refreshAssignments,
151
+ isLoading,
152
+ getDeviceInfo,
153
+ onNavigate: config.onNavigate,
154
+ threads,
155
+ unreadCount,
156
+ refreshThreads,
157
+ getThreadMessages,
158
+ sendMessage,
159
+ markAsRead
160
+ }
161
+ },
162
+ children
163
+ );
164
+ }
165
+
166
+ // src/BugBearButton.tsx
167
+ var import_react2 = __toESM(require("react"));
168
+ var import_react_native2 = require("react-native");
169
+ var { width: screenWidth, height: screenHeight } = import_react_native2.Dimensions.get("window");
170
+ var templateInfo = {
171
+ steps: { name: "Steps", icon: "\u{1F4DD}", action: "Follow each step" },
172
+ checklist: { name: "Checklist", icon: "\u2705", action: "Pass/Fail each item" },
173
+ rubric: { name: "Rubric", icon: "\u{1F4CA}", action: "Rate each criterion" },
174
+ freeform: { name: "Freeform", icon: "\u{1F4AD}", action: "Provide observations" }
175
+ };
176
+ function BugBearButton({
177
+ getAppContext,
178
+ position = "bottom-right",
179
+ buttonStyle,
180
+ draggable = true,
181
+ initialX,
182
+ initialY,
183
+ minY = 100,
184
+ maxYOffset = 160
185
+ }) {
186
+ const {
187
+ client,
188
+ shouldShowWidget,
189
+ testerInfo,
190
+ assignments,
191
+ currentAssignment,
192
+ refreshAssignments,
193
+ isLoading,
194
+ getDeviceInfo,
195
+ onNavigate,
196
+ threads,
197
+ unreadCount,
198
+ refreshThreads,
199
+ getThreadMessages,
200
+ sendMessage,
201
+ markAsRead
202
+ } = useBugBear();
203
+ const [modalVisible, setModalVisible] = (0, import_react2.useState)(false);
204
+ const [activeTab, setActiveTab] = (0, import_react2.useState)("tests");
205
+ const [showSteps, setShowSteps] = (0, import_react2.useState)(false);
206
+ const [messageView, setMessageView] = (0, import_react2.useState)("list");
207
+ const [selectedThread, setSelectedThread] = (0, import_react2.useState)(null);
208
+ const [threadMessages, setThreadMessages] = (0, import_react2.useState)([]);
209
+ const [replyText, setReplyText] = (0, import_react2.useState)("");
210
+ const [sendingReply, setSendingReply] = (0, import_react2.useState)(false);
211
+ const [loadingMessages, setLoadingMessages] = (0, import_react2.useState)(false);
212
+ const getInitialPosition = () => {
213
+ const buttonSize = 56;
214
+ const margin = 16;
215
+ if (initialX !== void 0 && initialY !== void 0) {
216
+ return { x: initialX, y: initialY };
217
+ }
218
+ const x = position === "bottom-right" ? screenWidth - buttonSize - margin : margin;
219
+ const y = screenHeight - 160;
220
+ return { x, y };
221
+ };
222
+ const initialPos = getInitialPosition();
223
+ const pan = (0, import_react2.useRef)(new import_react_native2.Animated.ValueXY(initialPos)).current;
224
+ const isDragging = (0, import_react2.useRef)(false);
225
+ const panResponder = (0, import_react2.useRef)(
226
+ import_react_native2.PanResponder.create({
227
+ onStartShouldSetPanResponder: () => draggable,
228
+ onMoveShouldSetPanResponder: (_, gestureState) => {
229
+ return draggable && (Math.abs(gestureState.dx) > 5 || Math.abs(gestureState.dy) > 5);
230
+ },
231
+ onPanResponderGrant: () => {
232
+ isDragging.current = false;
233
+ pan.setOffset({
234
+ x: pan.x._value,
235
+ y: pan.y._value
236
+ });
237
+ pan.setValue({ x: 0, y: 0 });
238
+ },
239
+ onPanResponderMove: (_, gestureState) => {
240
+ if (Math.abs(gestureState.dx) > 5 || Math.abs(gestureState.dy) > 5) {
241
+ isDragging.current = true;
242
+ }
243
+ import_react_native2.Animated.event(
244
+ [null, { dx: pan.x, dy: pan.y }],
245
+ { useNativeDriver: false }
246
+ )(_, gestureState);
247
+ },
248
+ onPanResponderRelease: (_, gestureState) => {
249
+ pan.flattenOffset();
250
+ const currentX = pan.x._value;
251
+ const currentY = pan.y._value;
252
+ const buttonSize = 56;
253
+ const margin = 16;
254
+ const snapX = currentX < screenWidth / 2 ? margin : screenWidth - buttonSize - margin;
255
+ const snapY = Math.max(minY, Math.min(currentY, screenHeight - maxYOffset));
256
+ import_react_native2.Animated.spring(pan, {
257
+ toValue: { x: snapX, y: snapY },
258
+ useNativeDriver: false,
259
+ friction: 7,
260
+ tension: 40
261
+ }).start();
262
+ if (!isDragging.current && Math.abs(gestureState.dx) < 5 && Math.abs(gestureState.dy) < 5) {
263
+ setModalVisible(true);
264
+ }
265
+ isDragging.current = false;
266
+ }
267
+ })
268
+ ).current;
269
+ const [testView, setTestView] = (0, import_react2.useState)("detail");
270
+ const [selectedTestId, setSelectedTestId] = (0, import_react2.useState)(null);
271
+ const displayedAssignment = selectedTestId ? assignments.find((a) => a.id === selectedTestId) || currentAssignment : currentAssignment;
272
+ const [reportType, setReportType] = (0, import_react2.useState)("bug");
273
+ const [description, setDescription] = (0, import_react2.useState)("");
274
+ const [severity, setSeverity] = (0, import_react2.useState)("medium");
275
+ const [submitting, setSubmitting] = (0, import_react2.useState)(false);
276
+ const [submitted, setSubmitted] = (0, import_react2.useState)(false);
277
+ const [justPassed, setJustPassed] = (0, import_react2.useState)(false);
278
+ const [criteriaResults, setCriteriaResults] = (0, import_react2.useState)({});
279
+ (0, import_react2.useEffect)(() => {
280
+ setCriteriaResults({});
281
+ setShowSteps(false);
282
+ }, [displayedAssignment?.id]);
283
+ if (isLoading || !shouldShowWidget) {
284
+ return null;
285
+ }
286
+ const pendingCount = assignments.filter((a) => a.status === "pending").length;
287
+ const inProgressCount = assignments.filter((a) => a.status === "in_progress").length;
288
+ const handlePass = async () => {
289
+ if (!client || !displayedAssignment) return;
290
+ setSubmitting(true);
291
+ await client.submitReport({
292
+ type: "test_pass",
293
+ description: `Test passed: ${displayedAssignment.testCase.title}`,
294
+ assignmentId: displayedAssignment.id,
295
+ testCaseId: displayedAssignment.testCase.id,
296
+ appContext: getAppContext?.() || { currentRoute: "unknown" },
297
+ deviceInfo: getDeviceInfo()
298
+ });
299
+ await refreshAssignments();
300
+ setSubmitting(false);
301
+ setJustPassed(true);
302
+ setTimeout(() => {
303
+ setJustPassed(false);
304
+ setSelectedTestId(null);
305
+ setTestView("detail");
306
+ }, 1200);
307
+ };
308
+ const handleFail = () => {
309
+ setActiveTab("report");
310
+ setReportType("test_fail");
311
+ };
312
+ const handleSubmitReport = async () => {
313
+ if (!client || !description.trim()) return;
314
+ setSubmitting(true);
315
+ const isTestFailure = reportType === "test_fail" && displayedAssignment;
316
+ await client.submitReport({
317
+ type: reportType,
318
+ description,
319
+ severity: reportType === "bug" || reportType === "test_fail" ? severity : void 0,
320
+ assignmentId: isTestFailure ? displayedAssignment.id : void 0,
321
+ testCaseId: isTestFailure ? displayedAssignment.testCase.id : void 0,
322
+ appContext: getAppContext?.() || { currentRoute: "unknown" },
323
+ deviceInfo: getDeviceInfo()
324
+ });
325
+ setDescription("");
326
+ setSeverity("medium");
327
+ setSubmitted(true);
328
+ if (isTestFailure) {
329
+ await refreshAssignments();
330
+ }
331
+ setTimeout(() => {
332
+ setSubmitted(false);
333
+ setActiveTab("tests");
334
+ if (isTestFailure) {
335
+ setSelectedTestId(null);
336
+ setTestView("list");
337
+ }
338
+ }, 2e3);
339
+ setSubmitting(false);
340
+ };
341
+ const handleNavigate = () => {
342
+ if (displayedAssignment?.testCase.targetRoute && onNavigate) {
343
+ onNavigate(displayedAssignment.testCase.targetRoute);
344
+ setModalVisible(false);
345
+ }
346
+ };
347
+ const handleOpenThread = async (thread) => {
348
+ setSelectedThread(thread);
349
+ setMessageView("thread");
350
+ setLoadingMessages(true);
351
+ const messages = await getThreadMessages(thread.id);
352
+ setThreadMessages(messages);
353
+ setLoadingMessages(false);
354
+ if (thread.unreadCount > 0) {
355
+ await markAsRead(thread.id);
356
+ }
357
+ };
358
+ const handleSendReply = async () => {
359
+ if (!selectedThread || !replyText.trim()) return;
360
+ setSendingReply(true);
361
+ const success = await sendMessage(selectedThread.id, replyText.trim());
362
+ if (success) {
363
+ setReplyText("");
364
+ const messages = await getThreadMessages(selectedThread.id);
365
+ setThreadMessages(messages);
366
+ }
367
+ setSendingReply(false);
368
+ };
369
+ const handleBackToThreadList = () => {
370
+ setMessageView("list");
371
+ setSelectedThread(null);
372
+ setThreadMessages([]);
373
+ setReplyText("");
374
+ };
375
+ const formatRelativeTime = (dateString) => {
376
+ const date = new Date(dateString);
377
+ const now = /* @__PURE__ */ new Date();
378
+ const diffMs = now.getTime() - date.getTime();
379
+ const diffMins = Math.floor(diffMs / 6e4);
380
+ const diffHours = Math.floor(diffMs / 36e5);
381
+ const diffDays = Math.floor(diffMs / 864e5);
382
+ if (diffMins < 1) return "Just now";
383
+ if (diffMins < 60) return `${diffMins}m ago`;
384
+ if (diffHours < 24) return `${diffHours}h ago`;
385
+ if (diffDays === 1) return "Yesterday";
386
+ if (diffDays < 7) return `${diffDays}d ago`;
387
+ return date.toLocaleDateString();
388
+ };
389
+ const formatMessageTime = (dateString) => {
390
+ const date = new Date(dateString);
391
+ return date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
392
+ };
393
+ const getPriorityColor = (priority) => {
394
+ switch (priority) {
395
+ case "urgent":
396
+ return "#DC2626";
397
+ case "high":
398
+ return "#F97316";
399
+ case "normal":
400
+ return "#6B7280";
401
+ case "low":
402
+ return "#9CA3AF";
403
+ default:
404
+ return "#6B7280";
405
+ }
406
+ };
407
+ const getThreadTypeIcon = (type) => {
408
+ switch (type) {
409
+ case "announcement":
410
+ return "\u{1F4E2}";
411
+ case "direct":
412
+ return "\u{1F4AC}";
413
+ case "report":
414
+ return "\u{1F41B}";
415
+ case "general_note":
416
+ return "\u{1F4DD}";
417
+ default:
418
+ return "\u{1F4AC}";
419
+ }
420
+ };
421
+ const staticPositionStyle = position === "bottom-right" ? { right: 16, bottom: 100 } : { left: 16, bottom: 100 };
422
+ const renderTestContent = () => {
423
+ if (!displayedAssignment) return null;
424
+ const template = displayedAssignment.testCase.track?.testTemplate || "steps";
425
+ const steps = displayedAssignment.testCase.steps;
426
+ const info = templateInfo[template];
427
+ const rubricMode = displayedAssignment.testCase.track?.rubricMode || "pass_fail";
428
+ return /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: [styles.templateBadge, { backgroundColor: displayedAssignment.testCase.track?.color ? `${displayedAssignment.testCase.track.color}20` : "#F3F4F6" }] }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.templateIcon }, info.icon), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.templateName }, info.name), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.templateAction }, "\u2022 ", info.action)), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.TouchableOpacity, { onPress: () => setShowSteps(!showSteps), style: styles.stepsToggle }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.stepsToggleText }, showSteps ? "\u25BC" : "\u25B6", " ", template === "freeform" ? "Instructions" : `${steps.length} ${template === "checklist" ? "items" : template === "rubric" ? "criteria" : "steps"}`)), showSteps && template === "steps" && /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.stepsList }, steps.map((step, idx) => /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { key: idx, style: styles.step }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.stepNumber }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.stepNumberText }, step.stepNumber)), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.stepContent }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.stepAction }, step.action), step.expectedResult && /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.stepExpected }, "\u2192 ", step.expectedResult))))), showSteps && template === "checklist" && /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.stepsList }, steps.map((step, idx) => /* @__PURE__ */ import_react2.default.createElement(
429
+ import_react_native2.TouchableOpacity,
430
+ {
431
+ key: idx,
432
+ onPress: () => setCriteriaResults((prev) => {
433
+ const newResults = { ...prev };
434
+ if (prev[idx] === true) {
435
+ delete newResults[idx];
436
+ } else {
437
+ newResults[idx] = true;
438
+ }
439
+ return newResults;
440
+ }),
441
+ style: [
442
+ styles.checklistItem,
443
+ criteriaResults[idx] === true && styles.checklistItemChecked
444
+ ]
445
+ },
446
+ /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: [
447
+ styles.checkbox,
448
+ criteriaResults[idx] === true && styles.checkboxChecked
449
+ ] }, criteriaResults[idx] === true && /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.checkmark }, "\u2713")),
450
+ /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: [
451
+ styles.checklistText,
452
+ criteriaResults[idx] === true && styles.checklistTextChecked
453
+ ] }, step.action)
454
+ )), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.resetRow }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.helperText }, "Tap to check off each item"), Object.keys(criteriaResults).length > 0 && /* @__PURE__ */ import_react2.default.createElement(import_react_native2.TouchableOpacity, { onPress: () => setCriteriaResults({}) }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.resetText }, "\u21BA Reset")))), showSteps && template === "rubric" && /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.stepsList }, steps.map((step, idx) => /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { key: idx, style: styles.rubricItem }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.rubricHeader }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.rubricNumber }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.rubricNumberText }, idx + 1)), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.rubricTitle }, step.action)), step.expectedResult && /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.rubricExpected }, step.expectedResult), rubricMode === "pass_fail" && /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.passFailButtons }, /* @__PURE__ */ import_react2.default.createElement(
455
+ import_react_native2.TouchableOpacity,
456
+ {
457
+ onPress: () => setCriteriaResults((prev) => ({ ...prev, [idx]: true })),
458
+ style: [
459
+ styles.passFailButton,
460
+ criteriaResults[idx] === true && styles.passButtonActive
461
+ ]
462
+ },
463
+ /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: [
464
+ styles.passFailButtonText,
465
+ criteriaResults[idx] === true && styles.passButtonTextActive
466
+ ] }, "\u2713 Pass")
467
+ ), /* @__PURE__ */ import_react2.default.createElement(
468
+ import_react_native2.TouchableOpacity,
469
+ {
470
+ onPress: () => setCriteriaResults((prev) => ({ ...prev, [idx]: false })),
471
+ style: [
472
+ styles.passFailButton,
473
+ criteriaResults[idx] === false && styles.failButtonActive
474
+ ]
475
+ },
476
+ /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: [
477
+ styles.passFailButtonText,
478
+ criteriaResults[idx] === false && styles.failButtonTextActive
479
+ ] }, "\u2717 Fail")
480
+ )), rubricMode === "rating" && /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.ratingButtons }, [1, 2, 3, 4, 5].map((n) => /* @__PURE__ */ import_react2.default.createElement(
481
+ import_react_native2.TouchableOpacity,
482
+ {
483
+ key: n,
484
+ onPress: () => setCriteriaResults((prev) => ({ ...prev, [idx]: n })),
485
+ style: [
486
+ styles.ratingButton,
487
+ criteriaResults[idx] === n && styles.ratingButtonActive
488
+ ]
489
+ },
490
+ /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: [
491
+ styles.ratingButtonText,
492
+ criteriaResults[idx] === n && styles.ratingButtonTextActive
493
+ ] }, n)
494
+ ))))), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.resetRow }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.helperText }, rubricMode === "rating" ? "Rate 1-5 for each criterion" : "Mark each as Pass or Fail"), Object.keys(criteriaResults).length > 0 && /* @__PURE__ */ import_react2.default.createElement(import_react_native2.TouchableOpacity, { onPress: () => setCriteriaResults({}) }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.resetText }, "\u21BA Reset")))), showSteps && template === "freeform" && /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.freeformBox }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.freeformTitle }, "\u{1F4AD} Open Observation"), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.freeformText }, "Review the area and note:"), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.freeformBullet }, "\u2022 What works well"), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.freeformBullet }, "\u2022 Issues or concerns"), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.freeformBullet }, "\u2022 Suggestions")));
495
+ };
496
+ const totalBadgeCount = pendingCount + unreadCount;
497
+ const renderButtonContent = () => /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.fabEmoji }, "\u{1F43B}"), totalBadgeCount > 0 && /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: [styles.badge, unreadCount > 0 && pendingCount === 0 && styles.messageBadge] }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.badgeText }, totalBadgeCount)));
498
+ return /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, draggable ? /* @__PURE__ */ import_react2.default.createElement(
499
+ import_react_native2.Animated.View,
500
+ {
501
+ ...panResponder.panHandlers,
502
+ style: [
503
+ styles.fab,
504
+ styles.fabDraggable,
505
+ buttonStyle,
506
+ {
507
+ transform: [
508
+ { translateX: pan.x },
509
+ { translateY: pan.y }
510
+ ]
511
+ }
512
+ ]
513
+ },
514
+ renderButtonContent()
515
+ ) : /* @__PURE__ */ import_react2.default.createElement(
516
+ import_react_native2.TouchableOpacity,
517
+ {
518
+ style: [styles.fab, staticPositionStyle, buttonStyle],
519
+ onPress: () => setModalVisible(true),
520
+ activeOpacity: 0.8
521
+ },
522
+ renderButtonContent()
523
+ ), /* @__PURE__ */ import_react2.default.createElement(
524
+ import_react_native2.Modal,
525
+ {
526
+ visible: modalVisible,
527
+ animationType: "slide",
528
+ transparent: true,
529
+ onRequestClose: () => setModalVisible(false)
530
+ },
531
+ /* @__PURE__ */ import_react2.default.createElement(
532
+ import_react_native2.KeyboardAvoidingView,
533
+ {
534
+ behavior: import_react_native2.Platform.OS === "ios" ? "padding" : "height",
535
+ style: styles.modalContainer
536
+ },
537
+ /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.modalContent }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.header }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.headerLeft }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.headerEmoji }, "\u{1F43B}"), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, null, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.headerTitle }, "BugBear"), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.headerSubtitle }, testerInfo?.name))), /* @__PURE__ */ import_react2.default.createElement(
538
+ import_react_native2.TouchableOpacity,
539
+ {
540
+ onPress: () => setModalVisible(false),
541
+ style: styles.closeButton
542
+ },
543
+ /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.closeButtonText }, "\u2715")
544
+ )), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.tabs }, /* @__PURE__ */ import_react2.default.createElement(
545
+ import_react_native2.TouchableOpacity,
546
+ {
547
+ style: [styles.tab, activeTab === "tests" && styles.activeTab],
548
+ onPress: () => setActiveTab("tests")
549
+ },
550
+ /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: [styles.tabText, activeTab === "tests" && styles.activeTabText] }, "Tests ", pendingCount > 0 && `(${pendingCount})`)
551
+ ), /* @__PURE__ */ import_react2.default.createElement(
552
+ import_react_native2.TouchableOpacity,
553
+ {
554
+ style: [styles.tab, activeTab === "messages" && styles.activeTab],
555
+ onPress: () => setActiveTab("messages")
556
+ },
557
+ /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.tabWithBadge }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: [styles.tabText, activeTab === "messages" && styles.activeTabText] }, "Messages"), unreadCount > 0 && /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.tabBadge }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.tabBadgeText }, unreadCount)))
558
+ ), /* @__PURE__ */ import_react2.default.createElement(
559
+ import_react_native2.TouchableOpacity,
560
+ {
561
+ style: [styles.tab, activeTab === "report" && styles.activeTab],
562
+ onPress: () => setActiveTab("report")
563
+ },
564
+ /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: [styles.tabText, activeTab === "report" && styles.activeTabText] }, "Report")
565
+ )), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.ScrollView, { style: styles.content }, activeTab === "tests" && /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, null, assignments.length === 0 ? /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.emptyState }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.emptyEmoji }, "\u2705"), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.emptyTitle }, "All caught up!"), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.emptySubtitle }, "No tests assigned")) : testView === "list" ? (
566
+ /* List View - Show all tests */
567
+ /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, null, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.listHeader }, assignments.length, " test", assignments.length !== 1 ? "s" : "", " assigned"), assignments.map((assignment) => /* @__PURE__ */ import_react2.default.createElement(
568
+ import_react_native2.TouchableOpacity,
569
+ {
570
+ key: assignment.id,
571
+ onPress: () => {
572
+ setSelectedTestId(assignment.id);
573
+ setTestView("detail");
574
+ setShowSteps(false);
575
+ },
576
+ style: [
577
+ styles.listItem,
578
+ assignment.id === currentAssignment?.id && styles.listItemCurrent
579
+ ]
580
+ },
581
+ /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.listItemHeader }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.listItemKey }, assignment.testCase.testKey), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.listItemBadges }, assignment.testCase.track && /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: [styles.trackBadge, { backgroundColor: assignment.testCase.track.color }] }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.trackBadgeText }, templateInfo[assignment.testCase.track.testTemplate || "steps"].icon)), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: [
582
+ styles.priorityBadge,
583
+ assignment.testCase.priority === "P0" && styles.priorityP0,
584
+ assignment.testCase.priority === "P1" && styles.priorityP1
585
+ ] }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.priorityText }, assignment.testCase.priority)))),
586
+ /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.listItemTitle, numberOfLines: 2 }, assignment.testCase.title),
587
+ /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.listItemMeta }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.listItemMetaText }, assignment.testCase.steps.length, " ", assignment.testCase.track?.testTemplate === "checklist" ? "items" : assignment.testCase.track?.testTemplate === "rubric" ? "criteria" : "steps"), assignment.id === currentAssignment?.id && /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.currentBadge }, "\u2022 Current"))
588
+ )))
589
+ ) : justPassed ? (
590
+ /* Success state after passing */
591
+ /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.emptyState }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.passedEmoji }, "\u2713"), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.passedTitle }, "Passed!"), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.emptySubtitle }, "Loading next test..."))
592
+ ) : displayedAssignment ? (
593
+ /* Detail View - Show single test */
594
+ /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, null, /* @__PURE__ */ import_react2.default.createElement(
595
+ import_react_native2.TouchableOpacity,
596
+ {
597
+ onPress: () => {
598
+ setTestView("list");
599
+ setSelectedTestId(null);
600
+ },
601
+ style: styles.backButton
602
+ },
603
+ /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.backButtonText }, "\u2190 All Tests (", assignments.length, ")")
604
+ ), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.testCard }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.testHeader }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.testKey }, displayedAssignment.testCase.testKey), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.testHeaderBadges }, displayedAssignment.testCase.track && /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: [styles.trackBadge, { backgroundColor: displayedAssignment.testCase.track.color }] }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.trackBadgeText }, templateInfo[displayedAssignment.testCase.track.testTemplate || "steps"].icon)), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: [
605
+ styles.priorityBadge,
606
+ displayedAssignment.testCase.priority === "P0" && styles.priorityP0,
607
+ displayedAssignment.testCase.priority === "P1" && styles.priorityP1
608
+ ] }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.priorityText }, displayedAssignment.testCase.priority)))), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.testTitle }, displayedAssignment.testCase.title), displayedAssignment.testCase.description && /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.testDescription }, displayedAssignment.testCase.description), displayedAssignment.testCase.targetRoute && onNavigate && /* @__PURE__ */ import_react2.default.createElement(import_react_native2.TouchableOpacity, { onPress: handleNavigate, style: styles.navigateButton }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.navigateButtonText }, "Go to test location \u2192")), renderTestContent(), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.expectedResult }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.expectedLabel }, displayedAssignment.testCase.track?.testTemplate === "checklist" ? "Pass criteria:" : displayedAssignment.testCase.track?.testTemplate === "rubric" ? "Target score:" : "Expected:"), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.expectedText }, displayedAssignment.testCase.expectedResult))), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.actionButtons }, /* @__PURE__ */ import_react2.default.createElement(
609
+ import_react_native2.TouchableOpacity,
610
+ {
611
+ style: styles.failButton,
612
+ onPress: handleFail,
613
+ disabled: submitting
614
+ },
615
+ /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.failButtonText }, "\u2717 Fail")
616
+ ), /* @__PURE__ */ import_react2.default.createElement(
617
+ import_react_native2.TouchableOpacity,
618
+ {
619
+ style: styles.passButton,
620
+ onPress: handlePass,
621
+ disabled: submitting
622
+ },
623
+ /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.passButtonText }, submitting ? "..." : "\u2713 Pass")
624
+ )))
625
+ ) : null), activeTab === "messages" && /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, null, messageView === "list" ? (
626
+ /* Thread List View */
627
+ /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, null, threads.length === 0 ? /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.emptyState }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.emptyEmoji }, "\u{1F4AC}"), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.emptyTitle }, "No messages yet"), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.emptySubtitle }, "Messages from admins will appear here")) : /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, null, threads.map((thread) => /* @__PURE__ */ import_react2.default.createElement(
628
+ import_react_native2.TouchableOpacity,
629
+ {
630
+ key: thread.id,
631
+ onPress: () => handleOpenThread(thread),
632
+ style: [
633
+ styles.threadItem,
634
+ thread.unreadCount > 0 && styles.threadItemUnread
635
+ ]
636
+ },
637
+ /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.threadHeader }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.threadTitleRow }, thread.isPinned && /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.pinnedIcon }, "\u{1F4CC}"), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.threadTypeIcon }, getThreadTypeIcon(thread.threadType)), /* @__PURE__ */ import_react2.default.createElement(
638
+ import_react_native2.Text,
639
+ {
640
+ style: [styles.threadSubject, thread.unreadCount > 0 && styles.threadSubjectUnread],
641
+ numberOfLines: 1
642
+ },
643
+ thread.subject || "No subject"
644
+ )), (thread.priority === "high" || thread.priority === "urgent") && /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: [styles.priorityDot, { backgroundColor: getPriorityColor(thread.priority) }] })),
645
+ thread.lastMessage && /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.threadPreview, numberOfLines: 1 }, thread.lastMessage.senderName, ": ", thread.lastMessage.content),
646
+ /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.threadMeta }, thread.unreadCount > 0 ? /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.unreadBadge }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.unreadBadgeText }, thread.unreadCount, " new")) : /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.threadMetaText }, "Read"), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.threadTime }, formatRelativeTime(thread.lastMessageAt)))
647
+ ))))
648
+ ) : (
649
+ /* Thread Detail View */
650
+ /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.threadDetailContainer }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.TouchableOpacity, { onPress: handleBackToThreadList, style: styles.backButton }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.backButtonText }, "\u2190 Back to Messages")), selectedThread && /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.threadDetailHeader }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.threadDetailIcon }, getThreadTypeIcon(selectedThread.threadType)), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.threadDetailSubject, numberOfLines: 2 }, selectedThread.subject || "No subject")), loadingMessages ? /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.loadingMessages }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.loadingText }, "Loading messages...")) : /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.messagesContainer }, threadMessages.map((message) => /* @__PURE__ */ import_react2.default.createElement(
651
+ import_react_native2.View,
652
+ {
653
+ key: message.id,
654
+ style: [
655
+ styles.messageBubble,
656
+ message.senderType === "tester" ? styles.messageBubbleTester : styles.messageBubbleAdmin
657
+ ]
658
+ },
659
+ /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: [
660
+ styles.messageSender,
661
+ message.senderType === "tester" && styles.messageSenderTester
662
+ ] }, message.senderType === "tester" ? "You" : message.senderName),
663
+ /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: [
664
+ styles.messageContent,
665
+ message.senderType === "tester" && styles.messageContentTester
666
+ ] }, message.content),
667
+ /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: [
668
+ styles.messageTime,
669
+ message.senderType === "tester" && styles.messageTimeTester
670
+ ] }, formatMessageTime(message.createdAt))
671
+ ))))
672
+ )), activeTab === "report" && /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, null, submitted ? /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.emptyState }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.emptyEmoji }, "\u{1F389}"), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.emptyTitle }, "Report submitted!")) : /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.reportTypes }, [
673
+ { type: "bug", label: "\u{1F41B} Bug" },
674
+ { type: "feedback", label: "\u{1F4A1} Feedback" },
675
+ { type: "suggestion", label: "\u2728 Idea" }
676
+ ].map(({ type, label }) => /* @__PURE__ */ import_react2.default.createElement(
677
+ import_react_native2.TouchableOpacity,
678
+ {
679
+ key: type,
680
+ style: [
681
+ styles.reportTypeButton,
682
+ reportType === type && styles.reportTypeActive
683
+ ],
684
+ onPress: () => setReportType(type)
685
+ },
686
+ /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: [
687
+ styles.reportTypeText,
688
+ reportType === type && styles.reportTypeTextActive
689
+ ] }, label)
690
+ ))), (reportType === "bug" || reportType === "test_fail") && /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.severitySection }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.label }, "Severity"), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.severityButtons }, ["critical", "high", "medium", "low"].map((sev) => {
691
+ const activeStyles = {
692
+ critical: styles.severityCriticalActive,
693
+ high: styles.severityHighActive,
694
+ medium: styles.severityMediumActive,
695
+ low: styles.severityLowActive
696
+ };
697
+ return /* @__PURE__ */ import_react2.default.createElement(
698
+ import_react_native2.TouchableOpacity,
699
+ {
700
+ key: sev,
701
+ style: [
702
+ styles.severityButton,
703
+ severity === sev && activeStyles[sev]
704
+ ],
705
+ onPress: () => setSeverity(sev)
706
+ },
707
+ /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: [
708
+ styles.severityText,
709
+ severity === sev && styles.severityTextActive
710
+ ] }, sev)
711
+ );
712
+ }))), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.descriptionSection }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.label }, "What happened?"), /* @__PURE__ */ import_react2.default.createElement(
713
+ import_react_native2.TextInput,
714
+ {
715
+ style: styles.textInput,
716
+ value: description,
717
+ onChangeText: setDescription,
718
+ placeholder: "Describe the issue...",
719
+ placeholderTextColor: "#9CA3AF",
720
+ multiline: true,
721
+ numberOfLines: 4,
722
+ textAlignVertical: "top"
723
+ }
724
+ )), /* @__PURE__ */ import_react2.default.createElement(
725
+ import_react_native2.TouchableOpacity,
726
+ {
727
+ style: [
728
+ styles.submitButton,
729
+ (!description.trim() || submitting) && styles.submitButtonDisabled
730
+ ],
731
+ onPress: handleSubmitReport,
732
+ disabled: !description.trim() || submitting
733
+ },
734
+ /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.submitButtonText }, submitting ? "Submitting..." : "Submit Report")
735
+ )))), activeTab === "messages" && messageView === "thread" && selectedThread ? (
736
+ /* Reply Composer */
737
+ /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.replyComposer }, /* @__PURE__ */ import_react2.default.createElement(
738
+ import_react_native2.TextInput,
739
+ {
740
+ style: styles.replyInput,
741
+ value: replyText,
742
+ onChangeText: setReplyText,
743
+ placeholder: "Type a reply...",
744
+ placeholderTextColor: "#9CA3AF",
745
+ multiline: true,
746
+ maxLength: 1e3
747
+ }
748
+ ), /* @__PURE__ */ import_react2.default.createElement(
749
+ import_react_native2.TouchableOpacity,
750
+ {
751
+ style: [
752
+ styles.sendButton,
753
+ (!replyText.trim() || sendingReply) && styles.sendButtonDisabled
754
+ ],
755
+ onPress: handleSendReply,
756
+ disabled: !replyText.trim() || sendingReply
757
+ },
758
+ /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.sendButtonText }, sendingReply ? "..." : "Send")
759
+ ))
760
+ ) : (
761
+ /* Standard Footer */
762
+ /* @__PURE__ */ import_react2.default.createElement(import_react_native2.View, { style: styles.footer }, activeTab === "messages" ? /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.footerText }, threads.length, " thread", threads.length !== 1 ? "s" : "", " \xB7 ", unreadCount, " unread"), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.TouchableOpacity, { onPress: refreshThreads }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.refreshText }, "\u21BB Refresh"))) : /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.footerText }, pendingCount, " pending \xB7 ", inProgressCount, " in progress"), /* @__PURE__ */ import_react2.default.createElement(import_react_native2.TouchableOpacity, { onPress: refreshAssignments }, /* @__PURE__ */ import_react2.default.createElement(import_react_native2.Text, { style: styles.refreshText }, "\u21BB Refresh"))))
763
+ ))
764
+ )
765
+ ));
766
+ }
767
+ var styles = import_react_native2.StyleSheet.create({
768
+ fab: {
769
+ position: "absolute",
770
+ width: 56,
771
+ height: 56,
772
+ borderRadius: 28,
773
+ backgroundColor: "#7C3AED",
774
+ justifyContent: "center",
775
+ alignItems: "center",
776
+ shadowColor: "#000",
777
+ shadowOffset: { width: 0, height: 4 },
778
+ shadowOpacity: 0.3,
779
+ shadowRadius: 8,
780
+ elevation: 8,
781
+ zIndex: 999999
782
+ },
783
+ fabDraggable: {
784
+ // When draggable, position is controlled by transforms
785
+ // so we use top/left: 0 as the base
786
+ top: 0,
787
+ left: 0
788
+ },
789
+ fabEmoji: {
790
+ fontSize: 28
791
+ },
792
+ badge: {
793
+ position: "absolute",
794
+ top: -4,
795
+ right: -4,
796
+ backgroundColor: "#EF4444",
797
+ borderRadius: 10,
798
+ minWidth: 20,
799
+ height: 20,
800
+ justifyContent: "center",
801
+ alignItems: "center",
802
+ paddingHorizontal: 6
803
+ },
804
+ badgeText: {
805
+ color: "#fff",
806
+ fontSize: 12,
807
+ fontWeight: "bold"
808
+ },
809
+ modalContainer: {
810
+ flex: 1,
811
+ justifyContent: "flex-end",
812
+ backgroundColor: "rgba(0, 0, 0, 0.5)"
813
+ },
814
+ modalContent: {
815
+ backgroundColor: "#fff",
816
+ borderTopLeftRadius: 20,
817
+ borderTopRightRadius: 20,
818
+ maxHeight: "85%"
819
+ },
820
+ header: {
821
+ flexDirection: "row",
822
+ alignItems: "center",
823
+ justifyContent: "space-between",
824
+ backgroundColor: "#7C3AED",
825
+ paddingHorizontal: 16,
826
+ paddingVertical: 12,
827
+ borderTopLeftRadius: 20,
828
+ borderTopRightRadius: 20
829
+ },
830
+ headerLeft: {
831
+ flexDirection: "row",
832
+ alignItems: "center"
833
+ },
834
+ headerEmoji: {
835
+ fontSize: 24,
836
+ marginRight: 10
837
+ },
838
+ headerTitle: {
839
+ color: "#fff",
840
+ fontSize: 16,
841
+ fontWeight: "600"
842
+ },
843
+ headerSubtitle: {
844
+ color: "#DDD6FE",
845
+ fontSize: 12
846
+ },
847
+ closeButton: {
848
+ padding: 8
849
+ },
850
+ closeButtonText: {
851
+ color: "#fff",
852
+ fontSize: 18
853
+ },
854
+ tabs: {
855
+ flexDirection: "row",
856
+ borderBottomWidth: 1,
857
+ borderBottomColor: "#E5E7EB"
858
+ },
859
+ tab: {
860
+ flex: 1,
861
+ paddingVertical: 12,
862
+ alignItems: "center"
863
+ },
864
+ activeTab: {
865
+ borderBottomWidth: 2,
866
+ borderBottomColor: "#7C3AED"
867
+ },
868
+ tabText: {
869
+ fontSize: 14,
870
+ fontWeight: "500",
871
+ color: "#6B7280"
872
+ },
873
+ activeTabText: {
874
+ color: "#7C3AED"
875
+ },
876
+ content: {
877
+ padding: 16,
878
+ maxHeight: 400
879
+ },
880
+ footer: {
881
+ flexDirection: "row",
882
+ justifyContent: "space-between",
883
+ alignItems: "center",
884
+ paddingHorizontal: 16,
885
+ paddingVertical: 12,
886
+ borderTopWidth: 1,
887
+ borderTopColor: "#E5E7EB",
888
+ backgroundColor: "#F9FAFB"
889
+ },
890
+ footerText: {
891
+ fontSize: 12,
892
+ color: "#9CA3AF"
893
+ },
894
+ refreshText: {
895
+ fontSize: 12,
896
+ color: "#6B7280"
897
+ },
898
+ emptyState: {
899
+ alignItems: "center",
900
+ paddingVertical: 40
901
+ },
902
+ emptyEmoji: {
903
+ fontSize: 48
904
+ },
905
+ emptyTitle: {
906
+ fontSize: 18,
907
+ fontWeight: "600",
908
+ color: "#374151",
909
+ marginTop: 12
910
+ },
911
+ passedEmoji: {
912
+ fontSize: 56,
913
+ color: "#22C55E"
914
+ },
915
+ passedTitle: {
916
+ fontSize: 20,
917
+ fontWeight: "600",
918
+ color: "#22C55E",
919
+ marginTop: 12
920
+ },
921
+ emptySubtitle: {
922
+ fontSize: 14,
923
+ color: "#9CA3AF",
924
+ marginTop: 4
925
+ },
926
+ // List view styles
927
+ listHeader: {
928
+ fontSize: 12,
929
+ color: "#6B7280",
930
+ marginBottom: 12
931
+ },
932
+ listItem: {
933
+ backgroundColor: "#F9FAFB",
934
+ borderRadius: 12,
935
+ padding: 12,
936
+ marginBottom: 8,
937
+ borderWidth: 1,
938
+ borderColor: "#E5E7EB"
939
+ },
940
+ listItemCurrent: {
941
+ backgroundColor: "#EDE9FE",
942
+ borderColor: "#C4B5FD"
943
+ },
944
+ listItemHeader: {
945
+ flexDirection: "row",
946
+ justifyContent: "space-between",
947
+ alignItems: "center",
948
+ marginBottom: 4
949
+ },
950
+ listItemKey: {
951
+ fontSize: 12,
952
+ fontFamily: import_react_native2.Platform.OS === "ios" ? "Menlo" : "monospace",
953
+ color: "#6B7280"
954
+ },
955
+ listItemBadges: {
956
+ flexDirection: "row",
957
+ gap: 4
958
+ },
959
+ listItemTitle: {
960
+ fontSize: 14,
961
+ fontWeight: "600",
962
+ color: "#111827",
963
+ marginBottom: 4
964
+ },
965
+ listItemMeta: {
966
+ flexDirection: "row",
967
+ alignItems: "center"
968
+ },
969
+ listItemMetaText: {
970
+ fontSize: 12,
971
+ color: "#9CA3AF"
972
+ },
973
+ currentBadge: {
974
+ fontSize: 12,
975
+ color: "#7C3AED",
976
+ fontWeight: "600",
977
+ marginLeft: 8
978
+ },
979
+ // Back button
980
+ backButton: {
981
+ marginBottom: 12
982
+ },
983
+ backButtonText: {
984
+ fontSize: 14,
985
+ color: "#7C3AED",
986
+ fontWeight: "500"
987
+ },
988
+ // Test card styles
989
+ testCard: {
990
+ backgroundColor: "#F9FAFB",
991
+ borderRadius: 12,
992
+ padding: 16,
993
+ marginBottom: 16
994
+ },
995
+ testHeader: {
996
+ flexDirection: "row",
997
+ justifyContent: "space-between",
998
+ alignItems: "center",
999
+ marginBottom: 8
1000
+ },
1001
+ testHeaderBadges: {
1002
+ flexDirection: "row",
1003
+ gap: 4
1004
+ },
1005
+ testKey: {
1006
+ fontSize: 12,
1007
+ fontFamily: import_react_native2.Platform.OS === "ios" ? "Menlo" : "monospace",
1008
+ color: "#6B7280"
1009
+ },
1010
+ trackBadge: {
1011
+ paddingHorizontal: 6,
1012
+ paddingVertical: 2,
1013
+ borderRadius: 4
1014
+ },
1015
+ trackBadgeText: {
1016
+ fontSize: 12,
1017
+ color: "#fff"
1018
+ },
1019
+ priorityBadge: {
1020
+ backgroundColor: "#E5E7EB",
1021
+ paddingHorizontal: 8,
1022
+ paddingVertical: 2,
1023
+ borderRadius: 4
1024
+ },
1025
+ priorityP0: {
1026
+ backgroundColor: "#FEE2E2"
1027
+ },
1028
+ priorityP1: {
1029
+ backgroundColor: "#FED7AA"
1030
+ },
1031
+ priorityText: {
1032
+ fontSize: 12,
1033
+ fontWeight: "600",
1034
+ color: "#374151"
1035
+ },
1036
+ testTitle: {
1037
+ fontSize: 16,
1038
+ fontWeight: "600",
1039
+ color: "#111827",
1040
+ marginBottom: 4
1041
+ },
1042
+ testDescription: {
1043
+ fontSize: 14,
1044
+ color: "#6B7280",
1045
+ marginBottom: 8
1046
+ },
1047
+ // Navigate button
1048
+ navigateButton: {
1049
+ backgroundColor: "#EFF6FF",
1050
+ borderWidth: 1,
1051
+ borderColor: "#BFDBFE",
1052
+ borderRadius: 8,
1053
+ paddingVertical: 10,
1054
+ paddingHorizontal: 16,
1055
+ alignItems: "center",
1056
+ marginBottom: 12
1057
+ },
1058
+ navigateButtonText: {
1059
+ fontSize: 14,
1060
+ fontWeight: "500",
1061
+ color: "#1D4ED8"
1062
+ },
1063
+ // Template badge
1064
+ templateBadge: {
1065
+ flexDirection: "row",
1066
+ alignItems: "center",
1067
+ paddingHorizontal: 10,
1068
+ paddingVertical: 6,
1069
+ borderRadius: 6,
1070
+ marginBottom: 8
1071
+ },
1072
+ templateIcon: {
1073
+ fontSize: 14,
1074
+ marginRight: 6
1075
+ },
1076
+ templateName: {
1077
+ fontSize: 12,
1078
+ fontWeight: "600",
1079
+ color: "#374151",
1080
+ marginRight: 4
1081
+ },
1082
+ templateAction: {
1083
+ fontSize: 12,
1084
+ color: "#6B7280"
1085
+ },
1086
+ // Steps toggle
1087
+ stepsToggle: {
1088
+ paddingVertical: 8
1089
+ },
1090
+ stepsToggleText: {
1091
+ fontSize: 14,
1092
+ color: "#7C3AED",
1093
+ fontWeight: "500"
1094
+ },
1095
+ stepsList: {
1096
+ marginTop: 8
1097
+ },
1098
+ step: {
1099
+ flexDirection: "row",
1100
+ marginBottom: 12
1101
+ },
1102
+ stepNumber: {
1103
+ width: 24,
1104
+ height: 24,
1105
+ borderRadius: 12,
1106
+ backgroundColor: "#EDE9FE",
1107
+ justifyContent: "center",
1108
+ alignItems: "center",
1109
+ marginRight: 12
1110
+ },
1111
+ stepNumberText: {
1112
+ fontSize: 12,
1113
+ fontWeight: "600",
1114
+ color: "#7C3AED"
1115
+ },
1116
+ stepContent: {
1117
+ flex: 1
1118
+ },
1119
+ stepAction: {
1120
+ fontSize: 14,
1121
+ color: "#374151"
1122
+ },
1123
+ stepExpected: {
1124
+ fontSize: 12,
1125
+ color: "#9CA3AF",
1126
+ marginTop: 2
1127
+ },
1128
+ // Checklist styles
1129
+ checklistItem: {
1130
+ flexDirection: "row",
1131
+ alignItems: "center",
1132
+ backgroundColor: "#fff",
1133
+ borderWidth: 1,
1134
+ borderColor: "#E5E7EB",
1135
+ borderRadius: 8,
1136
+ padding: 12,
1137
+ marginBottom: 8
1138
+ },
1139
+ checklistItemChecked: {
1140
+ backgroundColor: "#ECFDF5",
1141
+ borderColor: "#86EFAC"
1142
+ },
1143
+ checkbox: {
1144
+ width: 24,
1145
+ height: 24,
1146
+ borderRadius: 4,
1147
+ borderWidth: 2,
1148
+ borderColor: "#22D3EE",
1149
+ marginRight: 12,
1150
+ justifyContent: "center",
1151
+ alignItems: "center"
1152
+ },
1153
+ checkboxChecked: {
1154
+ backgroundColor: "#22C55E",
1155
+ borderColor: "#22C55E"
1156
+ },
1157
+ checkmark: {
1158
+ color: "#fff",
1159
+ fontSize: 14,
1160
+ fontWeight: "bold"
1161
+ },
1162
+ checklistText: {
1163
+ flex: 1,
1164
+ fontSize: 14,
1165
+ color: "#374151"
1166
+ },
1167
+ checklistTextChecked: {
1168
+ color: "#15803D"
1169
+ },
1170
+ // Rubric styles
1171
+ rubricItem: {
1172
+ backgroundColor: "#fff",
1173
+ borderWidth: 1,
1174
+ borderColor: "#E5E7EB",
1175
+ borderRadius: 8,
1176
+ padding: 12,
1177
+ marginBottom: 8
1178
+ },
1179
+ rubricHeader: {
1180
+ flexDirection: "row",
1181
+ alignItems: "center",
1182
+ marginBottom: 4
1183
+ },
1184
+ rubricNumber: {
1185
+ width: 24,
1186
+ height: 24,
1187
+ borderRadius: 4,
1188
+ backgroundColor: "#EDE9FE",
1189
+ justifyContent: "center",
1190
+ alignItems: "center",
1191
+ marginRight: 8
1192
+ },
1193
+ rubricNumberText: {
1194
+ fontSize: 12,
1195
+ fontWeight: "600",
1196
+ color: "#7C3AED"
1197
+ },
1198
+ rubricTitle: {
1199
+ flex: 1,
1200
+ fontSize: 14,
1201
+ fontWeight: "500",
1202
+ color: "#111827"
1203
+ },
1204
+ rubricExpected: {
1205
+ fontSize: 12,
1206
+ color: "#6B7280",
1207
+ marginLeft: 32,
1208
+ marginBottom: 8
1209
+ },
1210
+ passFailButtons: {
1211
+ flexDirection: "row",
1212
+ gap: 8,
1213
+ marginLeft: 32
1214
+ },
1215
+ passFailButton: {
1216
+ flex: 1,
1217
+ paddingVertical: 8,
1218
+ paddingHorizontal: 12,
1219
+ borderRadius: 6,
1220
+ backgroundColor: "#F3F4F6",
1221
+ alignItems: "center"
1222
+ },
1223
+ passButtonActive: {
1224
+ backgroundColor: "#22C55E"
1225
+ },
1226
+ failButtonActive: {
1227
+ backgroundColor: "#EF4444"
1228
+ },
1229
+ passFailButtonText: {
1230
+ fontSize: 12,
1231
+ fontWeight: "600",
1232
+ color: "#6B7280"
1233
+ },
1234
+ passButtonTextActive: {
1235
+ color: "#fff"
1236
+ },
1237
+ failButtonTextActive: {
1238
+ color: "#fff"
1239
+ },
1240
+ ratingButtons: {
1241
+ flexDirection: "row",
1242
+ gap: 6,
1243
+ marginLeft: 32
1244
+ },
1245
+ ratingButton: {
1246
+ width: 36,
1247
+ height: 36,
1248
+ borderRadius: 6,
1249
+ backgroundColor: "#F3F4F6",
1250
+ justifyContent: "center",
1251
+ alignItems: "center"
1252
+ },
1253
+ ratingButtonActive: {
1254
+ backgroundColor: "#7C3AED"
1255
+ },
1256
+ ratingButtonText: {
1257
+ fontSize: 14,
1258
+ fontWeight: "600",
1259
+ color: "#6B7280"
1260
+ },
1261
+ ratingButtonTextActive: {
1262
+ color: "#fff"
1263
+ },
1264
+ // Reset row
1265
+ resetRow: {
1266
+ flexDirection: "row",
1267
+ justifyContent: "space-between",
1268
+ alignItems: "center",
1269
+ marginTop: 8
1270
+ },
1271
+ helperText: {
1272
+ fontSize: 12,
1273
+ color: "#9CA3AF"
1274
+ },
1275
+ resetText: {
1276
+ fontSize: 12,
1277
+ color: "#9CA3AF"
1278
+ },
1279
+ // Freeform styles
1280
+ freeformBox: {
1281
+ backgroundColor: "#FFFBEB",
1282
+ borderWidth: 1,
1283
+ borderColor: "#FDE68A",
1284
+ borderRadius: 8,
1285
+ padding: 12,
1286
+ marginTop: 8
1287
+ },
1288
+ freeformTitle: {
1289
+ fontSize: 14,
1290
+ fontWeight: "600",
1291
+ color: "#92400E",
1292
+ marginBottom: 4
1293
+ },
1294
+ freeformText: {
1295
+ fontSize: 12,
1296
+ color: "#A16207",
1297
+ marginBottom: 4
1298
+ },
1299
+ freeformBullet: {
1300
+ fontSize: 12,
1301
+ color: "#A16207",
1302
+ marginLeft: 8
1303
+ },
1304
+ // Expected result
1305
+ expectedResult: {
1306
+ backgroundColor: "#ECFDF5",
1307
+ padding: 12,
1308
+ borderRadius: 8,
1309
+ marginTop: 12
1310
+ },
1311
+ expectedLabel: {
1312
+ fontSize: 12,
1313
+ fontWeight: "600",
1314
+ color: "#065F46",
1315
+ marginBottom: 4
1316
+ },
1317
+ expectedText: {
1318
+ fontSize: 14,
1319
+ color: "#047857"
1320
+ },
1321
+ // Action buttons
1322
+ actionButtons: {
1323
+ flexDirection: "row",
1324
+ gap: 12
1325
+ },
1326
+ failButton: {
1327
+ flex: 1,
1328
+ backgroundColor: "#FEE2E2",
1329
+ paddingVertical: 14,
1330
+ borderRadius: 12,
1331
+ alignItems: "center"
1332
+ },
1333
+ failButtonText: {
1334
+ fontSize: 16,
1335
+ fontWeight: "600",
1336
+ color: "#DC2626"
1337
+ },
1338
+ passButton: {
1339
+ flex: 1,
1340
+ backgroundColor: "#16A34A",
1341
+ paddingVertical: 14,
1342
+ borderRadius: 12,
1343
+ alignItems: "center"
1344
+ },
1345
+ passButtonText: {
1346
+ fontSize: 16,
1347
+ fontWeight: "600",
1348
+ color: "#fff"
1349
+ },
1350
+ // Report form styles
1351
+ reportTypes: {
1352
+ flexDirection: "row",
1353
+ gap: 8,
1354
+ marginBottom: 16
1355
+ },
1356
+ reportTypeButton: {
1357
+ flex: 1,
1358
+ backgroundColor: "#F3F4F6",
1359
+ paddingVertical: 10,
1360
+ borderRadius: 8,
1361
+ alignItems: "center"
1362
+ },
1363
+ reportTypeActive: {
1364
+ backgroundColor: "#EDE9FE",
1365
+ borderWidth: 2,
1366
+ borderColor: "#7C3AED"
1367
+ },
1368
+ reportTypeText: {
1369
+ fontSize: 14,
1370
+ color: "#6B7280"
1371
+ },
1372
+ reportTypeTextActive: {
1373
+ color: "#7C3AED",
1374
+ fontWeight: "600"
1375
+ },
1376
+ severitySection: {
1377
+ marginBottom: 16
1378
+ },
1379
+ label: {
1380
+ fontSize: 14,
1381
+ fontWeight: "500",
1382
+ color: "#374151",
1383
+ marginBottom: 8
1384
+ },
1385
+ severityButtons: {
1386
+ flexDirection: "row",
1387
+ gap: 6
1388
+ },
1389
+ severityButton: {
1390
+ flex: 1,
1391
+ backgroundColor: "#F3F4F6",
1392
+ paddingVertical: 8,
1393
+ borderRadius: 6,
1394
+ alignItems: "center"
1395
+ },
1396
+ severityCriticalActive: {
1397
+ backgroundColor: "#DC2626"
1398
+ },
1399
+ severityHighActive: {
1400
+ backgroundColor: "#F97316"
1401
+ },
1402
+ severityMediumActive: {
1403
+ backgroundColor: "#EAB308"
1404
+ },
1405
+ severityLowActive: {
1406
+ backgroundColor: "#6B7280"
1407
+ },
1408
+ severityText: {
1409
+ fontSize: 12,
1410
+ color: "#6B7280",
1411
+ textTransform: "capitalize"
1412
+ },
1413
+ severityTextActive: {
1414
+ color: "#fff",
1415
+ fontWeight: "600"
1416
+ },
1417
+ descriptionSection: {
1418
+ marginBottom: 16
1419
+ },
1420
+ textInput: {
1421
+ backgroundColor: "#F9FAFB",
1422
+ borderWidth: 1,
1423
+ borderColor: "#E5E7EB",
1424
+ borderRadius: 12,
1425
+ padding: 12,
1426
+ fontSize: 14,
1427
+ minHeight: 100,
1428
+ color: "#111827"
1429
+ },
1430
+ submitButton: {
1431
+ backgroundColor: "#7C3AED",
1432
+ paddingVertical: 14,
1433
+ borderRadius: 12,
1434
+ alignItems: "center"
1435
+ },
1436
+ submitButtonDisabled: {
1437
+ opacity: 0.5
1438
+ },
1439
+ submitButtonText: {
1440
+ fontSize: 16,
1441
+ fontWeight: "600",
1442
+ color: "#fff"
1443
+ },
1444
+ // Message badge (when only messages, not tests)
1445
+ messageBadge: {
1446
+ backgroundColor: "#3B82F6"
1447
+ },
1448
+ // Tab with badge
1449
+ tabWithBadge: {
1450
+ flexDirection: "row",
1451
+ alignItems: "center"
1452
+ },
1453
+ tabBadge: {
1454
+ backgroundColor: "#EF4444",
1455
+ borderRadius: 8,
1456
+ minWidth: 16,
1457
+ height: 16,
1458
+ justifyContent: "center",
1459
+ alignItems: "center",
1460
+ marginLeft: 4,
1461
+ paddingHorizontal: 4
1462
+ },
1463
+ tabBadgeText: {
1464
+ color: "#fff",
1465
+ fontSize: 10,
1466
+ fontWeight: "bold"
1467
+ },
1468
+ // Thread list styles
1469
+ threadItem: {
1470
+ backgroundColor: "#F9FAFB",
1471
+ borderRadius: 12,
1472
+ padding: 12,
1473
+ marginBottom: 8,
1474
+ borderWidth: 1,
1475
+ borderColor: "#E5E7EB"
1476
+ },
1477
+ threadItemUnread: {
1478
+ backgroundColor: "#EFF6FF",
1479
+ borderColor: "#BFDBFE"
1480
+ },
1481
+ threadHeader: {
1482
+ flexDirection: "row",
1483
+ justifyContent: "space-between",
1484
+ alignItems: "center",
1485
+ marginBottom: 4
1486
+ },
1487
+ threadTitleRow: {
1488
+ flexDirection: "row",
1489
+ alignItems: "center",
1490
+ flex: 1
1491
+ },
1492
+ pinnedIcon: {
1493
+ fontSize: 12,
1494
+ marginRight: 4
1495
+ },
1496
+ threadTypeIcon: {
1497
+ fontSize: 14,
1498
+ marginRight: 6
1499
+ },
1500
+ threadSubject: {
1501
+ fontSize: 14,
1502
+ fontWeight: "500",
1503
+ color: "#374151",
1504
+ flex: 1
1505
+ },
1506
+ threadSubjectUnread: {
1507
+ fontWeight: "600",
1508
+ color: "#111827"
1509
+ },
1510
+ priorityDot: {
1511
+ width: 8,
1512
+ height: 8,
1513
+ borderRadius: 4,
1514
+ marginLeft: 8
1515
+ },
1516
+ threadPreview: {
1517
+ fontSize: 13,
1518
+ color: "#6B7280",
1519
+ marginBottom: 4
1520
+ },
1521
+ threadMeta: {
1522
+ flexDirection: "row",
1523
+ justifyContent: "space-between",
1524
+ alignItems: "center"
1525
+ },
1526
+ threadMetaText: {
1527
+ fontSize: 12,
1528
+ color: "#9CA3AF"
1529
+ },
1530
+ unreadBadge: {
1531
+ backgroundColor: "#3B82F6",
1532
+ paddingHorizontal: 8,
1533
+ paddingVertical: 2,
1534
+ borderRadius: 10
1535
+ },
1536
+ unreadBadgeText: {
1537
+ fontSize: 11,
1538
+ fontWeight: "600",
1539
+ color: "#fff"
1540
+ },
1541
+ threadTime: {
1542
+ fontSize: 12,
1543
+ color: "#9CA3AF"
1544
+ },
1545
+ // Thread detail styles
1546
+ threadDetailContainer: {
1547
+ flex: 1
1548
+ },
1549
+ threadDetailHeader: {
1550
+ flexDirection: "row",
1551
+ alignItems: "center",
1552
+ backgroundColor: "#F3F4F6",
1553
+ padding: 12,
1554
+ borderRadius: 8,
1555
+ marginBottom: 12
1556
+ },
1557
+ threadDetailIcon: {
1558
+ fontSize: 18,
1559
+ marginRight: 8
1560
+ },
1561
+ threadDetailSubject: {
1562
+ flex: 1,
1563
+ fontSize: 15,
1564
+ fontWeight: "600",
1565
+ color: "#111827"
1566
+ },
1567
+ loadingMessages: {
1568
+ padding: 20,
1569
+ alignItems: "center"
1570
+ },
1571
+ loadingText: {
1572
+ fontSize: 14,
1573
+ color: "#6B7280"
1574
+ },
1575
+ messagesContainer: {
1576
+ paddingBottom: 8
1577
+ },
1578
+ messageBubble: {
1579
+ maxWidth: "80%",
1580
+ padding: 12,
1581
+ borderRadius: 16,
1582
+ marginBottom: 8
1583
+ },
1584
+ messageBubbleAdmin: {
1585
+ alignSelf: "flex-start",
1586
+ backgroundColor: "#F3F4F6",
1587
+ borderBottomLeftRadius: 4
1588
+ },
1589
+ messageBubbleTester: {
1590
+ alignSelf: "flex-end",
1591
+ backgroundColor: "#7C3AED",
1592
+ borderBottomRightRadius: 4
1593
+ },
1594
+ messageSender: {
1595
+ fontSize: 12,
1596
+ fontWeight: "600",
1597
+ color: "#6B7280",
1598
+ marginBottom: 2
1599
+ },
1600
+ messageSenderTester: {
1601
+ color: "#DDD6FE"
1602
+ },
1603
+ messageContent: {
1604
+ fontSize: 14,
1605
+ color: "#111827",
1606
+ lineHeight: 20
1607
+ },
1608
+ messageContentTester: {
1609
+ color: "#fff"
1610
+ },
1611
+ messageTime: {
1612
+ fontSize: 11,
1613
+ color: "#9CA3AF",
1614
+ marginTop: 4,
1615
+ textAlign: "right"
1616
+ },
1617
+ messageTimeTester: {
1618
+ color: "#C4B5FD"
1619
+ },
1620
+ // Reply composer styles
1621
+ replyComposer: {
1622
+ flexDirection: "row",
1623
+ alignItems: "flex-end",
1624
+ padding: 12,
1625
+ borderTopWidth: 1,
1626
+ borderTopColor: "#E5E7EB",
1627
+ backgroundColor: "#fff"
1628
+ },
1629
+ replyInput: {
1630
+ flex: 1,
1631
+ backgroundColor: "#F9FAFB",
1632
+ borderWidth: 1,
1633
+ borderColor: "#E5E7EB",
1634
+ borderRadius: 20,
1635
+ paddingHorizontal: 16,
1636
+ paddingVertical: 10,
1637
+ fontSize: 14,
1638
+ color: "#111827",
1639
+ maxHeight: 100,
1640
+ marginRight: 8
1641
+ },
1642
+ sendButton: {
1643
+ backgroundColor: "#7C3AED",
1644
+ paddingHorizontal: 16,
1645
+ paddingVertical: 10,
1646
+ borderRadius: 20,
1647
+ justifyContent: "center",
1648
+ alignItems: "center"
1649
+ },
1650
+ sendButtonDisabled: {
1651
+ backgroundColor: "#C4B5FD"
1652
+ },
1653
+ sendButtonText: {
1654
+ fontSize: 14,
1655
+ fontWeight: "600",
1656
+ color: "#fff"
1657
+ }
1658
+ });
1659
+ // Annotate the CommonJS export names for ESM import in node:
1660
+ 0 && (module.exports = {
1661
+ BugBearButton,
1662
+ BugBearProvider,
1663
+ useBugBear
1664
+ });