@bbearai/react 0.1.6 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -50,6 +50,14 @@ var BugBearContext = (0, import_react.createContext)({
50
50
  },
51
51
  updateTesterProfile: async () => ({ success: false }),
52
52
  refreshTesterInfo: async () => {
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 () => {
53
61
  }
54
62
  });
55
63
  function useBugBear() {
@@ -63,11 +71,52 @@ function BugBearProvider({ config, children, enabled = true }) {
63
71
  const [assignments, setAssignments] = (0, import_react.useState)([]);
64
72
  const [isLoading, setIsLoading] = (0, import_react.useState)(true);
65
73
  const hasInitialized = (0, import_react.useRef)(false);
74
+ const [activeSession, setActiveSession] = (0, import_react.useState)(null);
75
+ const [sessionFindings, setSessionFindings] = (0, import_react.useState)([]);
66
76
  const refreshAssignments = (0, import_react.useCallback)(async () => {
67
77
  if (!client) return;
68
78
  const newAssignments = await client.getAssignedTests();
69
79
  setAssignments(newAssignments);
70
80
  }, [client]);
81
+ const refreshSession = (0, import_react.useCallback)(async () => {
82
+ if (!client) return;
83
+ const session = await client.getActiveSession();
84
+ setActiveSession(session);
85
+ if (session) {
86
+ const findings = await client.getSessionFindings(session.id);
87
+ setSessionFindings(findings);
88
+ } else {
89
+ setSessionFindings([]);
90
+ }
91
+ }, [client]);
92
+ const startSession = (0, import_react.useCallback)(async (options = {}) => {
93
+ if (!client) return { success: false, error: "Client not initialized" };
94
+ const result = await client.startSession(options);
95
+ if (result.success && result.session) {
96
+ setActiveSession(result.session);
97
+ setSessionFindings([]);
98
+ }
99
+ return { success: result.success, error: result.error };
100
+ }, [client]);
101
+ const endSession = (0, import_react.useCallback)(async (notes) => {
102
+ if (!client || !activeSession) return { success: false, error: "No active session" };
103
+ const routesCovered = client.getNavigationHistory();
104
+ const result = await client.endSession(activeSession.id, { notes, routesCovered });
105
+ if (result.success) {
106
+ setActiveSession(null);
107
+ setSessionFindings([]);
108
+ }
109
+ return { success: result.success, error: result.error };
110
+ }, [client, activeSession]);
111
+ const addFinding = (0, import_react.useCallback)(async (options) => {
112
+ if (!client || !activeSession) return { success: false, error: "No active session" };
113
+ const result = await client.addFinding(activeSession.id, options);
114
+ if (result.success && result.finding) {
115
+ setSessionFindings((prev) => [...prev, result.finding]);
116
+ setActiveSession((prev) => prev ? { ...prev, findingsCount: prev.findingsCount + 1 } : null);
117
+ }
118
+ return result;
119
+ }, [client, activeSession]);
71
120
  const initializeBugBear = (0, import_react.useCallback)(async (bugBearClient) => {
72
121
  setIsLoading(true);
73
122
  try {
@@ -80,8 +129,16 @@ function BugBearProvider({ config, children, enabled = true }) {
80
129
  setTesterInfo(info);
81
130
  setIsTester(!!info);
82
131
  if (info && qaEnabled) {
83
- const newAssignments = await bugBearClient.getAssignedTests();
132
+ const [newAssignments, session] = await Promise.all([
133
+ bugBearClient.getAssignedTests(),
134
+ bugBearClient.getActiveSession()
135
+ ]);
84
136
  setAssignments(newAssignments);
137
+ setActiveSession(session);
138
+ if (session) {
139
+ const findings = await bugBearClient.getSessionFindings(session.id);
140
+ setSessionFindings(findings);
141
+ }
85
142
  }
86
143
  } catch (err) {
87
144
  console.error("BugBear: Init error", err);
@@ -136,7 +193,14 @@ function BugBearProvider({ config, children, enabled = true }) {
136
193
  onNavigate: config.onNavigate,
137
194
  refreshTesterStatus,
138
195
  updateTesterProfile,
139
- refreshTesterInfo
196
+ refreshTesterInfo,
197
+ // QA Sessions
198
+ activeSession,
199
+ sessionFindings,
200
+ startSession,
201
+ endSession,
202
+ addFinding,
203
+ refreshSession
140
204
  },
141
205
  children
142
206
  }
@@ -215,7 +279,7 @@ function BugBearPanel({
215
279
  defaultCollapsed = false,
216
280
  draggable = true
217
281
  }) {
218
- const { client, shouldShowWidget, testerInfo, assignments, currentAssignment, refreshAssignments, isLoading, onNavigate, updateTesterProfile, refreshTesterInfo } = useBugBear();
282
+ const { client, shouldShowWidget, testerInfo, assignments, currentAssignment, refreshAssignments, isLoading, onNavigate, updateTesterProfile, refreshTesterInfo, activeSession, sessionFindings, startSession, endSession, addFinding } = useBugBear();
219
283
  const [collapsed, setCollapsed] = (0, import_react2.useState)(defaultCollapsed);
220
284
  const [activeTab, setActiveTab] = (0, import_react2.useState)("tests");
221
285
  const [showSteps, setShowSteps] = (0, import_react2.useState)(false);
@@ -232,6 +296,16 @@ function BugBearPanel({
232
296
  const [submitting, setSubmitting] = (0, import_react2.useState)(false);
233
297
  const [submitted, setSubmitted] = (0, import_react2.useState)(false);
234
298
  const [justPassed, setJustPassed] = (0, import_react2.useState)(false);
299
+ const [showFeedbackPrompt, setShowFeedbackPrompt] = (0, import_react2.useState)(false);
300
+ const [pendingFeedbackStatus, setPendingFeedbackStatus] = (0, import_react2.useState)(null);
301
+ const [feedbackRating, setFeedbackRating] = (0, import_react2.useState)(5);
302
+ const [feedbackNote, setFeedbackNote] = (0, import_react2.useState)("");
303
+ const [feedbackFlags, setFeedbackFlags] = (0, import_react2.useState)({
304
+ isOutdated: false,
305
+ needsMoreDetail: false,
306
+ stepsUnclear: false,
307
+ expectedResultUnclear: false
308
+ });
235
309
  const [criteriaResults, setCriteriaResults] = (0, import_react2.useState)({});
236
310
  const [profileEditing, setProfileEditing] = (0, import_react2.useState)(false);
237
311
  const [profileName, setProfileName] = (0, import_react2.useState)("");
@@ -240,6 +314,23 @@ function BugBearPanel({
240
314
  const [profilePlatforms, setProfilePlatforms] = (0, import_react2.useState)([]);
241
315
  const [savingProfile, setSavingProfile] = (0, import_react2.useState)(false);
242
316
  const [profileSaved, setProfileSaved] = (0, import_react2.useState)(false);
317
+ const [showProfileOverlay, setShowProfileOverlay] = (0, import_react2.useState)(false);
318
+ const [startingSession, setStartingSession] = (0, import_react2.useState)(false);
319
+ const [sessionFocusArea, setSessionFocusArea] = (0, import_react2.useState)("");
320
+ const [sessionPlatform, setSessionPlatform] = (0, import_react2.useState)("web");
321
+ const [suggestedRoutes, setSuggestedRoutes] = (0, import_react2.useState)([]);
322
+ const [focusAreas, setFocusAreas] = (0, import_react2.useState)([]);
323
+ const [showAddFinding, setShowAddFinding] = (0, import_react2.useState)(false);
324
+ const [findingType, setFindingType] = (0, import_react2.useState)("bug");
325
+ const [findingTitle, setFindingTitle] = (0, import_react2.useState)("");
326
+ const [findingDescription, setFindingDescription] = (0, import_react2.useState)("");
327
+ const [findingSeverity, setFindingSeverity] = (0, import_react2.useState)("medium");
328
+ const [addingFinding, setAddingFinding] = (0, import_react2.useState)(false);
329
+ const [endingSession, setEndingSession] = (0, import_react2.useState)(false);
330
+ const [sessionNotes, setSessionNotes] = (0, import_react2.useState)("");
331
+ const [showEndConfirm, setShowEndConfirm] = (0, import_react2.useState)(false);
332
+ const [sessionElapsedTime, setSessionElapsedTime] = (0, import_react2.useState)(0);
333
+ const [assignmentElapsedTime, setAssignmentElapsedTime] = (0, import_react2.useState)(0);
243
334
  (0, import_react2.useEffect)(() => {
244
335
  if (typeof window === "undefined") return;
245
336
  try {
@@ -274,6 +365,84 @@ function BugBearPanel({
274
365
  setCriteriaResults({});
275
366
  setShowSteps(false);
276
367
  }, [displayedAssignment?.id]);
368
+ (0, import_react2.useEffect)(() => {
369
+ if (!activeSession) {
370
+ setSessionElapsedTime(0);
371
+ return;
372
+ }
373
+ const startTime = new Date(activeSession.startedAt).getTime();
374
+ const now = Date.now();
375
+ setSessionElapsedTime(Math.floor((now - startTime) / 1e3));
376
+ const interval = setInterval(() => {
377
+ const elapsed = Math.floor((Date.now() - startTime) / 1e3);
378
+ setSessionElapsedTime(elapsed);
379
+ }, 1e3);
380
+ return () => clearInterval(interval);
381
+ }, [activeSession]);
382
+ (0, import_react2.useEffect)(() => {
383
+ const activeAssignment = displayedAssignment?.status === "in_progress" ? displayedAssignment : null;
384
+ if (!activeAssignment?.startedAt) {
385
+ setAssignmentElapsedTime(0);
386
+ return;
387
+ }
388
+ const startTime = new Date(activeAssignment.startedAt).getTime();
389
+ const now = Date.now();
390
+ setAssignmentElapsedTime(Math.floor((now - startTime) / 1e3));
391
+ const interval = setInterval(() => {
392
+ const elapsed = Math.floor((Date.now() - startTime) / 1e3);
393
+ setAssignmentElapsedTime(elapsed);
394
+ }, 1e3);
395
+ return () => clearInterval(interval);
396
+ }, [displayedAssignment?.id, displayedAssignment?.status, displayedAssignment?.startedAt]);
397
+ (0, import_react2.useEffect)(() => {
398
+ if (!client || activeSession) {
399
+ setSuggestedRoutes([]);
400
+ setFocusAreas([]);
401
+ return;
402
+ }
403
+ const loadSuggestions = async () => {
404
+ try {
405
+ const supabase = client.supabase;
406
+ if (!supabase) return;
407
+ const projectId = client.config.projectId;
408
+ const [routeStatsRes, focusAreasRes] = await Promise.all([
409
+ 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),
410
+ supabase.rpc("get_active_focus_areas", { p_project_id: projectId })
411
+ ]);
412
+ if (routeStatsRes.data && routeStatsRes.data.length > 0) {
413
+ const suggestions = routeStatsRes.data.filter((r) => r.route && r.priority_score > 0).slice(0, 5).map((r) => {
414
+ let reason = "";
415
+ if (r.open_bugs > 0) {
416
+ reason = `${r.open_bugs} open bug${r.open_bugs > 1 ? "s" : ""}`;
417
+ } else if (!r.last_tested_at) {
418
+ reason = "Never tested";
419
+ } else if (r.test_case_count === 0) {
420
+ reason = "No test coverage";
421
+ } else {
422
+ reason = `Priority: ${r.priority_score}`;
423
+ }
424
+ return {
425
+ route: r.route,
426
+ reason,
427
+ priorityScore: r.priority_score
428
+ };
429
+ });
430
+ setSuggestedRoutes(suggestions);
431
+ }
432
+ if (focusAreasRes.data && focusAreasRes.data.length > 0) {
433
+ setFocusAreas(focusAreasRes.data.slice(0, 3).map((fa) => ({
434
+ id: fa.id,
435
+ name: fa.name,
436
+ description: fa.description,
437
+ priority: fa.priority
438
+ })));
439
+ }
440
+ } catch (err) {
441
+ console.error("BugBear: Failed to load suggestions", err);
442
+ }
443
+ };
444
+ loadSuggestions();
445
+ }, [client, activeSession]);
277
446
  const handleMouseDown = (0, import_react2.useCallback)((e) => {
278
447
  if (!draggable || !panelPosition) return;
279
448
  const target = e.target;
@@ -324,27 +493,86 @@ function BugBearPanel({
324
493
  return null;
325
494
  }
326
495
  const handlePass = async () => {
327
- if (!client || !displayedAssignment) return;
328
- setSubmitting(true);
329
- await client.submitReport({
330
- type: "test_pass",
331
- description: `Test passed: ${displayedAssignment.testCase.title}`,
332
- assignmentId: displayedAssignment.id,
333
- testCaseId: displayedAssignment.testCase.id,
334
- appContext: getAppContext?.() || { currentRoute: window.location.pathname }
496
+ if (!displayedAssignment) return;
497
+ setPendingFeedbackStatus("passed");
498
+ setShowFeedbackPrompt(true);
499
+ setFeedbackRating(5);
500
+ setFeedbackNote("");
501
+ setFeedbackFlags({
502
+ isOutdated: false,
503
+ needsMoreDetail: false,
504
+ stepsUnclear: false,
505
+ expectedResultUnclear: false
335
506
  });
336
- await refreshAssignments();
337
- setSubmitting(false);
338
- setJustPassed(true);
339
- setTimeout(() => {
340
- setJustPassed(false);
341
- setSelectedTestId(null);
342
- setTestView("detail");
343
- }, 1200);
344
507
  };
345
508
  const handleFail = async () => {
346
- setActiveTab("report");
347
- setReportType("test_fail");
509
+ if (!displayedAssignment) return;
510
+ setPendingFeedbackStatus("failed");
511
+ setShowFeedbackPrompt(true);
512
+ setFeedbackRating(3);
513
+ setFeedbackNote("");
514
+ setFeedbackFlags({
515
+ isOutdated: false,
516
+ needsMoreDetail: false,
517
+ stepsUnclear: false,
518
+ expectedResultUnclear: false
519
+ });
520
+ };
521
+ const handleSubmitFeedback = async (skipFeedback = false) => {
522
+ if (!client || !displayedAssignment) return;
523
+ setSubmitting(true);
524
+ const feedback = skipFeedback ? void 0 : {
525
+ rating: feedbackRating,
526
+ feedbackNote: feedbackNote.trim() || void 0,
527
+ isOutdated: feedbackFlags.isOutdated,
528
+ needsMoreDetail: feedbackFlags.needsMoreDetail,
529
+ stepsUnclear: feedbackFlags.stepsUnclear,
530
+ expectedResultUnclear: feedbackFlags.expectedResultUnclear
531
+ };
532
+ if (pendingFeedbackStatus === "passed") {
533
+ await client.submitReport({
534
+ type: "test_pass",
535
+ description: `Test passed: ${displayedAssignment.testCase.title}`,
536
+ assignmentId: displayedAssignment.id,
537
+ testCaseId: displayedAssignment.testCase.id,
538
+ appContext: getAppContext?.() || { currentRoute: window.location.pathname }
539
+ });
540
+ if (feedback) {
541
+ await client.submitTestFeedback({
542
+ testCaseId: displayedAssignment.testCase.id,
543
+ assignmentId: displayedAssignment.id,
544
+ feedback,
545
+ timeToCompleteSeconds: assignmentElapsedTime || void 0
546
+ });
547
+ }
548
+ await refreshAssignments();
549
+ setSubmitting(false);
550
+ setShowFeedbackPrompt(false);
551
+ setPendingFeedbackStatus(null);
552
+ setJustPassed(true);
553
+ setTimeout(() => {
554
+ setJustPassed(false);
555
+ setSelectedTestId(null);
556
+ setTestView("detail");
557
+ }, 1200);
558
+ } else if (pendingFeedbackStatus === "failed") {
559
+ if (feedback) {
560
+ await client.submitTestFeedback({
561
+ testCaseId: displayedAssignment.testCase.id,
562
+ assignmentId: displayedAssignment.id,
563
+ feedback,
564
+ timeToCompleteSeconds: assignmentElapsedTime || void 0
565
+ });
566
+ }
567
+ setSubmitting(false);
568
+ setShowFeedbackPrompt(false);
569
+ setPendingFeedbackStatus(null);
570
+ setActiveTab("report");
571
+ setReportType("test_fail");
572
+ }
573
+ };
574
+ const handleSkipFeedback = () => {
575
+ handleSubmitFeedback(true);
348
576
  };
349
577
  const handleSubmitReport = async () => {
350
578
  if (!client || !description.trim()) return;
@@ -376,6 +604,15 @@ function BugBearPanel({
376
604
  };
377
605
  const pendingCount = assignments.filter((a) => a.status === "pending").length;
378
606
  const inProgressCount = assignments.filter((a) => a.status === "in_progress").length;
607
+ const handleOpenProfile = () => {
608
+ if (testerInfo) {
609
+ setProfileName(testerInfo.name);
610
+ setProfileAdditionalEmails(testerInfo.additionalEmails || []);
611
+ setProfilePlatforms(testerInfo.platforms || []);
612
+ }
613
+ setProfileEditing(false);
614
+ setShowProfileOverlay(true);
615
+ };
379
616
  const handleStartEditProfile = () => {
380
617
  if (testerInfo) {
381
618
  setProfileName(testerInfo.name);
@@ -388,6 +625,11 @@ function BugBearPanel({
388
625
  setProfileEditing(false);
389
626
  setNewEmailInput("");
390
627
  };
628
+ const handleCloseProfile = () => {
629
+ setShowProfileOverlay(false);
630
+ setProfileEditing(false);
631
+ setNewEmailInput("");
632
+ };
391
633
  const handleAddEmail = () => {
392
634
  const email = newEmailInput.trim().toLowerCase();
393
635
  if (email && email.includes("@") && !profileAdditionalEmails.includes(email)) {
@@ -416,10 +658,61 @@ function BugBearPanel({
416
658
  if (result.success) {
417
659
  setProfileEditing(false);
418
660
  setProfileSaved(true);
419
- setTimeout(() => setProfileSaved(false), 2e3);
661
+ setTimeout(() => {
662
+ setProfileSaved(false);
663
+ setShowProfileOverlay(false);
664
+ }, 1500);
420
665
  }
421
666
  setSavingProfile(false);
422
667
  };
668
+ const handleStartSession = async () => {
669
+ setStartingSession(true);
670
+ const result = await startSession({
671
+ focusArea: sessionFocusArea.trim() || void 0,
672
+ platform: sessionPlatform
673
+ });
674
+ if (result.success) {
675
+ setSessionFocusArea("");
676
+ }
677
+ setStartingSession(false);
678
+ };
679
+ const handleEndSession = async () => {
680
+ setEndingSession(true);
681
+ const result = await endSession(sessionNotes.trim() || void 0);
682
+ if (result.success) {
683
+ setSessionNotes("");
684
+ setShowEndConfirm(false);
685
+ }
686
+ setEndingSession(false);
687
+ };
688
+ const handleAddFinding = async () => {
689
+ if (!findingTitle.trim()) return;
690
+ setAddingFinding(true);
691
+ const result = await addFinding({
692
+ type: findingType,
693
+ title: findingTitle.trim(),
694
+ description: findingDescription.trim() || void 0,
695
+ severity: findingType === "bug" ? findingSeverity : void 0,
696
+ route: typeof window !== "undefined" ? window.location.pathname : void 0
697
+ });
698
+ if (result.success) {
699
+ setFindingTitle("");
700
+ setFindingDescription("");
701
+ setFindingType("bug");
702
+ setFindingSeverity("medium");
703
+ setShowAddFinding(false);
704
+ }
705
+ setAddingFinding(false);
706
+ };
707
+ const formatElapsedTime = (seconds) => {
708
+ const h = Math.floor(seconds / 3600);
709
+ const m = Math.floor(seconds % 3600 / 60);
710
+ const s = seconds % 60;
711
+ if (h > 0) {
712
+ return `${h}:${m.toString().padStart(2, "0")}:${s.toString().padStart(2, "0")}`;
713
+ }
714
+ return `${m}:${s.toString().padStart(2, "0")}`;
715
+ };
423
716
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
424
717
  "div",
425
718
  {
@@ -467,7 +760,17 @@ function BugBearPanel({
467
760
  "BugBear",
468
761
  draggable && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-purple-300 text-xs", title: "Drag to move, double-click to reset", children: "\u22EE\u22EE" })
469
762
  ] }),
470
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-purple-200 text-xs", children: testerInfo?.name })
763
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
764
+ "button",
765
+ {
766
+ onClick: handleOpenProfile,
767
+ className: "text-purple-200 text-xs flex items-center gap-1 hover:text-white transition-colors",
768
+ children: [
769
+ testerInfo?.name,
770
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-[10px]", children: "\u270E" })
771
+ ]
772
+ }
773
+ )
471
774
  ] })
472
775
  ] }),
473
776
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
@@ -481,35 +784,40 @@ function BugBearPanel({
481
784
  ]
482
785
  }
483
786
  ),
484
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex border-b border-gray-200", children: [
787
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex gap-2 p-2 bg-gray-50 border-b border-gray-200", children: [
485
788
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
486
789
  "button",
487
790
  {
488
791
  onClick: () => setActiveTab("tests"),
489
- className: `flex-1 px-4 py-2 text-sm font-medium transition-colors ${activeTab === "tests" ? "text-purple-600 border-b-2 border-purple-600" : "text-gray-500 hover:text-gray-700"}`,
792
+ className: `flex-1 py-2 px-3 rounded-lg text-sm font-semibold transition-all flex items-center justify-center gap-1.5 ${activeTab === "tests" ? "bg-purple-600 text-white shadow-sm" : "bg-white text-gray-600 hover:bg-gray-100 border border-gray-200"}`,
490
793
  children: [
491
- "Tests ",
492
- pendingCount > 0 && `(${pendingCount})`
794
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: "\u{1F4CB}" }),
795
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: "Run Tests" }),
796
+ pendingCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: `ml-1 px-1.5 py-0.5 rounded-full text-xs ${activeTab === "tests" ? "bg-purple-500 text-white" : "bg-purple-100 text-purple-600"}`, children: pendingCount })
493
797
  ]
494
798
  }
495
799
  ),
496
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
497
- "button",
498
- {
499
- onClick: () => setActiveTab("profile"),
500
- className: `flex-1 px-4 py-2 text-sm font-medium transition-colors ${activeTab === "profile" ? "text-purple-600 border-b-2 border-purple-600" : "text-gray-500 hover:text-gray-700"}`,
501
- children: "Profile"
502
- }
503
- ),
504
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
800
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
505
801
  "button",
506
802
  {
507
- onClick: () => setActiveTab("report"),
508
- className: `flex-1 px-4 py-2 text-sm font-medium transition-colors ${activeTab === "report" ? "text-purple-600 border-b-2 border-purple-600" : "text-gray-500 hover:text-gray-700"}`,
509
- children: "Report"
803
+ onClick: () => setActiveTab("session"),
804
+ className: `flex-1 py-2 px-3 rounded-lg text-sm font-semibold transition-all flex items-center justify-center gap-1.5 relative ${activeTab === "session" ? "bg-amber-500 text-white shadow-sm" : "bg-white text-gray-600 hover:bg-gray-100 border border-gray-200"}`,
805
+ children: [
806
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: "\u{1F50D}" }),
807
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: "Explore" }),
808
+ activeSession && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "absolute -top-1 -right-1 w-3 h-3 bg-green-500 rounded-full border-2 border-white animate-pulse" })
809
+ ]
510
810
  }
511
811
  )
512
812
  ] }),
813
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex justify-center py-1.5 border-b border-gray-200 bg-white", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
814
+ "button",
815
+ {
816
+ onClick: () => setActiveTab("report"),
817
+ className: `px-4 py-1 text-xs font-medium transition-colors rounded-full ${activeTab === "report" ? "bg-red-50 text-red-600 border border-red-200" : "text-gray-500 hover:text-gray-700 hover:bg-gray-50"}`,
818
+ children: "\u{1F41B} Report Bug / Feedback"
819
+ }
820
+ ) }),
513
821
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "p-4 max-h-96 overflow-y-auto", children: [
514
822
  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: [
515
823
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-4xl", children: "\u2705" }),
@@ -560,6 +868,83 @@ function BugBearPanel({
560
868
  assignment.id
561
869
  ))
562
870
  ] })
871
+ ) : showFeedbackPrompt && displayedAssignment ? (
872
+ /* Feedback prompt after completing a test */
873
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "p-3", children: [
874
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "text-center mb-4", children: [
875
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-3xl", children: pendingFeedbackStatus === "passed" ? "\u2713" : "\u2717" }),
876
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: `font-semibold mt-1 ${pendingFeedbackStatus === "passed" ? "text-green-600" : "text-red-600"}`, children: pendingFeedbackStatus === "passed" ? "Test Passed!" : "Test Failed" })
877
+ ] }),
878
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "bg-purple-50 border border-purple-100 rounded-lg p-3 mb-4", children: [
879
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-purple-800 text-sm font-medium mb-1", children: "Help us improve this test" }),
880
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-purple-600 text-xs", children: "Your feedback shapes better tests for everyone." })
881
+ ] }),
882
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mb-4", children: [
883
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "block text-xs font-medium text-gray-600 mb-2", children: "How was this test?" }),
884
+ /* @__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)(
885
+ "button",
886
+ {
887
+ type: "button",
888
+ onClick: () => setFeedbackRating(star),
889
+ className: `text-2xl transition-colors ${star <= feedbackRating ? "text-yellow-400" : "text-gray-300"} hover:text-yellow-400`,
890
+ children: "\u2605"
891
+ },
892
+ star
893
+ )) }),
894
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-center text-xs text-gray-500 mt-1", children: feedbackRating === 1 ? "Needs work" : feedbackRating === 2 ? "Could be better" : feedbackRating === 3 ? "Okay" : feedbackRating === 4 ? "Good" : "Great!" })
895
+ ] }),
896
+ feedbackRating < 4 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mb-4", children: [
897
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "block text-xs font-medium text-gray-600 mb-2", children: "What could be improved?" }),
898
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "grid grid-cols-2 gap-2", children: [
899
+ { key: "stepsUnclear", label: "Steps unclear" },
900
+ { key: "expectedResultUnclear", label: "Expected result unclear" },
901
+ { key: "needsMoreDetail", label: "Needs more detail" },
902
+ { key: "isOutdated", label: "Seems outdated" }
903
+ ].map(({ key, label }) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
904
+ "button",
905
+ {
906
+ type: "button",
907
+ onClick: () => setFeedbackFlags((prev) => ({ ...prev, [key]: !prev[key] })),
908
+ className: `px-2 py-1.5 rounded text-xs font-medium border transition-colors ${feedbackFlags[key] ? "bg-purple-100 border-purple-300 text-purple-700" : "bg-white border-gray-200 text-gray-600 hover:border-purple-200"}`,
909
+ children: label
910
+ },
911
+ key
912
+ )) })
913
+ ] }),
914
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mb-4", children: [
915
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "block text-xs font-medium text-gray-600 mb-1", children: "Suggestions? (optional)" }),
916
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
917
+ "textarea",
918
+ {
919
+ value: feedbackNote,
920
+ onChange: (e) => setFeedbackNote(e.target.value),
921
+ placeholder: "How could this test be improved?",
922
+ className: "w-full px-3 py-2 text-sm border border-gray-200 rounded-lg resize-none focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-transparent",
923
+ rows: 2
924
+ }
925
+ )
926
+ ] }),
927
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex gap-2", children: [
928
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
929
+ "button",
930
+ {
931
+ onClick: handleSkipFeedback,
932
+ disabled: submitting,
933
+ className: "flex-1 px-3 py-2 text-sm font-medium text-gray-600 bg-gray-100 rounded-lg hover:bg-gray-200 transition-colors disabled:opacity-50",
934
+ children: "Skip"
935
+ }
936
+ ),
937
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
938
+ "button",
939
+ {
940
+ onClick: () => handleSubmitFeedback(false),
941
+ disabled: submitting,
942
+ className: "flex-1 px-3 py-2 text-sm font-medium text-white bg-purple-600 rounded-lg hover:bg-purple-700 transition-colors disabled:opacity-50",
943
+ children: submitting ? "Submitting..." : "Submit Feedback"
944
+ }
945
+ )
946
+ ] })
947
+ ] })
563
948
  ) : justPassed ? (
564
949
  /* Success state after passing */
565
950
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "text-center py-8", children: [
@@ -601,6 +986,14 @@ function BugBearPanel({
601
986
  ] })
602
987
  ] }),
603
988
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h4", { className: "font-medium text-gray-900 text-sm mb-1", children: displayedAssignment.testCase.title }),
989
+ 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-600 bg-green-50 px-2 py-1 rounded", children: [
990
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: "relative flex h-2 w-2", children: [
991
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75" }),
992
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "relative inline-flex rounded-full h-2 w-2 bg-green-500" })
993
+ ] }),
994
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-medium", children: "Testing" }),
995
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-mono", children: formatElapsedTime(assignmentElapsedTime) })
996
+ ] }),
604
997
  displayedAssignment.testCase.description && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-gray-500 text-xs mb-2", children: displayedAssignment.testCase.description }),
605
998
  displayedAssignment.testCase.targetRoute && onNavigate && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
606
999
  "button",
@@ -777,7 +1170,345 @@ function BugBearPanel({
777
1170
  ] })
778
1171
  ] })
779
1172
  ) : null }),
780
- activeTab === "profile" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { children: profileSaved ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "text-center py-8", children: [
1173
+ activeTab === "session" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { children: !activeSession ? (
1174
+ /* Start Session View */
1175
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
1176
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "text-center mb-4", children: [
1177
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-4xl", children: "\u{1F50D}" }),
1178
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { className: "font-semibold text-gray-900 mt-2", children: "Exploratory QA Session" }),
1179
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-gray-500 text-xs mt-1", children: "Explore freely and capture findings as you go" })
1180
+ ] }),
1181
+ focusAreas.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mb-4", children: [
1182
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "block text-xs font-medium text-gray-700 mb-2", children: [
1183
+ "\u{1F4CC} Focus Areas",
1184
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "ml-1 text-[10px] text-gray-500 font-normal", children: "from your team" })
1185
+ ] }),
1186
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "space-y-1.5", children: focusAreas.map((area) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1187
+ "button",
1188
+ {
1189
+ onClick: () => setSessionFocusArea(area.name),
1190
+ className: `w-full text-left px-3 py-2 rounded-lg text-xs transition-colors border ${sessionFocusArea === area.name ? "bg-amber-50 border-amber-300 text-amber-700" : "bg-amber-50/50 border-amber-200 text-gray-700 hover:bg-amber-50"}`,
1191
+ children: [
1192
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center justify-between", children: [
1193
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-medium", children: area.name }),
1194
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: `text-[10px] px-1.5 py-0.5 rounded ${area.priority >= 70 ? "bg-red-100 text-red-600" : area.priority >= 50 ? "bg-amber-100 text-amber-600" : "bg-gray-100 text-gray-500"}`, children: area.priority >= 70 ? "Urgent" : area.priority >= 50 ? "Important" : "Suggested" })
1195
+ ] }),
1196
+ area.description && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-[10px] text-gray-500 mt-0.5 line-clamp-2", children: area.description })
1197
+ ]
1198
+ },
1199
+ area.id
1200
+ )) })
1201
+ ] }),
1202
+ suggestedRoutes.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mb-4", children: [
1203
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "block text-xs font-medium text-gray-700 mb-2", children: "\u{1F3AF} Suggested Routes to Explore" }),
1204
+ /* @__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)(
1205
+ "button",
1206
+ {
1207
+ onClick: () => setSessionFocusArea(suggestion.route),
1208
+ className: `w-full text-left px-3 py-2 rounded-lg text-xs transition-colors border ${sessionFocusArea === suggestion.route ? "bg-purple-50 border-purple-300 text-purple-700" : "bg-gray-50 border-gray-200 text-gray-700 hover:bg-gray-100"}`,
1209
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center justify-between", children: [
1210
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-medium truncate", children: suggestion.route }),
1211
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: `text-[10px] px-1.5 py-0.5 rounded ${suggestion.priorityScore >= 40 ? "bg-red-100 text-red-600" : suggestion.priorityScore >= 25 ? "bg-amber-100 text-amber-600" : "bg-gray-100 text-gray-500"}`, children: suggestion.reason })
1212
+ ] })
1213
+ },
1214
+ idx
1215
+ )) })
1216
+ ] }),
1217
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mb-3", children: [
1218
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "block text-xs font-medium text-gray-700 mb-1", children: "Focus Area (optional)" }),
1219
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1220
+ "input",
1221
+ {
1222
+ type: "text",
1223
+ value: sessionFocusArea,
1224
+ onChange: (e) => setSessionFocusArea(e.target.value),
1225
+ placeholder: "e.g., checkout flow, settings page",
1226
+ className: "w-full px-3 py-2 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500"
1227
+ }
1228
+ )
1229
+ ] }),
1230
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mb-4", children: [
1231
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "block text-xs font-medium text-gray-700 mb-1", children: "Platform" }),
1232
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex gap-2", children: [
1233
+ { key: "web", label: "\u{1F310} Web" },
1234
+ { key: "ios", label: "\u{1F4F1} iOS" },
1235
+ { key: "android", label: "\u{1F916} Android" }
1236
+ ].map(({ key, label }) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1237
+ "button",
1238
+ {
1239
+ onClick: () => setSessionPlatform(key),
1240
+ className: `flex-1 py-2 px-3 rounded-lg text-xs font-medium transition-colors border-2 ${sessionPlatform === key ? "bg-purple-50 border-purple-500 text-purple-700" : "bg-gray-50 border-transparent text-gray-600 hover:bg-gray-100"}`,
1241
+ children: label
1242
+ },
1243
+ key
1244
+ )) })
1245
+ ] }),
1246
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1247
+ "button",
1248
+ {
1249
+ onClick: handleStartSession,
1250
+ disabled: startingSession,
1251
+ 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",
1252
+ children: startingSession ? "Starting..." : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
1253
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: "\u25B6" }),
1254
+ " Start Session"
1255
+ ] })
1256
+ }
1257
+ )
1258
+ ] })
1259
+ ) : showEndConfirm ? (
1260
+ /* End Session Confirmation */
1261
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
1262
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "text-center mb-4", children: [
1263
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-4xl", children: "\u270B" }),
1264
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { className: "font-semibold text-gray-900 mt-2", children: "End Session?" }),
1265
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: "text-gray-500 text-xs mt-1", children: [
1266
+ "Duration: ",
1267
+ formatElapsedTime(sessionElapsedTime),
1268
+ " \u2022 ",
1269
+ sessionFindings.length,
1270
+ " finding",
1271
+ sessionFindings.length !== 1 ? "s" : ""
1272
+ ] })
1273
+ ] }),
1274
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mb-4", children: [
1275
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "block text-xs font-medium text-gray-700 mb-1", children: "Session Notes (optional)" }),
1276
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1277
+ "textarea",
1278
+ {
1279
+ value: sessionNotes,
1280
+ onChange: (e) => setSessionNotes(e.target.value),
1281
+ placeholder: "Any overall observations from this session...",
1282
+ rows: 3,
1283
+ className: "w-full px-3 py-2 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500 resize-none"
1284
+ }
1285
+ )
1286
+ ] }),
1287
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex gap-2", children: [
1288
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1289
+ "button",
1290
+ {
1291
+ onClick: () => setShowEndConfirm(false),
1292
+ className: "flex-1 py-2 px-3 bg-gray-100 text-gray-700 rounded-lg font-medium text-sm hover:bg-gray-200 transition-colors",
1293
+ children: "Cancel"
1294
+ }
1295
+ ),
1296
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1297
+ "button",
1298
+ {
1299
+ onClick: handleEndSession,
1300
+ disabled: endingSession,
1301
+ 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",
1302
+ children: endingSession ? "Ending..." : "End Session"
1303
+ }
1304
+ )
1305
+ ] })
1306
+ ] })
1307
+ ) : showAddFinding ? (
1308
+ /* Add Finding Form */
1309
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
1310
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center justify-between mb-3", children: [
1311
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { className: "font-semibold text-gray-900 text-sm", children: "Add Finding" }),
1312
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1313
+ "button",
1314
+ {
1315
+ onClick: () => setShowAddFinding(false),
1316
+ className: "text-gray-400 hover:text-gray-600",
1317
+ children: "\u2715"
1318
+ }
1319
+ )
1320
+ ] }),
1321
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex gap-1 mb-3", children: [
1322
+ { type: "bug", label: "\u{1F41B} Bug", color: "red" },
1323
+ { type: "concern", label: "\u26A0\uFE0F Concern", color: "orange" },
1324
+ { type: "suggestion", label: "\u{1F4A1} Idea", color: "blue" },
1325
+ { type: "question", label: "\u2753 Question", color: "purple" }
1326
+ ].map(({ type, label, color }) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1327
+ "button",
1328
+ {
1329
+ onClick: () => setFindingType(type),
1330
+ className: `flex-1 py-1.5 px-2 rounded text-xs font-medium transition-colors ${findingType === type ? color === "red" ? "bg-red-100 text-red-700 ring-2 ring-red-400" : color === "orange" ? "bg-orange-100 text-orange-700 ring-2 ring-orange-400" : color === "blue" ? "bg-blue-100 text-blue-700 ring-2 ring-blue-400" : "bg-purple-100 text-purple-700 ring-2 ring-purple-400" : "bg-gray-100 text-gray-600 hover:bg-gray-200"}`,
1331
+ children: label
1332
+ },
1333
+ type
1334
+ )) }),
1335
+ findingType === "bug" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mb-3", children: [
1336
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "block text-xs font-medium text-gray-700 mb-1", children: "Severity" }),
1337
+ /* @__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)(
1338
+ "button",
1339
+ {
1340
+ onClick: () => setFindingSeverity(sev),
1341
+ 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-gray-500 text-white" : "bg-blue-500 text-white" : "bg-gray-100 text-gray-600 hover:bg-gray-200"}`,
1342
+ children: sev === "observation" ? "obs" : sev
1343
+ },
1344
+ sev
1345
+ )) })
1346
+ ] }),
1347
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mb-3", children: [
1348
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "block text-xs font-medium text-gray-700 mb-1", children: "Title *" }),
1349
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1350
+ "input",
1351
+ {
1352
+ type: "text",
1353
+ value: findingTitle,
1354
+ onChange: (e) => setFindingTitle(e.target.value),
1355
+ placeholder: "Brief description of what you found",
1356
+ className: "w-full px-3 py-2 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500"
1357
+ }
1358
+ )
1359
+ ] }),
1360
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mb-3", children: [
1361
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "block text-xs font-medium text-gray-700 mb-1", children: "Details (optional)" }),
1362
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1363
+ "textarea",
1364
+ {
1365
+ value: findingDescription,
1366
+ onChange: (e) => setFindingDescription(e.target.value),
1367
+ placeholder: "Steps to reproduce, expected behavior, etc.",
1368
+ rows: 2,
1369
+ className: "w-full px-3 py-2 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500 resize-none"
1370
+ }
1371
+ )
1372
+ ] }),
1373
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1374
+ "button",
1375
+ {
1376
+ onClick: handleAddFinding,
1377
+ disabled: addingFinding || !findingTitle.trim(),
1378
+ className: "w-full py-2 px-4 bg-purple-600 text-white rounded-lg font-medium text-sm hover:bg-purple-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors",
1379
+ children: addingFinding ? "Adding..." : "Add Finding"
1380
+ }
1381
+ )
1382
+ ] })
1383
+ ) : (
1384
+ /* Active Session View */
1385
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
1386
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "bg-green-50 rounded-lg p-3 mb-3 border border-green-200", children: [
1387
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center justify-between", children: [
1388
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-2", children: [
1389
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "w-2 h-2 bg-green-500 rounded-full animate-pulse" }),
1390
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-medium text-green-800 text-sm", children: "Session Active" })
1391
+ ] }),
1392
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-mono text-green-700 text-lg font-semibold", children: formatElapsedTime(sessionElapsedTime) })
1393
+ ] }),
1394
+ activeSession.focusArea && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("p", { className: "text-green-700 text-xs mt-1", children: [
1395
+ "Focus: ",
1396
+ activeSession.focusArea
1397
+ ] })
1398
+ ] }),
1399
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1400
+ "button",
1401
+ {
1402
+ onClick: () => setShowAddFinding(true),
1403
+ className: "w-full py-3 px-4 bg-purple-600 text-white rounded-lg font-semibold text-sm hover:bg-purple-700 transition-colors flex items-center justify-center gap-2 mb-3",
1404
+ children: [
1405
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: "+" }),
1406
+ " Add Finding"
1407
+ ]
1408
+ }
1409
+ ),
1410
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mb-3", children: [
1411
+ /* @__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-gray-500", children: [
1412
+ "Findings (",
1413
+ sessionFindings.length,
1414
+ ")"
1415
+ ] }) }),
1416
+ sessionFindings.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "text-center py-4 bg-gray-50 rounded-lg", children: [
1417
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-gray-400 text-xs", children: "No findings yet" }),
1418
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-gray-400 text-xs", children: "Explore and add findings as you go" })
1419
+ ] }) : /* @__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)(
1420
+ "div",
1421
+ {
1422
+ className: `p-2 rounded-lg border text-xs ${finding.type === "bug" ? "bg-red-50 border-red-200" : finding.type === "concern" ? "bg-orange-50 border-orange-200" : finding.type === "suggestion" ? "bg-blue-50 border-blue-200" : "bg-purple-50 border-purple-200"}`,
1423
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-start gap-2", children: [
1424
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { children: finding.type === "bug" ? "\u{1F41B}" : finding.type === "concern" ? "\u26A0\uFE0F" : finding.type === "suggestion" ? "\u{1F4A1}" : "\u2753" }),
1425
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex-1 min-w-0", children: [
1426
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "font-medium text-gray-900 truncate", children: finding.title }),
1427
+ 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-200 text-red-800" : finding.severity === "high" ? "bg-orange-200 text-orange-800" : finding.severity === "medium" ? "bg-yellow-200 text-yellow-800" : "bg-gray-200 text-gray-700"}`, children: finding.severity })
1428
+ ] })
1429
+ ] })
1430
+ },
1431
+ finding.id
1432
+ )) })
1433
+ ] }),
1434
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1435
+ "button",
1436
+ {
1437
+ onClick: () => setShowEndConfirm(true),
1438
+ className: "w-full py-2 px-4 bg-gray-100 text-gray-700 rounded-lg font-medium text-sm hover:bg-gray-200 transition-colors",
1439
+ children: "End Session"
1440
+ }
1441
+ )
1442
+ ] })
1443
+ ) }),
1444
+ activeTab === "report" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { children: submitted ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "text-center py-8", children: [
1445
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-4xl", children: "\u{1F389}" }),
1446
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-gray-600 mt-2 font-medium", children: "Report submitted!" })
1447
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
1448
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex gap-2 mb-4", children: [
1449
+ { type: "bug", label: "\u{1F41B} Bug", color: "red" },
1450
+ { type: "feedback", label: "\u{1F4A1} Feedback", color: "blue" },
1451
+ { type: "suggestion", label: "\u2728 Idea", color: "purple" }
1452
+ ].map(({ type, label, color }) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1453
+ "button",
1454
+ {
1455
+ onClick: () => setReportType(type),
1456
+ className: `flex-1 py-1.5 px-2 rounded-lg text-xs font-medium transition-colors ${reportType === type ? color === "red" ? "bg-red-100 text-red-700 ring-2 ring-red-500" : color === "blue" ? "bg-blue-100 text-blue-700 ring-2 ring-blue-500" : "bg-purple-100 text-purple-700 ring-2 ring-purple-500" : "bg-gray-100 text-gray-600 hover:bg-gray-200"}`,
1457
+ children: label
1458
+ },
1459
+ type
1460
+ )) }),
1461
+ (reportType === "bug" || reportType === "test_fail") && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mb-3", children: [
1462
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "block text-xs font-medium text-gray-700 mb-1", children: "Severity" }),
1463
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex gap-1", children: ["critical", "high", "medium", "low"].map((sev) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1464
+ "button",
1465
+ {
1466
+ onClick: () => setSeverity(sev),
1467
+ 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-gray-500 text-white" : "bg-gray-100 text-gray-600 hover:bg-gray-200"}`,
1468
+ children: sev
1469
+ },
1470
+ sev
1471
+ )) })
1472
+ ] }),
1473
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mb-3", children: [
1474
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "block text-xs font-medium text-gray-700 mb-1", children: "What happened?" }),
1475
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1476
+ "textarea",
1477
+ {
1478
+ value: description,
1479
+ onChange: (e) => setDescription(e.target.value),
1480
+ placeholder: "Describe the issue...",
1481
+ rows: 3,
1482
+ className: "w-full px-3 py-2 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500 resize-none"
1483
+ }
1484
+ )
1485
+ ] }),
1486
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1487
+ "button",
1488
+ {
1489
+ onClick: handleSubmitReport,
1490
+ disabled: submitting || !description.trim(),
1491
+ className: "w-full py-2 px-4 bg-purple-600 text-white rounded-lg font-medium text-sm hover:bg-purple-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors",
1492
+ children: submitting ? "Submitting..." : "Submit Report"
1493
+ }
1494
+ )
1495
+ ] }) })
1496
+ ] }),
1497
+ showProfileOverlay && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "absolute inset-0 bg-white z-50 flex flex-col rounded-xl overflow-hidden", children: [
1498
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "bg-purple-600 text-white px-4 py-3 flex items-center justify-between", children: [
1499
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1500
+ "button",
1501
+ {
1502
+ onClick: handleCloseProfile,
1503
+ className: "text-sm text-purple-200 hover:text-white transition-colors",
1504
+ children: "\u2190 Back"
1505
+ }
1506
+ ),
1507
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "font-semibold text-sm", children: "Profile" }),
1508
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "w-12" }),
1509
+ " "
1510
+ ] }),
1511
+ /* @__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: [
781
1512
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-4xl", children: "\u2705" }),
782
1513
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-gray-600 mt-2 font-medium", children: "Profile saved!" })
783
1514
  ] }) : profileEditing ? (
@@ -920,59 +1651,7 @@ function BugBearPanel({
920
1651
  }
921
1652
  )
922
1653
  ] })
923
- ) }),
924
- activeTab === "report" && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { children: submitted ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "text-center py-8", children: [
925
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-4xl", children: "\u{1F389}" }),
926
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-gray-600 mt-2 font-medium", children: "Report submitted!" })
927
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
928
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex gap-2 mb-4", children: [
929
- { type: "bug", label: "\u{1F41B} Bug", color: "red" },
930
- { type: "feedback", label: "\u{1F4A1} Feedback", color: "blue" },
931
- { type: "suggestion", label: "\u2728 Idea", color: "purple" }
932
- ].map(({ type, label, color }) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
933
- "button",
934
- {
935
- onClick: () => setReportType(type),
936
- className: `flex-1 py-1.5 px-2 rounded-lg text-xs font-medium transition-colors ${reportType === type ? color === "red" ? "bg-red-100 text-red-700 ring-2 ring-red-500" : color === "blue" ? "bg-blue-100 text-blue-700 ring-2 ring-blue-500" : "bg-purple-100 text-purple-700 ring-2 ring-purple-500" : "bg-gray-100 text-gray-600 hover:bg-gray-200"}`,
937
- children: label
938
- },
939
- type
940
- )) }),
941
- (reportType === "bug" || reportType === "test_fail") && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mb-3", children: [
942
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "block text-xs font-medium text-gray-700 mb-1", children: "Severity" }),
943
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex gap-1", children: ["critical", "high", "medium", "low"].map((sev) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
944
- "button",
945
- {
946
- onClick: () => setSeverity(sev),
947
- 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-gray-500 text-white" : "bg-gray-100 text-gray-600 hover:bg-gray-200"}`,
948
- children: sev
949
- },
950
- sev
951
- )) })
952
- ] }),
953
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mb-3", children: [
954
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "block text-xs font-medium text-gray-700 mb-1", children: "What happened?" }),
955
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
956
- "textarea",
957
- {
958
- value: description,
959
- onChange: (e) => setDescription(e.target.value),
960
- placeholder: "Describe the issue...",
961
- rows: 3,
962
- className: "w-full px-3 py-2 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500 resize-none"
963
- }
964
- )
965
- ] }),
966
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
967
- "button",
968
- {
969
- onClick: handleSubmitReport,
970
- disabled: submitting || !description.trim(),
971
- className: "w-full py-2 px-4 bg-purple-600 text-white rounded-lg font-medium text-sm hover:bg-purple-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors",
972
- children: submitting ? "Submitting..." : "Submit Report"
973
- }
974
- )
975
- ] }) })
1654
+ ) })
976
1655
  ] }),
977
1656
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "px-4 py-2 bg-gray-50 border-t border-gray-200 flex items-center justify-between text-xs text-gray-400", children: [
978
1657
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { children: [