nuxt-users 1.6.4 → 1.6.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.
Files changed (37) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/dist/cli.mjs +1 -1
  3. package/dist/module.json +1 -1
  4. package/dist/module.mjs +26 -21
  5. package/dist/runtime/components/LoginForm.vue +6 -3
  6. package/dist/runtime/components/LoginForm.vue.d.ts +4 -4
  7. package/dist/runtime/components/LogoutLink.vue +21 -7
  8. package/dist/runtime/components/LogoutLink.vue.d.ts +22 -5
  9. package/dist/runtime/components/PasswordStrengthIndicator.vue +38 -27
  10. package/dist/runtime/components/ProfileInfo.vue +33 -0
  11. package/dist/runtime/components/ResetPasswordForm.vue +89 -96
  12. package/dist/runtime/components/ResetPasswordForm.vue.d.ts +15 -1
  13. package/dist/runtime/composables/useAuth.d.ts +2 -2
  14. package/dist/runtime/composables/useAuth.js +2 -2
  15. package/dist/runtime/composables/usePasswordValidation.d.ts +1 -1
  16. package/dist/runtime/middleware/auth.client.d.ts +2 -0
  17. package/dist/runtime/middleware/auth.client.js +20 -0
  18. package/dist/runtime/plugin.js +1 -46
  19. package/dist/runtime/server/api/{login.post.js → auth/login.post.js} +1 -1
  20. package/dist/runtime/server/api/{logout.get.js → auth/logout.get.js} +1 -1
  21. package/dist/runtime/server/api/auth/reset-password.post.js +0 -3
  22. package/dist/runtime/server/api/{update-password.post.js → auth/update-password.post.js} +3 -3
  23. package/dist/runtime/server/api/user/profile.get.d.ts +4 -0
  24. package/dist/runtime/server/api/{profile.get.js → user/profile.get.js} +2 -3
  25. package/dist/runtime/server/middleware/auth.server.d.ts +2 -0
  26. package/dist/runtime/server/middleware/auth.server.js +32 -0
  27. package/dist/runtime/server/utils/user.d.ts +2 -2
  28. package/dist/runtime/server/utils/user.js +7 -2
  29. package/package.json +1 -1
  30. package/dist/runtime/components/ForgotPasswordForm.vue +0 -77
  31. package/dist/runtime/components/ProfileForm.vue +0 -240
  32. package/dist/runtime/components/ProfileForm.vue.d.ts +0 -20
  33. package/dist/runtime/server/api/profile.get.d.ts +0 -11
  34. /package/dist/runtime/components/{ForgotPasswordForm.vue.d.ts → ProfileInfo.vue.d.ts} +0 -0
  35. /package/dist/runtime/server/api/{login.post.d.ts → auth/login.post.d.ts} +0 -0
  36. /package/dist/runtime/server/api/{logout.get.d.ts → auth/logout.get.d.ts} +0 -0
  37. /package/dist/runtime/server/api/{update-password.post.d.ts → auth/update-password.post.d.ts} +0 -0
package/CHANGELOG.md CHANGED
@@ -1,6 +1,56 @@
1
1
  # Changelog
2
2
 
3
3
 
4
+ ## v1.6.6
5
+
6
+ [compare changes](https://github.com/rrd108/nuxt-users/compare/v1.6.5...v1.6.6)
7
+
8
+ ### 🩹 Fixes
9
+
10
+ - Authentication middleware ([c7de8a4](https://github.com/rrd108/nuxt-users/commit/c7de8a4))
11
+ - Lint and type errors ([dd6ee40](https://github.com/rrd108/nuxt-users/commit/dd6ee40))
12
+ - Type errors ([65c81fb](https://github.com/rrd108/nuxt-users/commit/65c81fb))
13
+ - **cli:** Updates CLI version dynamically ([1cca13a](https://github.com/rrd108/nuxt-users/commit/1cca13a))
14
+ - Authentication middleware logic ([18a3bb5](https://github.com/rrd108/nuxt-users/commit/18a3bb5))
15
+ - Password validation and UI ([07b44b3](https://github.com/rrd108/nuxt-users/commit/07b44b3))
16
+
17
+ ### 💅 Refactors
18
+
19
+ - API routes ([3c7603f](https://github.com/rrd108/nuxt-users/commit/3c7603f))
20
+ - Authentication components and logic ([4da544f](https://github.com/rrd108/nuxt-users/commit/4da544f))
21
+
22
+ ### 🏡 Chore
23
+
24
+ - **release:** V1.6.5 ([f35e526](https://github.com/rrd108/nuxt-users/commit/f35e526))
25
+
26
+ ### ✅ Tests
27
+
28
+ - Fix mock ([791dc94](https://github.com/rrd108/nuxt-users/commit/791dc94))
29
+ - Adds unit tests for auth API endpoints ([d844630](https://github.com/rrd108/nuxt-users/commit/d844630))
30
+
31
+ ### ❤️ Contributors
32
+
33
+ - Rrd108 ([@rrd108](https://github.com/rrd108))
34
+
35
+ ## v1.6.5
36
+
37
+ [compare changes](https://github.com/rrd108/nuxt-users/compare/v1.6.4...v1.6.5)
38
+
39
+ ### 🩹 Fixes
40
+
41
+ - Authentication middleware ([c7de8a4](https://github.com/rrd108/nuxt-users/commit/c7de8a4))
42
+ - Lint and type errors ([dd6ee40](https://github.com/rrd108/nuxt-users/commit/dd6ee40))
43
+ - Type errors ([65c81fb](https://github.com/rrd108/nuxt-users/commit/65c81fb))
44
+
45
+ ### 🏡 Chore
46
+
47
+ - Fix formatting ([fdec442](https://github.com/rrd108/nuxt-users/commit/fdec442))
48
+ - **release:** V1.6.4 ([c395d20](https://github.com/rrd108/nuxt-users/commit/c395d20))
49
+
50
+ ### ❤️ Contributors
51
+
52
+ - Rrd108 ([@rrd108](https://github.com/rrd108))
53
+
4
54
  ## v1.6.4
5
55
 
6
56
  [compare changes](https://github.com/rrd108/nuxt-users/compare/v1.6.3...v1.6.4)
package/dist/cli.mjs CHANGED
@@ -247,7 +247,7 @@ const main = defineCommand({
247
247
  meta: {
248
248
  name: "nuxt-users",
249
249
  description: "CLI for Nuxt Users Module - Manage users, migrations, and database operations",
250
- version: "1.6.3"
250
+ version: "1.6.5"
251
251
  },
252
252
  subCommands: {
253
253
  migrate,
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nuxt-users",
3
3
  "configKey": "nuxtUsers",
4
- "version": "1.6.4",
4
+ "version": "1.6.6",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.1",
7
7
  "unbuild": "3.6.0"
package/dist/module.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { defineNuxtModule, createResolver, addPlugin, addImportsDir, addServerHandler, addComponent } from '@nuxt/kit';
1
+ import { defineNuxtModule, createResolver, addPlugin, addImportsDir, addServerHandler, addRouteMiddleware, addComponent } from '@nuxt/kit';
2
2
  import { defu } from 'defu';
3
3
 
4
4
  const defaultOptions = {
@@ -84,54 +84,59 @@ const module = defineNuxtModule({
84
84
  });
85
85
  addImportsDir(resolver.resolve("./runtime/composables"));
86
86
  addServerHandler({
87
- route: "/api/login",
87
+ middleware: true,
88
+ handler: resolver.resolve("./runtime/server/middleware/auth.server")
89
+ });
90
+ addRouteMiddleware({
91
+ name: "auth.client",
92
+ path: resolver.resolve("./runtime/middleware/auth.client"),
93
+ global: true
94
+ });
95
+ addServerHandler({
96
+ route: "/api/auth/login",
88
97
  method: "post",
89
- handler: resolver.resolve("./runtime/server/api/login.post")
98
+ handler: resolver.resolve("./runtime/server/api/auth/login.post")
90
99
  });
91
100
  addServerHandler({
92
- route: "/api/forgot-password",
101
+ route: "/api/auth/forgot-password",
93
102
  method: "post",
94
103
  handler: resolver.resolve("./runtime/server/api/auth/forgot-password.post")
95
104
  });
96
105
  addServerHandler({
97
- route: "/api/reset-password",
106
+ route: "/api/auth/reset-password",
98
107
  method: "post",
99
108
  handler: resolver.resolve("./runtime/server/api/auth/reset-password.post")
100
109
  });
101
110
  addServerHandler({
102
- route: "/api/logout",
111
+ route: "/api/auth/logout",
103
112
  method: "get",
104
- handler: resolver.resolve("./runtime/server/api/logout.get")
113
+ handler: resolver.resolve("./runtime/server/api/auth/logout.get")
105
114
  });
106
115
  addServerHandler({
107
- route: "/api/profile",
116
+ route: "/api/user/profile",
108
117
  method: "get",
109
- handler: resolver.resolve("./runtime/server/api/profile.get")
118
+ handler: resolver.resolve("./runtime/server/api/user/profile.get")
110
119
  });
111
120
  addServerHandler({
112
- route: "/api/update-password",
121
+ route: "/api/auth/update-password",
113
122
  method: "post",
114
- handler: resolver.resolve("./runtime/server/api/update-password.post")
123
+ handler: resolver.resolve("./runtime/server/api/auth/update-password.post")
115
124
  });
116
125
  addComponent({
117
126
  name: "LoginForm",
118
127
  filePath: resolver.resolve("./runtime/components/LoginForm.vue")
119
128
  });
120
- addComponent({
121
- name: "ForgotPasswordForm",
122
- filePath: resolver.resolve("./runtime/components/ForgotPasswordForm.vue")
123
- });
124
- addComponent({
125
- name: "ResetPasswordForm",
126
- filePath: resolver.resolve("./runtime/components/ResetPasswordForm.vue")
127
- });
128
129
  addComponent({
129
130
  name: "LogoutLink",
130
131
  filePath: resolver.resolve("./runtime/components/LogoutLink.vue")
131
132
  });
132
133
  addComponent({
133
- name: "ProfileForm",
134
- filePath: resolver.resolve("./runtime/components/ProfileForm.vue")
134
+ name: "ProfileInfo",
135
+ filePath: resolver.resolve("./runtime/components/ProfileInfo.vue")
136
+ });
137
+ addComponent({
138
+ name: "ResetPasswordForm",
139
+ filePath: resolver.resolve("./runtime/components/ResetPasswordForm.vue")
135
140
  });
136
141
  nuxt.options.css = nuxt.options.css || [];
137
142
  nuxt.options.css.push(resolver.resolve("./runtime/assets/colors.css"));
@@ -1,8 +1,11 @@
1
1
  <script setup>
2
2
  import { ref } from "vue";
3
3
  import { navigateTo } from "#app";
4
+ import { useRuntimeConfig } from "#imports";
5
+ const { public: { nuxtUsers } } = useRuntimeConfig();
6
+ const { passwordValidation } = nuxtUsers;
4
7
  const props = defineProps({
5
- apiEndpoint: { type: String, required: false, default: "/api/login" },
8
+ apiEndpoint: { type: String, required: false, default: "/api/auth/login" },
6
9
  redirectTo: { type: String, required: false, default: "/" },
7
10
  forgotPasswordEndpoint: { type: String, required: false, default: "/api/auth/forgot-password" }
8
11
  });
@@ -108,7 +111,7 @@ const handleForgotPassword = async () => {
108
111
  name="password"
109
112
  placeholder="Enter your password"
110
113
  required
111
- minlength="6"
114
+ :minlength="passwordValidation.minLength"
112
115
  >
113
116
  </div>
114
117
  </slot>
@@ -190,5 +193,5 @@ const handleForgotPassword = async () => {
190
193
  </template>
191
194
 
192
195
  <style scoped>
193
- section{background:linear-gradient(135deg,var(--color-bg-primary) 0,var(--color-bg-secondary) 100%);border:1px solid var(--color-border);border-radius:.75em;box-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -1px rgba(0,0,0,.06);margin:0 auto;max-width:25rem;padding:1em}form{gap:1.5rem}.form-group,form{display:flex;flex-direction:column}label{display:block;font-weight:500;margin-bottom:.5em}input[type=email],input[type=password],label{color:var(--color-gray-700);font-size:.875rem}input[type=email],input[type=password]{background-color:var(--color-bg-secondary);border:1px solid var(--color-border-light);border-radius:8px;box-sizing:border-box;padding:.75em 1em;transition:all .2s ease-in-out;width:100%}input[type=email]:focus,input[type=password]:focus{background-color:var(--color-bg-primary);border-color:var(--color-primary);box-shadow:0 0 0 3px rgba(59,130,246,.1);outline:none}.login-header{margin-bottom:1em;text-align:center}.login-title{color:var(--color-gray-800);font-size:1.875rem;font-weight:700;line-height:1.2;margin:0 0 .5em}.login-subtitle{color:var(--color-gray-500);font-size:.875rem;line-height:1.5;margin:0}.remember-me{align-items:center;display:flex;gap:.5rem}input[type=checkbox]{margin:0;width:auto}.remember-me label{color:var(--color-gray-700);font-size:.875rem;margin:0}button[type=submit]{align-items:center;background-color:var(--color-primary);border:1px solid var(--color-primary);border-radius:8px;color:var(--color-white);cursor:pointer;display:flex;font-size:.875rem;font-weight:500;gap:.5rem;justify-content:center;min-height:44px;padding:.75em 1em;transition:all .2s ease-in-out;width:100%}button[type=submit]:hover:not(:disabled){background-color:var(--color-primary-dark);border-color:var(--color-primary-dark)}button[type=submit]:disabled{cursor:not-allowed;opacity:.6}.loading-spinner{animation:spin 1s linear infinite;border:2px solid var(--color-white);border-radius:50%;border-top:2px solid transparent;height:16px;width:16px}@keyframes spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.login-footer{margin-top:1em;text-align:center}.forgot-password{margin:0}.forgot-link{align-items:center;color:var(--color-primary);display:inline-flex;font-size:.875rem;gap:.5rem;text-decoration:none;transition:color .2s ease-in-out}.forgot-link:hover:not(.loading){color:var(--color-primary-dark);text-decoration:underline}.forgot-link.loading{cursor:not-allowed;opacity:.6;pointer-events:none}.error-message{background-color:var(--color-error-light);border:1px solid var(--color-error-border);border-radius:8px;color:var(--color-error)}.error-message,.success-message{font-size:.875rem;margin-top:1em;padding:.75em 1em;text-align:center}.success-message{background-color:var(--color-success-light);border:1px solid var(--color-success-border);border-radius:8px;color:var(--color-success)}@media (max-width:480px){.login-form-container{margin:1em;padding:1.5em}.login-title{font-size:1.5rem}}
196
+ section{background:linear-gradient(135deg,var(--color-bg-primary) 0,var(--color-bg-secondary) 100%);border:1px solid var(--color-border);border-radius:.75em;box-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -1px rgba(0,0,0,.06);margin:0 auto;max-width:25rem;padding:1em}form{gap:1.5rem}.form-group,form{display:flex;flex-direction:column}label{display:block;font-weight:500;margin-bottom:.5em}input[type=email],input[type=password],label{color:var(--color-gray-700);font-size:.875rem}input[type=email],input[type=password]{background-color:var(--color-bg-secondary);border:1px solid var(--color-border-light);border-radius:.5em;box-sizing:border-box;padding:.75em 1em;transition:all .2s ease-in-out;width:100%}input[type=email]:focus,input[type=password]:focus{background-color:var(--color-bg-primary);border-color:var(--color-primary);box-shadow:0 0 0 3px rgba(59,130,246,.1);outline:none}.login-header{margin-bottom:1em;text-align:center}.login-title{color:var(--color-gray-800);font-size:1.875rem;font-weight:700;line-height:1.2;margin:0 0 .5em}.login-subtitle{color:var(--color-gray-500);font-size:.875rem;line-height:1.5;margin:0}.remember-me{align-items:center;display:flex;gap:.5rem}input[type=checkbox]{margin:0;width:auto}.remember-me label{color:var(--color-gray-700);font-size:.875rem;margin:0}button[type=submit]{align-items:center;background-color:var(--color-primary);border:1px solid var(--color-primary);border-radius:.5em;color:var(--color-white);cursor:pointer;display:flex;font-size:.875rem;font-weight:500;gap:.5rem;justify-content:center;min-height:44px;padding:.75em 1em;transition:all .2s ease-in-out;width:100%}button[type=submit]:hover:not(:disabled){background-color:var(--color-primary-dark);border-color:var(--color-primary-dark)}button[type=submit]:disabled{cursor:not-allowed;opacity:.6}.loading-spinner{animation:spin 1s linear infinite;border:2px solid var(--color-white);border-radius:50%;border-top:2px solid transparent;height:16px;width:16px}@keyframes spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.login-footer{margin-top:1em;text-align:center}.forgot-password{margin:0}.forgot-link{align-items:center;color:var(--color-primary);display:inline-flex;font-size:.875rem;gap:.5rem;text-decoration:none;transition:color .2s ease-in-out}.forgot-link:hover:not(.loading){color:var(--color-primary-dark);text-decoration:underline}.forgot-link.loading{cursor:not-allowed;opacity:.6;pointer-events:none}.error-message{background-color:var(--color-error-light);border:1px solid var(--color-error-border);border-radius:.5em;color:var(--color-error)}.error-message,.success-message{font-size:.875rem;margin-top:1em;padding:.75em 1em;text-align:center}.success-message{background-color:var(--color-success-light);border:1px solid var(--color-success-border);border-radius:.5em;color:var(--color-success)}@media (max-width:480px){.login-form-container{margin:1em;padding:1.5em}.login-title{font-size:1.5rem}}
194
197
  </style>
@@ -22,16 +22,16 @@ type __VLS_Slots = {} & {
22
22
  'error-message'?: (props: typeof __VLS_15) => any;
23
23
  };
24
24
  declare const __VLS_component: import("vue").DefineComponent<LoginFormProps, void, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
25
- error: (error: string) => any;
26
- submit: (data: LoginFormData) => any;
27
25
  success: (user: UserWithoutPassword) => any;
26
+ error: (error: string) => any;
28
27
  "forgot-password-error": (error: string) => any;
28
+ submit: (data: LoginFormData) => any;
29
29
  "forgot-password-success": () => any;
30
30
  }, string, import("vue").PublicProps, Readonly<LoginFormProps> & Readonly<{
31
- onError?: ((error: string) => any) | undefined;
32
- onSubmit?: ((data: LoginFormData) => any) | undefined;
33
31
  onSuccess?: ((user: UserWithoutPassword) => any) | undefined;
32
+ onError?: ((error: string) => any) | undefined;
34
33
  "onForgot-password-error"?: ((error: string) => any) | undefined;
34
+ onSubmit?: ((data: LoginFormData) => any) | undefined;
35
35
  "onForgot-password-success"?: (() => any) | undefined;
36
36
  }>, {
37
37
  apiEndpoint: string;
@@ -6,7 +6,14 @@ const props = defineProps({
6
6
  redirectTo: { type: String, required: false, default: "/login" },
7
7
  linkText: { type: String, required: false, default: "Logout" },
8
8
  confirmMessage: { type: String, required: false, default: "Are you sure you want to logout?" },
9
- class: { type: String, required: false }
9
+ class: { type: String, required: false },
10
+ style: { type: [String, Object], required: false },
11
+ linkClass: { type: String, required: false },
12
+ linkStyle: { type: [String, Object], required: false },
13
+ errorClass: { type: String, required: false },
14
+ errorStyle: { type: [String, Object], required: false },
15
+ containerClass: { type: String, required: false },
16
+ containerStyle: { type: [String, Object], required: false }
10
17
  });
11
18
  const emit = defineEmits(["success", "click", "error"]);
12
19
  const { logout } = useAuth();
@@ -37,26 +44,33 @@ const handleLogout = async (event) => {
37
44
  </script>
38
45
 
39
46
  <template>
40
- <div>
47
+ <div
48
+ :class="['logout-container', props.containerClass]"
49
+ :style="props.containerStyle"
50
+ >
41
51
  <div
42
52
  v-if="error"
43
- class="error-message"
53
+ :class="['error-message', props.errorClass]"
54
+ :style="props.errorStyle"
44
55
  >
45
56
  {{ error }}
46
57
  </div>
47
58
 
48
59
  <a
49
60
  href="#"
50
- :class="['logout-link', { loading: isLoading }, props.class]"
61
+ :class="['logout-link', { loading: isLoading }, props.linkClass]"
62
+ :style="props.linkStyle"
51
63
  :disabled="isLoading"
52
64
  @click="handleLogout"
53
65
  >
54
- <span v-if="isLoading">Logging out...</span>
55
- <span v-else>{{ linkText }}</span>
66
+ <slot>
67
+ <span v-if="isLoading">Logging out...</span>
68
+ <span v-else>{{ linkText }}</span>
69
+ </slot>
56
70
  </a>
57
71
  </div>
58
72
  </template>
59
73
 
60
74
  <style scoped>
61
- .logout-link{color:#dc3545;cursor:pointer;font-size:.875rem;font-weight:500;text-decoration:none;transition:color .2s}.logout-link:hover:not(.loading){color:#c82333;text-decoration:underline}.logout-link.loading{cursor:not-allowed;opacity:.6;pointer-events:none}.error-message{color:#dc3545;font-size:.875rem;margin-bottom:.5rem}
75
+ .logout-link{background:var(--color-error);border:2px solid transparent;border-color:var(--color-error-dark);border-radius:.5em;color:#fff;display:inline-block;font-weight:500;padding:.5em 1em;text-decoration:none;transition:all .2s}.logout-link:hover{border-color:var(--color-primary)}.error-message{color:var(--color-error);font-size:1rem;margin-bottom:.5em}
62
76
  </style>
@@ -3,18 +3,35 @@ interface LogoutLinkProps {
3
3
  linkText?: string;
4
4
  confirmMessage?: string;
5
5
  class?: string;
6
+ style?: string | Record<string, string | number>;
7
+ linkClass?: string;
8
+ linkStyle?: string | Record<string, string | number>;
9
+ errorClass?: string;
10
+ errorStyle?: string | Record<string, string | number>;
11
+ containerClass?: string;
12
+ containerStyle?: string | Record<string, string | number>;
6
13
  }
7
- declare const _default: import("vue").DefineComponent<LogoutLinkProps, void, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
8
- click: () => any;
9
- error: (error: string) => any;
14
+ declare var __VLS_1: {};
15
+ type __VLS_Slots = {} & {
16
+ default?: (props: typeof __VLS_1) => any;
17
+ };
18
+ declare const __VLS_component: import("vue").DefineComponent<LogoutLinkProps, void, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
10
19
  success: () => any;
20
+ error: (error: string) => any;
21
+ click: () => any;
11
22
  }, string, import("vue").PublicProps, Readonly<LogoutLinkProps> & Readonly<{
12
- onClick?: (() => any) | undefined;
13
- onError?: ((error: string) => any) | undefined;
14
23
  onSuccess?: (() => any) | undefined;
24
+ onError?: ((error: string) => any) | undefined;
25
+ onClick?: (() => any) | undefined;
15
26
  }>, {
16
27
  redirectTo: string;
17
28
  linkText: string;
18
29
  confirmMessage: string;
19
30
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
31
+ declare const _default: __VLS_WithSlots<typeof __VLS_component, __VLS_Slots>;
20
32
  export default _default;
33
+ type __VLS_WithSlots<T, S> = T & {
34
+ new (): {
35
+ $slots: S;
36
+ };
37
+ };
@@ -1,49 +1,60 @@
1
1
  <script setup>
2
2
  import { computed } from "vue";
3
+ import { useRuntimeConfig } from "#imports";
3
4
  const props = defineProps({
4
5
  password: { type: String, required: true },
5
6
  validationResult: { type: [Object, null], required: true },
6
7
  showHints: { type: Boolean, required: false, default: true },
7
8
  showRules: { type: Boolean, required: false, default: true }
8
9
  });
10
+ const { public: { nuxtUsers } } = useRuntimeConfig();
11
+ const moduleOptions = nuxtUsers;
9
12
  const hasErrors = computed(() => (props.validationResult?.errors?.length || 0) > 0);
10
13
  const hasHints = computed(() => (props.validationResult?.hints?.length || 0) > 0);
11
14
  const hasPassword = computed(() => props.password.length > 0);
12
15
  const validationRules = computed(() => {
13
16
  if (!props.password) return [];
14
17
  const rules = [];
15
- const minLength = 8;
16
- const hasMinLength = props.password.length >= minLength;
17
- rules.push({
18
- text: `At least ${minLength} characters`,
19
- passed: hasMinLength
20
- });
21
- const hasUppercase = /[A-Z]/.test(props.password);
22
- rules.push({
23
- text: "Contains uppercase letter (A-Z)",
24
- passed: hasUppercase
25
- });
26
- const hasLowercase = /[a-z]/.test(props.password);
27
- rules.push({
28
- text: "Contains lowercase letter (a-z)",
29
- passed: hasLowercase
30
- });
31
- const hasNumbers = /\d/.test(props.password);
32
- rules.push({
33
- text: "Contains number (0-9)",
34
- passed: hasNumbers
35
- });
36
- const hasSpecialChars = /[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]/.test(props.password);
37
- rules.push({
38
- text: "Contains special character (!@#$%^&*)",
39
- passed: hasSpecialChars
40
- });
18
+ if (moduleOptions.passwordValidation.minLength) {
19
+ const hasMinLength = props.password.length >= moduleOptions.passwordValidation.minLength;
20
+ rules.push({
21
+ text: `At least ${moduleOptions.passwordValidation.minLength} characters`,
22
+ passed: hasMinLength
23
+ });
24
+ }
25
+ if (moduleOptions.passwordValidation.requireUppercase) {
26
+ const hasUppercase = /[A-Z]/.test(props.password);
27
+ rules.push({
28
+ text: "Contains uppercase letter (A-Z)",
29
+ passed: hasUppercase
30
+ });
31
+ }
32
+ if (moduleOptions.passwordValidation.requireLowercase) {
33
+ const hasLowercase = /[a-z]/.test(props.password);
34
+ rules.push({
35
+ text: "Contains lowercase letter (a-z)",
36
+ passed: hasLowercase
37
+ });
38
+ }
39
+ if (moduleOptions.passwordValidation.requireNumbers) {
40
+ const hasNumbers = /\d/.test(props.password);
41
+ rules.push({
42
+ text: "Contains number (0-9)",
43
+ passed: hasNumbers
44
+ });
45
+ }
46
+ if (moduleOptions.passwordValidation.requireSpecialChars) {
47
+ const hasSpecialChars = /[!@#$%^&*()_+\-=[\]{};':"\\|,.<>/?]/.test(props.password);
48
+ rules.push({
49
+ text: "Contains special character (!@#$%^&*)",
50
+ passed: hasSpecialChars
51
+ });
52
+ }
41
53
  return rules;
42
54
  });
43
55
  </script>
44
56
 
45
57
  <template>
46
- <!-- Password strength indicator -->
47
58
  <div
48
59
  v-if="hasPassword"
49
60
  class="password-strength"
@@ -0,0 +1,33 @@
1
+ <script setup>
2
+ import { useAuth } from "../composables/useAuth";
3
+ const { user } = useAuth();
4
+ </script>
5
+
6
+ <template>
7
+ <div>
8
+ <h2>Profile Information</h2>
9
+ <dl
10
+ v-if="user"
11
+ class="profile-info"
12
+ >
13
+ <dt>Name:</dt>
14
+ <dd>{{ user.name }}</dd>
15
+
16
+ <dt>Email:</dt>
17
+ <dd>{{ user.email }}</dd>
18
+
19
+ <dt>Role:</dt>
20
+ <dd>{{ user.role }}</dd>
21
+
22
+ <dt>Registered:</dt>
23
+ <dd>{{ Intl.DateTimeFormat().format(new Date(user.created_at)) }}</dd>
24
+
25
+ <dt>Last updated:</dt>
26
+ <dd>{{ Intl.DateTimeFormat().format(new Date(user.updated_at)) }}</dd>
27
+ </dl>
28
+ </div>
29
+ </template>
30
+
31
+ <style scoped>
32
+ div{background-color:var(--color-bg-primary);border-top-left-radius:.5em;border-top-right-radius:.5em;margin:0 auto;max-width:42em;padding:1em}h2{border-bottom:.1em solid var(--color-border)}dl{display:grid;font-size:1rem;gap:16px;grid-template-columns:1fr 5fr;list-style:none;margin:0;padding:0}
33
+ </style>