@bbearai/react 0.1.5 → 0.1.6

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ReactNode, Component, ErrorInfo } from 'react';
3
3
  import * as _bbearai_core from '@bbearai/core';
4
- import { BugBearConfig, BugBearClient, TesterInfo, TestAssignment, AppContext, captureError } from '@bbearai/core';
4
+ import { BugBearConfig, BugBearClient, TesterInfo, TestAssignment, TesterProfileUpdate, AppContext, captureError } from '@bbearai/core';
5
5
  export { AppContext, BugBearConfig, BugBearReport, ConsoleLogEntry, DeviceInfo, EnhancedBugContext, NetworkRequest, QATrack, ReportType, Severity, TestAssignment, TestTemplate, TesterInfo, captureError, contextCapture } from '@bbearai/core';
6
6
 
7
7
  interface BugBearContextValue {
@@ -18,6 +18,13 @@ interface BugBearContextValue {
18
18
  onNavigate?: (route: string) => void;
19
19
  /** Re-check tester status (call after auth state changes) */
20
20
  refreshTesterStatus: () => Promise<void>;
21
+ /** Update tester profile */
22
+ updateTesterProfile: (updates: TesterProfileUpdate) => Promise<{
23
+ success: boolean;
24
+ error?: string;
25
+ }>;
26
+ /** Refresh tester info from server */
27
+ refreshTesterInfo: () => Promise<void>;
21
28
  }
22
29
  declare function useBugBear(): BugBearContextValue;
23
30
  interface BugBearProviderProps {
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { ReactNode, Component, ErrorInfo } from 'react';
3
3
  import * as _bbearai_core from '@bbearai/core';
4
- import { BugBearConfig, BugBearClient, TesterInfo, TestAssignment, AppContext, captureError } from '@bbearai/core';
4
+ import { BugBearConfig, BugBearClient, TesterInfo, TestAssignment, TesterProfileUpdate, AppContext, captureError } from '@bbearai/core';
5
5
  export { AppContext, BugBearConfig, BugBearReport, ConsoleLogEntry, DeviceInfo, EnhancedBugContext, NetworkRequest, QATrack, ReportType, Severity, TestAssignment, TestTemplate, TesterInfo, captureError, contextCapture } from '@bbearai/core';
6
6
 
7
7
  interface BugBearContextValue {
@@ -18,6 +18,13 @@ interface BugBearContextValue {
18
18
  onNavigate?: (route: string) => void;
19
19
  /** Re-check tester status (call after auth state changes) */
20
20
  refreshTesterStatus: () => Promise<void>;
21
+ /** Update tester profile */
22
+ updateTesterProfile: (updates: TesterProfileUpdate) => Promise<{
23
+ success: boolean;
24
+ error?: string;
25
+ }>;
26
+ /** Refresh tester info from server */
27
+ refreshTesterInfo: () => Promise<void>;
21
28
  }
22
29
  declare function useBugBear(): BugBearContextValue;
23
30
  interface BugBearProviderProps {
package/dist/index.js CHANGED
@@ -47,6 +47,9 @@ var BugBearContext = (0, import_react.createContext)({
47
47
  isLoading: true,
48
48
  onNavigate: void 0,
49
49
  refreshTesterStatus: async () => {
50
+ },
51
+ updateTesterProfile: async () => ({ success: false }),
52
+ refreshTesterInfo: async () => {
50
53
  }
51
54
  });
52
55
  function useBugBear() {
@@ -91,6 +94,20 @@ function BugBearProvider({ config, children, enabled = true }) {
91
94
  setClient(freshClient);
92
95
  await initializeBugBear(freshClient);
93
96
  }, [config, initializeBugBear]);
97
+ const updateTesterProfile = (0, import_react.useCallback)(async (updates) => {
98
+ if (!client) return { success: false, error: "Client not initialized" };
99
+ const result = await client.updateTesterProfile(updates);
100
+ if (result.success) {
101
+ const info = await client.getTesterInfo();
102
+ setTesterInfo(info);
103
+ }
104
+ return result;
105
+ }, [client]);
106
+ const refreshTesterInfo = (0, import_react.useCallback)(async () => {
107
+ if (!client) return;
108
+ const info = await client.getTesterInfo();
109
+ setTesterInfo(info);
110
+ }, [client]);
94
111
  (0, import_react.useEffect)(() => {
95
112
  if (enabled && !hasInitialized.current) {
96
113
  hasInitialized.current = true;
@@ -117,7 +134,9 @@ function BugBearProvider({ config, children, enabled = true }) {
117
134
  refreshAssignments,
118
135
  isLoading,
119
136
  onNavigate: config.onNavigate,
120
- refreshTesterStatus
137
+ refreshTesterStatus,
138
+ updateTesterProfile,
139
+ refreshTesterInfo
121
140
  },
122
141
  children
123
142
  }
@@ -196,7 +215,7 @@ function BugBearPanel({
196
215
  defaultCollapsed = false,
197
216
  draggable = true
198
217
  }) {
199
- const { client, shouldShowWidget, testerInfo, assignments, currentAssignment, refreshAssignments, isLoading, onNavigate } = useBugBear();
218
+ const { client, shouldShowWidget, testerInfo, assignments, currentAssignment, refreshAssignments, isLoading, onNavigate, updateTesterProfile, refreshTesterInfo } = useBugBear();
200
219
  const [collapsed, setCollapsed] = (0, import_react2.useState)(defaultCollapsed);
201
220
  const [activeTab, setActiveTab] = (0, import_react2.useState)("tests");
202
221
  const [showSteps, setShowSteps] = (0, import_react2.useState)(false);
@@ -214,6 +233,13 @@ function BugBearPanel({
214
233
  const [submitted, setSubmitted] = (0, import_react2.useState)(false);
215
234
  const [justPassed, setJustPassed] = (0, import_react2.useState)(false);
216
235
  const [criteriaResults, setCriteriaResults] = (0, import_react2.useState)({});
236
+ const [profileEditing, setProfileEditing] = (0, import_react2.useState)(false);
237
+ const [profileName, setProfileName] = (0, import_react2.useState)("");
238
+ const [profileAdditionalEmails, setProfileAdditionalEmails] = (0, import_react2.useState)([]);
239
+ const [newEmailInput, setNewEmailInput] = (0, import_react2.useState)("");
240
+ const [profilePlatforms, setProfilePlatforms] = (0, import_react2.useState)([]);
241
+ const [savingProfile, setSavingProfile] = (0, import_react2.useState)(false);
242
+ const [profileSaved, setProfileSaved] = (0, import_react2.useState)(false);
217
243
  (0, import_react2.useEffect)(() => {
218
244
  if (typeof window === "undefined") return;
219
245
  try {
@@ -350,6 +376,50 @@ function BugBearPanel({
350
376
  };
351
377
  const pendingCount = assignments.filter((a) => a.status === "pending").length;
352
378
  const inProgressCount = assignments.filter((a) => a.status === "in_progress").length;
379
+ const handleStartEditProfile = () => {
380
+ if (testerInfo) {
381
+ setProfileName(testerInfo.name);
382
+ setProfileAdditionalEmails(testerInfo.additionalEmails || []);
383
+ setProfilePlatforms(testerInfo.platforms || []);
384
+ }
385
+ setProfileEditing(true);
386
+ };
387
+ const handleCancelEditProfile = () => {
388
+ setProfileEditing(false);
389
+ setNewEmailInput("");
390
+ };
391
+ const handleAddEmail = () => {
392
+ const email = newEmailInput.trim().toLowerCase();
393
+ if (email && email.includes("@") && !profileAdditionalEmails.includes(email)) {
394
+ setProfileAdditionalEmails([...profileAdditionalEmails, email]);
395
+ setNewEmailInput("");
396
+ }
397
+ };
398
+ const handleRemoveEmail = (email) => {
399
+ setProfileAdditionalEmails(profileAdditionalEmails.filter((e) => e !== email));
400
+ };
401
+ const handleTogglePlatform = (platform) => {
402
+ if (profilePlatforms.includes(platform)) {
403
+ setProfilePlatforms(profilePlatforms.filter((p) => p !== platform));
404
+ } else {
405
+ setProfilePlatforms([...profilePlatforms, platform]);
406
+ }
407
+ };
408
+ const handleSaveProfile = async () => {
409
+ setSavingProfile(true);
410
+ const updates = {
411
+ name: profileName.trim(),
412
+ additionalEmails: profileAdditionalEmails,
413
+ platforms: profilePlatforms
414
+ };
415
+ const result = await updateTesterProfile(updates);
416
+ if (result.success) {
417
+ setProfileEditing(false);
418
+ setProfileSaved(true);
419
+ setTimeout(() => setProfileSaved(false), 2e3);
420
+ }
421
+ setSavingProfile(false);
422
+ };
353
423
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
354
424
  "div",
355
425
  {
@@ -423,6 +493,14 @@ function BugBearPanel({
423
493
  ]
424
494
  }
425
495
  ),
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
+ ),
426
504
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
427
505
  "button",
428
506
  {
@@ -699,6 +777,150 @@ function BugBearPanel({
699
777
  ] })
700
778
  ] })
701
779
  ) : 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: [
781
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-4xl", children: "\u2705" }),
782
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-gray-600 mt-2 font-medium", children: "Profile saved!" })
783
+ ] }) : profileEditing ? (
784
+ /* Edit Profile Form */
785
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
786
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center justify-between mb-4", children: [
787
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { className: "font-semibold text-gray-900", children: "Edit Profile" }),
788
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
789
+ "button",
790
+ {
791
+ onClick: handleCancelEditProfile,
792
+ className: "text-sm text-gray-500 hover:text-gray-700",
793
+ children: "Cancel"
794
+ }
795
+ )
796
+ ] }),
797
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mb-4", children: [
798
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "block text-xs font-medium text-gray-700 mb-1", children: "Name" }),
799
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
800
+ "input",
801
+ {
802
+ type: "text",
803
+ value: profileName,
804
+ onChange: (e) => setProfileName(e.target.value),
805
+ placeholder: "Your name",
806
+ 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"
807
+ }
808
+ )
809
+ ] }),
810
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mb-4", children: [
811
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "block text-xs font-medium text-gray-700 mb-1", children: "Primary Email" }),
812
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "px-3 py-2 bg-gray-100 rounded-lg", children: [
813
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-sm text-gray-700", children: testerInfo?.email }),
814
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-xs text-gray-400 mt-0.5", children: "Main communication email" })
815
+ ] })
816
+ ] }),
817
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mb-4", children: [
818
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "block text-xs font-medium text-gray-700 mb-1", children: "Additional Testing Emails" }),
819
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-xs text-gray-500 mb-2", children: "Add other emails you use to test on different accounts" }),
820
+ profileAdditionalEmails.map((email) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-2 mb-2", children: [
821
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "flex-1 px-3 py-1.5 bg-purple-50 text-purple-700 text-sm rounded-full", children: email }),
822
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
823
+ "button",
824
+ {
825
+ onClick: () => handleRemoveEmail(email),
826
+ className: "text-purple-400 hover:text-red-500 text-sm",
827
+ children: "\u2715"
828
+ }
829
+ )
830
+ ] }, email)),
831
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex gap-2", children: [
832
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
833
+ "input",
834
+ {
835
+ type: "email",
836
+ value: newEmailInput,
837
+ onChange: (e) => setNewEmailInput(e.target.value),
838
+ placeholder: "email@example.com",
839
+ className: "flex-1 px-3 py-2 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500",
840
+ onKeyDown: (e) => e.key === "Enter" && handleAddEmail()
841
+ }
842
+ ),
843
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
844
+ "button",
845
+ {
846
+ onClick: handleAddEmail,
847
+ disabled: !newEmailInput.trim(),
848
+ className: "px-3 py-2 bg-purple-600 text-white text-sm rounded-lg hover:bg-purple-700 disabled:opacity-50 disabled:cursor-not-allowed",
849
+ children: "Add"
850
+ }
851
+ )
852
+ ] })
853
+ ] }),
854
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "mb-4", children: [
855
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { className: "block text-xs font-medium text-gray-700 mb-1", children: "Testing Platforms" }),
856
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-xs text-gray-500 mb-2", children: "Select the platforms you can test on" }),
857
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex gap-2", children: [
858
+ { key: "ios", label: "\u{1F4F1} iOS" },
859
+ { key: "android", label: "\u{1F916} Android" },
860
+ { key: "web", label: "\u{1F310} Web" }
861
+ ].map(({ key, label }) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
862
+ "button",
863
+ {
864
+ onClick: () => handleTogglePlatform(key),
865
+ className: `flex-1 py-2 px-3 rounded-lg text-sm font-medium transition-colors border-2 ${profilePlatforms.includes(key) ? "bg-purple-50 border-purple-500 text-purple-700" : "bg-gray-50 border-transparent text-gray-600 hover:bg-gray-100"}`,
866
+ children: label
867
+ },
868
+ key
869
+ )) })
870
+ ] }),
871
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
872
+ "button",
873
+ {
874
+ onClick: handleSaveProfile,
875
+ disabled: savingProfile,
876
+ className: "w-full py-2 px-4 bg-green-600 text-white rounded-lg font-medium text-sm hover:bg-green-700 disabled:opacity-50 transition-colors",
877
+ children: savingProfile ? "Saving..." : "Save Profile"
878
+ }
879
+ )
880
+ ] })
881
+ ) : (
882
+ /* Profile View */
883
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
884
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "bg-gray-50 rounded-lg p-4 text-center mb-4", children: [
885
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "w-16 h-16 mx-auto bg-purple-600 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() || "?" }) }),
886
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { className: "font-semibold text-gray-900", children: testerInfo?.name }),
887
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-sm text-gray-500", children: testerInfo?.email }),
888
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center justify-center gap-6 mt-4 pt-4 border-t border-gray-200", children: [
889
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "text-center", children: [
890
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-xl font-bold text-purple-600", children: testerInfo?.assignedTests || 0 }),
891
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-xs text-gray-500", children: "Assigned" })
892
+ ] }),
893
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "text-center", children: [
894
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-xl font-bold text-purple-600", children: testerInfo?.completedTests || 0 }),
895
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-xs text-gray-500", children: "Completed" })
896
+ ] })
897
+ ] })
898
+ ] }),
899
+ (testerInfo?.additionalEmails?.length || 0) > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "bg-gray-50 rounded-lg p-3 mb-3", children: [
900
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-xs font-medium text-gray-500 uppercase tracking-wide mb-2", children: "Additional Emails" }),
901
+ testerInfo?.additionalEmails?.map((email) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-sm text-gray-700", children: email }, email))
902
+ ] }),
903
+ (testerInfo?.platforms?.length || 0) > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "bg-gray-50 rounded-lg p-3 mb-3", children: [
904
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-xs font-medium text-gray-500 uppercase tracking-wide mb-2", children: "Testing Platforms" }),
905
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex flex-wrap gap-2", children: testerInfo?.platforms?.map((platform) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
906
+ "span",
907
+ {
908
+ className: "px-2 py-1 bg-purple-100 text-purple-700 text-xs rounded-full font-medium",
909
+ children: platform === "ios" ? "\u{1F4F1} iOS" : platform === "android" ? "\u{1F916} Android" : "\u{1F310} Web"
910
+ },
911
+ platform
912
+ )) })
913
+ ] }),
914
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
915
+ "button",
916
+ {
917
+ onClick: handleStartEditProfile,
918
+ className: "w-full py-2 px-4 bg-purple-600 text-white rounded-lg font-medium text-sm hover:bg-purple-700 transition-colors",
919
+ children: "Edit Profile"
920
+ }
921
+ )
922
+ ] })
923
+ ) }),
702
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: [
703
925
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: "text-4xl", children: "\u{1F389}" }),
704
926
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { className: "text-gray-600 mt-2 font-medium", children: "Report submitted!" })
package/dist/index.mjs CHANGED
@@ -17,6 +17,9 @@ var BugBearContext = createContext({
17
17
  isLoading: true,
18
18
  onNavigate: void 0,
19
19
  refreshTesterStatus: async () => {
20
+ },
21
+ updateTesterProfile: async () => ({ success: false }),
22
+ refreshTesterInfo: async () => {
20
23
  }
21
24
  });
22
25
  function useBugBear() {
@@ -61,6 +64,20 @@ function BugBearProvider({ config, children, enabled = true }) {
61
64
  setClient(freshClient);
62
65
  await initializeBugBear(freshClient);
63
66
  }, [config, initializeBugBear]);
67
+ const updateTesterProfile = useCallback(async (updates) => {
68
+ if (!client) return { success: false, error: "Client not initialized" };
69
+ const result = await client.updateTesterProfile(updates);
70
+ if (result.success) {
71
+ const info = await client.getTesterInfo();
72
+ setTesterInfo(info);
73
+ }
74
+ return result;
75
+ }, [client]);
76
+ const refreshTesterInfo = useCallback(async () => {
77
+ if (!client) return;
78
+ const info = await client.getTesterInfo();
79
+ setTesterInfo(info);
80
+ }, [client]);
64
81
  useEffect(() => {
65
82
  if (enabled && !hasInitialized.current) {
66
83
  hasInitialized.current = true;
@@ -87,7 +104,9 @@ function BugBearProvider({ config, children, enabled = true }) {
87
104
  refreshAssignments,
88
105
  isLoading,
89
106
  onNavigate: config.onNavigate,
90
- refreshTesterStatus
107
+ refreshTesterStatus,
108
+ updateTesterProfile,
109
+ refreshTesterInfo
91
110
  },
92
111
  children
93
112
  }
@@ -166,7 +185,7 @@ function BugBearPanel({
166
185
  defaultCollapsed = false,
167
186
  draggable = true
168
187
  }) {
169
- const { client, shouldShowWidget, testerInfo, assignments, currentAssignment, refreshAssignments, isLoading, onNavigate } = useBugBear();
188
+ const { client, shouldShowWidget, testerInfo, assignments, currentAssignment, refreshAssignments, isLoading, onNavigate, updateTesterProfile, refreshTesterInfo } = useBugBear();
170
189
  const [collapsed, setCollapsed] = useState2(defaultCollapsed);
171
190
  const [activeTab, setActiveTab] = useState2("tests");
172
191
  const [showSteps, setShowSteps] = useState2(false);
@@ -184,6 +203,13 @@ function BugBearPanel({
184
203
  const [submitted, setSubmitted] = useState2(false);
185
204
  const [justPassed, setJustPassed] = useState2(false);
186
205
  const [criteriaResults, setCriteriaResults] = useState2({});
206
+ const [profileEditing, setProfileEditing] = useState2(false);
207
+ const [profileName, setProfileName] = useState2("");
208
+ const [profileAdditionalEmails, setProfileAdditionalEmails] = useState2([]);
209
+ const [newEmailInput, setNewEmailInput] = useState2("");
210
+ const [profilePlatforms, setProfilePlatforms] = useState2([]);
211
+ const [savingProfile, setSavingProfile] = useState2(false);
212
+ const [profileSaved, setProfileSaved] = useState2(false);
187
213
  useEffect2(() => {
188
214
  if (typeof window === "undefined") return;
189
215
  try {
@@ -320,6 +346,50 @@ function BugBearPanel({
320
346
  };
321
347
  const pendingCount = assignments.filter((a) => a.status === "pending").length;
322
348
  const inProgressCount = assignments.filter((a) => a.status === "in_progress").length;
349
+ const handleStartEditProfile = () => {
350
+ if (testerInfo) {
351
+ setProfileName(testerInfo.name);
352
+ setProfileAdditionalEmails(testerInfo.additionalEmails || []);
353
+ setProfilePlatforms(testerInfo.platforms || []);
354
+ }
355
+ setProfileEditing(true);
356
+ };
357
+ const handleCancelEditProfile = () => {
358
+ setProfileEditing(false);
359
+ setNewEmailInput("");
360
+ };
361
+ const handleAddEmail = () => {
362
+ const email = newEmailInput.trim().toLowerCase();
363
+ if (email && email.includes("@") && !profileAdditionalEmails.includes(email)) {
364
+ setProfileAdditionalEmails([...profileAdditionalEmails, email]);
365
+ setNewEmailInput("");
366
+ }
367
+ };
368
+ const handleRemoveEmail = (email) => {
369
+ setProfileAdditionalEmails(profileAdditionalEmails.filter((e) => e !== email));
370
+ };
371
+ const handleTogglePlatform = (platform) => {
372
+ if (profilePlatforms.includes(platform)) {
373
+ setProfilePlatforms(profilePlatforms.filter((p) => p !== platform));
374
+ } else {
375
+ setProfilePlatforms([...profilePlatforms, platform]);
376
+ }
377
+ };
378
+ const handleSaveProfile = async () => {
379
+ setSavingProfile(true);
380
+ const updates = {
381
+ name: profileName.trim(),
382
+ additionalEmails: profileAdditionalEmails,
383
+ platforms: profilePlatforms
384
+ };
385
+ const result = await updateTesterProfile(updates);
386
+ if (result.success) {
387
+ setProfileEditing(false);
388
+ setProfileSaved(true);
389
+ setTimeout(() => setProfileSaved(false), 2e3);
390
+ }
391
+ setSavingProfile(false);
392
+ };
323
393
  return /* @__PURE__ */ jsxs(
324
394
  "div",
325
395
  {
@@ -393,6 +463,14 @@ function BugBearPanel({
393
463
  ]
394
464
  }
395
465
  ),
466
+ /* @__PURE__ */ jsx2(
467
+ "button",
468
+ {
469
+ onClick: () => setActiveTab("profile"),
470
+ 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"}`,
471
+ children: "Profile"
472
+ }
473
+ ),
396
474
  /* @__PURE__ */ jsx2(
397
475
  "button",
398
476
  {
@@ -669,6 +747,150 @@ function BugBearPanel({
669
747
  ] })
670
748
  ] })
671
749
  ) : null }),
750
+ activeTab === "profile" && /* @__PURE__ */ jsx2("div", { children: profileSaved ? /* @__PURE__ */ jsxs("div", { className: "text-center py-8", children: [
751
+ /* @__PURE__ */ jsx2("span", { className: "text-4xl", children: "\u2705" }),
752
+ /* @__PURE__ */ jsx2("p", { className: "text-gray-600 mt-2 font-medium", children: "Profile saved!" })
753
+ ] }) : profileEditing ? (
754
+ /* Edit Profile Form */
755
+ /* @__PURE__ */ jsxs("div", { children: [
756
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-4", children: [
757
+ /* @__PURE__ */ jsx2("h3", { className: "font-semibold text-gray-900", children: "Edit Profile" }),
758
+ /* @__PURE__ */ jsx2(
759
+ "button",
760
+ {
761
+ onClick: handleCancelEditProfile,
762
+ className: "text-sm text-gray-500 hover:text-gray-700",
763
+ children: "Cancel"
764
+ }
765
+ )
766
+ ] }),
767
+ /* @__PURE__ */ jsxs("div", { className: "mb-4", children: [
768
+ /* @__PURE__ */ jsx2("label", { className: "block text-xs font-medium text-gray-700 mb-1", children: "Name" }),
769
+ /* @__PURE__ */ jsx2(
770
+ "input",
771
+ {
772
+ type: "text",
773
+ value: profileName,
774
+ onChange: (e) => setProfileName(e.target.value),
775
+ placeholder: "Your name",
776
+ 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"
777
+ }
778
+ )
779
+ ] }),
780
+ /* @__PURE__ */ jsxs("div", { className: "mb-4", children: [
781
+ /* @__PURE__ */ jsx2("label", { className: "block text-xs font-medium text-gray-700 mb-1", children: "Primary Email" }),
782
+ /* @__PURE__ */ jsxs("div", { className: "px-3 py-2 bg-gray-100 rounded-lg", children: [
783
+ /* @__PURE__ */ jsx2("p", { className: "text-sm text-gray-700", children: testerInfo?.email }),
784
+ /* @__PURE__ */ jsx2("p", { className: "text-xs text-gray-400 mt-0.5", children: "Main communication email" })
785
+ ] })
786
+ ] }),
787
+ /* @__PURE__ */ jsxs("div", { className: "mb-4", children: [
788
+ /* @__PURE__ */ jsx2("label", { className: "block text-xs font-medium text-gray-700 mb-1", children: "Additional Testing Emails" }),
789
+ /* @__PURE__ */ jsx2("p", { className: "text-xs text-gray-500 mb-2", children: "Add other emails you use to test on different accounts" }),
790
+ profileAdditionalEmails.map((email) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mb-2", children: [
791
+ /* @__PURE__ */ jsx2("span", { className: "flex-1 px-3 py-1.5 bg-purple-50 text-purple-700 text-sm rounded-full", children: email }),
792
+ /* @__PURE__ */ jsx2(
793
+ "button",
794
+ {
795
+ onClick: () => handleRemoveEmail(email),
796
+ className: "text-purple-400 hover:text-red-500 text-sm",
797
+ children: "\u2715"
798
+ }
799
+ )
800
+ ] }, email)),
801
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
802
+ /* @__PURE__ */ jsx2(
803
+ "input",
804
+ {
805
+ type: "email",
806
+ value: newEmailInput,
807
+ onChange: (e) => setNewEmailInput(e.target.value),
808
+ placeholder: "email@example.com",
809
+ className: "flex-1 px-3 py-2 text-sm border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500",
810
+ onKeyDown: (e) => e.key === "Enter" && handleAddEmail()
811
+ }
812
+ ),
813
+ /* @__PURE__ */ jsx2(
814
+ "button",
815
+ {
816
+ onClick: handleAddEmail,
817
+ disabled: !newEmailInput.trim(),
818
+ className: "px-3 py-2 bg-purple-600 text-white text-sm rounded-lg hover:bg-purple-700 disabled:opacity-50 disabled:cursor-not-allowed",
819
+ children: "Add"
820
+ }
821
+ )
822
+ ] })
823
+ ] }),
824
+ /* @__PURE__ */ jsxs("div", { className: "mb-4", children: [
825
+ /* @__PURE__ */ jsx2("label", { className: "block text-xs font-medium text-gray-700 mb-1", children: "Testing Platforms" }),
826
+ /* @__PURE__ */ jsx2("p", { className: "text-xs text-gray-500 mb-2", children: "Select the platforms you can test on" }),
827
+ /* @__PURE__ */ jsx2("div", { className: "flex gap-2", children: [
828
+ { key: "ios", label: "\u{1F4F1} iOS" },
829
+ { key: "android", label: "\u{1F916} Android" },
830
+ { key: "web", label: "\u{1F310} Web" }
831
+ ].map(({ key, label }) => /* @__PURE__ */ jsx2(
832
+ "button",
833
+ {
834
+ onClick: () => handleTogglePlatform(key),
835
+ className: `flex-1 py-2 px-3 rounded-lg text-sm font-medium transition-colors border-2 ${profilePlatforms.includes(key) ? "bg-purple-50 border-purple-500 text-purple-700" : "bg-gray-50 border-transparent text-gray-600 hover:bg-gray-100"}`,
836
+ children: label
837
+ },
838
+ key
839
+ )) })
840
+ ] }),
841
+ /* @__PURE__ */ jsx2(
842
+ "button",
843
+ {
844
+ onClick: handleSaveProfile,
845
+ disabled: savingProfile,
846
+ className: "w-full py-2 px-4 bg-green-600 text-white rounded-lg font-medium text-sm hover:bg-green-700 disabled:opacity-50 transition-colors",
847
+ children: savingProfile ? "Saving..." : "Save Profile"
848
+ }
849
+ )
850
+ ] })
851
+ ) : (
852
+ /* Profile View */
853
+ /* @__PURE__ */ jsxs("div", { children: [
854
+ /* @__PURE__ */ jsxs("div", { className: "bg-gray-50 rounded-lg p-4 text-center mb-4", children: [
855
+ /* @__PURE__ */ jsx2("div", { className: "w-16 h-16 mx-auto bg-purple-600 rounded-full flex items-center justify-center mb-3", children: /* @__PURE__ */ jsx2("span", { className: "text-2xl font-semibold text-white", children: testerInfo?.name?.charAt(0)?.toUpperCase() || "?" }) }),
856
+ /* @__PURE__ */ jsx2("h3", { className: "font-semibold text-gray-900", children: testerInfo?.name }),
857
+ /* @__PURE__ */ jsx2("p", { className: "text-sm text-gray-500", children: testerInfo?.email }),
858
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-6 mt-4 pt-4 border-t border-gray-200", children: [
859
+ /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
860
+ /* @__PURE__ */ jsx2("p", { className: "text-xl font-bold text-purple-600", children: testerInfo?.assignedTests || 0 }),
861
+ /* @__PURE__ */ jsx2("p", { className: "text-xs text-gray-500", children: "Assigned" })
862
+ ] }),
863
+ /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
864
+ /* @__PURE__ */ jsx2("p", { className: "text-xl font-bold text-purple-600", children: testerInfo?.completedTests || 0 }),
865
+ /* @__PURE__ */ jsx2("p", { className: "text-xs text-gray-500", children: "Completed" })
866
+ ] })
867
+ ] })
868
+ ] }),
869
+ (testerInfo?.additionalEmails?.length || 0) > 0 && /* @__PURE__ */ jsxs("div", { className: "bg-gray-50 rounded-lg p-3 mb-3", children: [
870
+ /* @__PURE__ */ jsx2("p", { className: "text-xs font-medium text-gray-500 uppercase tracking-wide mb-2", children: "Additional Emails" }),
871
+ testerInfo?.additionalEmails?.map((email) => /* @__PURE__ */ jsx2("p", { className: "text-sm text-gray-700", children: email }, email))
872
+ ] }),
873
+ (testerInfo?.platforms?.length || 0) > 0 && /* @__PURE__ */ jsxs("div", { className: "bg-gray-50 rounded-lg p-3 mb-3", children: [
874
+ /* @__PURE__ */ jsx2("p", { className: "text-xs font-medium text-gray-500 uppercase tracking-wide mb-2", children: "Testing Platforms" }),
875
+ /* @__PURE__ */ jsx2("div", { className: "flex flex-wrap gap-2", children: testerInfo?.platforms?.map((platform) => /* @__PURE__ */ jsx2(
876
+ "span",
877
+ {
878
+ className: "px-2 py-1 bg-purple-100 text-purple-700 text-xs rounded-full font-medium",
879
+ children: platform === "ios" ? "\u{1F4F1} iOS" : platform === "android" ? "\u{1F916} Android" : "\u{1F310} Web"
880
+ },
881
+ platform
882
+ )) })
883
+ ] }),
884
+ /* @__PURE__ */ jsx2(
885
+ "button",
886
+ {
887
+ onClick: handleStartEditProfile,
888
+ className: "w-full py-2 px-4 bg-purple-600 text-white rounded-lg font-medium text-sm hover:bg-purple-700 transition-colors",
889
+ children: "Edit Profile"
890
+ }
891
+ )
892
+ ] })
893
+ ) }),
672
894
  activeTab === "report" && /* @__PURE__ */ jsx2("div", { children: submitted ? /* @__PURE__ */ jsxs("div", { className: "text-center py-8", children: [
673
895
  /* @__PURE__ */ jsx2("span", { className: "text-4xl", children: "\u{1F389}" }),
674
896
  /* @__PURE__ */ jsx2("p", { className: "text-gray-600 mt-2 font-medium", children: "Report submitted!" })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bbearai/react",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "BugBear React components for web apps",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",