@bbearai/react 0.1.7 → 0.1.9
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 +31 -1
- package/dist/index.d.ts +31 -1
- package/dist/index.js +1383 -158
- package/dist/index.mjs +1384 -159
- package/package.json +11 -3
package/dist/index.js
CHANGED
|
@@ -50,7 +50,25 @@ var BugBearContext = (0, import_react.createContext)({
|
|
|
50
50
|
},
|
|
51
51
|
updateTesterProfile: async () => ({ success: false }),
|
|
52
52
|
refreshTesterInfo: async () => {
|
|
53
|
-
}
|
|
53
|
+
},
|
|
54
|
+
// QA Sessions
|
|
55
|
+
activeSession: null,
|
|
56
|
+
sessionFindings: [],
|
|
57
|
+
startSession: async () => ({ success: false }),
|
|
58
|
+
endSession: async () => ({ success: false }),
|
|
59
|
+
addFinding: async () => ({ success: false }),
|
|
60
|
+
refreshSession: async () => {
|
|
61
|
+
},
|
|
62
|
+
// Messaging
|
|
63
|
+
threads: [],
|
|
64
|
+
unreadCount: 0,
|
|
65
|
+
refreshThreads: async () => {
|
|
66
|
+
},
|
|
67
|
+
getThreadMessages: async () => [],
|
|
68
|
+
sendMessage: async () => false,
|
|
69
|
+
markAsRead: async () => {
|
|
70
|
+
},
|
|
71
|
+
createThread: async () => ({ success: false })
|
|
54
72
|
});
|
|
55
73
|
function useBugBear() {
|
|
56
74
|
return (0, import_react.useContext)(BugBearContext);
|
|
@@ -63,11 +81,86 @@ function BugBearProvider({ config, children, enabled = true }) {
|
|
|
63
81
|
const [assignments, setAssignments] = (0, import_react.useState)([]);
|
|
64
82
|
const [isLoading, setIsLoading] = (0, import_react.useState)(true);
|
|
65
83
|
const hasInitialized = (0, import_react.useRef)(false);
|
|
84
|
+
const [activeSession, setActiveSession] = (0, import_react.useState)(null);
|
|
85
|
+
const [sessionFindings, setSessionFindings] = (0, import_react.useState)([]);
|
|
86
|
+
const [threads, setThreads] = (0, import_react.useState)([]);
|
|
87
|
+
const [unreadCount, setUnreadCount] = (0, import_react.useState)(0);
|
|
66
88
|
const refreshAssignments = (0, import_react.useCallback)(async () => {
|
|
67
89
|
if (!client) return;
|
|
68
90
|
const newAssignments = await client.getAssignedTests();
|
|
69
91
|
setAssignments(newAssignments);
|
|
70
92
|
}, [client]);
|
|
93
|
+
const refreshSession = (0, import_react.useCallback)(async () => {
|
|
94
|
+
if (!client) return;
|
|
95
|
+
const session = await client.getActiveSession();
|
|
96
|
+
setActiveSession(session);
|
|
97
|
+
if (session) {
|
|
98
|
+
const findings = await client.getSessionFindings(session.id);
|
|
99
|
+
setSessionFindings(findings);
|
|
100
|
+
} else {
|
|
101
|
+
setSessionFindings([]);
|
|
102
|
+
}
|
|
103
|
+
}, [client]);
|
|
104
|
+
const startSession = (0, import_react.useCallback)(async (options = {}) => {
|
|
105
|
+
if (!client) return { success: false, error: "Client not initialized" };
|
|
106
|
+
const result = await client.startSession(options);
|
|
107
|
+
if (result.success && result.session) {
|
|
108
|
+
setActiveSession(result.session);
|
|
109
|
+
setSessionFindings([]);
|
|
110
|
+
}
|
|
111
|
+
return { success: result.success, error: result.error };
|
|
112
|
+
}, [client]);
|
|
113
|
+
const endSession = (0, import_react.useCallback)(async (notes) => {
|
|
114
|
+
if (!client || !activeSession) return { success: false, error: "No active session" };
|
|
115
|
+
const routesCovered = client.getNavigationHistory();
|
|
116
|
+
const result = await client.endSession(activeSession.id, { notes, routesCovered });
|
|
117
|
+
if (result.success) {
|
|
118
|
+
setActiveSession(null);
|
|
119
|
+
setSessionFindings([]);
|
|
120
|
+
}
|
|
121
|
+
return { success: result.success, error: result.error };
|
|
122
|
+
}, [client, activeSession]);
|
|
123
|
+
const addFinding = (0, import_react.useCallback)(async (options) => {
|
|
124
|
+
if (!client || !activeSession) return { success: false, error: "No active session" };
|
|
125
|
+
const result = await client.addFinding(activeSession.id, options);
|
|
126
|
+
if (result.success && result.finding) {
|
|
127
|
+
setSessionFindings((prev) => [...prev, result.finding]);
|
|
128
|
+
setActiveSession((prev) => prev ? { ...prev, findingsCount: prev.findingsCount + 1 } : null);
|
|
129
|
+
}
|
|
130
|
+
return result;
|
|
131
|
+
}, [client, activeSession]);
|
|
132
|
+
const refreshThreads = (0, import_react.useCallback)(async () => {
|
|
133
|
+
if (!client) return;
|
|
134
|
+
const newThreads = await client.getThreadsForTester();
|
|
135
|
+
setThreads(newThreads);
|
|
136
|
+
const totalUnread = newThreads.reduce((sum, t) => sum + t.unreadCount, 0);
|
|
137
|
+
setUnreadCount(totalUnread);
|
|
138
|
+
}, [client]);
|
|
139
|
+
const getThreadMessages = (0, import_react.useCallback)(async (threadId) => {
|
|
140
|
+
if (!client) return [];
|
|
141
|
+
return client.getThreadMessages(threadId);
|
|
142
|
+
}, [client]);
|
|
143
|
+
const sendMessage = (0, import_react.useCallback)(async (threadId, content) => {
|
|
144
|
+
if (!client) return false;
|
|
145
|
+
const success = await client.sendMessage(threadId, content);
|
|
146
|
+
if (success) {
|
|
147
|
+
await refreshThreads();
|
|
148
|
+
}
|
|
149
|
+
return success;
|
|
150
|
+
}, [client, refreshThreads]);
|
|
151
|
+
const markAsRead = (0, import_react.useCallback)(async (threadId) => {
|
|
152
|
+
if (!client) return;
|
|
153
|
+
await client.markThreadAsRead(threadId);
|
|
154
|
+
await refreshThreads();
|
|
155
|
+
}, [client, refreshThreads]);
|
|
156
|
+
const createThread = (0, import_react.useCallback)(async (options) => {
|
|
157
|
+
if (!client) return { success: false, error: "Client not initialized" };
|
|
158
|
+
const result = await client.createThread(options);
|
|
159
|
+
if (result.success) {
|
|
160
|
+
await refreshThreads();
|
|
161
|
+
}
|
|
162
|
+
return result;
|
|
163
|
+
}, [client, refreshThreads]);
|
|
71
164
|
const initializeBugBear = (0, import_react.useCallback)(async (bugBearClient) => {
|
|
72
165
|
setIsLoading(true);
|
|
73
166
|
try {
|
|
@@ -80,8 +173,20 @@ function BugBearProvider({ config, children, enabled = true }) {
|
|
|
80
173
|
setTesterInfo(info);
|
|
81
174
|
setIsTester(!!info);
|
|
82
175
|
if (info && qaEnabled) {
|
|
83
|
-
const newAssignments = await
|
|
176
|
+
const [newAssignments, session, newThreads] = await Promise.all([
|
|
177
|
+
bugBearClient.getAssignedTests(),
|
|
178
|
+
bugBearClient.getActiveSession(),
|
|
179
|
+
bugBearClient.getThreadsForTester()
|
|
180
|
+
]);
|
|
84
181
|
setAssignments(newAssignments);
|
|
182
|
+
setActiveSession(session);
|
|
183
|
+
setThreads(newThreads);
|
|
184
|
+
const totalUnread = newThreads.reduce((sum, t) => sum + t.unreadCount, 0);
|
|
185
|
+
setUnreadCount(totalUnread);
|
|
186
|
+
if (session) {
|
|
187
|
+
const findings = await bugBearClient.getSessionFindings(session.id);
|
|
188
|
+
setSessionFindings(findings);
|
|
189
|
+
}
|
|
85
190
|
}
|
|
86
191
|
} catch (err) {
|
|
87
192
|
console.error("BugBear: Init error", err);
|
|
@@ -136,7 +241,22 @@ function BugBearProvider({ config, children, enabled = true }) {
|
|
|
136
241
|
onNavigate: config.onNavigate,
|
|
137
242
|
refreshTesterStatus,
|
|
138
243
|
updateTesterProfile,
|
|
139
|
-
refreshTesterInfo
|
|
244
|
+
refreshTesterInfo,
|
|
245
|
+
// QA Sessions
|
|
246
|
+
activeSession,
|
|
247
|
+
sessionFindings,
|
|
248
|
+
startSession,
|
|
249
|
+
endSession,
|
|
250
|
+
addFinding,
|
|
251
|
+
refreshSession,
|
|
252
|
+
// Messaging
|
|
253
|
+
threads,
|
|
254
|
+
unreadCount,
|
|
255
|
+
refreshThreads,
|
|
256
|
+
getThreadMessages,
|
|
257
|
+
sendMessage,
|
|
258
|
+
markAsRead,
|
|
259
|
+
createThread
|
|
140
260
|
},
|
|
141
261
|
children
|
|
142
262
|
}
|
|
@@ -215,13 +335,77 @@ function BugBearPanel({
|
|
|
215
335
|
defaultCollapsed = false,
|
|
216
336
|
draggable = true
|
|
217
337
|
}) {
|
|
218
|
-
const { client, shouldShowWidget, testerInfo, assignments, currentAssignment, refreshAssignments, isLoading, onNavigate, updateTesterProfile, refreshTesterInfo } = useBugBear();
|
|
338
|
+
const { client, shouldShowWidget, testerInfo, assignments, currentAssignment, refreshAssignments, isLoading, onNavigate, updateTesterProfile, refreshTesterInfo, activeSession, sessionFindings, startSession, endSession, addFinding, threads, unreadCount, refreshThreads, getThreadMessages, sendMessage, markAsRead, createThread } = useBugBear();
|
|
219
339
|
const [collapsed, setCollapsed] = (0, import_react2.useState)(defaultCollapsed);
|
|
220
340
|
const [activeTab, setActiveTab] = (0, import_react2.useState)("tests");
|
|
221
341
|
const [showSteps, setShowSteps] = (0, import_react2.useState)(false);
|
|
222
342
|
const [testView, setTestView] = (0, import_react2.useState)("detail");
|
|
223
343
|
const [selectedTestId, setSelectedTestId] = (0, import_react2.useState)(null);
|
|
344
|
+
const [messageView, setMessageView] = (0, import_react2.useState)("list");
|
|
345
|
+
const [selectedThread, setSelectedThread] = (0, import_react2.useState)(null);
|
|
346
|
+
const [threadMessages, setThreadMessages] = (0, import_react2.useState)([]);
|
|
347
|
+
const [replyText, setReplyText] = (0, import_react2.useState)("");
|
|
348
|
+
const [sendingReply, setSendingReply] = (0, import_react2.useState)(false);
|
|
349
|
+
const [loadingMessages, setLoadingMessages] = (0, import_react2.useState)(false);
|
|
350
|
+
const [composeSubject, setComposeSubject] = (0, import_react2.useState)("");
|
|
351
|
+
const [composeMessage, setComposeMessage] = (0, import_react2.useState)("");
|
|
352
|
+
const [sendingNewMessage, setSendingNewMessage] = (0, import_react2.useState)(false);
|
|
224
353
|
const displayedAssignment = selectedTestId ? assignments.find((a) => a.id === selectedTestId) || currentAssignment : currentAssignment;
|
|
354
|
+
const groupedAssignments = (0, import_react2.useMemo)(() => {
|
|
355
|
+
const groups = /* @__PURE__ */ new Map();
|
|
356
|
+
for (const assignment of assignments) {
|
|
357
|
+
const groupId = assignment.testCase.group?.id || "ungrouped";
|
|
358
|
+
const group = assignment.testCase.group || null;
|
|
359
|
+
if (!groups.has(groupId)) {
|
|
360
|
+
groups.set(groupId, {
|
|
361
|
+
group,
|
|
362
|
+
assignments: [],
|
|
363
|
+
stats: { total: 0, passed: 0, failed: 0, pending: 0, skipped: 0 }
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
const folder = groups.get(groupId);
|
|
367
|
+
folder.assignments.push(assignment);
|
|
368
|
+
folder.stats.total++;
|
|
369
|
+
if (assignment.status === "passed") folder.stats.passed++;
|
|
370
|
+
else if (assignment.status === "failed") folder.stats.failed++;
|
|
371
|
+
else if (assignment.status === "skipped") folder.stats.skipped++;
|
|
372
|
+
else folder.stats.pending++;
|
|
373
|
+
}
|
|
374
|
+
const sortedGroups = Array.from(groups.values()).sort((a, b) => {
|
|
375
|
+
if (!a.group && !b.group) return 0;
|
|
376
|
+
if (!a.group) return 1;
|
|
377
|
+
if (!b.group) return -1;
|
|
378
|
+
return a.group.sortOrder - b.group.sortOrder;
|
|
379
|
+
});
|
|
380
|
+
return sortedGroups;
|
|
381
|
+
}, [assignments]);
|
|
382
|
+
const toggleFolderCollapse = (0, import_react2.useCallback)((groupId) => {
|
|
383
|
+
setCollapsedFolders((prev) => {
|
|
384
|
+
const next = new Set(prev);
|
|
385
|
+
if (next.has(groupId)) {
|
|
386
|
+
next.delete(groupId);
|
|
387
|
+
} else {
|
|
388
|
+
next.add(groupId);
|
|
389
|
+
}
|
|
390
|
+
return next;
|
|
391
|
+
});
|
|
392
|
+
}, []);
|
|
393
|
+
const getStatusBadge = (status) => {
|
|
394
|
+
switch (status) {
|
|
395
|
+
case "passed":
|
|
396
|
+
return { icon: "\u2705", label: "Passed", className: "bg-green-900/30 text-green-400" };
|
|
397
|
+
case "failed":
|
|
398
|
+
return { icon: "\u274C", label: "Failed", className: "bg-red-900/30 text-red-400" };
|
|
399
|
+
case "skipped":
|
|
400
|
+
return { icon: "\u23ED\uFE0F", label: "Skipped", className: "bg-yellow-900/30 text-yellow-400" };
|
|
401
|
+
case "in_progress":
|
|
402
|
+
return { icon: "\u{1F504}", label: "In Progress", className: "bg-blue-900/30 text-blue-400" };
|
|
403
|
+
case "blocked":
|
|
404
|
+
return { icon: "\u{1F6AB}", label: "Blocked", className: "bg-orange-900/30 text-orange-400" };
|
|
405
|
+
default:
|
|
406
|
+
return { icon: "\u23F3", label: "Pending", className: "bg-zinc-700 text-zinc-400" };
|
|
407
|
+
}
|
|
408
|
+
};
|
|
225
409
|
const [panelPosition, setPanelPosition] = (0, import_react2.useState)(null);
|
|
226
410
|
const [isDragging, setIsDragging] = (0, import_react2.useState)(false);
|
|
227
411
|
const dragStartRef = (0, import_react2.useRef)(null);
|
|
@@ -232,7 +416,21 @@ function BugBearPanel({
|
|
|
232
416
|
const [submitting, setSubmitting] = (0, import_react2.useState)(false);
|
|
233
417
|
const [submitted, setSubmitted] = (0, import_react2.useState)(false);
|
|
234
418
|
const [justPassed, setJustPassed] = (0, import_react2.useState)(false);
|
|
419
|
+
const [showFeedbackPrompt, setShowFeedbackPrompt] = (0, import_react2.useState)(false);
|
|
420
|
+
const [pendingFeedbackStatus, setPendingFeedbackStatus] = (0, import_react2.useState)(null);
|
|
421
|
+
const [feedbackRating, setFeedbackRating] = (0, import_react2.useState)(5);
|
|
422
|
+
const [feedbackNote, setFeedbackNote] = (0, import_react2.useState)("");
|
|
423
|
+
const [feedbackFlags, setFeedbackFlags] = (0, import_react2.useState)({
|
|
424
|
+
isOutdated: false,
|
|
425
|
+
needsMoreDetail: false,
|
|
426
|
+
stepsUnclear: false,
|
|
427
|
+
expectedResultUnclear: false
|
|
428
|
+
});
|
|
235
429
|
const [criteriaResults, setCriteriaResults] = (0, import_react2.useState)({});
|
|
430
|
+
const [showSkipModal, setShowSkipModal] = (0, import_react2.useState)(false);
|
|
431
|
+
const [selectedSkipReason, setSelectedSkipReason] = (0, import_react2.useState)(null);
|
|
432
|
+
const [skipNotes, setSkipNotes] = (0, import_react2.useState)("");
|
|
433
|
+
const [skipping, setSkipping] = (0, import_react2.useState)(false);
|
|
236
434
|
const [profileEditing, setProfileEditing] = (0, import_react2.useState)(false);
|
|
237
435
|
const [profileName, setProfileName] = (0, import_react2.useState)("");
|
|
238
436
|
const [profileAdditionalEmails, setProfileAdditionalEmails] = (0, import_react2.useState)([]);
|
|
@@ -241,6 +439,23 @@ function BugBearPanel({
|
|
|
241
439
|
const [savingProfile, setSavingProfile] = (0, import_react2.useState)(false);
|
|
242
440
|
const [profileSaved, setProfileSaved] = (0, import_react2.useState)(false);
|
|
243
441
|
const [showProfileOverlay, setShowProfileOverlay] = (0, import_react2.useState)(false);
|
|
442
|
+
const [collapsedFolders, setCollapsedFolders] = (0, import_react2.useState)(/* @__PURE__ */ new Set());
|
|
443
|
+
const [startingSession, setStartingSession] = (0, import_react2.useState)(false);
|
|
444
|
+
const [sessionFocusArea, setSessionFocusArea] = (0, import_react2.useState)("");
|
|
445
|
+
const [sessionPlatform, setSessionPlatform] = (0, import_react2.useState)("web");
|
|
446
|
+
const [suggestedRoutes, setSuggestedRoutes] = (0, import_react2.useState)([]);
|
|
447
|
+
const [focusAreas, setFocusAreas] = (0, import_react2.useState)([]);
|
|
448
|
+
const [showAddFinding, setShowAddFinding] = (0, import_react2.useState)(false);
|
|
449
|
+
const [findingType, setFindingType] = (0, import_react2.useState)("bug");
|
|
450
|
+
const [findingTitle, setFindingTitle] = (0, import_react2.useState)("");
|
|
451
|
+
const [findingDescription, setFindingDescription] = (0, import_react2.useState)("");
|
|
452
|
+
const [findingSeverity, setFindingSeverity] = (0, import_react2.useState)("medium");
|
|
453
|
+
const [addingFinding, setAddingFinding] = (0, import_react2.useState)(false);
|
|
454
|
+
const [endingSession, setEndingSession] = (0, import_react2.useState)(false);
|
|
455
|
+
const [sessionNotes, setSessionNotes] = (0, import_react2.useState)("");
|
|
456
|
+
const [showEndConfirm, setShowEndConfirm] = (0, import_react2.useState)(false);
|
|
457
|
+
const [sessionElapsedTime, setSessionElapsedTime] = (0, import_react2.useState)(0);
|
|
458
|
+
const [assignmentElapsedTime, setAssignmentElapsedTime] = (0, import_react2.useState)(0);
|
|
244
459
|
(0, import_react2.useEffect)(() => {
|
|
245
460
|
if (typeof window === "undefined") return;
|
|
246
461
|
try {
|
|
@@ -275,6 +490,84 @@ function BugBearPanel({
|
|
|
275
490
|
setCriteriaResults({});
|
|
276
491
|
setShowSteps(false);
|
|
277
492
|
}, [displayedAssignment?.id]);
|
|
493
|
+
(0, import_react2.useEffect)(() => {
|
|
494
|
+
if (!activeSession) {
|
|
495
|
+
setSessionElapsedTime(0);
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
const startTime = new Date(activeSession.startedAt).getTime();
|
|
499
|
+
const now = Date.now();
|
|
500
|
+
setSessionElapsedTime(Math.floor((now - startTime) / 1e3));
|
|
501
|
+
const interval = setInterval(() => {
|
|
502
|
+
const elapsed = Math.floor((Date.now() - startTime) / 1e3);
|
|
503
|
+
setSessionElapsedTime(elapsed);
|
|
504
|
+
}, 1e3);
|
|
505
|
+
return () => clearInterval(interval);
|
|
506
|
+
}, [activeSession]);
|
|
507
|
+
(0, import_react2.useEffect)(() => {
|
|
508
|
+
const activeAssignment = displayedAssignment?.status === "in_progress" ? displayedAssignment : null;
|
|
509
|
+
if (!activeAssignment?.startedAt) {
|
|
510
|
+
setAssignmentElapsedTime(0);
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
const startTime = new Date(activeAssignment.startedAt).getTime();
|
|
514
|
+
const now = Date.now();
|
|
515
|
+
setAssignmentElapsedTime(Math.floor((now - startTime) / 1e3));
|
|
516
|
+
const interval = setInterval(() => {
|
|
517
|
+
const elapsed = Math.floor((Date.now() - startTime) / 1e3);
|
|
518
|
+
setAssignmentElapsedTime(elapsed);
|
|
519
|
+
}, 1e3);
|
|
520
|
+
return () => clearInterval(interval);
|
|
521
|
+
}, [displayedAssignment?.id, displayedAssignment?.status, displayedAssignment?.startedAt]);
|
|
522
|
+
(0, import_react2.useEffect)(() => {
|
|
523
|
+
if (!client || activeSession) {
|
|
524
|
+
setSuggestedRoutes([]);
|
|
525
|
+
setFocusAreas([]);
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
const loadSuggestions = async () => {
|
|
529
|
+
try {
|
|
530
|
+
const supabase = client.supabase;
|
|
531
|
+
if (!supabase) return;
|
|
532
|
+
const projectId = client.config.projectId;
|
|
533
|
+
const [routeStatsRes, focusAreasRes] = await Promise.all([
|
|
534
|
+
supabase.from("route_test_stats").select("route, priority_score, open_bugs, exploratory_issues, last_tested_at, test_case_count").eq("project_id", projectId).order("priority_score", { ascending: false }).limit(10),
|
|
535
|
+
supabase.rpc("get_active_focus_areas", { p_project_id: projectId })
|
|
536
|
+
]);
|
|
537
|
+
if (routeStatsRes.data && routeStatsRes.data.length > 0) {
|
|
538
|
+
const suggestions = routeStatsRes.data.filter((r) => r.route && r.priority_score > 0).slice(0, 5).map((r) => {
|
|
539
|
+
let reason = "";
|
|
540
|
+
if (r.open_bugs > 0) {
|
|
541
|
+
reason = `${r.open_bugs} open bug${r.open_bugs > 1 ? "s" : ""}`;
|
|
542
|
+
} else if (!r.last_tested_at) {
|
|
543
|
+
reason = "Never tested";
|
|
544
|
+
} else if (r.test_case_count === 0) {
|
|
545
|
+
reason = "No test coverage";
|
|
546
|
+
} else {
|
|
547
|
+
reason = `Priority: ${r.priority_score}`;
|
|
548
|
+
}
|
|
549
|
+
return {
|
|
550
|
+
route: r.route,
|
|
551
|
+
reason,
|
|
552
|
+
priorityScore: r.priority_score
|
|
553
|
+
};
|
|
554
|
+
});
|
|
555
|
+
setSuggestedRoutes(suggestions);
|
|
556
|
+
}
|
|
557
|
+
if (focusAreasRes.data && focusAreasRes.data.length > 0) {
|
|
558
|
+
setFocusAreas(focusAreasRes.data.slice(0, 3).map((fa) => ({
|
|
559
|
+
id: fa.id,
|
|
560
|
+
name: fa.name,
|
|
561
|
+
description: fa.description,
|
|
562
|
+
priority: fa.priority
|
|
563
|
+
})));
|
|
564
|
+
}
|
|
565
|
+
} catch (err) {
|
|
566
|
+
console.error("BugBear: Failed to load suggestions", err);
|
|
567
|
+
}
|
|
568
|
+
};
|
|
569
|
+
loadSuggestions();
|
|
570
|
+
}, [client, activeSession]);
|
|
278
571
|
const handleMouseDown = (0, import_react2.useCallback)((e) => {
|
|
279
572
|
if (!draggable || !panelPosition) return;
|
|
280
573
|
const target = e.target;
|
|
@@ -325,27 +618,115 @@ function BugBearPanel({
|
|
|
325
618
|
return null;
|
|
326
619
|
}
|
|
327
620
|
const handlePass = async () => {
|
|
328
|
-
if (!
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
621
|
+
if (!displayedAssignment) return;
|
|
622
|
+
setPendingFeedbackStatus("passed");
|
|
623
|
+
setShowFeedbackPrompt(true);
|
|
624
|
+
setFeedbackRating(5);
|
|
625
|
+
setFeedbackNote("");
|
|
626
|
+
setFeedbackFlags({
|
|
627
|
+
isOutdated: false,
|
|
628
|
+
needsMoreDetail: false,
|
|
629
|
+
stepsUnclear: false,
|
|
630
|
+
expectedResultUnclear: false
|
|
336
631
|
});
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
632
|
+
};
|
|
633
|
+
const handleFail = async () => {
|
|
634
|
+
if (!displayedAssignment) return;
|
|
635
|
+
setPendingFeedbackStatus("failed");
|
|
636
|
+
setShowFeedbackPrompt(true);
|
|
637
|
+
setFeedbackRating(3);
|
|
638
|
+
setFeedbackNote("");
|
|
639
|
+
setFeedbackFlags({
|
|
640
|
+
isOutdated: false,
|
|
641
|
+
needsMoreDetail: false,
|
|
642
|
+
stepsUnclear: false,
|
|
643
|
+
expectedResultUnclear: false
|
|
644
|
+
});
|
|
645
|
+
};
|
|
646
|
+
const handleOpenSkipModal = () => {
|
|
647
|
+
setShowSkipModal(true);
|
|
648
|
+
setSelectedSkipReason(null);
|
|
649
|
+
setSkipNotes("");
|
|
650
|
+
};
|
|
651
|
+
const handleSkip = async () => {
|
|
652
|
+
if (!client || !displayedAssignment || !selectedSkipReason) return;
|
|
653
|
+
setSkipping(true);
|
|
654
|
+
const result = await client.skipAssignment(
|
|
655
|
+
displayedAssignment.id,
|
|
656
|
+
selectedSkipReason,
|
|
657
|
+
skipNotes.trim() || void 0
|
|
658
|
+
);
|
|
659
|
+
if (result.success) {
|
|
660
|
+
await refreshAssignments();
|
|
661
|
+
setShowSkipModal(false);
|
|
662
|
+
setSelectedSkipReason(null);
|
|
663
|
+
setSkipNotes("");
|
|
342
664
|
setSelectedTestId(null);
|
|
343
665
|
setTestView("detail");
|
|
344
|
-
}
|
|
666
|
+
}
|
|
667
|
+
setSkipping(false);
|
|
345
668
|
};
|
|
346
|
-
const
|
|
347
|
-
|
|
348
|
-
|
|
669
|
+
const skipReasonOptions = [
|
|
670
|
+
{ value: "blocked", label: "Blocked", description: "Environment issue or external blocker" },
|
|
671
|
+
{ value: "not_ready", label: "Not Ready", description: "Feature not yet implemented" },
|
|
672
|
+
{ value: "dependency", label: "Dependency", description: "Waiting on another test or task" },
|
|
673
|
+
{ value: "other", label: "Other", description: "Other reason (please specify)" }
|
|
674
|
+
];
|
|
675
|
+
const handleSubmitFeedback = async (skipFeedback = false) => {
|
|
676
|
+
if (!client || !displayedAssignment) return;
|
|
677
|
+
setSubmitting(true);
|
|
678
|
+
const feedback = skipFeedback ? void 0 : {
|
|
679
|
+
rating: feedbackRating,
|
|
680
|
+
feedbackNote: feedbackNote.trim() || void 0,
|
|
681
|
+
isOutdated: feedbackFlags.isOutdated,
|
|
682
|
+
needsMoreDetail: feedbackFlags.needsMoreDetail,
|
|
683
|
+
stepsUnclear: feedbackFlags.stepsUnclear,
|
|
684
|
+
expectedResultUnclear: feedbackFlags.expectedResultUnclear
|
|
685
|
+
};
|
|
686
|
+
if (pendingFeedbackStatus === "passed") {
|
|
687
|
+
await client.submitReport({
|
|
688
|
+
type: "test_pass",
|
|
689
|
+
description: `Test passed: ${displayedAssignment.testCase.title}`,
|
|
690
|
+
assignmentId: displayedAssignment.id,
|
|
691
|
+
testCaseId: displayedAssignment.testCase.id,
|
|
692
|
+
appContext: getAppContext?.() || { currentRoute: window.location.pathname }
|
|
693
|
+
});
|
|
694
|
+
if (feedback) {
|
|
695
|
+
await client.submitTestFeedback({
|
|
696
|
+
testCaseId: displayedAssignment.testCase.id,
|
|
697
|
+
assignmentId: displayedAssignment.id,
|
|
698
|
+
feedback,
|
|
699
|
+
timeToCompleteSeconds: assignmentElapsedTime || void 0
|
|
700
|
+
});
|
|
701
|
+
}
|
|
702
|
+
await refreshAssignments();
|
|
703
|
+
setSubmitting(false);
|
|
704
|
+
setShowFeedbackPrompt(false);
|
|
705
|
+
setPendingFeedbackStatus(null);
|
|
706
|
+
setJustPassed(true);
|
|
707
|
+
setTimeout(() => {
|
|
708
|
+
setJustPassed(false);
|
|
709
|
+
setSelectedTestId(null);
|
|
710
|
+
setTestView("detail");
|
|
711
|
+
}, 1200);
|
|
712
|
+
} else if (pendingFeedbackStatus === "failed") {
|
|
713
|
+
if (feedback) {
|
|
714
|
+
await client.submitTestFeedback({
|
|
715
|
+
testCaseId: displayedAssignment.testCase.id,
|
|
716
|
+
assignmentId: displayedAssignment.id,
|
|
717
|
+
feedback,
|
|
718
|
+
timeToCompleteSeconds: assignmentElapsedTime || void 0
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
setSubmitting(false);
|
|
722
|
+
setShowFeedbackPrompt(false);
|
|
723
|
+
setPendingFeedbackStatus(null);
|
|
724
|
+
setActiveTab("report");
|
|
725
|
+
setReportType("test_fail");
|
|
726
|
+
}
|
|
727
|
+
};
|
|
728
|
+
const handleSkipFeedback = () => {
|
|
729
|
+
handleSubmitFeedback(true);
|
|
349
730
|
};
|
|
350
731
|
const handleSubmitReport = async () => {
|
|
351
732
|
if (!client || !description.trim()) return;
|
|
@@ -438,6 +819,135 @@ function BugBearPanel({
|
|
|
438
819
|
}
|
|
439
820
|
setSavingProfile(false);
|
|
440
821
|
};
|
|
822
|
+
const handleStartSession = async () => {
|
|
823
|
+
setStartingSession(true);
|
|
824
|
+
const result = await startSession({
|
|
825
|
+
focusArea: sessionFocusArea.trim() || void 0,
|
|
826
|
+
platform: sessionPlatform
|
|
827
|
+
});
|
|
828
|
+
if (result.success) {
|
|
829
|
+
setSessionFocusArea("");
|
|
830
|
+
}
|
|
831
|
+
setStartingSession(false);
|
|
832
|
+
};
|
|
833
|
+
const handleEndSession = async () => {
|
|
834
|
+
setEndingSession(true);
|
|
835
|
+
const result = await endSession(sessionNotes.trim() || void 0);
|
|
836
|
+
if (result.success) {
|
|
837
|
+
setSessionNotes("");
|
|
838
|
+
setShowEndConfirm(false);
|
|
839
|
+
}
|
|
840
|
+
setEndingSession(false);
|
|
841
|
+
};
|
|
842
|
+
const handleAddFinding = async () => {
|
|
843
|
+
if (!findingTitle.trim()) return;
|
|
844
|
+
setAddingFinding(true);
|
|
845
|
+
const result = await addFinding({
|
|
846
|
+
type: findingType,
|
|
847
|
+
title: findingTitle.trim(),
|
|
848
|
+
description: findingDescription.trim() || void 0,
|
|
849
|
+
severity: findingType === "bug" ? findingSeverity : void 0,
|
|
850
|
+
route: typeof window !== "undefined" ? window.location.pathname : void 0
|
|
851
|
+
});
|
|
852
|
+
if (result.success) {
|
|
853
|
+
setFindingTitle("");
|
|
854
|
+
setFindingDescription("");
|
|
855
|
+
setFindingType("bug");
|
|
856
|
+
setFindingSeverity("medium");
|
|
857
|
+
setShowAddFinding(false);
|
|
858
|
+
}
|
|
859
|
+
setAddingFinding(false);
|
|
860
|
+
};
|
|
861
|
+
const formatElapsedTime = (seconds) => {
|
|
862
|
+
const h = Math.floor(seconds / 3600);
|
|
863
|
+
const m = Math.floor(seconds % 3600 / 60);
|
|
864
|
+
const s = seconds % 60;
|
|
865
|
+
if (h > 0) {
|
|
866
|
+
return `${h}:${m.toString().padStart(2, "0")}:${s.toString().padStart(2, "0")}`;
|
|
867
|
+
}
|
|
868
|
+
return `${m}:${s.toString().padStart(2, "0")}`;
|
|
869
|
+
};
|
|
870
|
+
const formatRelativeTime = (dateString) => {
|
|
871
|
+
const date = new Date(dateString);
|
|
872
|
+
const now = /* @__PURE__ */ new Date();
|
|
873
|
+
const diffMs = now.getTime() - date.getTime();
|
|
874
|
+
const diffMins = Math.floor(diffMs / 6e4);
|
|
875
|
+
const diffHours = Math.floor(diffMs / 36e5);
|
|
876
|
+
const diffDays = Math.floor(diffMs / 864e5);
|
|
877
|
+
if (diffMins < 1) return "Just now";
|
|
878
|
+
if (diffMins < 60) return `${diffMins}m ago`;
|
|
879
|
+
if (diffHours < 24) return `${diffHours}h ago`;
|
|
880
|
+
if (diffDays === 1) return "Yesterday";
|
|
881
|
+
if (diffDays < 7) return `${diffDays}d ago`;
|
|
882
|
+
return date.toLocaleDateString();
|
|
883
|
+
};
|
|
884
|
+
const formatMessageTime = (dateString) => {
|
|
885
|
+
const date = new Date(dateString);
|
|
886
|
+
return date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
|
|
887
|
+
};
|
|
888
|
+
const getThreadTypeIcon = (type) => {
|
|
889
|
+
switch (type) {
|
|
890
|
+
case "announcement":
|
|
891
|
+
return "\u{1F4E2}";
|
|
892
|
+
case "direct":
|
|
893
|
+
return "\u{1F4AC}";
|
|
894
|
+
case "report":
|
|
895
|
+
return "\u{1F41B}";
|
|
896
|
+
case "general_note":
|
|
897
|
+
return "\u{1F4DD}";
|
|
898
|
+
default:
|
|
899
|
+
return "\u{1F4AC}";
|
|
900
|
+
}
|
|
901
|
+
};
|
|
902
|
+
const handleOpenThread = async (thread) => {
|
|
903
|
+
setSelectedThread(thread);
|
|
904
|
+
setMessageView("thread");
|
|
905
|
+
setLoadingMessages(true);
|
|
906
|
+
const messages = await getThreadMessages(thread.id);
|
|
907
|
+
setThreadMessages(messages);
|
|
908
|
+
setLoadingMessages(false);
|
|
909
|
+
if (thread.unreadCount > 0) {
|
|
910
|
+
await markAsRead(thread.id);
|
|
911
|
+
}
|
|
912
|
+
};
|
|
913
|
+
const handleSendReply = async () => {
|
|
914
|
+
if (!selectedThread || !replyText.trim()) return;
|
|
915
|
+
setSendingReply(true);
|
|
916
|
+
const success = await sendMessage(selectedThread.id, replyText.trim());
|
|
917
|
+
if (success) {
|
|
918
|
+
setReplyText("");
|
|
919
|
+
const messages = await getThreadMessages(selectedThread.id);
|
|
920
|
+
setThreadMessages(messages);
|
|
921
|
+
}
|
|
922
|
+
setSendingReply(false);
|
|
923
|
+
};
|
|
924
|
+
const handleBackToThreadList = () => {
|
|
925
|
+
setMessageView("list");
|
|
926
|
+
setSelectedThread(null);
|
|
927
|
+
setThreadMessages([]);
|
|
928
|
+
setReplyText("");
|
|
929
|
+
setComposeSubject("");
|
|
930
|
+
setComposeMessage("");
|
|
931
|
+
};
|
|
932
|
+
const handleStartNewMessage = () => {
|
|
933
|
+
setMessageView("compose");
|
|
934
|
+
setComposeSubject("");
|
|
935
|
+
setComposeMessage("");
|
|
936
|
+
};
|
|
937
|
+
const handleSendNewMessage = async () => {
|
|
938
|
+
if (!composeSubject.trim() || !composeMessage.trim()) return;
|
|
939
|
+
setSendingNewMessage(true);
|
|
940
|
+
const result = await createThread({
|
|
941
|
+
subject: composeSubject.trim(),
|
|
942
|
+
message: composeMessage.trim()
|
|
943
|
+
});
|
|
944
|
+
if (result.success) {
|
|
945
|
+
setComposeSubject("");
|
|
946
|
+
setComposeMessage("");
|
|
947
|
+
setMessageView("list");
|
|
948
|
+
}
|
|
949
|
+
setSendingNewMessage(false);
|
|
950
|
+
};
|
|
441
951
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
442
952
|
"div",
|
|
443
953
|
{
|
|
@@ -460,22 +970,22 @@ function BugBearPanel({
|
|
|
460
970
|
onClick: () => setCollapsed(false),
|
|
461
971
|
"data-drag-handle": true,
|
|
462
972
|
onDoubleClick: handleDoubleClick,
|
|
463
|
-
className: "flex items-center gap-2 px-3 py-2 bg-
|
|
973
|
+
className: "flex items-center gap-2 px-3 py-2 bg-blue-500 text-white rounded-full shadow-lg hover:bg-blue-600 transition-colors",
|
|
464
974
|
style: { cursor: draggable ? "grab" : "pointer" },
|
|
465
975
|
children: [
|
|
466
976
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(BugBearIcon, { size: 24 }),
|
|
467
977
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-medium", children: "BugBear" }),
|
|
468
|
-
pendingCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "bg-white text-
|
|
978
|
+
pendingCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "bg-white text-blue-400 text-xs font-bold px-1.5 py-0.5 rounded-full", children: pendingCount })
|
|
469
979
|
]
|
|
470
980
|
}
|
|
471
981
|
),
|
|
472
|
-
!collapsed && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "w-80 bg-
|
|
982
|
+
!collapsed && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "w-80 bg-zinc-900 rounded-xl shadow-2xl border border-zinc-800 overflow-hidden", children: [
|
|
473
983
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
474
984
|
"div",
|
|
475
985
|
{
|
|
476
986
|
"data-drag-handle": true,
|
|
477
987
|
onDoubleClick: handleDoubleClick,
|
|
478
|
-
className: "bg-
|
|
988
|
+
className: "bg-zinc-950 text-white px-4 py-3 flex items-center justify-between border-b border-zinc-800",
|
|
479
989
|
style: { cursor: draggable ? isDragging ? "grabbing" : "grab" : "default" },
|
|
480
990
|
children: [
|
|
481
991
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
@@ -483,13 +993,13 @@ function BugBearPanel({
|
|
|
483
993
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
|
|
484
994
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("h3", { className: "font-semibold text-sm flex items-center gap-2", children: [
|
|
485
995
|
"BugBear",
|
|
486
|
-
draggable && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-
|
|
996
|
+
draggable && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-zinc-500 text-xs", title: "Drag to move, double-click to reset", children: "\u22EE\u22EE" })
|
|
487
997
|
] }),
|
|
488
998
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
489
999
|
"button",
|
|
490
1000
|
{
|
|
491
1001
|
onClick: handleOpenProfile,
|
|
492
|
-
className: "text-
|
|
1002
|
+
className: "text-zinc-400 text-xs flex items-center gap-1 hover:text-white transition-colors",
|
|
493
1003
|
children: [
|
|
494
1004
|
testerInfo?.name,
|
|
495
1005
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-[10px]", children: "\u270E" })
|
|
@@ -502,30 +1012,52 @@ function BugBearPanel({
|
|
|
502
1012
|
"button",
|
|
503
1013
|
{
|
|
504
1014
|
onClick: () => setCollapsed(true),
|
|
505
|
-
className: "p-1 hover:bg-
|
|
1015
|
+
className: "p-1 hover:bg-zinc-800 rounded",
|
|
506
1016
|
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) })
|
|
507
1017
|
}
|
|
508
1018
|
)
|
|
509
1019
|
]
|
|
510
1020
|
}
|
|
511
1021
|
),
|
|
512
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex border-b border-
|
|
1022
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex border-b border-zinc-800 bg-zinc-900", children: [
|
|
513
1023
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
514
1024
|
"button",
|
|
515
1025
|
{
|
|
516
1026
|
onClick: () => setActiveTab("tests"),
|
|
517
|
-
className: `flex-1
|
|
1027
|
+
className: `flex-1 py-3 text-sm font-medium transition-all flex items-center justify-center gap-1.5 border-b-2 ${activeTab === "tests" ? "border-blue-500 text-blue-400" : "border-transparent text-zinc-500 hover:text-zinc-300"}`,
|
|
518
1028
|
children: [
|
|
519
1029
|
"Tests ",
|
|
520
1030
|
pendingCount > 0 && `(${pendingCount})`
|
|
521
1031
|
]
|
|
522
1032
|
}
|
|
523
1033
|
),
|
|
1034
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1035
|
+
"button",
|
|
1036
|
+
{
|
|
1037
|
+
onClick: () => setActiveTab("messages"),
|
|
1038
|
+
className: `flex-1 py-3 text-sm font-medium transition-all flex items-center justify-center gap-1.5 relative border-b-2 ${activeTab === "messages" ? "border-blue-500 text-blue-400" : "border-transparent text-zinc-500 hover:text-zinc-300"}`,
|
|
1039
|
+
children: [
|
|
1040
|
+
"Messages",
|
|
1041
|
+
unreadCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "absolute top-1.5 ml-16 min-w-[18px] h-[18px] px-1 bg-blue-500 rounded-full text-[10px] text-white font-bold flex items-center justify-center", children: unreadCount })
|
|
1042
|
+
]
|
|
1043
|
+
}
|
|
1044
|
+
),
|
|
1045
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1046
|
+
"button",
|
|
1047
|
+
{
|
|
1048
|
+
onClick: () => setActiveTab("session"),
|
|
1049
|
+
className: `flex-1 py-3 text-sm font-medium transition-all flex items-center justify-center gap-1.5 relative border-b-2 ${activeTab === "session" ? "border-blue-500 text-blue-400" : "border-transparent text-zinc-500 hover:text-zinc-300"}`,
|
|
1050
|
+
children: [
|
|
1051
|
+
"Explore",
|
|
1052
|
+
activeSession && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "absolute top-2 ml-14 w-2 h-2 bg-green-500 rounded-full animate-pulse" })
|
|
1053
|
+
]
|
|
1054
|
+
}
|
|
1055
|
+
),
|
|
524
1056
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
525
1057
|
"button",
|
|
526
1058
|
{
|
|
527
1059
|
onClick: () => setActiveTab("report"),
|
|
528
|
-
className: `flex-1
|
|
1060
|
+
className: `flex-1 py-3 text-sm font-medium transition-all flex items-center justify-center gap-1.5 border-b-2 ${activeTab === "report" ? "border-blue-500 text-blue-400" : "border-transparent text-zinc-500 hover:text-zinc-300"}`,
|
|
529
1061
|
children: "Report"
|
|
530
1062
|
}
|
|
531
1063
|
)
|
|
@@ -533,59 +1065,202 @@ function BugBearPanel({
|
|
|
533
1065
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "p-4 max-h-96 overflow-y-auto", children: [
|
|
534
1066
|
activeTab === "tests" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { children: assignments.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "text-center py-8", children: [
|
|
535
1067
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-4xl", children: "\u2705" }),
|
|
536
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-
|
|
537
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-
|
|
1068
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-zinc-400 mt-2 font-medium", children: "All caught up!" }),
|
|
1069
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-zinc-500 text-sm", children: "No tests assigned" })
|
|
538
1070
|
] }) : testView === "list" ? (
|
|
539
|
-
/* List View - Show
|
|
540
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "space-y-
|
|
541
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
"
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
1071
|
+
/* List View - Show tests grouped by folder */
|
|
1072
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "space-y-3", children: [
|
|
1073
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center justify-between mb-2 px-1", children: [
|
|
1074
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "text-xs font-medium text-zinc-500", children: [
|
|
1075
|
+
assignments.length,
|
|
1076
|
+
" test",
|
|
1077
|
+
assignments.length !== 1 ? "s" : "",
|
|
1078
|
+
" assigned"
|
|
1079
|
+
] }),
|
|
1080
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-2 text-xs", children: [
|
|
1081
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "text-green-400", children: [
|
|
1082
|
+
"\u2705 ",
|
|
1083
|
+
assignments.filter((a) => a.status === "passed").length
|
|
1084
|
+
] }),
|
|
1085
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "text-red-400", children: [
|
|
1086
|
+
"\u274C ",
|
|
1087
|
+
assignments.filter((a) => a.status === "failed").length
|
|
1088
|
+
] }),
|
|
1089
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "text-zinc-500", children: [
|
|
1090
|
+
"\u23F3 ",
|
|
1091
|
+
assignments.filter((a) => a.status === "pending" || a.status === "in_progress").length
|
|
1092
|
+
] })
|
|
1093
|
+
] })
|
|
1094
|
+
] }),
|
|
1095
|
+
groupedAssignments.map((folder) => {
|
|
1096
|
+
const groupId = folder.group?.id || "ungrouped";
|
|
1097
|
+
const isCollapsed = collapsedFolders.has(groupId);
|
|
1098
|
+
const completedCount = folder.stats.passed + folder.stats.failed + folder.stats.skipped;
|
|
1099
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "border border-zinc-700 rounded-lg overflow-hidden", children: [
|
|
1100
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1101
|
+
"button",
|
|
1102
|
+
{
|
|
1103
|
+
onClick: () => toggleFolderCollapse(groupId),
|
|
1104
|
+
className: "w-full flex items-center justify-between p-2.5 bg-zinc-800 hover:bg-zinc-700 transition-colors",
|
|
1105
|
+
children: [
|
|
1106
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
1107
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-sm", children: isCollapsed ? "\u{1F4C1}" : "\u{1F4C2}" }),
|
|
1108
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-medium text-sm text-zinc-100", children: folder.group?.name || "Other Tests" }),
|
|
1109
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "text-xs text-zinc-500", children: [
|
|
1110
|
+
"(",
|
|
1111
|
+
completedCount,
|
|
1112
|
+
"/",
|
|
1113
|
+
folder.stats.total,
|
|
1114
|
+
")"
|
|
1115
|
+
] })
|
|
1116
|
+
] }),
|
|
1117
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
1118
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "w-16 h-1.5 bg-zinc-700 rounded-full overflow-hidden", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1119
|
+
"div",
|
|
1120
|
+
{
|
|
1121
|
+
className: "h-full bg-green-500 transition-all",
|
|
1122
|
+
style: { width: `${completedCount / folder.stats.total * 100}%` }
|
|
1123
|
+
}
|
|
1124
|
+
) }),
|
|
1125
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-zinc-500 text-xs", children: isCollapsed ? "\u25B6" : "\u25BC" })
|
|
1126
|
+
] })
|
|
1127
|
+
]
|
|
1128
|
+
}
|
|
1129
|
+
),
|
|
1130
|
+
!isCollapsed && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "divide-y divide-zinc-800", children: folder.assignments.map((assignment) => {
|
|
1131
|
+
const statusBadge = getStatusBadge(assignment.status);
|
|
1132
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1133
|
+
"button",
|
|
1134
|
+
{
|
|
1135
|
+
onClick: () => {
|
|
1136
|
+
setSelectedTestId(assignment.id);
|
|
1137
|
+
setTestView("detail");
|
|
1138
|
+
setShowSteps(false);
|
|
1139
|
+
},
|
|
1140
|
+
className: `w-full text-left p-3 transition-colors ${assignment.id === currentAssignment?.id ? "bg-blue-950/30" : "bg-zinc-900 hover:bg-zinc-800"}`,
|
|
1141
|
+
children: [
|
|
1142
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-start justify-between mb-1", children: [
|
|
1143
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
1144
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-xs font-mono text-zinc-500", children: assignment.testCase.testKey }),
|
|
1145
|
+
assignment.isVerification && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-xs px-1.5 py-0.5 rounded bg-green-900/30 text-green-400 font-medium", children: "\u{1F527} Verify" })
|
|
1146
|
+
] }),
|
|
1147
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-1.5", children: [
|
|
1148
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: `text-xs px-1.5 py-0.5 rounded font-medium ${statusBadge.className}`, children: [
|
|
1149
|
+
statusBadge.icon,
|
|
1150
|
+
" ",
|
|
1151
|
+
statusBadge.label
|
|
1152
|
+
] }),
|
|
1153
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: `text-xs px-1.5 py-0.5 rounded font-medium ${assignment.testCase.priority === "P0" ? "bg-red-900/30 text-red-400" : assignment.testCase.priority === "P1" ? "bg-orange-900/30 text-orange-400" : "bg-zinc-700 text-zinc-400"}`, children: assignment.testCase.priority })
|
|
1154
|
+
] })
|
|
1155
|
+
] }),
|
|
1156
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h4", { className: "font-medium text-zinc-100 text-sm line-clamp-2", children: assignment.testCase.title }),
|
|
1157
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-2 mt-1 text-xs text-zinc-500", children: [
|
|
1158
|
+
assignment.testCase.track && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1159
|
+
"span",
|
|
1160
|
+
{
|
|
1161
|
+
className: "px-1 py-0.5 rounded text-white",
|
|
1162
|
+
style: { backgroundColor: assignment.testCase.track.color },
|
|
1163
|
+
children: templateInfo[assignment.testCase.track.testTemplate || "steps"].icon
|
|
1164
|
+
}
|
|
1165
|
+
),
|
|
1166
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { children: [
|
|
1167
|
+
assignment.testCase.steps.length,
|
|
1168
|
+
" ",
|
|
1169
|
+
assignment.testCase.track?.testTemplate === "checklist" ? "items" : assignment.testCase.track?.testTemplate === "rubric" ? "criteria" : "steps"
|
|
1170
|
+
] }),
|
|
1171
|
+
assignment.id === currentAssignment?.id && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-blue-400 font-medium", children: "\u2022 Current" })
|
|
1172
|
+
] })
|
|
1173
|
+
]
|
|
1174
|
+
},
|
|
1175
|
+
assignment.id
|
|
1176
|
+
);
|
|
1177
|
+
}) })
|
|
1178
|
+
] }, groupId);
|
|
1179
|
+
})
|
|
1180
|
+
] })
|
|
1181
|
+
) : showFeedbackPrompt && displayedAssignment ? (
|
|
1182
|
+
/* Feedback prompt after completing a test */
|
|
1183
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "p-3", children: [
|
|
1184
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "text-center mb-4", children: [
|
|
1185
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-3xl", children: pendingFeedbackStatus === "passed" ? "\u2713" : "\u2717" }),
|
|
1186
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: `font-semibold mt-1 ${pendingFeedbackStatus === "passed" ? "text-green-400" : "text-red-400"}`, children: pendingFeedbackStatus === "passed" ? "Test Passed!" : "Test Failed" })
|
|
1187
|
+
] }),
|
|
1188
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "bg-blue-950/30 border border-blue-900 rounded-lg p-3 mb-4", children: [
|
|
1189
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-blue-300 text-sm font-medium mb-1", children: "Help us improve this test" }),
|
|
1190
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-blue-400 text-xs", children: "Your feedback shapes better tests for everyone." })
|
|
1191
|
+
] }),
|
|
1192
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mb-4", children: [
|
|
1193
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "block text-xs font-medium text-zinc-400 mb-2", children: "How was this test?" }),
|
|
1194
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex items-center gap-1 justify-center", children: [1, 2, 3, 4, 5].map((star) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1195
|
+
"button",
|
|
1196
|
+
{
|
|
1197
|
+
type: "button",
|
|
1198
|
+
onClick: () => setFeedbackRating(star),
|
|
1199
|
+
className: `text-2xl transition-colors ${star <= feedbackRating ? "text-yellow-400" : "text-zinc-600"} hover:text-yellow-400`,
|
|
1200
|
+
children: "\u2605"
|
|
554
1201
|
},
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
1202
|
+
star
|
|
1203
|
+
)) }),
|
|
1204
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-center text-xs text-zinc-500 mt-1", children: feedbackRating === 1 ? "Needs work" : feedbackRating === 2 ? "Could be better" : feedbackRating === 3 ? "Okay" : feedbackRating === 4 ? "Good" : "Great!" })
|
|
1205
|
+
] }),
|
|
1206
|
+
feedbackRating < 4 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mb-4", children: [
|
|
1207
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "block text-xs font-medium text-zinc-400 mb-2", children: "What could be improved?" }),
|
|
1208
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
1209
|
+
{ key: "stepsUnclear", label: "Steps unclear" },
|
|
1210
|
+
{ key: "expectedResultUnclear", label: "Expected result unclear" },
|
|
1211
|
+
{ key: "needsMoreDetail", label: "Needs more detail" },
|
|
1212
|
+
{ key: "isOutdated", label: "Seems outdated" }
|
|
1213
|
+
].map(({ key, label }) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1214
|
+
"button",
|
|
1215
|
+
{
|
|
1216
|
+
type: "button",
|
|
1217
|
+
onClick: () => setFeedbackFlags((prev) => ({ ...prev, [key]: !prev[key] })),
|
|
1218
|
+
className: `px-2 py-1.5 rounded text-xs font-medium border transition-colors ${feedbackFlags[key] ? "bg-blue-900/50 border-blue-700 text-blue-300" : "bg-zinc-800 border-zinc-700 text-zinc-400 hover:border-blue-800"}`,
|
|
1219
|
+
children: label
|
|
1220
|
+
},
|
|
1221
|
+
key
|
|
1222
|
+
)) })
|
|
1223
|
+
] }),
|
|
1224
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mb-4", children: [
|
|
1225
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "block text-xs font-medium text-zinc-400 mb-1", children: "Suggestions? (optional)" }),
|
|
1226
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1227
|
+
"textarea",
|
|
1228
|
+
{
|
|
1229
|
+
value: feedbackNote,
|
|
1230
|
+
onChange: (e) => setFeedbackNote(e.target.value),
|
|
1231
|
+
placeholder: "How could this test be improved?",
|
|
1232
|
+
className: "w-full px-3 py-2 text-sm bg-zinc-800 text-zinc-100 border border-zinc-700 rounded-lg resize-none focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent placeholder:text-zinc-500",
|
|
1233
|
+
rows: 2
|
|
1234
|
+
}
|
|
1235
|
+
)
|
|
1236
|
+
] }),
|
|
1237
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex gap-2", children: [
|
|
1238
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1239
|
+
"button",
|
|
1240
|
+
{
|
|
1241
|
+
onClick: handleSkipFeedback,
|
|
1242
|
+
disabled: submitting,
|
|
1243
|
+
className: "flex-1 px-3 py-2 text-sm font-medium text-zinc-400 bg-zinc-700 rounded-lg hover:bg-zinc-700 transition-colors disabled:opacity-50",
|
|
1244
|
+
children: "Skip"
|
|
1245
|
+
}
|
|
1246
|
+
),
|
|
1247
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1248
|
+
"button",
|
|
1249
|
+
{
|
|
1250
|
+
onClick: () => handleSubmitFeedback(false),
|
|
1251
|
+
disabled: submitting,
|
|
1252
|
+
className: "flex-1 px-3 py-2 text-sm font-medium text-white bg-blue-500 rounded-lg hover:bg-blue-600 transition-colors disabled:opacity-50",
|
|
1253
|
+
children: submitting ? "Submitting..." : "Submit Feedback"
|
|
1254
|
+
}
|
|
1255
|
+
)
|
|
1256
|
+
] })
|
|
582
1257
|
] })
|
|
583
1258
|
) : justPassed ? (
|
|
584
1259
|
/* Success state after passing */
|
|
585
1260
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "text-center py-8", children: [
|
|
586
1261
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-5xl", children: "\u2713" }),
|
|
587
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-green-
|
|
588
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-
|
|
1262
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-green-400 mt-3 font-semibold text-lg", children: "Passed!" }),
|
|
1263
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-zinc-500 text-sm mt-1", children: "Loading next test..." })
|
|
589
1264
|
] })
|
|
590
1265
|
) : displayedAssignment ? (
|
|
591
1266
|
/* Detail View - Show single test */
|
|
@@ -597,7 +1272,7 @@ function BugBearPanel({
|
|
|
597
1272
|
setTestView("list");
|
|
598
1273
|
setSelectedTestId(null);
|
|
599
1274
|
},
|
|
600
|
-
className: "flex items-center gap-1 text-xs text-
|
|
1275
|
+
className: "flex items-center gap-1 text-xs text-blue-400 font-medium hover:text-blue-300 mb-2",
|
|
601
1276
|
children: [
|
|
602
1277
|
"\u2190 All Tests (",
|
|
603
1278
|
assignments.length,
|
|
@@ -605,9 +1280,53 @@ function BugBearPanel({
|
|
|
605
1280
|
]
|
|
606
1281
|
}
|
|
607
1282
|
),
|
|
608
|
-
|
|
1283
|
+
(() => {
|
|
1284
|
+
const currentGroup = displayedAssignment.testCase.group;
|
|
1285
|
+
const groupAssignments = currentGroup ? assignments.filter((a) => a.testCase.group?.id === currentGroup.id) : assignments;
|
|
1286
|
+
const completed = groupAssignments.filter(
|
|
1287
|
+
(a) => a.status === "passed" || a.status === "failed" || a.status === "skipped"
|
|
1288
|
+
).length;
|
|
1289
|
+
const total = groupAssignments.length;
|
|
1290
|
+
const progressPercent = total > 0 ? completed / total * 100 : 0;
|
|
1291
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mb-3 p-2 bg-blue-950/30 rounded-lg", children: [
|
|
1292
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center justify-between mb-1.5", children: [
|
|
1293
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-xs font-medium text-blue-300", children: currentGroup ? `\u{1F4C1} ${currentGroup.name}` : "\u{1F4CB} All Tests" }),
|
|
1294
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "text-xs text-blue-400", children: [
|
|
1295
|
+
completed,
|
|
1296
|
+
"/",
|
|
1297
|
+
total,
|
|
1298
|
+
" complete"
|
|
1299
|
+
] })
|
|
1300
|
+
] }),
|
|
1301
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "w-full h-2 bg-zinc-700 rounded-full overflow-hidden", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1302
|
+
"div",
|
|
1303
|
+
{
|
|
1304
|
+
className: "h-full bg-blue-500 transition-all duration-300",
|
|
1305
|
+
style: { width: `${progressPercent}%` }
|
|
1306
|
+
}
|
|
1307
|
+
) }),
|
|
1308
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex justify-between mt-1 text-[10px] text-blue-400", children: [
|
|
1309
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { children: [
|
|
1310
|
+
"\u2705 ",
|
|
1311
|
+
groupAssignments.filter((a) => a.status === "passed").length,
|
|
1312
|
+
" passed"
|
|
1313
|
+
] }),
|
|
1314
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { children: [
|
|
1315
|
+
"\u274C ",
|
|
1316
|
+
groupAssignments.filter((a) => a.status === "failed").length,
|
|
1317
|
+
" failed"
|
|
1318
|
+
] }),
|
|
1319
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { children: [
|
|
1320
|
+
"\u23ED\uFE0F ",
|
|
1321
|
+
groupAssignments.filter((a) => a.status === "skipped").length,
|
|
1322
|
+
" skipped"
|
|
1323
|
+
] })
|
|
1324
|
+
] })
|
|
1325
|
+
] });
|
|
1326
|
+
})(),
|
|
1327
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "bg-zinc-800 rounded-lg p-3 mb-3", children: [
|
|
609
1328
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-start justify-between mb-2", children: [
|
|
610
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-xs font-mono text-
|
|
1329
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-xs font-mono text-zinc-500", children: displayedAssignment.testCase.testKey }),
|
|
611
1330
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-1", children: [
|
|
612
1331
|
displayedAssignment.testCase.track && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
613
1332
|
"span",
|
|
@@ -617,16 +1336,24 @@ function BugBearPanel({
|
|
|
617
1336
|
children: templateInfo[displayedAssignment.testCase.track.testTemplate || "steps"].icon
|
|
618
1337
|
}
|
|
619
1338
|
),
|
|
620
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: `text-xs px-1.5 py-0.5 rounded font-medium ${displayedAssignment.testCase.priority === "P0" ? "bg-red-
|
|
1339
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: `text-xs px-1.5 py-0.5 rounded font-medium ${displayedAssignment.testCase.priority === "P0" ? "bg-red-900/30 text-red-400" : displayedAssignment.testCase.priority === "P1" ? "bg-orange-900/30 text-orange-400" : "bg-zinc-700 text-zinc-400"}`, children: displayedAssignment.testCase.priority })
|
|
621
1340
|
] })
|
|
622
1341
|
] }),
|
|
623
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h4", { className: "font-medium text-
|
|
624
|
-
displayedAssignment.
|
|
1342
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h4", { className: "font-medium text-zinc-100 text-sm mb-1", children: displayedAssignment.testCase.title }),
|
|
1343
|
+
displayedAssignment.status === "in_progress" && displayedAssignment.startedAt && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-1.5 mb-2 text-xs text-green-400 bg-green-900/20 px-2 py-1 rounded", children: [
|
|
1344
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "relative flex h-2 w-2", children: [
|
|
1345
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75" }),
|
|
1346
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "relative inline-flex rounded-full h-2 w-2 bg-green-500" })
|
|
1347
|
+
] }),
|
|
1348
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-medium", children: "Testing" }),
|
|
1349
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-mono", children: formatElapsedTime(assignmentElapsedTime) })
|
|
1350
|
+
] }),
|
|
1351
|
+
displayedAssignment.testCase.description && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-zinc-500 text-xs mb-2", children: displayedAssignment.testCase.description }),
|
|
625
1352
|
displayedAssignment.testCase.targetRoute && onNavigate && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
626
1353
|
"button",
|
|
627
1354
|
{
|
|
628
1355
|
onClick: () => onNavigate(displayedAssignment.testCase.targetRoute),
|
|
629
|
-
className: "w-full mb-2 py-1.5 px-3 bg-blue-
|
|
1356
|
+
className: "w-full mb-2 py-1.5 px-3 bg-blue-900/20 text-blue-300 border border-blue-800 rounded-lg text-xs font-medium hover:bg-blue-900/30 transition-colors flex items-center justify-center gap-1",
|
|
630
1357
|
children: [
|
|
631
1358
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: "Go to test location" }),
|
|
632
1359
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: "\u2192" })
|
|
@@ -649,7 +1376,7 @@ function BugBearPanel({
|
|
|
649
1376
|
children: [
|
|
650
1377
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: info.icon }),
|
|
651
1378
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-medium", children: info.name }),
|
|
652
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "text-
|
|
1379
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "text-zinc-500", children: [
|
|
653
1380
|
"\u2022 ",
|
|
654
1381
|
info.action
|
|
655
1382
|
] })
|
|
@@ -660,7 +1387,7 @@ function BugBearPanel({
|
|
|
660
1387
|
"button",
|
|
661
1388
|
{
|
|
662
1389
|
onClick: () => setShowSteps(!showSteps),
|
|
663
|
-
className: "text-
|
|
1390
|
+
className: "text-blue-400 text-xs font-medium hover:text-blue-300 flex items-center gap-1",
|
|
664
1391
|
children: [
|
|
665
1392
|
showSteps ? "\u25BC" : "\u25B6",
|
|
666
1393
|
" ",
|
|
@@ -669,10 +1396,10 @@ function BugBearPanel({
|
|
|
669
1396
|
}
|
|
670
1397
|
),
|
|
671
1398
|
showSteps && template === "steps" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "mt-2 space-y-2", children: steps.map((step, idx) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex gap-2 text-xs", children: [
|
|
672
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "w-5 h-5 rounded-full bg-
|
|
1399
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "w-5 h-5 rounded-full bg-blue-900/50 text-blue-300 flex items-center justify-center flex-shrink-0 font-medium", children: step.stepNumber }),
|
|
673
1400
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
|
|
674
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-
|
|
675
|
-
step.expectedResult && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: "text-
|
|
1401
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-zinc-300", children: step.action }),
|
|
1402
|
+
step.expectedResult && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: "text-zinc-500 mt-0.5", children: [
|
|
676
1403
|
"\u2192 ",
|
|
677
1404
|
step.expectedResult
|
|
678
1405
|
] })
|
|
@@ -691,39 +1418,39 @@ function BugBearPanel({
|
|
|
691
1418
|
}
|
|
692
1419
|
return newResults;
|
|
693
1420
|
}),
|
|
694
|
-
className: `w-full flex items-center gap-2 text-xs p-2 rounded border transition-colors text-left ${criteriaResults[idx] === true ? "bg-green-
|
|
1421
|
+
className: `w-full flex items-center gap-2 text-xs p-2 rounded border transition-colors text-left ${criteriaResults[idx] === true ? "bg-green-900/20 border-green-700" : "bg-zinc-900 border-zinc-700 hover:bg-zinc-800"}`,
|
|
695
1422
|
children: [
|
|
696
1423
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: `w-5 h-5 rounded border-2 flex items-center justify-center ${criteriaResults[idx] === true ? "bg-green-500 border-green-500 text-white" : "border-cyan-400 text-cyan-600"}`, children: criteriaResults[idx] === true ? "\u2713" : "" }),
|
|
697
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: `flex-1 ${criteriaResults[idx] === true ? "text-green-
|
|
1424
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: `flex-1 ${criteriaResults[idx] === true ? "text-green-400" : "text-zinc-300"}`, children: step.action })
|
|
698
1425
|
]
|
|
699
1426
|
},
|
|
700
1427
|
idx
|
|
701
1428
|
)),
|
|
702
1429
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center justify-between mt-2", children: [
|
|
703
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-xs text-
|
|
1430
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-xs text-zinc-500", children: "Tap to check off each item." }),
|
|
704
1431
|
Object.keys(criteriaResults).length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
705
1432
|
"button",
|
|
706
1433
|
{
|
|
707
1434
|
onClick: () => setCriteriaResults({}),
|
|
708
|
-
className: "text-xs text-
|
|
1435
|
+
className: "text-xs text-zinc-500 hover:text-red-500 transition-colors",
|
|
709
1436
|
children: "\u21BA Reset"
|
|
710
1437
|
}
|
|
711
1438
|
)
|
|
712
1439
|
] })
|
|
713
1440
|
] }),
|
|
714
1441
|
showSteps && template === "rubric" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mt-2 space-y-2", children: [
|
|
715
|
-
steps.map((step, idx) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "bg-
|
|
1442
|
+
steps.map((step, idx) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "bg-zinc-800 p-2 rounded border border-zinc-700", children: [
|
|
716
1443
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-2 text-xs mb-1", children: [
|
|
717
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "w-5 h-5 rounded bg-
|
|
718
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-
|
|
1444
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "w-5 h-5 rounded bg-blue-900/50 text-blue-300 flex items-center justify-center font-medium", children: idx + 1 }),
|
|
1445
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-zinc-100 font-medium flex-1", children: step.action })
|
|
719
1446
|
] }),
|
|
720
|
-
step.expectedResult && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-xs text-
|
|
1447
|
+
step.expectedResult && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-xs text-zinc-500 ml-7 mb-2", children: step.expectedResult }),
|
|
721
1448
|
rubricMode === "pass_fail" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex gap-2 ml-7", children: [
|
|
722
1449
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
723
1450
|
"button",
|
|
724
1451
|
{
|
|
725
1452
|
onClick: () => setCriteriaResults((prev) => ({ ...prev, [idx]: true })),
|
|
726
|
-
className: `flex-1 py-1 px-2 rounded text-xs font-medium transition-colors ${criteriaResults[idx] === true ? "bg-green-500 text-white" : "bg-
|
|
1453
|
+
className: `flex-1 py-1 px-2 rounded text-xs font-medium transition-colors ${criteriaResults[idx] === true ? "bg-green-500 text-white" : "bg-zinc-700 text-zinc-400 hover:bg-green-900/30"}`,
|
|
727
1454
|
children: "\u2713 Pass"
|
|
728
1455
|
}
|
|
729
1456
|
),
|
|
@@ -731,7 +1458,7 @@ function BugBearPanel({
|
|
|
731
1458
|
"button",
|
|
732
1459
|
{
|
|
733
1460
|
onClick: () => setCriteriaResults((prev) => ({ ...prev, [idx]: false })),
|
|
734
|
-
className: `flex-1 py-1 px-2 rounded text-xs font-medium transition-colors ${criteriaResults[idx] === false ? "bg-red-500 text-white" : "bg-
|
|
1461
|
+
className: `flex-1 py-1 px-2 rounded text-xs font-medium transition-colors ${criteriaResults[idx] === false ? "bg-red-500 text-white" : "bg-zinc-700 text-zinc-400 hover:bg-red-900/30"}`,
|
|
735
1462
|
children: "\u2717 Fail"
|
|
736
1463
|
}
|
|
737
1464
|
)
|
|
@@ -740,28 +1467,28 @@ function BugBearPanel({
|
|
|
740
1467
|
"button",
|
|
741
1468
|
{
|
|
742
1469
|
onClick: () => setCriteriaResults((prev) => ({ ...prev, [idx]: n })),
|
|
743
|
-
className: `w-8 h-8 rounded font-medium text-sm transition-colors ${criteriaResults[idx] === n ? "bg-
|
|
1470
|
+
className: `w-8 h-8 rounded font-medium text-sm transition-colors ${criteriaResults[idx] === n ? "bg-blue-500 text-white" : "bg-zinc-700 text-zinc-400 hover:bg-blue-900/50"}`,
|
|
744
1471
|
children: n
|
|
745
1472
|
},
|
|
746
1473
|
n
|
|
747
1474
|
)) })
|
|
748
1475
|
] }, idx)),
|
|
749
1476
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center justify-between mt-2", children: [
|
|
750
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-xs text-
|
|
1477
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-xs text-zinc-500", children: rubricMode === "rating" ? "Rate 1-5 for each criterion." : "Mark each criterion as Pass or Fail." }),
|
|
751
1478
|
Object.keys(criteriaResults).length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
752
1479
|
"button",
|
|
753
1480
|
{
|
|
754
1481
|
onClick: () => setCriteriaResults({}),
|
|
755
|
-
className: "text-xs text-
|
|
1482
|
+
className: "text-xs text-zinc-500 hover:text-red-500 transition-colors",
|
|
756
1483
|
children: "\u21BA Reset"
|
|
757
1484
|
}
|
|
758
1485
|
)
|
|
759
1486
|
] })
|
|
760
1487
|
] }),
|
|
761
|
-
showSteps && template === "freeform" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mt-2 p-2 bg-amber-
|
|
762
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-amber-
|
|
763
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-amber-
|
|
764
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("ul", { className: "text-amber-
|
|
1488
|
+
showSteps && template === "freeform" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mt-2 p-2 bg-amber-900/20 rounded border border-amber-800 text-xs", children: [
|
|
1489
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-amber-300 font-medium mb-1", children: "\u{1F4AD} Open Observation" }),
|
|
1490
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-amber-400", children: "Review the area described above and note:" }),
|
|
1491
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("ul", { className: "text-amber-400 mt-1 ml-4 list-disc", children: [
|
|
765
1492
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("li", { children: "What works well" }),
|
|
766
1493
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("li", { children: "Issues or concerns" }),
|
|
767
1494
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("li", { children: "Suggestions" })
|
|
@@ -769,7 +1496,7 @@ function BugBearPanel({
|
|
|
769
1496
|
] })
|
|
770
1497
|
] });
|
|
771
1498
|
})(),
|
|
772
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mt-3 p-2 bg-green-
|
|
1499
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mt-3 p-2 bg-green-900/20 rounded text-xs text-green-400", children: [
|
|
773
1500
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-medium", children: displayedAssignment.testCase.track?.testTemplate === "checklist" ? "Pass criteria:" : displayedAssignment.testCase.track?.testTemplate === "rubric" ? "Target score:" : "Expected:" }),
|
|
774
1501
|
" ",
|
|
775
1502
|
displayedAssignment.testCase.expectedResult
|
|
@@ -780,26 +1507,507 @@ function BugBearPanel({
|
|
|
780
1507
|
"button",
|
|
781
1508
|
{
|
|
782
1509
|
onClick: handleFail,
|
|
783
|
-
disabled: submitting,
|
|
784
|
-
className: "flex-1 py-2 px-3 bg-red-
|
|
1510
|
+
disabled: submitting || skipping,
|
|
1511
|
+
className: "flex-1 py-2 px-3 bg-red-900/30 text-red-400 rounded-lg font-medium text-sm hover:bg-red-800/30 disabled:opacity-50 transition-colors",
|
|
785
1512
|
children: "\u2717 Fail"
|
|
786
1513
|
}
|
|
787
1514
|
),
|
|
1515
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1516
|
+
"button",
|
|
1517
|
+
{
|
|
1518
|
+
onClick: handleOpenSkipModal,
|
|
1519
|
+
disabled: submitting || skipping,
|
|
1520
|
+
className: "py-2 px-3 bg-yellow-900/30 text-yellow-400 rounded-lg font-medium text-sm hover:bg-yellow-800/30 disabled:opacity-50 transition-colors",
|
|
1521
|
+
children: "\u23ED\uFE0F Skip"
|
|
1522
|
+
}
|
|
1523
|
+
),
|
|
788
1524
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
789
1525
|
"button",
|
|
790
1526
|
{
|
|
791
1527
|
onClick: handlePass,
|
|
792
|
-
disabled: submitting,
|
|
1528
|
+
disabled: submitting || skipping,
|
|
793
1529
|
className: "flex-1 py-2 px-3 bg-green-600 text-white rounded-lg font-medium text-sm hover:bg-green-700 disabled:opacity-50 transition-colors",
|
|
794
1530
|
children: submitting ? "..." : "\u2713 Pass"
|
|
795
1531
|
}
|
|
796
1532
|
)
|
|
797
|
-
] })
|
|
1533
|
+
] }),
|
|
1534
|
+
showSkipModal && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50", onClick: () => setShowSkipModal(false), children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "bg-zinc-800 rounded-xl p-4 w-72 shadow-xl border border-zinc-700", onClick: (e) => e.stopPropagation(), children: [
|
|
1535
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { className: "font-semibold text-zinc-100 mb-3", children: "Skip Test" }),
|
|
1536
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-xs text-zinc-500 mb-3", children: "Please select a reason for skipping this test." }),
|
|
1537
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "space-y-2 mb-4", children: skipReasonOptions.map((option) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1538
|
+
"button",
|
|
1539
|
+
{
|
|
1540
|
+
onClick: () => setSelectedSkipReason(option.value),
|
|
1541
|
+
className: `w-full text-left p-2.5 rounded-lg border transition-colors ${selectedSkipReason === option.value ? "bg-yellow-900/20 border-yellow-700 text-yellow-300" : "bg-zinc-800 border-zinc-700 text-zinc-300 hover:border-yellow-800"}`,
|
|
1542
|
+
children: [
|
|
1543
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "font-medium text-sm", children: option.label }),
|
|
1544
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "text-xs text-zinc-500", children: option.description })
|
|
1545
|
+
]
|
|
1546
|
+
},
|
|
1547
|
+
option.value
|
|
1548
|
+
)) }),
|
|
1549
|
+
selectedSkipReason === "other" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mb-4", children: [
|
|
1550
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "block text-xs font-medium text-zinc-400 mb-1", children: "Notes (required)" }),
|
|
1551
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1552
|
+
"textarea",
|
|
1553
|
+
{
|
|
1554
|
+
value: skipNotes,
|
|
1555
|
+
onChange: (e) => setSkipNotes(e.target.value),
|
|
1556
|
+
placeholder: "Please explain why you're skipping...",
|
|
1557
|
+
className: "w-full px-3 py-2 text-sm bg-zinc-800 text-zinc-100 border border-zinc-700 rounded-lg resize-none focus:outline-none focus:ring-2 focus:ring-yellow-500 focus:border-transparent placeholder:text-zinc-500",
|
|
1558
|
+
rows: 2
|
|
1559
|
+
}
|
|
1560
|
+
)
|
|
1561
|
+
] }),
|
|
1562
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex gap-2", children: [
|
|
1563
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1564
|
+
"button",
|
|
1565
|
+
{
|
|
1566
|
+
onClick: () => setShowSkipModal(false),
|
|
1567
|
+
className: "flex-1 py-2 px-3 text-sm font-medium text-zinc-400 bg-zinc-700 rounded-lg hover:bg-zinc-700 transition-colors",
|
|
1568
|
+
children: "Cancel"
|
|
1569
|
+
}
|
|
1570
|
+
),
|
|
1571
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1572
|
+
"button",
|
|
1573
|
+
{
|
|
1574
|
+
onClick: handleSkip,
|
|
1575
|
+
disabled: !selectedSkipReason || selectedSkipReason === "other" && !skipNotes.trim() || skipping,
|
|
1576
|
+
className: "flex-1 py-2 px-3 text-sm font-medium text-white bg-yellow-500 rounded-lg hover:bg-yellow-600 disabled:opacity-50 disabled:cursor-not-allowed transition-colors",
|
|
1577
|
+
children: skipping ? "Skipping..." : "Skip Test"
|
|
1578
|
+
}
|
|
1579
|
+
)
|
|
1580
|
+
] })
|
|
1581
|
+
] }) })
|
|
798
1582
|
] })
|
|
799
1583
|
) : null }),
|
|
1584
|
+
activeTab === "messages" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { children: messageView === "compose" ? (
|
|
1585
|
+
/* Compose New Message */
|
|
1586
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
|
|
1587
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1588
|
+
"button",
|
|
1589
|
+
{
|
|
1590
|
+
onClick: handleBackToThreadList,
|
|
1591
|
+
className: "text-sm text-zinc-400 hover:text-zinc-200 mb-3 flex items-center gap-1",
|
|
1592
|
+
children: "\u2190 Back to Messages"
|
|
1593
|
+
}
|
|
1594
|
+
),
|
|
1595
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "text-center mb-4", children: [
|
|
1596
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { className: "font-semibold text-zinc-100", children: "New Message" }),
|
|
1597
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-zinc-500 text-xs mt-1", children: "Send a message to the QA team" })
|
|
1598
|
+
] }),
|
|
1599
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "space-y-3", children: [
|
|
1600
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
|
|
1601
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "block text-xs font-medium text-zinc-300 mb-1", children: "Subject" }),
|
|
1602
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1603
|
+
"input",
|
|
1604
|
+
{
|
|
1605
|
+
type: "text",
|
|
1606
|
+
value: composeSubject,
|
|
1607
|
+
onChange: (e) => setComposeSubject(e.target.value),
|
|
1608
|
+
placeholder: "What's this about?",
|
|
1609
|
+
maxLength: 100,
|
|
1610
|
+
className: "w-full px-3 py-2 text-sm bg-zinc-800 text-zinc-100 border border-zinc-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 placeholder:text-zinc-500"
|
|
1611
|
+
}
|
|
1612
|
+
)
|
|
1613
|
+
] }),
|
|
1614
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
|
|
1615
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "block text-xs font-medium text-zinc-300 mb-1", children: "Message" }),
|
|
1616
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1617
|
+
"textarea",
|
|
1618
|
+
{
|
|
1619
|
+
value: composeMessage,
|
|
1620
|
+
onChange: (e) => setComposeMessage(e.target.value),
|
|
1621
|
+
placeholder: "Write your message...",
|
|
1622
|
+
maxLength: 2e3,
|
|
1623
|
+
rows: 6,
|
|
1624
|
+
className: "w-full px-3 py-2 text-sm bg-zinc-800 text-zinc-100 border border-zinc-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 placeholder:text-zinc-500 resize-none"
|
|
1625
|
+
}
|
|
1626
|
+
)
|
|
1627
|
+
] }),
|
|
1628
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1629
|
+
"button",
|
|
1630
|
+
{
|
|
1631
|
+
onClick: handleSendNewMessage,
|
|
1632
|
+
disabled: !composeSubject.trim() || !composeMessage.trim() || sendingNewMessage,
|
|
1633
|
+
className: "w-full py-2 px-4 bg-blue-500 text-white rounded-lg font-medium text-sm hover:bg-blue-600 disabled:opacity-50 disabled:cursor-not-allowed transition-colors",
|
|
1634
|
+
children: sendingNewMessage ? "Sending..." : "Send Message"
|
|
1635
|
+
}
|
|
1636
|
+
)
|
|
1637
|
+
] })
|
|
1638
|
+
] })
|
|
1639
|
+
) : messageView === "thread" && selectedThread ? (
|
|
1640
|
+
/* Thread Detail View */
|
|
1641
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
|
|
1642
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1643
|
+
"button",
|
|
1644
|
+
{
|
|
1645
|
+
onClick: handleBackToThreadList,
|
|
1646
|
+
className: "text-sm text-zinc-400 hover:text-zinc-200 mb-3 flex items-center gap-1",
|
|
1647
|
+
children: "\u2190 Back to Messages"
|
|
1648
|
+
}
|
|
1649
|
+
),
|
|
1650
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-2 mb-4 pb-3 border-b border-zinc-700", children: [
|
|
1651
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-lg", children: getThreadTypeIcon(selectedThread.threadType) }),
|
|
1652
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { className: "font-semibold text-zinc-100 text-sm leading-tight", children: selectedThread.subject || "No subject" })
|
|
1653
|
+
] }),
|
|
1654
|
+
loadingMessages ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "text-center py-6", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-zinc-500 text-sm", children: "Loading messages..." }) }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "space-y-3 mb-4", children: threadMessages.map((message) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1655
|
+
"div",
|
|
1656
|
+
{
|
|
1657
|
+
className: `p-3 rounded-lg ${message.senderType === "tester" ? "bg-blue-900/30 border border-blue-800 ml-6" : "bg-zinc-800 border border-zinc-700 mr-6"}`,
|
|
1658
|
+
children: [
|
|
1659
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: `text-xs font-medium mb-1 ${message.senderType === "tester" ? "text-blue-300" : "text-zinc-300"}`, children: message.senderType === "tester" ? "You" : message.senderName }),
|
|
1660
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: `text-sm ${message.senderType === "tester" ? "text-blue-100" : "text-zinc-200"}`, children: message.content }),
|
|
1661
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: `text-[10px] mt-1 ${message.senderType === "tester" ? "text-blue-400/60" : "text-zinc-500"}`, children: formatMessageTime(message.createdAt) })
|
|
1662
|
+
]
|
|
1663
|
+
},
|
|
1664
|
+
message.id
|
|
1665
|
+
)) }),
|
|
1666
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex gap-2 pt-3 border-t border-zinc-700", children: [
|
|
1667
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1668
|
+
"input",
|
|
1669
|
+
{
|
|
1670
|
+
type: "text",
|
|
1671
|
+
value: replyText,
|
|
1672
|
+
onChange: (e) => setReplyText(e.target.value),
|
|
1673
|
+
placeholder: "Type a reply...",
|
|
1674
|
+
maxLength: 1e3,
|
|
1675
|
+
className: "flex-1 px-3 py-2 text-sm bg-zinc-800 text-zinc-100 border border-zinc-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 placeholder:text-zinc-500",
|
|
1676
|
+
onKeyDown: (e) => e.key === "Enter" && !e.shiftKey && handleSendReply()
|
|
1677
|
+
}
|
|
1678
|
+
),
|
|
1679
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1680
|
+
"button",
|
|
1681
|
+
{
|
|
1682
|
+
onClick: handleSendReply,
|
|
1683
|
+
disabled: !replyText.trim() || sendingReply,
|
|
1684
|
+
className: "px-3 py-2 bg-blue-500 text-white text-sm rounded-lg hover:bg-blue-600 disabled:opacity-50 disabled:cursor-not-allowed transition-colors",
|
|
1685
|
+
children: sendingReply ? "..." : "Send"
|
|
1686
|
+
}
|
|
1687
|
+
)
|
|
1688
|
+
] })
|
|
1689
|
+
] })
|
|
1690
|
+
) : (
|
|
1691
|
+
/* Thread List View */
|
|
1692
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
|
|
1693
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1694
|
+
"button",
|
|
1695
|
+
{
|
|
1696
|
+
onClick: handleStartNewMessage,
|
|
1697
|
+
className: "w-full flex items-center justify-center gap-2 py-2 px-4 mb-3 bg-blue-500 text-white rounded-lg font-medium text-sm hover:bg-blue-600 transition-colors",
|
|
1698
|
+
children: "\u2709\uFE0F New Message"
|
|
1699
|
+
}
|
|
1700
|
+
),
|
|
1701
|
+
threads.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "text-center py-8", children: [
|
|
1702
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-4xl", children: "\u{1F4AC}" }),
|
|
1703
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-zinc-400 mt-2 font-medium", children: "No messages yet" }),
|
|
1704
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-zinc-500 text-sm", children: "Start a conversation or wait for messages from admins" })
|
|
1705
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "space-y-2", children: threads.map((thread) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1706
|
+
"button",
|
|
1707
|
+
{
|
|
1708
|
+
onClick: () => handleOpenThread(thread),
|
|
1709
|
+
className: `w-full text-left p-3 rounded-lg border transition-colors ${thread.unreadCount > 0 ? "bg-blue-900/20 border-blue-800 hover:bg-blue-900/30" : "bg-zinc-800 border-zinc-700 hover:bg-zinc-700"}`,
|
|
1710
|
+
children: [
|
|
1711
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center justify-between mb-1", children: [
|
|
1712
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-1.5 min-w-0 flex-1", children: [
|
|
1713
|
+
thread.isPinned && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-xs", children: "\u{1F4CC}" }),
|
|
1714
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-xs", children: getThreadTypeIcon(thread.threadType) }),
|
|
1715
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: `text-sm truncate ${thread.unreadCount > 0 ? "font-semibold text-zinc-100" : "text-zinc-300"}`, children: thread.subject || "No subject" })
|
|
1716
|
+
] }),
|
|
1717
|
+
(thread.priority === "high" || thread.priority === "urgent") && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: `w-2 h-2 rounded-full flex-shrink-0 ml-2 ${thread.priority === "urgent" ? "bg-red-500" : "bg-orange-500"}` })
|
|
1718
|
+
] }),
|
|
1719
|
+
thread.lastMessage && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: "text-xs text-zinc-500 truncate", children: [
|
|
1720
|
+
thread.lastMessage.senderName,
|
|
1721
|
+
": ",
|
|
1722
|
+
thread.lastMessage.content
|
|
1723
|
+
] }),
|
|
1724
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center justify-between mt-1.5", children: [
|
|
1725
|
+
thread.unreadCount > 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "text-[10px] px-1.5 py-0.5 bg-blue-500 text-white rounded-full font-medium", children: [
|
|
1726
|
+
thread.unreadCount,
|
|
1727
|
+
" new"
|
|
1728
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-[10px] text-zinc-600", children: "Read" }),
|
|
1729
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-[10px] text-zinc-600", children: formatRelativeTime(thread.lastMessageAt) })
|
|
1730
|
+
] })
|
|
1731
|
+
]
|
|
1732
|
+
},
|
|
1733
|
+
thread.id
|
|
1734
|
+
)) })
|
|
1735
|
+
] })
|
|
1736
|
+
) }),
|
|
1737
|
+
activeTab === "session" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { children: !activeSession ? (
|
|
1738
|
+
/* Start Session View */
|
|
1739
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
|
|
1740
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "text-center mb-4", children: [
|
|
1741
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-4xl", children: "\u{1F50D}" }),
|
|
1742
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { className: "font-semibold text-zinc-100 mt-2", children: "Exploratory QA Session" }),
|
|
1743
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-zinc-500 text-xs mt-1", children: "Explore freely and capture findings as you go" })
|
|
1744
|
+
] }),
|
|
1745
|
+
focusAreas.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mb-4", children: [
|
|
1746
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "block text-xs font-medium text-zinc-300 mb-2", children: [
|
|
1747
|
+
"\u{1F4CC} Focus Areas",
|
|
1748
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "ml-1 text-[10px] text-zinc-500 font-normal", children: "from your team" })
|
|
1749
|
+
] }),
|
|
1750
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "space-y-1.5", children: focusAreas.map((area) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1751
|
+
"button",
|
|
1752
|
+
{
|
|
1753
|
+
onClick: () => setSessionFocusArea(area.name),
|
|
1754
|
+
className: `w-full text-left px-3 py-2 rounded-lg text-xs transition-colors border ${sessionFocusArea === area.name ? "bg-amber-900/20 border-amber-700 text-amber-400" : "bg-amber-900/20/50 border-amber-800 text-zinc-300 hover:bg-amber-900/20"}`,
|
|
1755
|
+
children: [
|
|
1756
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
1757
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-medium", children: area.name }),
|
|
1758
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: `text-[10px] px-1.5 py-0.5 rounded ${area.priority >= 70 ? "bg-red-900/30 text-red-400" : area.priority >= 50 ? "bg-amber-900/30 text-amber-400" : "bg-zinc-700 text-zinc-500"}`, children: area.priority >= 70 ? "Urgent" : area.priority >= 50 ? "Important" : "Suggested" })
|
|
1759
|
+
] }),
|
|
1760
|
+
area.description && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-[10px] text-zinc-500 mt-0.5 line-clamp-2", children: area.description })
|
|
1761
|
+
]
|
|
1762
|
+
},
|
|
1763
|
+
area.id
|
|
1764
|
+
)) })
|
|
1765
|
+
] }),
|
|
1766
|
+
suggestedRoutes.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mb-4", children: [
|
|
1767
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "block text-xs font-medium text-zinc-300 mb-2", children: "\u{1F3AF} Suggested Routes to Explore" }),
|
|
1768
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "space-y-1.5 max-h-32 overflow-y-auto", children: suggestedRoutes.map((suggestion, idx) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1769
|
+
"button",
|
|
1770
|
+
{
|
|
1771
|
+
onClick: () => setSessionFocusArea(suggestion.route),
|
|
1772
|
+
className: `w-full text-left px-3 py-2 rounded-lg text-xs transition-colors border ${sessionFocusArea === suggestion.route ? "bg-blue-950/30 border-blue-700 text-blue-300" : "bg-zinc-800 border-zinc-700 text-zinc-300 hover:bg-zinc-700"}`,
|
|
1773
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
1774
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-medium truncate", children: suggestion.route }),
|
|
1775
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: `text-[10px] px-1.5 py-0.5 rounded ${suggestion.priorityScore >= 40 ? "bg-red-900/30 text-red-400" : suggestion.priorityScore >= 25 ? "bg-amber-900/30 text-amber-400" : "bg-zinc-700 text-zinc-500"}`, children: suggestion.reason })
|
|
1776
|
+
] })
|
|
1777
|
+
},
|
|
1778
|
+
idx
|
|
1779
|
+
)) })
|
|
1780
|
+
] }),
|
|
1781
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mb-3", children: [
|
|
1782
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "block text-xs font-medium text-zinc-300 mb-1", children: "Focus Area (optional)" }),
|
|
1783
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1784
|
+
"input",
|
|
1785
|
+
{
|
|
1786
|
+
type: "text",
|
|
1787
|
+
value: sessionFocusArea,
|
|
1788
|
+
onChange: (e) => setSessionFocusArea(e.target.value),
|
|
1789
|
+
placeholder: "e.g., checkout flow, settings page",
|
|
1790
|
+
className: "w-full px-3 py-2 text-sm bg-zinc-800 text-zinc-100 border border-zinc-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 placeholder:text-zinc-500"
|
|
1791
|
+
}
|
|
1792
|
+
)
|
|
1793
|
+
] }),
|
|
1794
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mb-4", children: [
|
|
1795
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "block text-xs font-medium text-zinc-300 mb-1", children: "Platform" }),
|
|
1796
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex gap-2", children: [
|
|
1797
|
+
{ key: "web", label: "\u{1F310} Web" },
|
|
1798
|
+
{ key: "ios", label: "\u{1F4F1} iOS" },
|
|
1799
|
+
{ key: "android", label: "\u{1F916} Android" }
|
|
1800
|
+
].map(({ key, label }) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1801
|
+
"button",
|
|
1802
|
+
{
|
|
1803
|
+
onClick: () => setSessionPlatform(key),
|
|
1804
|
+
className: `flex-1 py-2 px-3 rounded-lg text-xs font-medium transition-colors border-2 ${sessionPlatform === key ? "bg-blue-950/30 border-blue-500 text-blue-300" : "bg-zinc-800 border-transparent text-zinc-400 hover:bg-zinc-700"}`,
|
|
1805
|
+
children: label
|
|
1806
|
+
},
|
|
1807
|
+
key
|
|
1808
|
+
)) })
|
|
1809
|
+
] }),
|
|
1810
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1811
|
+
"button",
|
|
1812
|
+
{
|
|
1813
|
+
onClick: handleStartSession,
|
|
1814
|
+
disabled: startingSession,
|
|
1815
|
+
className: "w-full py-3 px-4 bg-green-600 text-white rounded-lg font-semibold text-sm hover:bg-green-700 disabled:opacity-50 transition-colors flex items-center justify-center gap-2",
|
|
1816
|
+
children: startingSession ? "Starting..." : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
|
|
1817
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: "\u25B6" }),
|
|
1818
|
+
" Start Session"
|
|
1819
|
+
] })
|
|
1820
|
+
}
|
|
1821
|
+
)
|
|
1822
|
+
] })
|
|
1823
|
+
) : showEndConfirm ? (
|
|
1824
|
+
/* End Session Confirmation */
|
|
1825
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
|
|
1826
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "text-center mb-4", children: [
|
|
1827
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-4xl", children: "\u270B" }),
|
|
1828
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { className: "font-semibold text-zinc-100 mt-2", children: "End Session?" }),
|
|
1829
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: "text-zinc-500 text-xs mt-1", children: [
|
|
1830
|
+
"Duration: ",
|
|
1831
|
+
formatElapsedTime(sessionElapsedTime),
|
|
1832
|
+
" \u2022 ",
|
|
1833
|
+
sessionFindings.length,
|
|
1834
|
+
" finding",
|
|
1835
|
+
sessionFindings.length !== 1 ? "s" : ""
|
|
1836
|
+
] })
|
|
1837
|
+
] }),
|
|
1838
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mb-4", children: [
|
|
1839
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "block text-xs font-medium text-zinc-300 mb-1", children: "Session Notes (optional)" }),
|
|
1840
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1841
|
+
"textarea",
|
|
1842
|
+
{
|
|
1843
|
+
value: sessionNotes,
|
|
1844
|
+
onChange: (e) => setSessionNotes(e.target.value),
|
|
1845
|
+
placeholder: "Any overall observations from this session...",
|
|
1846
|
+
rows: 3,
|
|
1847
|
+
className: "w-full px-3 py-2 text-sm bg-zinc-800 text-zinc-100 border border-zinc-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 placeholder:text-zinc-500 resize-none"
|
|
1848
|
+
}
|
|
1849
|
+
)
|
|
1850
|
+
] }),
|
|
1851
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex gap-2", children: [
|
|
1852
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1853
|
+
"button",
|
|
1854
|
+
{
|
|
1855
|
+
onClick: () => setShowEndConfirm(false),
|
|
1856
|
+
className: "flex-1 py-2 px-3 bg-zinc-700 text-zinc-300 rounded-lg font-medium text-sm hover:bg-zinc-700 transition-colors",
|
|
1857
|
+
children: "Cancel"
|
|
1858
|
+
}
|
|
1859
|
+
),
|
|
1860
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1861
|
+
"button",
|
|
1862
|
+
{
|
|
1863
|
+
onClick: handleEndSession,
|
|
1864
|
+
disabled: endingSession,
|
|
1865
|
+
className: "flex-1 py-2 px-3 bg-red-600 text-white rounded-lg font-medium text-sm hover:bg-red-700 disabled:opacity-50 transition-colors",
|
|
1866
|
+
children: endingSession ? "Ending..." : "End Session"
|
|
1867
|
+
}
|
|
1868
|
+
)
|
|
1869
|
+
] })
|
|
1870
|
+
] })
|
|
1871
|
+
) : showAddFinding ? (
|
|
1872
|
+
/* Add Finding Form */
|
|
1873
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
|
|
1874
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center justify-between mb-3", children: [
|
|
1875
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { className: "font-semibold text-zinc-100 text-sm", children: "Add Finding" }),
|
|
1876
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1877
|
+
"button",
|
|
1878
|
+
{
|
|
1879
|
+
onClick: () => setShowAddFinding(false),
|
|
1880
|
+
className: "text-zinc-500 hover:text-zinc-400",
|
|
1881
|
+
children: "\u2715"
|
|
1882
|
+
}
|
|
1883
|
+
)
|
|
1884
|
+
] }),
|
|
1885
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex gap-1 mb-3", children: [
|
|
1886
|
+
{ type: "bug", label: "\u{1F41B} Bug", color: "red" },
|
|
1887
|
+
{ type: "concern", label: "\u26A0\uFE0F Concern", color: "orange" },
|
|
1888
|
+
{ type: "suggestion", label: "\u{1F4A1} Idea", color: "blue" },
|
|
1889
|
+
{ type: "question", label: "\u2753 Question", color: "purple" }
|
|
1890
|
+
].map(({ type, label, color }) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1891
|
+
"button",
|
|
1892
|
+
{
|
|
1893
|
+
onClick: () => setFindingType(type),
|
|
1894
|
+
className: `flex-1 py-1.5 px-2 rounded text-xs font-medium transition-colors ${findingType === type ? color === "red" ? "bg-red-900/30 text-red-400 ring-2 ring-red-400" : color === "orange" ? "bg-orange-900/30 text-orange-400 ring-2 ring-orange-400" : color === "blue" ? "bg-blue-900/30 text-blue-300 ring-2 ring-blue-400" : "bg-blue-900/50 text-blue-300 ring-2 ring-blue-400" : "bg-zinc-700 text-zinc-400 hover:bg-zinc-700"}`,
|
|
1895
|
+
children: label
|
|
1896
|
+
},
|
|
1897
|
+
type
|
|
1898
|
+
)) }),
|
|
1899
|
+
findingType === "bug" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mb-3", children: [
|
|
1900
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "block text-xs font-medium text-zinc-300 mb-1", children: "Severity" }),
|
|
1901
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex gap-1", children: ["critical", "high", "medium", "low", "observation"].map((sev) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1902
|
+
"button",
|
|
1903
|
+
{
|
|
1904
|
+
onClick: () => setFindingSeverity(sev),
|
|
1905
|
+
className: `flex-1 py-1 px-1 rounded text-xs font-medium capitalize transition-colors ${findingSeverity === sev ? sev === "critical" ? "bg-red-600 text-white" : sev === "high" ? "bg-orange-500 text-white" : sev === "medium" ? "bg-yellow-500 text-black" : sev === "low" ? "bg-zinc-8000 text-white" : "bg-blue-500 text-white" : "bg-zinc-700 text-zinc-400 hover:bg-zinc-700"}`,
|
|
1906
|
+
children: sev === "observation" ? "obs" : sev
|
|
1907
|
+
},
|
|
1908
|
+
sev
|
|
1909
|
+
)) })
|
|
1910
|
+
] }),
|
|
1911
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mb-3", children: [
|
|
1912
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "block text-xs font-medium text-zinc-300 mb-1", children: "Title *" }),
|
|
1913
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1914
|
+
"input",
|
|
1915
|
+
{
|
|
1916
|
+
type: "text",
|
|
1917
|
+
value: findingTitle,
|
|
1918
|
+
onChange: (e) => setFindingTitle(e.target.value),
|
|
1919
|
+
placeholder: "Brief description of what you found",
|
|
1920
|
+
className: "w-full px-3 py-2 text-sm bg-zinc-800 text-zinc-100 border border-zinc-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 placeholder:text-zinc-500"
|
|
1921
|
+
}
|
|
1922
|
+
)
|
|
1923
|
+
] }),
|
|
1924
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mb-3", children: [
|
|
1925
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "block text-xs font-medium text-zinc-300 mb-1", children: "Details (optional)" }),
|
|
1926
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1927
|
+
"textarea",
|
|
1928
|
+
{
|
|
1929
|
+
value: findingDescription,
|
|
1930
|
+
onChange: (e) => setFindingDescription(e.target.value),
|
|
1931
|
+
placeholder: "Steps to reproduce, expected behavior, etc.",
|
|
1932
|
+
rows: 2,
|
|
1933
|
+
className: "w-full px-3 py-2 text-sm bg-zinc-800 text-zinc-100 border border-zinc-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 placeholder:text-zinc-500 resize-none"
|
|
1934
|
+
}
|
|
1935
|
+
)
|
|
1936
|
+
] }),
|
|
1937
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1938
|
+
"button",
|
|
1939
|
+
{
|
|
1940
|
+
onClick: handleAddFinding,
|
|
1941
|
+
disabled: addingFinding || !findingTitle.trim(),
|
|
1942
|
+
className: "w-full py-2 px-4 bg-blue-500 text-white rounded-lg font-medium text-sm hover:bg-blue-600 disabled:opacity-50 disabled:cursor-not-allowed transition-colors",
|
|
1943
|
+
children: addingFinding ? "Adding..." : "Add Finding"
|
|
1944
|
+
}
|
|
1945
|
+
)
|
|
1946
|
+
] })
|
|
1947
|
+
) : (
|
|
1948
|
+
/* Active Session View */
|
|
1949
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
|
|
1950
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "bg-green-900/20 rounded-lg p-3 mb-3 border border-green-800", children: [
|
|
1951
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
1952
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
1953
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "w-2 h-2 bg-green-500 rounded-full animate-pulse" }),
|
|
1954
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-medium text-green-300 text-sm", children: "Session Active" })
|
|
1955
|
+
] }),
|
|
1956
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-mono text-green-400 text-lg font-semibold", children: formatElapsedTime(sessionElapsedTime) })
|
|
1957
|
+
] }),
|
|
1958
|
+
activeSession.focusArea && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: "text-green-400 text-xs mt-1", children: [
|
|
1959
|
+
"Focus: ",
|
|
1960
|
+
activeSession.focusArea
|
|
1961
|
+
] })
|
|
1962
|
+
] }),
|
|
1963
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1964
|
+
"button",
|
|
1965
|
+
{
|
|
1966
|
+
onClick: () => setShowAddFinding(true),
|
|
1967
|
+
className: "w-full py-3 px-4 bg-blue-500 text-white rounded-lg font-semibold text-sm hover:bg-blue-600 transition-colors flex items-center justify-center gap-2 mb-3",
|
|
1968
|
+
children: [
|
|
1969
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: "+" }),
|
|
1970
|
+
" Add Finding"
|
|
1971
|
+
]
|
|
1972
|
+
}
|
|
1973
|
+
),
|
|
1974
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mb-3", children: [
|
|
1975
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex items-center justify-between mb-2", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "text-xs font-medium text-zinc-500", children: [
|
|
1976
|
+
"Findings (",
|
|
1977
|
+
sessionFindings.length,
|
|
1978
|
+
")"
|
|
1979
|
+
] }) }),
|
|
1980
|
+
sessionFindings.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "text-center py-4 bg-zinc-800 rounded-lg", children: [
|
|
1981
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-zinc-500 text-xs", children: "No findings yet" }),
|
|
1982
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-zinc-500 text-xs", children: "Explore and add findings as you go" })
|
|
1983
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "space-y-2 max-h-32 overflow-y-auto", children: sessionFindings.map((finding) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1984
|
+
"div",
|
|
1985
|
+
{
|
|
1986
|
+
className: `p-2 rounded-lg border text-xs ${finding.type === "bug" ? "bg-red-900/20 border-red-800" : finding.type === "concern" ? "bg-orange-900/20 border-orange-200" : finding.type === "suggestion" ? "bg-blue-900/20 border-blue-800" : "bg-blue-950/30 border-blue-800"}`,
|
|
1987
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-start gap-2", children: [
|
|
1988
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: finding.type === "bug" ? "\u{1F41B}" : finding.type === "concern" ? "\u26A0\uFE0F" : finding.type === "suggestion" ? "\u{1F4A1}" : "\u2753" }),
|
|
1989
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex-1 min-w-0", children: [
|
|
1990
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "font-medium text-zinc-100 truncate", children: finding.title }),
|
|
1991
|
+
finding.severity && finding.type === "bug" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: `inline-block mt-0.5 px-1 py-0.5 rounded text-[10px] font-medium ${finding.severity === "critical" ? "bg-red-900/40 text-red-300" : finding.severity === "high" ? "bg-orange-900/40 text-orange-300" : finding.severity === "medium" ? "bg-yellow-900/40 text-yellow-300" : "bg-zinc-700 text-zinc-300"}`, children: finding.severity })
|
|
1992
|
+
] })
|
|
1993
|
+
] })
|
|
1994
|
+
},
|
|
1995
|
+
finding.id
|
|
1996
|
+
)) })
|
|
1997
|
+
] }),
|
|
1998
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1999
|
+
"button",
|
|
2000
|
+
{
|
|
2001
|
+
onClick: () => setShowEndConfirm(true),
|
|
2002
|
+
className: "w-full py-2 px-4 bg-zinc-700 text-zinc-300 rounded-lg font-medium text-sm hover:bg-zinc-700 transition-colors",
|
|
2003
|
+
children: "End Session"
|
|
2004
|
+
}
|
|
2005
|
+
)
|
|
2006
|
+
] })
|
|
2007
|
+
) }),
|
|
800
2008
|
activeTab === "report" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { children: submitted ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "text-center py-8", children: [
|
|
801
2009
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-4xl", children: "\u{1F389}" }),
|
|
802
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-
|
|
2010
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-zinc-400 mt-2 font-medium", children: "Report submitted!" })
|
|
803
2011
|
] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
|
|
804
2012
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex gap-2 mb-4", children: [
|
|
805
2013
|
{ type: "bug", label: "\u{1F41B} Bug", color: "red" },
|
|
@@ -809,25 +2017,25 @@ function BugBearPanel({
|
|
|
809
2017
|
"button",
|
|
810
2018
|
{
|
|
811
2019
|
onClick: () => setReportType(type),
|
|
812
|
-
className: `flex-1 py-1.5 px-2 rounded-lg text-xs font-medium transition-colors ${reportType === type ? color === "red" ? "bg-red-
|
|
2020
|
+
className: `flex-1 py-1.5 px-2 rounded-lg text-xs font-medium transition-colors ${reportType === type ? color === "red" ? "bg-red-900/30 text-red-400 ring-2 ring-red-500" : color === "blue" ? "bg-blue-900/30 text-blue-300 ring-2 ring-blue-500" : "bg-blue-900/50 text-blue-300 ring-2 ring-blue-500" : "bg-zinc-700 text-zinc-400 hover:bg-zinc-700"}`,
|
|
813
2021
|
children: label
|
|
814
2022
|
},
|
|
815
2023
|
type
|
|
816
2024
|
)) }),
|
|
817
2025
|
(reportType === "bug" || reportType === "test_fail") && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mb-3", children: [
|
|
818
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "block text-xs font-medium text-
|
|
2026
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "block text-xs font-medium text-zinc-300 mb-1", children: "Severity" }),
|
|
819
2027
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex gap-1", children: ["critical", "high", "medium", "low"].map((sev) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
820
2028
|
"button",
|
|
821
2029
|
{
|
|
822
2030
|
onClick: () => setSeverity(sev),
|
|
823
|
-
className: `flex-1 py-1 px-2 rounded text-xs font-medium capitalize transition-colors ${severity === sev ? sev === "critical" ? "bg-red-600 text-white" : sev === "high" ? "bg-orange-500 text-white" : sev === "medium" ? "bg-yellow-500 text-black" : "bg-
|
|
2031
|
+
className: `flex-1 py-1 px-2 rounded text-xs font-medium capitalize transition-colors ${severity === sev ? sev === "critical" ? "bg-red-600 text-white" : sev === "high" ? "bg-orange-500 text-white" : sev === "medium" ? "bg-yellow-500 text-black" : "bg-zinc-8000 text-white" : "bg-zinc-700 text-zinc-400 hover:bg-zinc-700"}`,
|
|
824
2032
|
children: sev
|
|
825
2033
|
},
|
|
826
2034
|
sev
|
|
827
2035
|
)) })
|
|
828
2036
|
] }),
|
|
829
2037
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mb-3", children: [
|
|
830
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "block text-xs font-medium text-
|
|
2038
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "block text-xs font-medium text-zinc-300 mb-1", children: "What happened?" }),
|
|
831
2039
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
832
2040
|
"textarea",
|
|
833
2041
|
{
|
|
@@ -835,7 +2043,7 @@ function BugBearPanel({
|
|
|
835
2043
|
onChange: (e) => setDescription(e.target.value),
|
|
836
2044
|
placeholder: "Describe the issue...",
|
|
837
2045
|
rows: 3,
|
|
838
|
-
className: "w-full px-3 py-2 text-sm border border-
|
|
2046
|
+
className: "w-full px-3 py-2 text-sm bg-zinc-800 text-zinc-100 border border-zinc-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 placeholder:text-zinc-500 resize-none"
|
|
839
2047
|
}
|
|
840
2048
|
)
|
|
841
2049
|
] }),
|
|
@@ -844,19 +2052,19 @@ function BugBearPanel({
|
|
|
844
2052
|
{
|
|
845
2053
|
onClick: handleSubmitReport,
|
|
846
2054
|
disabled: submitting || !description.trim(),
|
|
847
|
-
className: "w-full py-2 px-4 bg-
|
|
2055
|
+
className: "w-full py-2 px-4 bg-blue-500 text-white rounded-lg font-medium text-sm hover:bg-blue-600 disabled:opacity-50 disabled:cursor-not-allowed transition-colors",
|
|
848
2056
|
children: submitting ? "Submitting..." : "Submit Report"
|
|
849
2057
|
}
|
|
850
2058
|
)
|
|
851
2059
|
] }) })
|
|
852
2060
|
] }),
|
|
853
|
-
showProfileOverlay && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "absolute inset-0 bg-
|
|
854
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "bg-
|
|
2061
|
+
showProfileOverlay && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "absolute inset-0 bg-zinc-900 z-50 flex flex-col rounded-xl overflow-hidden", children: [
|
|
2062
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "bg-zinc-950 text-white px-4 py-3 flex items-center justify-between border-b border-zinc-800", children: [
|
|
855
2063
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
856
2064
|
"button",
|
|
857
2065
|
{
|
|
858
2066
|
onClick: handleCloseProfile,
|
|
859
|
-
className: "text-sm text-
|
|
2067
|
+
className: "text-sm text-zinc-400 hover:text-white transition-colors",
|
|
860
2068
|
children: "\u2190 Back"
|
|
861
2069
|
}
|
|
862
2070
|
),
|
|
@@ -866,23 +2074,23 @@ function BugBearPanel({
|
|
|
866
2074
|
] }),
|
|
867
2075
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex-1 overflow-y-auto p-4", children: profileSaved ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "text-center py-8", children: [
|
|
868
2076
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-4xl", children: "\u2705" }),
|
|
869
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-
|
|
2077
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-zinc-400 mt-2 font-medium", children: "Profile saved!" })
|
|
870
2078
|
] }) : profileEditing ? (
|
|
871
2079
|
/* Edit Profile Form */
|
|
872
2080
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
|
|
873
2081
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center justify-between mb-4", children: [
|
|
874
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { className: "font-semibold text-
|
|
2082
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { className: "font-semibold text-zinc-100", children: "Edit Profile" }),
|
|
875
2083
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
876
2084
|
"button",
|
|
877
2085
|
{
|
|
878
2086
|
onClick: handleCancelEditProfile,
|
|
879
|
-
className: "text-sm text-
|
|
2087
|
+
className: "text-sm text-zinc-500 hover:text-zinc-300",
|
|
880
2088
|
children: "Cancel"
|
|
881
2089
|
}
|
|
882
2090
|
)
|
|
883
2091
|
] }),
|
|
884
2092
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mb-4", children: [
|
|
885
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "block text-xs font-medium text-
|
|
2093
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "block text-xs font-medium text-zinc-300 mb-1", children: "Name" }),
|
|
886
2094
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
887
2095
|
"input",
|
|
888
2096
|
{
|
|
@@ -890,27 +2098,27 @@ function BugBearPanel({
|
|
|
890
2098
|
value: profileName,
|
|
891
2099
|
onChange: (e) => setProfileName(e.target.value),
|
|
892
2100
|
placeholder: "Your name",
|
|
893
|
-
className: "w-full px-3 py-2 text-sm border border-
|
|
2101
|
+
className: "w-full px-3 py-2 text-sm bg-zinc-800 text-zinc-100 border border-zinc-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 placeholder:text-zinc-500"
|
|
894
2102
|
}
|
|
895
2103
|
)
|
|
896
2104
|
] }),
|
|
897
2105
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mb-4", children: [
|
|
898
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "block text-xs font-medium text-
|
|
899
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "px-3 py-2 bg-
|
|
900
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-sm text-
|
|
901
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-xs text-
|
|
2106
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "block text-xs font-medium text-zinc-300 mb-1", children: "Primary Email" }),
|
|
2107
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "px-3 py-2 bg-zinc-800 rounded-lg", children: [
|
|
2108
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-sm text-zinc-300", children: testerInfo?.email }),
|
|
2109
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-xs text-zinc-500 mt-0.5", children: "Main communication email" })
|
|
902
2110
|
] })
|
|
903
2111
|
] }),
|
|
904
2112
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mb-4", children: [
|
|
905
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "block text-xs font-medium text-
|
|
906
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-xs text-
|
|
2113
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "block text-xs font-medium text-zinc-300 mb-1", children: "Additional Testing Emails" }),
|
|
2114
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-xs text-zinc-500 mb-2", children: "Add other emails you use to test on different accounts" }),
|
|
907
2115
|
profileAdditionalEmails.map((email) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-2 mb-2", children: [
|
|
908
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "flex-1 px-3 py-1.5 bg-
|
|
2116
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "flex-1 px-3 py-1.5 bg-blue-950/30 text-blue-300 text-sm rounded-full", children: email }),
|
|
909
2117
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
910
2118
|
"button",
|
|
911
2119
|
{
|
|
912
2120
|
onClick: () => handleRemoveEmail(email),
|
|
913
|
-
className: "text-
|
|
2121
|
+
className: "text-blue-400 hover:text-red-500 text-sm",
|
|
914
2122
|
children: "\u2715"
|
|
915
2123
|
}
|
|
916
2124
|
)
|
|
@@ -923,7 +2131,7 @@ function BugBearPanel({
|
|
|
923
2131
|
value: newEmailInput,
|
|
924
2132
|
onChange: (e) => setNewEmailInput(e.target.value),
|
|
925
2133
|
placeholder: "email@example.com",
|
|
926
|
-
className: "flex-1 px-3 py-2 text-sm border border-
|
|
2134
|
+
className: "flex-1 px-3 py-2 text-sm bg-zinc-800 text-zinc-100 border border-zinc-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 placeholder:text-zinc-500",
|
|
927
2135
|
onKeyDown: (e) => e.key === "Enter" && handleAddEmail()
|
|
928
2136
|
}
|
|
929
2137
|
),
|
|
@@ -932,15 +2140,15 @@ function BugBearPanel({
|
|
|
932
2140
|
{
|
|
933
2141
|
onClick: handleAddEmail,
|
|
934
2142
|
disabled: !newEmailInput.trim(),
|
|
935
|
-
className: "px-3 py-2 bg-
|
|
2143
|
+
className: "px-3 py-2 bg-blue-500 text-white text-sm rounded-lg hover:bg-blue-600 disabled:opacity-50 disabled:cursor-not-allowed",
|
|
936
2144
|
children: "Add"
|
|
937
2145
|
}
|
|
938
2146
|
)
|
|
939
2147
|
] })
|
|
940
2148
|
] }),
|
|
941
2149
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mb-4", children: [
|
|
942
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "block text-xs font-medium text-
|
|
943
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-xs text-
|
|
2150
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "block text-xs font-medium text-zinc-300 mb-1", children: "Testing Platforms" }),
|
|
2151
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-xs text-zinc-500 mb-2", children: "Select the platforms you can test on" }),
|
|
944
2152
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex gap-2", children: [
|
|
945
2153
|
{ key: "ios", label: "\u{1F4F1} iOS" },
|
|
946
2154
|
{ key: "android", label: "\u{1F916} Android" },
|
|
@@ -949,7 +2157,7 @@ function BugBearPanel({
|
|
|
949
2157
|
"button",
|
|
950
2158
|
{
|
|
951
2159
|
onClick: () => handleTogglePlatform(key),
|
|
952
|
-
className: `flex-1 py-2 px-3 rounded-lg text-sm font-medium transition-colors border-2 ${profilePlatforms.includes(key) ? "bg-
|
|
2160
|
+
className: `flex-1 py-2 px-3 rounded-lg text-sm font-medium transition-colors border-2 ${profilePlatforms.includes(key) ? "bg-blue-950/30 border-blue-500 text-blue-300" : "bg-zinc-800 border-transparent text-zinc-400 hover:bg-zinc-700"}`,
|
|
953
2161
|
children: label
|
|
954
2162
|
},
|
|
955
2163
|
key
|
|
@@ -968,31 +2176,31 @@ function BugBearPanel({
|
|
|
968
2176
|
) : (
|
|
969
2177
|
/* Profile View */
|
|
970
2178
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
|
|
971
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "bg-
|
|
972
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "w-16 h-16 mx-auto bg-
|
|
973
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { className: "font-semibold text-
|
|
974
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-sm text-
|
|
975
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center justify-center gap-6 mt-4 pt-4 border-t border-
|
|
2179
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "bg-zinc-800 rounded-lg p-4 text-center mb-4", children: [
|
|
2180
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "w-16 h-16 mx-auto bg-blue-500 rounded-full flex items-center justify-center mb-3", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-2xl font-semibold text-white", children: testerInfo?.name?.charAt(0)?.toUpperCase() || "?" }) }),
|
|
2181
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { className: "font-semibold text-zinc-100", children: testerInfo?.name }),
|
|
2182
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-sm text-zinc-500", children: testerInfo?.email }),
|
|
2183
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center justify-center gap-6 mt-4 pt-4 border-t border-zinc-700", children: [
|
|
976
2184
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "text-center", children: [
|
|
977
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-xl font-bold text-
|
|
978
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-xs text-
|
|
2185
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-xl font-bold text-blue-400", children: testerInfo?.assignedTests || 0 }),
|
|
2186
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-xs text-zinc-500", children: "Assigned" })
|
|
979
2187
|
] }),
|
|
980
2188
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "text-center", children: [
|
|
981
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-xl font-bold text-
|
|
982
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-xs text-
|
|
2189
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-xl font-bold text-blue-400", children: testerInfo?.completedTests || 0 }),
|
|
2190
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-xs text-zinc-500", children: "Completed" })
|
|
983
2191
|
] })
|
|
984
2192
|
] })
|
|
985
2193
|
] }),
|
|
986
|
-
(testerInfo?.additionalEmails?.length || 0) > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "bg-
|
|
987
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-xs font-medium text-
|
|
988
|
-
testerInfo?.additionalEmails?.map((email) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-sm text-
|
|
2194
|
+
(testerInfo?.additionalEmails?.length || 0) > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "bg-zinc-800 rounded-lg p-3 mb-3", children: [
|
|
2195
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-xs font-medium text-zinc-500 uppercase tracking-wide mb-2", children: "Additional Emails" }),
|
|
2196
|
+
testerInfo?.additionalEmails?.map((email) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-sm text-zinc-300", children: email }, email))
|
|
989
2197
|
] }),
|
|
990
|
-
(testerInfo?.platforms?.length || 0) > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "bg-
|
|
991
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-xs font-medium text-
|
|
2198
|
+
(testerInfo?.platforms?.length || 0) > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "bg-zinc-800 rounded-lg p-3 mb-3", children: [
|
|
2199
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-xs font-medium text-zinc-500 uppercase tracking-wide mb-2", children: "Testing Platforms" }),
|
|
992
2200
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex flex-wrap gap-2", children: testerInfo?.platforms?.map((platform) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
993
2201
|
"span",
|
|
994
2202
|
{
|
|
995
|
-
className: "px-2 py-1 bg-
|
|
2203
|
+
className: "px-2 py-1 bg-blue-900/50 text-blue-300 text-xs rounded-full font-medium",
|
|
996
2204
|
children: platform === "ios" ? "\u{1F4F1} iOS" : platform === "android" ? "\u{1F916} Android" : "\u{1F310} Web"
|
|
997
2205
|
},
|
|
998
2206
|
platform
|
|
@@ -1002,14 +2210,31 @@ function BugBearPanel({
|
|
|
1002
2210
|
"button",
|
|
1003
2211
|
{
|
|
1004
2212
|
onClick: handleStartEditProfile,
|
|
1005
|
-
className: "w-full py-2 px-4 bg-
|
|
2213
|
+
className: "w-full py-2 px-4 bg-blue-500 text-white rounded-lg font-medium text-sm hover:bg-blue-600 transition-colors",
|
|
1006
2214
|
children: "Edit Profile"
|
|
1007
2215
|
}
|
|
1008
2216
|
)
|
|
1009
2217
|
] })
|
|
1010
2218
|
) })
|
|
1011
2219
|
] }),
|
|
1012
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.
|
|
2220
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "px-4 py-2 bg-zinc-950 border-t border-zinc-800 flex items-center justify-between text-xs text-zinc-500", children: activeTab === "messages" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
|
|
2221
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { children: [
|
|
2222
|
+
threads.length,
|
|
2223
|
+
" thread",
|
|
2224
|
+
threads.length !== 1 ? "s" : "",
|
|
2225
|
+
" \xB7 ",
|
|
2226
|
+
unreadCount,
|
|
2227
|
+
" unread"
|
|
2228
|
+
] }),
|
|
2229
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
2230
|
+
"button",
|
|
2231
|
+
{
|
|
2232
|
+
onClick: refreshThreads,
|
|
2233
|
+
className: "hover:text-zinc-300",
|
|
2234
|
+
children: "\u21BB Refresh"
|
|
2235
|
+
}
|
|
2236
|
+
)
|
|
2237
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
|
|
1013
2238
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { children: [
|
|
1014
2239
|
pendingCount,
|
|
1015
2240
|
" pending \xB7 ",
|
|
@@ -1020,11 +2245,11 @@ function BugBearPanel({
|
|
|
1020
2245
|
"button",
|
|
1021
2246
|
{
|
|
1022
2247
|
onClick: refreshAssignments,
|
|
1023
|
-
className: "hover:text-
|
|
2248
|
+
className: "hover:text-zinc-300",
|
|
1024
2249
|
children: "\u21BB Refresh"
|
|
1025
2250
|
}
|
|
1026
2251
|
)
|
|
1027
|
-
] })
|
|
2252
|
+
] }) })
|
|
1028
2253
|
] })
|
|
1029
2254
|
]
|
|
1030
2255
|
}
|