@insforge/react 1.0.4 → 1.0.5-dev.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -16,7 +16,6 @@ if (typeof document !== 'undefined' && typeof window !== 'undefined') {
16
16
 
17
17
  var React2 = require('react');
18
18
  var jsxRuntime = require('react/jsx-runtime');
19
- var sdk = require('@insforge/sdk');
20
19
  var react = require('@insforge/shared/react');
21
20
  var lucideReact = require('lucide-react');
22
21
  var zod = require('zod');
@@ -246,7 +245,7 @@ var require_react_is_development = __commonJS({
246
245
  var ContextProvider = REACT_PROVIDER_TYPE;
247
246
  var Element = REACT_ELEMENT_TYPE;
248
247
  var ForwardRef = REACT_FORWARD_REF_TYPE;
249
- var Fragment9 = REACT_FRAGMENT_TYPE;
248
+ var Fragment10 = REACT_FRAGMENT_TYPE;
250
249
  var Lazy = REACT_LAZY_TYPE;
251
250
  var Memo = REACT_MEMO_TYPE;
252
251
  var Portal = REACT_PORTAL_TYPE;
@@ -305,7 +304,7 @@ var require_react_is_development = __commonJS({
305
304
  exports$1.ContextProvider = ContextProvider;
306
305
  exports$1.Element = Element;
307
306
  exports$1.ForwardRef = ForwardRef;
308
- exports$1.Fragment = Fragment9;
307
+ exports$1.Fragment = Fragment10;
309
308
  exports$1.Lazy = Lazy;
310
309
  exports$1.Memo = Memo;
311
310
  exports$1.Portal = Portal;
@@ -488,6 +487,8 @@ function useSearchParams() {
488
487
  const adapter = useNavigationAdapter();
489
488
  return adapter.useSearchParams();
490
489
  }
490
+
491
+ // src/core/InsforgeManager.ts
491
492
  var InsforgeManager = class _InsforgeManager {
492
493
  // Static private instance
493
494
  static instance = null;
@@ -503,7 +504,7 @@ var InsforgeManager = class _InsforgeManager {
503
504
  // Private constructor (prevents external instantiation)
504
505
  constructor(config) {
505
506
  this.config = config;
506
- this.sdk = sdk.createClient({ baseUrl: config.baseUrl });
507
+ this.sdk = config.client;
507
508
  this.user = void 0;
508
509
  this.isLoaded = false;
509
510
  }
@@ -511,6 +512,9 @@ var InsforgeManager = class _InsforgeManager {
511
512
  static getInstance(config) {
512
513
  if (typeof window !== "undefined" && _InsforgeManager.instance) {
513
514
  _InsforgeManager.instance.updateConfig(config);
515
+ if (config.client && _InsforgeManager.instance.sdk !== config.client) {
516
+ _InsforgeManager.instance.sdk = config.client;
517
+ }
514
518
  return _InsforgeManager.instance;
515
519
  }
516
520
  _InsforgeManager.instance = new _InsforgeManager(config);
@@ -588,12 +592,10 @@ var InsforgeManager = class _InsforgeManager {
588
592
  }
589
593
  const userResult = await this.sdk.auth.getCurrentUser();
590
594
  if (userResult.data) {
591
- const profile = userResult.data.user.profile;
592
595
  const userData = {
593
596
  id: userResult.data.user.id,
594
597
  email: userResult.data.user.email,
595
- name: profile?.name || "",
596
- avatarUrl: profile?.avatar_url || ""
598
+ profile: userResult.data.user.profile
597
599
  };
598
600
  this.user = userData;
599
601
  if (this.config.onAuthChange) {
@@ -629,12 +631,10 @@ var InsforgeManager = class _InsforgeManager {
629
631
  async handleAuthSuccess(authToken, fallbackUser) {
630
632
  const userResult = await this.sdk.auth.getCurrentUser();
631
633
  if (userResult.data) {
632
- const profile = userResult.data.user.profile;
633
634
  const userData = {
634
635
  id: userResult.data.user.id,
635
636
  email: userResult.data.user.email,
636
- name: profile?.name || "",
637
- avatarUrl: profile?.avatar_url || ""
637
+ profile: userResult.data.user.profile
638
638
  };
639
639
  this.user = userData;
640
640
  this.notifyListeners();
@@ -654,8 +654,7 @@ var InsforgeManager = class _InsforgeManager {
654
654
  const userData = {
655
655
  id: fallbackUser.id || "",
656
656
  email: fallbackUser.email || "",
657
- name: fallbackUser.name || "",
658
- avatarUrl: ""
657
+ profile: fallbackUser.profile || null
659
658
  };
660
659
  this.user = userData;
661
660
  this.notifyListeners();
@@ -676,7 +675,7 @@ var InsforgeManager = class _InsforgeManager {
676
675
  sdkResult.data.user ? {
677
676
  id: sdkResult.data.user.id,
678
677
  email: sdkResult.data.user.email,
679
- name: sdkResult.data.user.profile?.name || ""
678
+ profile: sdkResult.data.user.profile || void 0
680
679
  } : void 0
681
680
  );
682
681
  return sdkResult.data;
@@ -697,7 +696,7 @@ var InsforgeManager = class _InsforgeManager {
697
696
  sdkResult.data.user ? {
698
697
  id: sdkResult.data.user.id,
699
698
  email: sdkResult.data.user.email,
700
- name: sdkResult.data.user.profile?.name || ""
699
+ profile: sdkResult.data.user.profile || void 0
701
700
  } : void 0
702
701
  );
703
702
  }
@@ -735,27 +734,21 @@ var InsforgeManager = class _InsforgeManager {
735
734
  if (!this.user) {
736
735
  return { error: "No user signed in" };
737
736
  }
738
- const profileUpdate = {
739
- name: data.name,
740
- avatar_url: data.avatarUrl
741
- };
742
- const result = await this.sdk.auth.setProfile(profileUpdate);
737
+ if (!data) {
738
+ return { error: "No profile data provided" };
739
+ }
740
+ const result = await this.sdk.auth.setProfile(data);
743
741
  if (result.data) {
744
- const userResult = await this.sdk.auth.getCurrentUser();
745
- if (userResult.data) {
746
- const profile = userResult.data.user.profile;
747
- const updatedUser = {
748
- id: userResult.data.user.id,
749
- email: userResult.data.user.email,
750
- name: profile?.name || "",
751
- avatarUrl: profile?.avatar_url || ""
752
- };
753
- this.user = updatedUser;
754
- if (this.config.onAuthChange) {
755
- this.config.onAuthChange(updatedUser);
756
- }
757
- this.notifyListeners();
742
+ const updatedUser = {
743
+ id: this.user.id,
744
+ email: this.user.email,
745
+ profile: data
746
+ };
747
+ this.user = updatedUser;
748
+ if (this.config.onAuthChange) {
749
+ this.config.onAuthChange(updatedUser);
758
750
  }
751
+ this.notifyListeners();
759
752
  return null;
760
753
  }
761
754
  const error = result.error;
@@ -851,12 +844,10 @@ var InsforgeManager = class _InsforgeManager {
851
844
  return;
852
845
  }
853
846
  if (initialState.userId) {
854
- const normalizeValue = (val) => val && val !== "null" ? val : "";
855
847
  this.user = {
856
848
  id: initialState.userId,
857
- email: normalizeValue(initialState.user?.email),
858
- name: normalizeValue(initialState.user?.name),
859
- avatarUrl: normalizeValue(initialState.user?.avatarUrl)
849
+ email: initialState.user?.email || "",
850
+ profile: initialState.user?.profile || null
860
851
  };
861
852
  } else {
862
853
  this.user = null;
@@ -2200,7 +2191,7 @@ function StyleProvider({ children, nonce }) {
2200
2191
  }
2201
2192
  function InsforgeProviderCore({
2202
2193
  children,
2203
- baseUrl,
2194
+ client,
2204
2195
  afterSignInUrl = "/",
2205
2196
  onAuthChange,
2206
2197
  onSignIn,
@@ -2210,14 +2201,14 @@ function InsforgeProviderCore({
2210
2201
  }) {
2211
2202
  const manager = React2.useMemo(
2212
2203
  () => InsforgeManager.getInstance({
2213
- baseUrl,
2204
+ client,
2214
2205
  afterSignInUrl,
2215
2206
  onAuthChange,
2216
2207
  onSignIn,
2217
2208
  onSignOut,
2218
2209
  onRefresh
2219
2210
  }),
2220
- [baseUrl, afterSignInUrl, onAuthChange, onSignIn, onSignOut, onRefresh]
2211
+ [client, afterSignInUrl, onAuthChange, onSignIn, onSignOut, onRefresh]
2221
2212
  );
2222
2213
  if (initialState) {
2223
2214
  const currentState = manager.getState();
@@ -2269,7 +2260,7 @@ function InsforgeProviderCore({
2269
2260
  loginWithOAuth: (provider, redirectTo) => manager.loginWithOAuth(provider, redirectTo),
2270
2261
  getPublicAuthConfig: () => manager.getPublicAuthConfig(),
2271
2262
  // Config
2272
- baseUrl: manager.getConfig().baseUrl,
2263
+ baseUrl: manager.getConfig().client.getHttpClient().baseUrl,
2273
2264
  afterSignInUrl: manager.getConfig().afterSignInUrl || "/"
2274
2265
  }),
2275
2266
  [state, manager]
@@ -5278,9 +5269,384 @@ var UserButtonMenuItemIcon = styled.div`
5278
5269
  height: 100%;
5279
5270
  }
5280
5271
  `;
5281
- function UserButton({ afterSignOutUrl = "/", mode = "simple" }) {
5272
+ var ProfileModalOverlay = styled.div`
5273
+ position: fixed;
5274
+ inset: 0;
5275
+ background-color: rgba(0, 0, 0, 0.5);
5276
+ display: flex;
5277
+ align-items: center;
5278
+ justify-content: center;
5279
+ z-index: 100;
5280
+ padding: ${theme.spacing[4]};
5281
+ `;
5282
+ var ProfileModalContainer = styled.div`
5283
+ background-color: ${theme.colors.bgWhite};
5284
+ border-radius: ${theme.radius.xl};
5285
+ box-shadow: ${theme.shadow.lg};
5286
+ width: 100%;
5287
+ max-width: 400px;
5288
+ max-height: 90vh;
5289
+ overflow: hidden;
5290
+ display: flex;
5291
+ flex-direction: column;
5292
+ font-family: ${theme.fontFamily.base};
5293
+ `;
5294
+ var ProfileModalHeader = styled.div`
5295
+ display: flex;
5296
+ align-items: center;
5297
+ justify-content: space-between;
5298
+ padding: ${theme.spacing[4]} ${theme.spacing[6]};
5299
+ border-bottom: 1px solid ${theme.colors.border};
5300
+ `;
5301
+ var ProfileModalTitle = styled.h2`
5302
+ font-size: ${theme.fontSize.lg};
5303
+ font-weight: ${theme.fontWeight.semibold};
5304
+ color: ${theme.colors.text};
5305
+ margin: 0;
5306
+ `;
5307
+ var ProfileModalCloseButton = styled.button`
5308
+ display: flex;
5309
+ align-items: center;
5310
+ justify-content: center;
5311
+ width: 2rem;
5312
+ height: 2rem;
5313
+ border-radius: ${theme.radius.md};
5314
+ background: none;
5315
+ border: none;
5316
+ cursor: pointer;
5317
+ color: ${theme.colors.textSecondary};
5318
+ transition: all ${theme.transition.fast};
5319
+
5320
+ &:hover {
5321
+ background-color: ${theme.colors.bgLight};
5322
+ color: ${theme.colors.text};
5323
+ }
5324
+
5325
+ svg {
5326
+ width: 1.25rem;
5327
+ height: 1.25rem;
5328
+ }
5329
+ `;
5330
+ var ProfileModalBody = styled.div`
5331
+ padding: ${theme.spacing[6]};
5332
+ overflow-y: auto;
5333
+ flex: 1;
5334
+ `;
5335
+ var ProfileAvatarSection = styled.div`
5336
+ display: flex;
5337
+ flex-direction: column;
5338
+ align-items: center;
5339
+ margin-bottom: ${theme.spacing[6]};
5340
+ `;
5341
+ var ProfileAvatar = styled.div`
5342
+ width: 5rem;
5343
+ height: 5rem;
5344
+ border-radius: ${theme.radius.full};
5345
+ background-color: ${theme.colors.primary};
5346
+ color: ${theme.colors.bgWhite};
5347
+ display: flex;
5348
+ align-items: center;
5349
+ justify-content: center;
5350
+ font-weight: ${theme.fontWeight.semibold};
5351
+ font-size: ${theme.fontSize["2xl"]};
5352
+ overflow: hidden;
5353
+ `;
5354
+ var ProfileAvatarImage = styled.img`
5355
+ width: 100%;
5356
+ height: 100%;
5357
+ object-fit: cover;
5358
+ `;
5359
+ var ProfileFieldsContainer = styled.div`
5360
+ display: flex;
5361
+ flex-direction: column;
5362
+ gap: ${theme.spacing[4]};
5363
+ `;
5364
+ var ProfileField = styled.div`
5365
+ display: flex;
5366
+ flex-direction: column;
5367
+ gap: ${theme.spacing[1]};
5368
+ `;
5369
+ var ProfileFieldLabel = styled.label`
5370
+ font-size: ${theme.fontSize.sm};
5371
+ font-weight: ${theme.fontWeight.medium};
5372
+ color: ${theme.colors.textSecondary};
5373
+ text-transform: capitalize;
5374
+ `;
5375
+ var ProfileFieldValue = styled.div`
5376
+ font-size: ${theme.fontSize.base};
5377
+ color: ${theme.colors.text};
5378
+ padding: ${theme.spacing[2]} 0;
5379
+ word-break: break-word;
5380
+ `;
5381
+ var ProfileFieldInput = styled.input`
5382
+ width: 100%;
5383
+ height: ${theme.sizes.input.height};
5384
+ padding: 0 ${theme.spacing[3]};
5385
+ border: 1px solid ${theme.colors.border};
5386
+ border-radius: ${theme.radius.md};
5387
+ font-size: ${theme.fontSize.base};
5388
+ font-family: ${theme.fontFamily.base};
5389
+ color: ${theme.colors.text};
5390
+ background-color: ${theme.colors.bgWhite};
5391
+ transition: border-color ${theme.transition.fast};
5392
+
5393
+ &:focus {
5394
+ outline: none;
5395
+ border-color: ${theme.colors.borderFocus};
5396
+ }
5397
+
5398
+ &:disabled {
5399
+ background-color: ${theme.colors.bgLight};
5400
+ color: ${theme.colors.textSecondary};
5401
+ cursor: not-allowed;
5402
+ }
5403
+ `;
5404
+ var ProfileModalFooter = styled.div`
5405
+ display: flex;
5406
+ align-items: center;
5407
+ justify-content: flex-end;
5408
+ gap: ${theme.spacing[3]};
5409
+ padding: ${theme.spacing[4]} ${theme.spacing[6]};
5410
+ border-top: 1px solid ${theme.colors.border};
5411
+ `;
5412
+ var ProfileButton = styled.button`
5413
+ display: flex;
5414
+ align-items: center;
5415
+ justify-content: center;
5416
+ gap: ${theme.spacing[2]};
5417
+ height: ${theme.sizes.button.height};
5418
+ padding: 0 ${theme.spacing[4]};
5419
+ border-radius: ${theme.radius.md};
5420
+ font-size: ${theme.fontSize.sm};
5421
+ font-weight: ${theme.fontWeight.medium};
5422
+ font-family: ${theme.fontFamily.base};
5423
+ cursor: pointer;
5424
+ transition: all ${theme.transition.fast};
5425
+
5426
+ ${(props) => props.$primary ? `
5427
+ background-color: ${theme.colors.primary};
5428
+ color: ${theme.colors.bgWhite};
5429
+ border: none;
5430
+
5431
+ &:hover:not(:disabled) {
5432
+ background-color: ${theme.colors.primaryHover};
5433
+ }
5434
+
5435
+ &:disabled {
5436
+ opacity: 0.5;
5437
+ cursor: not-allowed;
5438
+ }
5439
+ ` : `
5440
+ background-color: ${theme.colors.bgWhite};
5441
+ color: ${theme.colors.text};
5442
+ border: 1px solid ${theme.colors.border};
5443
+
5444
+ &:hover:not(:disabled) {
5445
+ background-color: ${theme.colors.bgLight};
5446
+ }
5447
+
5448
+ &:disabled {
5449
+ opacity: 0.5;
5450
+ cursor: not-allowed;
5451
+ }
5452
+ `}
5453
+ `;
5454
+ var ProfileSpinner = styled.div`
5455
+ width: 1rem;
5456
+ height: 1rem;
5457
+ border: 2px solid transparent;
5458
+ border-top-color: currentColor;
5459
+ border-radius: ${theme.radius.full};
5460
+ animation: spin 0.6s linear infinite;
5461
+
5462
+ @keyframes spin {
5463
+ to {
5464
+ transform: rotate(360deg);
5465
+ }
5466
+ }
5467
+ `;
5468
+ var READ_ONLY_FIELDS = ["id", "email", "avatar_url", "created_at", "updated_at"];
5469
+ var HIDDEN_FIELDS = ["id"];
5470
+ function formatFieldLabel(key) {
5471
+ return key.replace(/_/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2").toLowerCase();
5472
+ }
5473
+ function UserProfileModal({ onClose, onError }) {
5474
+ const { user, updateUser, isLoaded } = useInsforge();
5475
+ const [isEditing, setIsEditing] = React2.useState(false);
5476
+ const [isSaving, setIsSaving] = React2.useState(false);
5477
+ const [imageError, setImageError] = React2.useState(false);
5478
+ const [formData, setFormData] = React2.useState({});
5479
+ React2.useEffect(() => {
5480
+ if (user) {
5481
+ const initialData = {
5482
+ name: user.profile?.name || ""
5483
+ };
5484
+ if (user.profile) {
5485
+ Object.entries(user.profile).forEach(([key, value]) => {
5486
+ if (!HIDDEN_FIELDS.includes(key) && typeof value === "string") {
5487
+ initialData[key] = value;
5488
+ }
5489
+ });
5490
+ }
5491
+ setFormData(initialData);
5492
+ }
5493
+ }, [user]);
5494
+ React2.useEffect(() => {
5495
+ setImageError(false);
5496
+ const avatarUrl = user?.profile?.avatar_url;
5497
+ if (!avatarUrl) {
5498
+ return;
5499
+ }
5500
+ const checkImageUrl = async () => {
5501
+ try {
5502
+ const response = await fetch(avatarUrl, {
5503
+ method: "HEAD",
5504
+ cache: "no-cache"
5505
+ });
5506
+ if (!response.ok) {
5507
+ setImageError(true);
5508
+ }
5509
+ } catch {
5510
+ setImageError(true);
5511
+ }
5512
+ };
5513
+ void checkImageUrl();
5514
+ }, [user?.profile?.avatar_url]);
5515
+ const handleFieldChange = React2.useCallback((key, value) => {
5516
+ setFormData((prev2) => ({
5517
+ ...prev2,
5518
+ [key]: value
5519
+ }));
5520
+ }, []);
5521
+ const handleSave = React2.useCallback(async () => {
5522
+ if (!user) return;
5523
+ setIsSaving(true);
5524
+ try {
5525
+ const { name, ...dynamicFields } = formData;
5526
+ const updateData = {
5527
+ profile: {
5528
+ name
5529
+ }
5530
+ };
5531
+ if (Object.keys(dynamicFields).length > 0) {
5532
+ updateData.profile = dynamicFields;
5533
+ }
5534
+ const result = await updateUser(updateData);
5535
+ if (result?.error) {
5536
+ onError?.(result.error);
5537
+ } else {
5538
+ setIsEditing(false);
5539
+ }
5540
+ } catch (error) {
5541
+ onError?.(error instanceof Error ? error.message : "Failed to update profile");
5542
+ } finally {
5543
+ setIsSaving(false);
5544
+ }
5545
+ }, [user, formData, updateUser, onError]);
5546
+ const handleCancel = React2.useCallback(() => {
5547
+ if (user) {
5548
+ const resetData = {
5549
+ name: user.profile?.name || ""
5550
+ };
5551
+ if (user.profile) {
5552
+ Object.entries(user.profile).forEach(([key, value]) => {
5553
+ if (!HIDDEN_FIELDS.includes(key) && typeof value === "string") {
5554
+ resetData[key] = value;
5555
+ }
5556
+ });
5557
+ }
5558
+ setFormData(resetData);
5559
+ }
5560
+ setIsEditing(false);
5561
+ }, [user]);
5562
+ const handleOverlayClick = React2.useCallback(
5563
+ (e) => {
5564
+ if (e.target === e.currentTarget) {
5565
+ onClose();
5566
+ }
5567
+ },
5568
+ [onClose]
5569
+ );
5570
+ React2.useEffect(() => {
5571
+ const handleEscape = (e) => {
5572
+ if (e.key === "Escape") {
5573
+ if (isEditing) {
5574
+ handleCancel();
5575
+ } else {
5576
+ onClose();
5577
+ }
5578
+ }
5579
+ };
5580
+ document.addEventListener("keydown", handleEscape);
5581
+ return () => document.removeEventListener("keydown", handleEscape);
5582
+ }, [isEditing, handleCancel, onClose]);
5583
+ if (!isLoaded || !user) {
5584
+ return null;
5585
+ }
5586
+ const initials = user.profile?.name ? user.profile.name.charAt(0).toUpperCase() : user.email.split("@")[0].slice(0, 2).toUpperCase();
5587
+ const fields = [];
5588
+ fields.push({ key: "email", value: user.email, readOnly: true });
5589
+ fields.push({
5590
+ key: "name",
5591
+ value: isEditing ? formData.name || "" : user.profile?.name || "",
5592
+ readOnly: false
5593
+ });
5594
+ if (user.profile) {
5595
+ Object.entries(user.profile).forEach(([key, value]) => {
5596
+ if (!HIDDEN_FIELDS.includes(key) && typeof value === "string") {
5597
+ fields.push({
5598
+ key,
5599
+ value: isEditing ? formData[key] ?? value : value,
5600
+ readOnly: READ_ONLY_FIELDS.includes(key)
5601
+ });
5602
+ }
5603
+ });
5604
+ }
5605
+ return /* @__PURE__ */ jsxRuntime.jsx(ProfileModalOverlay, { onClick: handleOverlayClick, children: /* @__PURE__ */ jsxRuntime.jsxs(ProfileModalContainer, { onClick: (e) => e.stopPropagation(), children: [
5606
+ /* @__PURE__ */ jsxRuntime.jsxs(ProfileModalHeader, { children: [
5607
+ /* @__PURE__ */ jsxRuntime.jsx(ProfileModalTitle, { children: "Profile" }),
5608
+ /* @__PURE__ */ jsxRuntime.jsx(ProfileModalCloseButton, { onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, {}) })
5609
+ ] }),
5610
+ /* @__PURE__ */ jsxRuntime.jsxs(ProfileModalBody, { children: [
5611
+ /* @__PURE__ */ jsxRuntime.jsx(ProfileAvatarSection, { children: /* @__PURE__ */ jsxRuntime.jsx(ProfileAvatar, { children: user.profile?.avatar_url && !imageError ? /* @__PURE__ */ jsxRuntime.jsx(
5612
+ ProfileAvatarImage,
5613
+ {
5614
+ src: user.profile.avatar_url,
5615
+ alt: user.profile?.name || user.email,
5616
+ onError: () => setImageError(true)
5617
+ }
5618
+ ) : initials }) }),
5619
+ /* @__PURE__ */ jsxRuntime.jsx(ProfileFieldsContainer, { children: fields.map(({ key, value, readOnly }) => /* @__PURE__ */ jsxRuntime.jsxs(ProfileField, { children: [
5620
+ /* @__PURE__ */ jsxRuntime.jsx(ProfileFieldLabel, { children: formatFieldLabel(key) }),
5621
+ isEditing && !readOnly ? /* @__PURE__ */ jsxRuntime.jsx(
5622
+ ProfileFieldInput,
5623
+ {
5624
+ type: "text",
5625
+ value: formData[key] ?? value,
5626
+ onChange: (e) => handleFieldChange(key, e.target.value),
5627
+ disabled: isSaving
5628
+ }
5629
+ ) : /* @__PURE__ */ jsxRuntime.jsx(ProfileFieldValue, { children: value || "-" })
5630
+ ] }, key)) })
5631
+ ] }),
5632
+ /* @__PURE__ */ jsxRuntime.jsx(ProfileModalFooter, { children: isEditing ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5633
+ /* @__PURE__ */ jsxRuntime.jsx(ProfileButton, { onClick: handleCancel, disabled: isSaving, children: "Cancel" }),
5634
+ /* @__PURE__ */ jsxRuntime.jsxs(ProfileButton, { $primary: true, onClick: () => void handleSave(), disabled: isSaving, children: [
5635
+ isSaving && /* @__PURE__ */ jsxRuntime.jsx(ProfileSpinner, {}),
5636
+ isSaving ? "Saving..." : "Save"
5637
+ ] })
5638
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(ProfileButton, { $primary: true, onClick: () => setIsEditing(true), children: "Edit Profile" }) })
5639
+ ] }) });
5640
+ }
5641
+ function UserButton({
5642
+ afterSignOutUrl = "/",
5643
+ mode = "simple",
5644
+ showProfile = true,
5645
+ onProfileError
5646
+ }) {
5282
5647
  const { user } = useInsforge();
5283
5648
  const [isOpen, setIsOpen] = React2.useState(false);
5649
+ const [showProfileModal, setShowProfileModal] = React2.useState(false);
5284
5650
  const [imageError, setImageError] = React2.useState(false);
5285
5651
  const [openUpward, setOpenUpward] = React2.useState(false);
5286
5652
  const [horizontalOffset, setHorizontalOffset] = React2.useState(0);
@@ -5288,7 +5654,7 @@ function UserButton({ afterSignOutUrl = "/", mode = "simple" }) {
5288
5654
  const menuRef = React2.useRef(null);
5289
5655
  React2.useEffect(() => {
5290
5656
  setImageError(false);
5291
- const avatarUrl = user?.avatarUrl;
5657
+ const avatarUrl = user?.profile?.avatar_url;
5292
5658
  if (!avatarUrl) {
5293
5659
  return;
5294
5660
  }
@@ -5309,7 +5675,7 @@ function UserButton({ afterSignOutUrl = "/", mode = "simple" }) {
5309
5675
  }
5310
5676
  };
5311
5677
  void checkImageUrl();
5312
- }, [user?.avatarUrl]);
5678
+ }, [user?.profile?.avatar_url]);
5313
5679
  React2.useEffect(() => {
5314
5680
  if (isOpen && dropdownRef.current) {
5315
5681
  const buttonRect = dropdownRef.current.getBoundingClientRect();
@@ -5353,7 +5719,7 @@ function UserButton({ afterSignOutUrl = "/", mode = "simple" }) {
5353
5719
  if (!user) {
5354
5720
  return null;
5355
5721
  }
5356
- const initials = user.name ? user.name.charAt(0).toUpperCase() : user.email.split("@")[0].slice(0, 2).toUpperCase();
5722
+ const initials = user.profile?.name ? user.profile.name.charAt(0).toUpperCase() : user.email.split("@")[0].slice(0, 2).toUpperCase();
5357
5723
  return /* @__PURE__ */ jsxRuntime.jsxs(UserButtonContainer, { ref: dropdownRef, children: [
5358
5724
  /* @__PURE__ */ jsxRuntime.jsxs(
5359
5725
  UserButtonButton,
@@ -5363,25 +5729,41 @@ function UserButton({ afterSignOutUrl = "/", mode = "simple" }) {
5363
5729
  "aria-expanded": isOpen,
5364
5730
  "aria-haspopup": "true",
5365
5731
  children: [
5366
- /* @__PURE__ */ jsxRuntime.jsx(UserButtonAvatar, { children: user.avatarUrl && !imageError ? /* @__PURE__ */ jsxRuntime.jsx(
5732
+ /* @__PURE__ */ jsxRuntime.jsx(UserButtonAvatar, { children: user.profile?.avatar_url && !imageError ? /* @__PURE__ */ jsxRuntime.jsx(
5367
5733
  UserButtonAvatarImage,
5368
5734
  {
5369
- src: user.avatarUrl,
5735
+ src: user.profile.avatar_url,
5370
5736
  alt: user.email,
5371
5737
  onError: () => setImageError(true)
5372
5738
  }
5373
5739
  ) : /* @__PURE__ */ jsxRuntime.jsx(UserButtonAvatarInitials, { children: initials }) }),
5374
5740
  mode === "detailed" && /* @__PURE__ */ jsxRuntime.jsxs(UserButtonInfo, { children: [
5375
- user.name && /* @__PURE__ */ jsxRuntime.jsx(UserButtonName, { children: user.name }),
5741
+ user.profile?.name && /* @__PURE__ */ jsxRuntime.jsx(UserButtonName, { children: user.profile.name }),
5376
5742
  /* @__PURE__ */ jsxRuntime.jsx(UserButtonEmail, { children: user.email })
5377
5743
  ] })
5378
5744
  ]
5379
5745
  }
5380
5746
  ),
5381
- isOpen && /* @__PURE__ */ jsxRuntime.jsx(UserButtonMenu, { ref: menuRef, $openUpward: openUpward, $horizontalOffset: horizontalOffset, children: /* @__PURE__ */ jsxRuntime.jsx(SignOutButton, { afterSignOutUrl, children: /* @__PURE__ */ jsxRuntime.jsxs(UserButtonMenuItem, { $signout: true, onClick: () => setIsOpen(false), children: [
5382
- /* @__PURE__ */ jsxRuntime.jsx(UserButtonMenuItemIcon, { children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.LogOut, {}) }),
5383
- "Sign out"
5384
- ] }) }) })
5747
+ isOpen && /* @__PURE__ */ jsxRuntime.jsxs(UserButtonMenu, { ref: menuRef, $openUpward: openUpward, $horizontalOffset: horizontalOffset, children: [
5748
+ showProfile && /* @__PURE__ */ jsxRuntime.jsxs(
5749
+ UserButtonMenuItem,
5750
+ {
5751
+ onClick: () => {
5752
+ setShowProfileModal(true);
5753
+ setIsOpen(false);
5754
+ },
5755
+ children: [
5756
+ /* @__PURE__ */ jsxRuntime.jsx(UserButtonMenuItemIcon, { children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.User, {}) }),
5757
+ "Profile"
5758
+ ]
5759
+ }
5760
+ ),
5761
+ /* @__PURE__ */ jsxRuntime.jsx(SignOutButton, { afterSignOutUrl, children: /* @__PURE__ */ jsxRuntime.jsxs(UserButtonMenuItem, { $signout: true, onClick: () => setIsOpen(false), children: [
5762
+ /* @__PURE__ */ jsxRuntime.jsx(UserButtonMenuItemIcon, { children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.LogOut, {}) }),
5763
+ "Sign out"
5764
+ ] }) })
5765
+ ] }),
5766
+ showProfileModal && /* @__PURE__ */ jsxRuntime.jsx(UserProfileModal, { onClose: () => setShowProfileModal(false), onError: onProfileError })
5385
5767
  ] });
5386
5768
  }
5387
5769
  function Protect({
@@ -5548,6 +5930,7 @@ exports.SignedIn = SignedIn;
5548
5930
  exports.SignedOut = SignedOut;
5549
5931
  exports.StyleProvider = StyleProvider;
5550
5932
  exports.UserButton = UserButton;
5933
+ exports.UserProfileModal = UserProfileModal;
5551
5934
  exports.VerifyEmail = VerifyEmail;
5552
5935
  exports.VerifyEmailStatus = VerifyEmailStatus;
5553
5936
  exports.checkPasswordStrength = checkPasswordStrength;