@thetechfossil/auth2 1.2.6 → 1.2.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.
@@ -1,6 +1,6 @@
1
- "use client";
2
1
  import React2, { createContext, forwardRef, useContext, useState, useMemo, useEffect, useRef, useCallback } from 'react';
3
2
  import axios from 'axios';
3
+ import { ImageManager, UpfilesClient } from '@thetechfossil/upfiles';
4
4
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
5
5
  import PhoneInputWithCountry from 'react-phone-number-input';
6
6
  import 'react-phone-number-input/style.css';
@@ -111,11 +111,10 @@ var HttpClient = class {
111
111
  }
112
112
  }
113
113
  };
114
-
115
- // src/core/auth-service.ts
116
114
  var AuthService = class {
117
115
  constructor(config) {
118
116
  this.token = null;
117
+ this.upfilesClient = null;
119
118
  this.config = {
120
119
  localStorageKey: "auth_token",
121
120
  csrfEnabled: true,
@@ -123,6 +122,15 @@ var AuthService = class {
123
122
  };
124
123
  this.httpClient = new HttpClient(this.config.baseUrl);
125
124
  this.loadTokenFromStorage();
125
+ if (this.config.upfilesConfig) {
126
+ this.upfilesClient = new UpfilesClient({
127
+ baseUrl: this.config.upfilesConfig.baseUrl,
128
+ apiKey: this.config.upfilesConfig.apiKey,
129
+ apiKeyHeader: this.config.upfilesConfig.apiKeyHeader,
130
+ presignUrl: this.config.upfilesConfig.presignUrl,
131
+ presignPath: this.config.upfilesConfig.presignPath
132
+ });
133
+ }
126
134
  if (typeof window !== "undefined") {
127
135
  const frontendBaseUrl = process.env.NEXT_PUBLIC_FRONTEND_BASE_URL || process.env.REACT_APP_FRONTEND_BASE_URL || process.env.NEXT_PUBLIC_APP_URL || window.location.origin;
128
136
  if (frontendBaseUrl) {
@@ -350,6 +358,28 @@ var AuthService = class {
350
358
  }
351
359
  return response;
352
360
  }
361
+ async uploadAndUpdateAvatar(file) {
362
+ if (!this.token) {
363
+ throw new Error("Not authenticated");
364
+ }
365
+ if (!this.upfilesClient) {
366
+ throw new Error("Upfiles configuration is required. Please provide upfilesConfig in AuthConfig.");
367
+ }
368
+ try {
369
+ const folderPath = this.config.upfilesConfig?.folderPath || "avatars/";
370
+ const uploadResult = await this.upfilesClient.upload(file, {
371
+ folderPath,
372
+ fetchThumbnails: true
373
+ });
374
+ const response = await this.updateAvatar(uploadResult.publicUrl);
375
+ return response;
376
+ } catch (error) {
377
+ throw new Error(`Failed to upload avatar: ${error.message || "Unknown error"}`);
378
+ }
379
+ }
380
+ getUpfilesClient() {
381
+ return this.upfilesClient;
382
+ }
353
383
  async requestEmailChange(newEmail) {
354
384
  if (!this.token) {
355
385
  throw new Error("Not authenticated");
@@ -582,6 +612,18 @@ var useAuth = (config) => {
582
612
  setLoading(false);
583
613
  }
584
614
  }, [authService]);
615
+ const uploadAndUpdateAvatar = useCallback(async (file) => {
616
+ setLoading(true);
617
+ try {
618
+ const response = await authService.uploadAndUpdateAvatar(file);
619
+ if (response.success && response.user) {
620
+ setUser(response.user);
621
+ }
622
+ return response;
623
+ } finally {
624
+ setLoading(false);
625
+ }
626
+ }, [authService]);
585
627
  return {
586
628
  user,
587
629
  isAuthenticated,
@@ -594,7 +636,8 @@ var useAuth = (config) => {
594
636
  updateProfile,
595
637
  getProfile,
596
638
  getAllUsers,
597
- getUserById
639
+ getUserById,
640
+ uploadAndUpdateAvatar
598
641
  };
599
642
  };
600
643
  var ThemeContext = createContext({ theme: "light", mounted: false });
@@ -4061,15 +4104,85 @@ var ChangePassword = ({ onSuccess, appearance }) => {
4061
4104
  )
4062
4105
  ] }) });
4063
4106
  };
4107
+ var AvatarUploader = ({
4108
+ onUploadComplete,
4109
+ onError,
4110
+ className,
4111
+ buttonClassName,
4112
+ maxFileSize = 5 * 1024 * 1024,
4113
+ // 5MB default
4114
+ upfilesConfig,
4115
+ buttonText = "Upload Avatar"
4116
+ }) => {
4117
+ const { uploadAndUpdateAvatar } = useAuth2();
4118
+ const [open, setOpen] = useState(false);
4119
+ const [uploading, setUploading] = useState(false);
4120
+ const handleSelect = async (image) => {
4121
+ setUploading(true);
4122
+ try {
4123
+ const response = await fetch(image.url);
4124
+ const blob = await response.blob();
4125
+ const file = new File([blob], image.originalName, { type: image.contentType });
4126
+ const result = await uploadAndUpdateAvatar(file);
4127
+ if (result.success && result.user?.avatar) {
4128
+ onUploadComplete?.(result.user.avatar);
4129
+ setOpen(false);
4130
+ } else {
4131
+ throw new Error(result.message || "Failed to update avatar");
4132
+ }
4133
+ } catch (error) {
4134
+ const err = error instanceof Error ? error : new Error("Upload failed");
4135
+ onError?.(err);
4136
+ } finally {
4137
+ setUploading(false);
4138
+ }
4139
+ };
4140
+ return /* @__PURE__ */ jsxs("div", { className, children: [
4141
+ /* @__PURE__ */ jsx(
4142
+ "button",
4143
+ {
4144
+ type: "button",
4145
+ onClick: () => setOpen(true),
4146
+ disabled: uploading,
4147
+ className: buttonClassName || "px-4 py-2 text-sm rounded border bg-blue-600 text-white hover:bg-blue-700 disabled:opacity-50",
4148
+ children: uploading ? "Uploading..." : buttonText
4149
+ }
4150
+ ),
4151
+ /* @__PURE__ */ jsx(
4152
+ ImageManager,
4153
+ {
4154
+ open,
4155
+ onOpenChange: setOpen,
4156
+ clientOptions: {
4157
+ baseUrl: upfilesConfig.baseUrl,
4158
+ apiKey: upfilesConfig.apiKey,
4159
+ apiKeyHeader: upfilesConfig.apiKeyHeader || "authorization",
4160
+ presignUrl: upfilesConfig.presignUrl,
4161
+ presignPath: upfilesConfig.presignPath
4162
+ },
4163
+ projectId: upfilesConfig.projectId,
4164
+ folderPath: upfilesConfig.folderPath || "avatars/",
4165
+ title: "Select Avatar",
4166
+ description: "Upload a new avatar or select from existing images.",
4167
+ mode: "full",
4168
+ maxFileSize,
4169
+ maxFiles: 1,
4170
+ autoRecordToDb: true,
4171
+ fetchThumbnails: true,
4172
+ onSelect: handleSelect
4173
+ }
4174
+ )
4175
+ ] });
4176
+ };
4064
4177
  var UserProfile = ({
4065
4178
  showAvatar = true,
4066
4179
  showEmailChange = true,
4067
- showPasswordChange = true
4180
+ showPasswordChange = true,
4181
+ upfilesConfig
4068
4182
  }) => {
4069
4183
  const { user, updateProfile, requestEmailChange } = useAuth2();
4070
4184
  const colors = useThemeColors();
4071
4185
  const [name, setName] = useState(user?.name || "");
4072
- const [avatar, setAvatar] = useState(user?.avatar || "");
4073
4186
  const [phoneNumber, setPhoneNumber] = useState(user?.phoneNumber || "");
4074
4187
  const [newEmail, setNewEmail] = useState("");
4075
4188
  const [isLoading, setIsLoading] = useState(false);
@@ -4085,9 +4198,6 @@ var UserProfile = ({
4085
4198
  if (name !== user?.name) {
4086
4199
  updates.name = name;
4087
4200
  }
4088
- if (showAvatar && avatar !== user?.avatar) {
4089
- updates.avatar = avatar;
4090
- }
4091
4201
  if (phoneNumber !== user?.phoneNumber) {
4092
4202
  updates.phoneNumber = phoneNumber;
4093
4203
  }
@@ -4108,6 +4218,12 @@ var UserProfile = ({
4108
4218
  setIsLoading(false);
4109
4219
  }
4110
4220
  };
4221
+ const handleAvatarUploadComplete = (avatarUrl) => {
4222
+ setSuccess("Avatar updated successfully!");
4223
+ };
4224
+ const handleAvatarUploadError = (error2) => {
4225
+ setError(error2.message || "Failed to upload avatar");
4226
+ };
4111
4227
  const handleRequestEmailChange = async (e) => {
4112
4228
  e.preventDefault();
4113
4229
  setIsLoading(true);
@@ -4210,34 +4326,36 @@ var UserProfile = ({
4210
4326
  }
4211
4327
  )
4212
4328
  ] }),
4213
- showAvatar && /* @__PURE__ */ jsxs("div", { style: { marginBottom: "20px" }, children: [
4214
- /* @__PURE__ */ jsx("label", { htmlFor: "avatar", style: {
4329
+ showAvatar && upfilesConfig && /* @__PURE__ */ jsxs("div", { style: { marginBottom: "20px" }, children: [
4330
+ /* @__PURE__ */ jsx("label", { style: {
4215
4331
  display: "block",
4216
4332
  marginBottom: "8px",
4217
4333
  fontWeight: 500,
4218
4334
  color: colors.textSecondary,
4219
4335
  fontSize: "14px"
4220
- }, children: "Avatar URL" }),
4221
- /* @__PURE__ */ jsx(
4222
- "input",
4336
+ }, children: "Avatar" }),
4337
+ user?.avatar && /* @__PURE__ */ jsx("div", { style: { marginBottom: "12px" }, children: /* @__PURE__ */ jsx(
4338
+ "img",
4223
4339
  {
4224
- id: "avatar",
4225
- type: "url",
4226
- value: avatar,
4227
- onChange: (e) => setAvatar(e.target.value),
4228
- disabled: isLoading,
4340
+ src: user.avatar,
4341
+ alt: "Current avatar",
4229
4342
  style: {
4230
- width: "100%",
4231
- padding: "12px 16px",
4232
- border: `1px solid ${colors.borderSecondary}`,
4233
- borderRadius: "8px",
4234
- fontSize: "16px",
4235
- boxSizing: "border-box",
4236
- backgroundColor: colors.bgSecondary,
4237
- color: colors.textPrimary,
4238
- transition: "all 0.2s ease"
4239
- },
4240
- placeholder: "https://example.com/avatar.jpg"
4343
+ width: "80px",
4344
+ height: "80px",
4345
+ borderRadius: "50%",
4346
+ objectFit: "cover",
4347
+ border: `2px solid ${colors.borderSecondary}`
4348
+ }
4349
+ }
4350
+ ) }),
4351
+ /* @__PURE__ */ jsx(
4352
+ AvatarUploader,
4353
+ {
4354
+ upfilesConfig,
4355
+ onUploadComplete: handleAvatarUploadComplete,
4356
+ onError: handleAvatarUploadError,
4357
+ maxFileSize: 5 * 1024 * 1024,
4358
+ accept: ["image/*"]
4241
4359
  }
4242
4360
  )
4243
4361
  ] }),
@@ -4349,7 +4467,66 @@ var UserProfile = ({
4349
4467
  ] })
4350
4468
  ] });
4351
4469
  };
4470
+ var AvatarManager = ({
4471
+ open,
4472
+ onOpenChange,
4473
+ onAvatarUpdated,
4474
+ onError,
4475
+ title = "Select Avatar",
4476
+ description = "Choose an existing image or upload a new one",
4477
+ className,
4478
+ gridClassName,
4479
+ maxFileSize = 5 * 1024 * 1024,
4480
+ // 5MB default
4481
+ mode = "full",
4482
+ showDelete = false,
4483
+ upfilesConfig
4484
+ }) => {
4485
+ const { updateProfile } = useAuth2();
4486
+ const [updating, setUpdating] = useState(false);
4487
+ const handleSelect = async (image) => {
4488
+ setUpdating(true);
4489
+ try {
4490
+ const response = await updateProfile({ avatar: image.url });
4491
+ if (response.success && response.user?.avatar) {
4492
+ onAvatarUpdated?.(response.user.avatar);
4493
+ onOpenChange(false);
4494
+ } else {
4495
+ throw new Error(response.message || "Failed to update avatar");
4496
+ }
4497
+ } catch (error) {
4498
+ const err = error instanceof Error ? error : new Error("Failed to update avatar");
4499
+ onError?.(err);
4500
+ } finally {
4501
+ setUpdating(false);
4502
+ }
4503
+ };
4504
+ return /* @__PURE__ */ jsx(
4505
+ ImageManager,
4506
+ {
4507
+ open,
4508
+ onOpenChange,
4509
+ clientOptions: {
4510
+ baseUrl: upfilesConfig.baseUrl,
4511
+ apiKey: upfilesConfig.apiKey,
4512
+ apiKeyHeader: upfilesConfig.apiKeyHeader || "authorization",
4513
+ presignUrl: upfilesConfig.presignUrl,
4514
+ presignPath: upfilesConfig.presignPath
4515
+ },
4516
+ folderPath: upfilesConfig.folderPath || "avatars/",
4517
+ title,
4518
+ description,
4519
+ className,
4520
+ gridClassName,
4521
+ onSelect: handleSelect,
4522
+ maxFileSize,
4523
+ mode,
4524
+ showDelete,
4525
+ fetchThumbnails: true
4526
+ }
4527
+ );
4528
+ };
4352
4529
 
4353
- export { AuthFlow, ChangePassword, EmailVerificationPage, ForgotPassword, LoginForm, OtpForm, PhoneInput, ProtectedRoute, PublicRoute, RegisterForm, ResetPassword, SignIn, SignOut, SignUp, UserButton, UserProfile, VerifyEmail };
4530
+ export { AuthFlow, AvatarManager, AvatarUploader, ChangePassword, EmailVerificationPage, ForgotPassword, LoginForm, OtpForm, PhoneInput, ProtectedRoute, PublicRoute, RegisterForm, ResetPassword, SignIn, SignOut, SignUp, UserButton, UserProfile, VerifyEmail };
4354
4531
  //# sourceMappingURL=out.js.map
4355
4532
  //# sourceMappingURL=index.components.mjs.map