nuxt-users 1.37.3 → 1.39.0

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.
Files changed (74) hide show
  1. package/README.md +5 -1
  2. package/dist/cli.mjs +10 -6
  3. package/dist/module.d.mts +1 -1
  4. package/dist/module.d.ts +1 -1
  5. package/dist/module.json +1 -1
  6. package/dist/module.mjs +16 -3
  7. package/dist/runtime/components/NUsersEmailConfirmation.d.vue.ts +5 -3
  8. package/dist/runtime/components/NUsersEmailConfirmation.vue +22 -15
  9. package/dist/runtime/components/NUsersEmailConfirmation.vue.d.ts +5 -3
  10. package/dist/runtime/components/NUsersGoogleLoginButton.d.vue.ts +4 -2
  11. package/dist/runtime/components/NUsersGoogleLoginButton.vue +5 -2
  12. package/dist/runtime/components/NUsersGoogleLoginButton.vue.d.ts +4 -2
  13. package/dist/runtime/components/NUsersList.vue +25 -11
  14. package/dist/runtime/components/NUsersLoginForm.d.vue.ts +13 -2
  15. package/dist/runtime/components/NUsersLoginForm.vue +35 -13
  16. package/dist/runtime/components/NUsersLoginForm.vue.d.ts +13 -2
  17. package/dist/runtime/components/NUsersLogoutLink.d.vue.ts +3 -4
  18. package/dist/runtime/components/NUsersLogoutLink.vue +8 -5
  19. package/dist/runtime/components/NUsersLogoutLink.vue.d.ts +3 -4
  20. package/dist/runtime/components/NUsersPasswordStrengthIndicator.d.vue.ts +6 -0
  21. package/dist/runtime/components/NUsersPasswordStrengthIndicator.vue +17 -9
  22. package/dist/runtime/components/NUsersPasswordStrengthIndicator.vue.d.ts +6 -0
  23. package/dist/runtime/components/NUsersProfileInfo.d.vue.ts +13 -1
  24. package/dist/runtime/components/NUsersProfileInfo.vue +23 -9
  25. package/dist/runtime/components/NUsersProfileInfo.vue.d.ts +13 -1
  26. package/dist/runtime/components/NUsersRegisterForm.d.vue.ts +26 -11
  27. package/dist/runtime/components/NUsersRegisterForm.vue +34 -16
  28. package/dist/runtime/components/NUsersRegisterForm.vue.d.ts +26 -11
  29. package/dist/runtime/components/NUsersResetPasswordForm.d.vue.ts +11 -2
  30. package/dist/runtime/components/NUsersResetPasswordForm.vue +25 -12
  31. package/dist/runtime/components/NUsersResetPasswordForm.vue.d.ts +11 -2
  32. package/dist/runtime/components/NUsersUserCard.d.vue.ts +2 -0
  33. package/dist/runtime/components/NUsersUserCard.vue +8 -4
  34. package/dist/runtime/components/NUsersUserCard.vue.d.ts +2 -0
  35. package/dist/runtime/components/NUsersUserForm.d.vue.ts +2 -2
  36. package/dist/runtime/components/NUsersUserForm.vue +5 -4
  37. package/dist/runtime/components/NUsersUserForm.vue.d.ts +2 -2
  38. package/dist/runtime/composables/useNuxtUsersLocale.d.ts +15 -0
  39. package/dist/runtime/composables/useNuxtUsersLocale.js +27 -0
  40. package/dist/runtime/composables/usePasswordValidation.d.ts +1 -1
  41. package/dist/runtime/composables/usePublicPaths.js +2 -1
  42. package/dist/runtime/locales/en.d.ts +2 -0
  43. package/dist/runtime/locales/en.js +130 -0
  44. package/dist/runtime/locales/hu.d.ts +3 -0
  45. package/dist/runtime/locales/hu.js +188 -0
  46. package/dist/runtime/locales/index.d.ts +5 -0
  47. package/dist/runtime/locales/index.js +8 -0
  48. package/dist/runtime/middleware/authorization.client.js +4 -1
  49. package/dist/runtime/plugin.auth-init.d.ts +9 -0
  50. package/dist/runtime/plugin.auth-init.js +16 -0
  51. package/dist/runtime/server/api/nuxt-users/confirm-email.get.js +3 -3
  52. package/dist/runtime/server/api/nuxt-users/password/forgot.post.js +2 -1
  53. package/dist/runtime/server/api/nuxt-users/password/reset.post.d.ts +1 -1
  54. package/dist/runtime/server/api/nuxt-users/password/reset.post.js +4 -2
  55. package/dist/runtime/server/api/nuxt-users/session/index.post.js +4 -2
  56. package/dist/runtime/server/middleware/authorization.server.js +6 -3
  57. package/dist/runtime/server/utils/google-oauth.js +2 -1
  58. package/dist/runtime/utils/deep-merge.d.ts +8 -0
  59. package/dist/runtime/utils/deep-merge.js +17 -0
  60. package/dist/runtime/utils/locale.d.ts +14 -0
  61. package/dist/runtime/utils/locale.js +38 -0
  62. package/dist/shared/{nuxt-users.BnOQs2LI.mjs → nuxt-users.DfZ7bc_P.mjs} +16 -8
  63. package/dist/shared/{nuxt-users.SXaMnaEL.d.ts → nuxt-users.FiQjvlZl.d.mts} +40 -2
  64. package/dist/shared/{nuxt-users.SXaMnaEL.d.mts → nuxt-users.FiQjvlZl.d.ts} +40 -2
  65. package/dist/utils.d.mts +4 -1
  66. package/dist/utils.d.ts +4 -1
  67. package/dist/utils.mjs +4 -1
  68. package/package.json +5 -3
  69. package/scripts/check-locale-consistency.ts +156 -0
  70. package/scripts/check-peer-dependencies.js +5 -5
  71. package/scripts/check-translations.ts +255 -0
  72. package/scripts/post-build.js +1 -1
  73. package/dist/runtime/plugin.client.d.ts +0 -2
  74. package/dist/runtime/plugin.client.js +0 -6
package/README.md CHANGED
@@ -39,7 +39,7 @@ A user authentication module for Nuxt 3 and Nuxt 4 with database support for SQL
39
39
 
40
40
  MIT
41
41
 
42
- ## Dev misc
42
+ ## Dev Misc
43
43
 
44
44
  ### Using yalc
45
45
 
@@ -49,4 +49,8 @@ yalc publish
49
49
 
50
50
  # Install the module in the consumer app
51
51
  yalc add nuxt-users
52
+
53
+ # Clean up consumer app
54
+ yalc remove nuxt-users
55
+ yarn add nuxt-users
52
56
  ```
package/dist/cli.mjs CHANGED
@@ -5,7 +5,10 @@ import { defaultOptions } from './module.mjs';
5
5
  import { loadNuxt } from '@nuxt/kit';
6
6
  import { defu } from 'defu';
7
7
  import { runMigrations, createUser as createUser$1, createUsersTable as createUsersTable$1, createPersonalAccessTokensTable as createPersonalAccessTokensTable$1, createPasswordResetTokensTable as createPasswordResetTokensTable$1, createMigrationsTable as createMigrationsTable$1 } from '../dist/runtime/server/utils/index.js';
8
- import { g as getPasswordValidationOptions, v as validatePassword } from './shared/nuxt-users.BnOQs2LI.mjs';
8
+ import { g as getPasswordValidationOptions, v as validatePassword } from './shared/nuxt-users.DfZ7bc_P.mjs';
9
+ import '../dist/runtime/locales/index.js';
10
+ import '../dist/runtime/utils/locale.js';
11
+ import '../dist/runtime/utils/deep-merge.js';
9
12
  import { addActiveToUsers as addActiveToUsers$1 } from '../dist/runtime/server/utils/add-active-to-users.js';
10
13
  import { useDb, checkTableExists } from '../dist/runtime/server/utils/db.js';
11
14
  import { getAppliedMigrations } from '../dist/runtime/server/utils/migrate.js';
@@ -75,10 +78,9 @@ const loadOptions = async () => {
75
78
  };
76
79
  }
77
80
  return mergedConfig;
78
- } else {
79
- console.log("[Nuxt Users] No nuxt-users configuration found, using environment variables");
80
- return getOptionsFromEnv();
81
81
  }
82
+ console.log("[Nuxt Users] No nuxt-users configuration found, using environment variables");
83
+ return getOptionsFromEnv();
82
84
  } catch (error) {
83
85
  console.log("[Nuxt Users] Could not load Nuxt project, using environment variables");
84
86
  console.error("[Nuxt Users] Error:", error);
@@ -369,14 +371,16 @@ const projectInfo = defineCommand({
369
371
  console.log("[Nuxt Users] -", migration);
370
372
  });
371
373
  }
372
- } else {
374
+ }
375
+ if (!migrationsTableExists) {
373
376
  console.log("[Nuxt Users] Table exists: no \u274C");
374
377
  console.log('[Nuxt Users] \u27A1\uFE0F Run "npx nuxt-users migrate" to set up the database');
375
378
  }
376
379
  } catch (error) {
377
380
  console.log("[Nuxt Users] Status: \u26A0\uFE0F Error -", error instanceof Error ? error.message : "Unknown error");
378
381
  }
379
- } else {
382
+ }
383
+ if (!nuxtUsersConfig) {
380
384
  console.log("[Nuxt Users] \u26A0\uFE0F Nuxt Users module not configured in runtime config");
381
385
  }
382
386
  } catch (error) {
package/dist/module.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as _nuxt_schema from '@nuxt/schema';
2
- import { R as RuntimeModuleOptions, M as ModuleOptions } from './shared/nuxt-users.SXaMnaEL.mjs';
2
+ import { R as RuntimeModuleOptions, M as ModuleOptions } from './shared/nuxt-users.FiQjvlZl.mjs';
3
3
  export * from '../dist/runtime/server/internal/index.js';
4
4
 
5
5
  declare const defaultOptions: ModuleOptions;
package/dist/module.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as _nuxt_schema from '@nuxt/schema';
2
- import { R as RuntimeModuleOptions, M as ModuleOptions } from './shared/nuxt-users.SXaMnaEL.js';
2
+ import { R as RuntimeModuleOptions, M as ModuleOptions } from './shared/nuxt-users.FiQjvlZl.js';
3
3
  export * from '../dist/runtime/server/internal/index.js';
4
4
 
5
5
  declare const defaultOptions: ModuleOptions;
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nuxt-users",
3
3
  "configKey": "nuxtUsers",
4
- "version": "1.37.3",
4
+ "version": "1.39.0",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -50,7 +50,12 @@ const defaultOptions = {
50
50
  requireSpecialChars: true,
51
51
  preventCommonPasswords: true
52
52
  },
53
- hardDelete: false
53
+ hardDelete: false,
54
+ locale: {
55
+ default: "en",
56
+ fallbackLocale: "en",
57
+ texts: {}
58
+ }
54
59
  };
55
60
  const module$1 = defineNuxtModule({
56
61
  meta: {
@@ -63,6 +68,9 @@ const module$1 = defineNuxtModule({
63
68
  const resolver = createResolver(import.meta.url);
64
69
  const { connector: _defaultConnector, ...defaultsWithoutConnector } = defaultOptions;
65
70
  const runtimeConfigOptions = defu(nuxt.options.runtimeConfig.nuxtUsers || {}, options, defaultsWithoutConnector);
71
+ if (!runtimeConfigOptions.locale) {
72
+ runtimeConfigOptions.locale = defaultOptions.locale;
73
+ }
66
74
  const configuredConnector = nuxt.options.runtimeConfig.nuxtUsers?.connector || options.connector;
67
75
  runtimeConfigOptions.connector = configuredConnector || defaultOptions.connector;
68
76
  nuxt.options.runtimeConfig.nuxtUsers = {
@@ -108,6 +116,7 @@ const module$1 = defineNuxtModule({
108
116
  nuxt.options.runtimeConfig.public = nuxt.options.runtimeConfig.public || {};
109
117
  nuxt.options.runtimeConfig.public.nuxtUsers = {
110
118
  passwordValidation: runtimeConfigOptions.passwordValidation,
119
+ locale: runtimeConfigOptions.locale,
111
120
  auth: {
112
121
  whitelist: (() => {
113
122
  const combinedWhitelist = [...defaultOptions.auth?.whitelist || [], ...runtimeConfigOptions.auth?.whitelist || []];
@@ -144,6 +153,10 @@ const module$1 = defineNuxtModule({
144
153
  },
145
154
  apiBasePath: runtimeConfigOptions.apiBasePath || defaultOptions.apiBasePath
146
155
  };
156
+ addPlugin({
157
+ src: resolver.resolve("./runtime/plugin.auth-init"),
158
+ name: "0-auth-init-nuxt-users"
159
+ });
147
160
  addPlugin({
148
161
  src: resolver.resolve("./runtime/plugin"),
149
162
  mode: "server"
@@ -159,7 +172,7 @@ const module$1 = defineNuxtModule({
159
172
  path: resolver.resolve("./runtime/middleware/authorization.client"),
160
173
  global: true
161
174
  });
162
- const base = nuxt.options.runtimeConfig.nuxtUsers.apiBasePath || defaultOptions.apiBasePath;
175
+ const base = runtimeConfigOptions.apiBasePath || defaultOptions.apiBasePath;
163
176
  addServerHandler({
164
177
  route: `${base}/session`,
165
178
  method: "post",
@@ -248,7 +261,7 @@ const module$1 = defineNuxtModule({
248
261
  nitroConfig.scanDirs.push(resolver.resolve("./runtime/server/tasks"));
249
262
  nitroConfig.prerender = nitroConfig.prerender || {};
250
263
  nitroConfig.prerender.ignore = nitroConfig.prerender.ignore || [];
251
- const apiBasePath = nuxt.options.runtimeConfig.nuxtUsers.apiBasePath || defaultOptions.apiBasePath;
264
+ const apiBasePath = runtimeConfigOptions.apiBasePath || defaultOptions.apiBasePath;
252
265
  const ignorePattern = `${apiBasePath}/**`;
253
266
  if (!nitroConfig.prerender.ignore.includes(ignorePattern)) {
254
267
  nitroConfig.prerender.ignore.push(ignorePattern);
@@ -2,6 +2,11 @@ interface Props {
2
2
  successTitle?: string;
3
3
  errorTitle?: string;
4
4
  loginButtonText?: string;
5
+ backToLoginText?: string;
6
+ processingTitle?: string;
7
+ processingMessage?: string;
8
+ successMessage?: string;
9
+ errorMessage?: string;
5
10
  loginUrl?: string;
6
11
  showLoginButton?: boolean;
7
12
  }
@@ -22,9 +27,6 @@ type __VLS_Slots = {} & {
22
27
  'loading-content'?: (props: typeof __VLS_13) => any;
23
28
  };
24
29
  declare const __VLS_base: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<Props> & Readonly<{}>, {
25
- successTitle: string;
26
- errorTitle: string;
27
- loginButtonText: string;
28
30
  loginUrl: string;
29
31
  showLoginButton: boolean;
30
32
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
@@ -1,15 +1,22 @@
1
1
  <script setup>
2
2
  import { computed } from "vue";
3
3
  import { useRoute } from "#app";
4
+ import { useNuxtUsersLocale } from "../composables/useNuxtUsersLocale";
5
+ const { t } = useNuxtUsersLocale();
4
6
  const route = useRoute();
5
7
  const status = computed(() => route.query.status);
6
8
  const message = computed(() => route.query.message);
7
9
  const isSuccess = computed(() => status.value === "success");
8
10
  const isError = computed(() => status.value === "error");
9
11
  const _props = defineProps({
10
- successTitle: { type: String, required: false, default: "Email Confirmed!" },
11
- errorTitle: { type: String, required: false, default: "Confirmation Failed" },
12
- loginButtonText: { type: String, required: false, default: "Continue to Login" },
12
+ successTitle: { type: String, required: false },
13
+ errorTitle: { type: String, required: false },
14
+ loginButtonText: { type: String, required: false },
15
+ backToLoginText: { type: String, required: false },
16
+ processingTitle: { type: String, required: false },
17
+ processingMessage: { type: String, required: false },
18
+ successMessage: { type: String, required: false },
19
+ errorMessage: { type: String, required: false },
13
20
  loginUrl: { type: String, required: false, default: "/login" },
14
21
  showLoginButton: { type: Boolean, required: false, default: true }
15
22
  });
@@ -44,10 +51,10 @@ const _props = defineProps({
44
51
  <div class="n-users-section-header">
45
52
  <slot name="success-content">
46
53
  <h1 class="n-users-login-title">
47
- {{ successTitle }}
54
+ {{ _props.successTitle || t("emailConfirmation.successTitle") }}
48
55
  </h1>
49
56
  <div class="n-users-success-message">
50
- {{ message || "Your email has been confirmed and your account is now active." }}
57
+ {{ message || _props.successMessage || t("emailConfirmation.successMessage") }}
51
58
  </div>
52
59
  </slot>
53
60
  </div>
@@ -58,10 +65,10 @@ const _props = defineProps({
58
65
  class="n-users-form-actions"
59
66
  >
60
67
  <a
61
- :href="loginUrl"
68
+ :href="_props.loginUrl"
62
69
  class="n-users-btn n-users-btn-primary"
63
70
  >
64
- {{ loginButtonText }}
71
+ {{ _props.loginButtonText || t("emailConfirmation.loginButton") }}
65
72
  </a>
66
73
  </div>
67
74
  </slot>
@@ -69,7 +76,7 @@ const _props = defineProps({
69
76
 
70
77
  <!-- Error state -->
71
78
  <div
72
- v-else-if="isError"
79
+ v-if="isError"
73
80
  class="n-users-confirmation-content"
74
81
  >
75
82
  <slot name="error-icon">
@@ -104,10 +111,10 @@ const _props = defineProps({
104
111
  <div class="n-users-section-header">
105
112
  <slot name="error-content">
106
113
  <h1 class="n-users-login-title">
107
- {{ errorTitle }}
114
+ {{ _props.errorTitle || t("emailConfirmation.errorTitle") }}
108
115
  </h1>
109
116
  <div class="n-users-error-message">
110
- {{ message || "The confirmation link is invalid or has expired. Please try registering again or contact support." }}
117
+ {{ message || _props.errorMessage || t("emailConfirmation.errorMessage") }}
111
118
  </div>
112
119
  </slot>
113
120
  </div>
@@ -115,10 +122,10 @@ const _props = defineProps({
115
122
  <slot name="error-actions">
116
123
  <div class="n-users-form-actions">
117
124
  <a
118
- :href="loginUrl"
125
+ :href="_props.loginUrl"
119
126
  class="n-users-btn n-users-btn-secondary"
120
127
  >
121
- Back to Login
128
+ {{ _props.backToLoginText || t("emailConfirmation.backToLogin") }}
122
129
  </a>
123
130
  </div>
124
131
  </slot>
@@ -126,7 +133,7 @@ const _props = defineProps({
126
133
 
127
134
  <!-- Loading/unknown state -->
128
135
  <div
129
- v-else
136
+ v-if="!isError"
130
137
  class="n-users-confirmation-content"
131
138
  >
132
139
  <slot name="loading-content">
@@ -135,10 +142,10 @@ const _props = defineProps({
135
142
  </div>
136
143
  <div class="n-users-section-header">
137
144
  <h1 class="n-users-login-title">
138
- Processing...
145
+ {{ _props.processingTitle || t("emailConfirmation.processing") }}
139
146
  </h1>
140
147
  <div class="n-users-form-help">
141
- Please wait while we process your email confirmation.
148
+ {{ _props.processingMessage || t("emailConfirmation.processingMessage") }}
142
149
  </div>
143
150
  </div>
144
151
  </slot>
@@ -2,6 +2,11 @@ interface Props {
2
2
  successTitle?: string;
3
3
  errorTitle?: string;
4
4
  loginButtonText?: string;
5
+ backToLoginText?: string;
6
+ processingTitle?: string;
7
+ processingMessage?: string;
8
+ successMessage?: string;
9
+ errorMessage?: string;
5
10
  loginUrl?: string;
6
11
  showLoginButton?: boolean;
7
12
  }
@@ -22,9 +27,6 @@ type __VLS_Slots = {} & {
22
27
  'loading-content'?: (props: typeof __VLS_13) => any;
23
28
  };
24
29
  declare const __VLS_base: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<Props> & Readonly<{}>, {
25
- successTitle: string;
26
- errorTitle: string;
27
- loginButtonText: string;
28
30
  loginUrl: string;
29
31
  showLoginButton: boolean;
30
32
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
@@ -6,9 +6,12 @@ interface Props {
6
6
  redirectEndpoint?: string;
7
7
  /**
8
8
  * Button text
9
- * @default 'Continue with Google'
10
9
  */
11
10
  buttonText?: string;
11
+ /**
12
+ * Redirecting text
13
+ */
14
+ redirectingText?: string;
12
15
  /**
13
16
  * Show Google logo in button
14
17
  * @default true
@@ -24,7 +27,6 @@ declare const __VLS_export: import("vue").DefineComponent<Props, {}, {}, {}, {},
24
27
  }, string, import("vue").PublicProps, Readonly<Props> & Readonly<{
25
28
  onClick?: (() => any) | undefined;
26
29
  }>, {
27
- buttonText: string;
28
30
  showLogo: boolean;
29
31
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
30
32
  declare const _default: typeof __VLS_export;
@@ -1,9 +1,12 @@
1
1
  <script setup>
2
2
  import { ref, computed } from "vue";
3
3
  import { useRuntimeConfig, navigateTo } from "#imports";
4
+ import { useNuxtUsersLocale } from "../composables/useNuxtUsersLocale";
5
+ const { t } = useNuxtUsersLocale();
4
6
  const props = defineProps({
5
7
  redirectEndpoint: { type: String, required: false },
6
- buttonText: { type: String, required: false, default: "Continue with Google" },
8
+ buttonText: { type: String, required: false },
9
+ redirectingText: { type: String, required: false },
7
10
  showLogo: { type: Boolean, required: false, default: true },
8
11
  class: { type: String, required: false }
9
12
  });
@@ -69,7 +72,7 @@ const handleGoogleLogin = async () => {
69
72
 
70
73
  <!-- Button text -->
71
74
  <span class="n-users-google-text">
72
- {{ isLoading ? "Redirecting..." : buttonText }}
75
+ {{ isLoading ? props.redirectingText || t("googleLogin.redirecting") : props.buttonText || t("googleLogin.buttonText") }}
73
76
  </span>
74
77
  </button>
75
78
  </template>
@@ -6,9 +6,12 @@ interface Props {
6
6
  redirectEndpoint?: string;
7
7
  /**
8
8
  * Button text
9
- * @default 'Continue with Google'
10
9
  */
11
10
  buttonText?: string;
11
+ /**
12
+ * Redirecting text
13
+ */
14
+ redirectingText?: string;
12
15
  /**
13
16
  * Show Google logo in button
14
17
  * @default true
@@ -24,7 +27,6 @@ declare const __VLS_export: import("vue").DefineComponent<Props, {}, {}, {}, {},
24
27
  }, string, import("vue").PublicProps, Readonly<Props> & Readonly<{
25
28
  onClick?: (() => any) | undefined;
26
29
  }>, {
27
- buttonText: string;
28
30
  showLogo: boolean;
29
31
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
30
32
  declare const _default: typeof __VLS_export;
@@ -2,10 +2,24 @@
2
2
  import { onMounted, computed, ref } from "vue";
3
3
  import { useUsers } from "../composables/useUsers";
4
4
  import { defaultDisplayFields, defaultFieldLabels } from "nuxt-users/utils";
5
+ import { useNuxtUsersLocale } from "../composables/useNuxtUsersLocale";
6
+ const { t } = useNuxtUsersLocale();
5
7
  const props = defineProps({
6
8
  displayFields: { type: Array, required: false, default: () => defaultDisplayFields },
7
9
  fieldLabels: { type: Object, required: false, default: () => defaultFieldLabels },
8
- filter: { type: [Object, Function], required: false, default: () => ({}) }
10
+ filter: { type: [Object, Function], required: false, default: () => ({}) },
11
+ title: { type: String, required: false },
12
+ loadingText: { type: String, required: false },
13
+ noUsersText: { type: String, required: false },
14
+ errorText: { type: String, required: false },
15
+ editButtonText: { type: String, required: false },
16
+ deleteButtonText: { type: String, required: false },
17
+ pageText: { type: String, required: false },
18
+ ofText: { type: String, required: false },
19
+ totalText: { type: String, required: false },
20
+ usersText: { type: String, required: false },
21
+ previousText: { type: String, required: false },
22
+ nextText: { type: String, required: false }
9
23
  });
10
24
  const emit = defineEmits(["editClick", "delete"]);
11
25
  const usersComposable = ref(null);
@@ -59,7 +73,7 @@ const handleFetchUsers = (page, limit) => {
59
73
  <div class="n-users-list">
60
74
  <slot name="title">
61
75
  <h2>
62
- Users List
76
+ {{ props.title || t("usersList.title") }}
63
77
  </h2>
64
78
  </slot>
65
79
 
@@ -68,7 +82,7 @@ const handleFetchUsers = (page, limit) => {
68
82
  :loading="loading"
69
83
  >
70
84
  <div v-if="loading">
71
- Loading users...
85
+ {{ props.loadingText || t("usersList.loading") }}
72
86
  </div>
73
87
  </slot>
74
88
 
@@ -77,13 +91,13 @@ const handleFetchUsers = (page, limit) => {
77
91
  :error="error"
78
92
  >
79
93
  <div v-if="error">
80
- Error: {{ error }}
94
+ {{ props.errorText || t("usersList.error") }} {{ error }}
81
95
  </div>
82
96
  </slot>
83
97
 
84
98
  <slot name="noUsers">
85
99
  <div v-if="users.length === 0">
86
- No users found
100
+ {{ props.noUsersText || t("usersList.noUsers") }}
87
101
  </div>
88
102
  </slot>
89
103
 
@@ -126,7 +140,7 @@ const handleFetchUsers = (page, limit) => {
126
140
  class="n-users-edit-btn"
127
141
  @click="editUserFn"
128
142
  >
129
- Edit
143
+ {{ props.editButtonText || t("common.edit") }}
130
144
  </button>
131
145
  </slot>
132
146
  </template>
@@ -142,7 +156,7 @@ const handleFetchUsers = (page, limit) => {
142
156
  class="n-users-delete-btn"
143
157
  @click="deleteUserFn"
144
158
  >
145
- Delete
159
+ {{ props.deleteButtonText || t("common.delete") }}
146
160
  </button>
147
161
  </slot>
148
162
  </template>
@@ -210,8 +224,8 @@ const handleFetchUsers = (page, limit) => {
210
224
  >
211
225
  <div v-if="pagination && pagination.totalPages > 1">
212
226
  <div>
213
- <span>Page {{ pagination.page }} of {{ pagination.totalPages }}</span>
214
- <span>Total: {{ pagination.total }} users</span>
227
+ <span>{{ props.pageText || t("usersList.page") }} {{ pagination.page }} {{ props.ofText || t("usersList.of") }} {{ pagination.totalPages }}</span>
228
+ <span>{{ props.totalText || t("usersList.total") }} {{ pagination.total }} {{ props.usersText || t("usersList.users") }}</span>
215
229
  </div>
216
230
  </div>
217
231
  </slot>
@@ -229,7 +243,7 @@ const handleFetchUsers = (page, limit) => {
229
243
  :disabled="loading"
230
244
  @click="handleFetchUsers(pagination.page - 1)"
231
245
  >
232
- Previous
246
+ {{ props.previousText || t("usersList.previous") }}
233
247
  </button>
234
248
 
235
249
  <button
@@ -237,7 +251,7 @@ const handleFetchUsers = (page, limit) => {
237
251
  :disabled="loading"
238
252
  @click="handleFetchUsers(pagination.page + 1)"
239
253
  >
240
- Next
254
+ {{ props.nextText || t("usersList.next") }}
241
255
  </button>
242
256
  </div>
243
257
  </div>
@@ -2,6 +2,17 @@ interface Props {
2
2
  apiEndpoint?: string;
3
3
  forgotPasswordEndpoint?: string;
4
4
  redirectTo?: string;
5
+ title?: string;
6
+ subtitle?: string;
7
+ emailLabel?: string;
8
+ emailPlaceholder?: string;
9
+ passwordLabel?: string;
10
+ passwordPlaceholder?: string;
11
+ rememberMeLabel?: string;
12
+ submitLabel?: string;
13
+ submittingLabel?: string;
14
+ forgotPasswordLabel?: string;
15
+ forgotPasswordSendingLabel?: string;
5
16
  }
6
17
  declare var __VLS_1: {}, __VLS_3: {}, __VLS_5: {}, __VLS_7: {}, __VLS_9: {}, __VLS_11: {}, __VLS_13: {
7
18
  message: string;
@@ -26,15 +37,15 @@ type __VLS_Slots = {} & {
26
37
  'error-message'?: (props: typeof __VLS_15) => any;
27
38
  };
28
39
  declare const __VLS_base: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
29
- success: (user: UserWithoutPassword, rememberMe: boolean) => any;
30
40
  error: (error: string) => any;
31
41
  submit: (data: LoginFormData) => any;
42
+ success: (user: UserWithoutPassword, rememberMe: boolean) => any;
32
43
  "forgot-password-error": (error: string) => any;
33
44
  "forgot-password-success": () => any;
34
45
  }, string, import("vue").PublicProps, Readonly<Props> & Readonly<{
35
- onSuccess?: ((user: UserWithoutPassword, rememberMe: boolean) => any) | undefined;
36
46
  onError?: ((error: string) => any) | undefined;
37
47
  onSubmit?: ((data: LoginFormData) => any) | undefined;
48
+ onSuccess?: ((user: UserWithoutPassword, rememberMe: boolean) => any) | undefined;
38
49
  "onForgot-password-error"?: ((error: string) => any) | undefined;
39
50
  "onForgot-password-success"?: (() => any) | undefined;
40
51
  }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
@@ -2,14 +2,36 @@
2
2
  import { ref, computed } from "vue";
3
3
  import { navigateTo } from "#app";
4
4
  import { useRuntimeConfig } from "#imports";
5
+ import { useNuxtUsersLocale } from "../composables/useNuxtUsersLocale";
5
6
  const { public: { nuxtUsers } } = useRuntimeConfig();
6
7
  const { passwordValidation } = nuxtUsers;
8
+ const { t } = useNuxtUsersLocale();
7
9
  const props = defineProps({
8
10
  apiEndpoint: { type: String, required: false },
9
11
  forgotPasswordEndpoint: { type: String, required: false },
10
- redirectTo: { type: String, required: false }
12
+ redirectTo: { type: String, required: false },
13
+ title: { type: String, required: false },
14
+ subtitle: { type: String, required: false },
15
+ emailLabel: { type: String, required: false },
16
+ emailPlaceholder: { type: String, required: false },
17
+ passwordLabel: { type: String, required: false },
18
+ passwordPlaceholder: { type: String, required: false },
19
+ rememberMeLabel: { type: String, required: false },
20
+ submitLabel: { type: String, required: false },
21
+ submittingLabel: { type: String, required: false },
22
+ forgotPasswordLabel: { type: String, required: false },
23
+ forgotPasswordSendingLabel: { type: String, required: false }
11
24
  });
12
25
  const emit = defineEmits(["success", "error", "forgot-password-error", "submit", "forgot-password-success"]);
26
+ const extractErrorMessage = (err, defaultMessage) => {
27
+ if (err && typeof err === "object" && "data" in err && err.data && typeof err.data === "object" && "statusMessage" in err.data) {
28
+ return String(err.data.statusMessage);
29
+ }
30
+ if (err instanceof Error) {
31
+ return err.message;
32
+ }
33
+ return defaultMessage;
34
+ };
13
35
  const apiEndpoint = computed(() => props.apiEndpoint || `${nuxtUsers?.apiBasePath}/session`);
14
36
  const forgotPasswordEndpoint = computed(() => props.forgotPasswordEndpoint || `${nuxtUsers?.apiBasePath}/password/forgot`);
15
37
  const isLoading = ref(false);
@@ -39,7 +61,7 @@ const handleSubmit = async () => {
39
61
  await navigateTo(props.redirectTo);
40
62
  }
41
63
  } catch (err) {
42
- const errorMessage = err && typeof err === "object" && "data" in err && err.data && typeof err.data === "object" && "statusMessage" in err.data ? String(err.data.statusMessage) : err instanceof Error ? err.message : "Login failed";
64
+ const errorMessage = extractErrorMessage(err, t("login.loginFailed"));
43
65
  error.value = errorMessage;
44
66
  emit("error", errorMessage);
45
67
  } finally {
@@ -48,7 +70,7 @@ const handleSubmit = async () => {
48
70
  };
49
71
  const handleForgotPassword = async () => {
50
72
  if (!formData.value.email) {
51
- error.value = "Please enter your email address first";
73
+ error.value = t("login.emailRequiredForForgotPassword");
52
74
  return;
53
75
  }
54
76
  isForgotPasswordLoading.value = true;
@@ -64,7 +86,7 @@ const handleForgotPassword = async () => {
64
86
  forgotPasswordMessage.value = response.message;
65
87
  emit("forgot-password-success");
66
88
  } catch (err) {
67
- const errorMessage = err && typeof err === "object" && "data" in err && err.data && typeof err.data === "object" && "statusMessage" in err.data ? String(err.data.statusMessage) : err instanceof Error ? err.message : "Failed to send password reset email";
89
+ const errorMessage = extractErrorMessage(err, t("login.forgotPasswordFailed"));
68
90
  error.value = errorMessage;
69
91
  emit("forgot-password-error", errorMessage);
70
92
  } finally {
@@ -80,10 +102,10 @@ const handleForgotPassword = async () => {
80
102
  <slot name="header">
81
103
  <div class="n-users-login-header">
82
104
  <h2 class="n-users-login-title">
83
- Welcome Back
105
+ {{ props.title || t("login.title") }}
84
106
  </h2>
85
107
  <p class="n-users-login-subtitle">
86
- Sign in to your account
108
+ {{ props.subtitle || t("login.subtitle") }}
87
109
  </p>
88
110
  </div>
89
111
  </slot>
@@ -91,13 +113,13 @@ const handleForgotPassword = async () => {
91
113
  <!-- Email field -->
92
114
  <slot name="email-field">
93
115
  <div class="n-users-form-group">
94
- <label for="email">Email</label>
116
+ <label for="email">{{ props.emailLabel || t("common.email") }}</label>
95
117
  <input
96
118
  id="email"
97
119
  v-model="formData.email"
98
120
  type="email"
99
121
  name="email"
100
- placeholder="Enter your email"
122
+ :placeholder="props.emailPlaceholder || t('login.emailPlaceholder')"
101
123
  required
102
124
  >
103
125
  </div>
@@ -106,13 +128,13 @@ const handleForgotPassword = async () => {
106
128
  <!-- Password field -->
107
129
  <slot name="password-field">
108
130
  <div class="n-users-form-group">
109
- <label for="password">Password</label>
131
+ <label for="password">{{ props.passwordLabel || t("login.passwordLabel") }}</label>
110
132
  <input
111
133
  id="password"
112
134
  v-model="formData.password"
113
135
  type="password"
114
136
  name="password"
115
- placeholder="Enter your password"
137
+ :placeholder="props.passwordPlaceholder || t('login.passwordPlaceholder')"
116
138
  required
117
139
  :minlength="passwordValidation.minLength"
118
140
  >
@@ -128,7 +150,7 @@ const handleForgotPassword = async () => {
128
150
  type="checkbox"
129
151
  name="rememberMe"
130
152
  >
131
- <label for="rememberMe">Remember me</label>
153
+ <label for="rememberMe">{{ props.rememberMeLabel || t("login.rememberMe") }}</label>
132
154
  </div>
133
155
  </slot>
134
156
 
@@ -142,7 +164,7 @@ const handleForgotPassword = async () => {
142
164
  v-if="isLoading"
143
165
  class="n-users-loading-spinner"
144
166
  />
145
- {{ isLoading ? "Signing in..." : "Sign In" }}
167
+ {{ isLoading ? props.submittingLabel || t("login.submitting") : props.submitLabel || t("login.submit") }}
146
168
  </button>
147
169
  </slot>
148
170
 
@@ -160,7 +182,7 @@ const handleForgotPassword = async () => {
160
182
  v-if="isForgotPasswordLoading"
161
183
  class="n-users-loading-spinner"
162
184
  />
163
- {{ isForgotPasswordLoading ? "Sending..." : "Forgot your password?" }}
185
+ {{ isForgotPasswordLoading ? props.forgotPasswordSendingLabel || t("login.forgotPasswordSending") : props.forgotPasswordLabel || t("login.forgotPassword") }}
164
186
  </a>
165
187
  </p>
166
188
  </div>