@adminforth/email-invite 1.3.1 → 2.0.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/build.log CHANGED
@@ -4,10 +4,8 @@
4
4
 
5
5
  sending incremental file list
6
6
  custom/
7
- custom/AuthCard.vue
8
- custom/PasswordField.vue
9
7
  custom/SetPassword.vue
10
8
  custom/tsconfig.json
11
9
 
12
- sent 13,216 bytes received 96 bytes 26,624.00 bytes/sec
13
- total size is 12,862 speedup is 0.97
10
+ sent 10,444 bytes received 58 bytes 21,004.00 bytes/sec
11
+ total size is 10,232 speedup is 0.97
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div class="relative flex items-center justify-center min-h-screen bg-gray-100 dark:bg-gray-800 relative w-screen h-screen"
2
+ <div class="relative flex items-center justify-center min-h-screen bg-lightHtml dark:bg-darkHtml w-screen h-screen"
3
3
  :style="coreStore.config?.loginBackgroundImage && backgroundPosition === 'over' ? {
4
4
  'background-image': 'url(' + loadFile(coreStore.config?.loginBackgroundImage) + ')',
5
5
  'background-size': 'cover',
@@ -26,55 +26,67 @@
26
26
  class="af-set-password-modal overflow-y-auto flex flex-grow
27
27
  overflow-x-hidden z-50 min-w-[350px] justify-center items-center md:inset-0 h-[calc(100%-1rem)] max-h-full">
28
28
  <div class="relative flex w-full max-h-full justify-center p-4">
29
- <AuthCard
30
- class="af-login-popup"
29
+ <DialogPanel
30
+ class="af-login-popup af-auth-card"
31
31
  :title="header.title"
32
32
  :subtitle="header.subtitle"
33
33
  :error="tokenValid && !passwordSet ? validationError || error : null"
34
+ :classes="AUTH_CARD_CLASSES"
34
35
  >
35
- <template #icon><IconUserAddOutline class="w-6 h-6" /></template>
36
+ <template #icon><IconUserPlus /></template>
36
37
 
37
38
  <div v-if="loading" class="flex justify-center py-6">
38
39
  <Spinner class="w-8 h-8" />
39
40
  </div>
40
41
 
41
- <p v-else-if="!tokenValid" role="alert" class="af-alert-error flex items-start gap-2.5 rounded-lg border border-red-200 bg-red-50 px-3.5 py-3 text-sm leading-5 text-red-800 dark:border-red-500/30 dark:bg-red-500/10 dark:text-red-300">
42
- <IconExclamationCircleOutline class="w-5 h-5 shrink-0" />
42
+ <p v-else-if="!tokenValid" role="alert" class="af-alert-error flex items-start gap-2.5 rounded-lg border border-lightDangerBorder bg-lightDangerSoft px-3.5 py-3 text-sm leading-5 text-lightDanger dark:border-darkDangerBorder dark:bg-darkDangerSoft dark:text-darkDanger">
43
+ <IconCircleAlert class="w-5 h-5 shrink-0" />
43
44
  {{ $t('This invitation link is invalid or has expired. Ask your administrator to send a new one.') }}
44
45
  </p>
45
46
 
46
47
  <template v-else-if="passwordSet">
47
- <p role="status" class="af-alert-success flex items-start gap-2.5 rounded-lg border border-green-200 bg-green-50 px-3.5 py-3 text-sm leading-5 text-green-800 dark:border-green-500/30 dark:bg-green-500/10 dark:text-green-300">
48
- <IconCheckCircleOutline class="w-5 h-5 shrink-0" />
48
+ <p role="status" class="af-alert-success flex items-start gap-2.5 rounded-lg border border-lightSuccessBorder bg-lightSuccessSoft px-3.5 py-3 text-sm leading-5 text-lightSuccess dark:border-darkSuccessBorder dark:bg-darkSuccessSoft dark:text-darkSuccess">
49
+ <IconCircleCheck class="w-5 h-5 shrink-0" />
49
50
  {{ isUserLoggedIn ? $t('Password set successfully to your new account.') : $t('Password set successfully! Log in with your email and the new password.') }}
50
51
  </p>
51
- <p v-if="isUserLoggedIn" class="text-sm text-gray-700 dark:text-gray-300">
52
+ <p v-if="isUserLoggedIn" class="text-sm text-lightLoginViewTextColor dark:text-darkLoginViewTextColor">
52
53
  {{ $t('You are still logged in to another account. Stay in it, or log out to log in to the new one.') }}
53
54
  </p>
54
55
  </template>
55
56
 
56
57
  <template v-else>
57
- <PasswordField
58
- v-model="password"
59
- id="password"
60
- :label="$t('Password')"
61
- :placeholder="$t('Enter your password')"
62
- :unmasked="unmasked"
63
- :invalid="Boolean(validationError)"
64
- @toggle="unmasked = !unmasked"
65
- @keydown.enter="passwordConfirmationInput!.focus()"
66
- />
67
- <PasswordField
68
- ref="passwordConfirmationInput"
69
- v-model="passwordConfirmation"
70
- id="password_confirmation"
71
- :label="$t('Confirm password')"
72
- :placeholder="$t('Confirm your password')"
73
- :unmasked="unmasked"
74
- :invalid="Boolean(validationError)"
75
- @toggle="unmasked = !unmasked"
76
- @keydown.enter="setPassword"
77
- />
58
+ <!-- enter is handled on the wrapper: Input spreads attrs on both its root and the input -->
59
+ <div class="af-password-field" @keydown.enter="passwordConfirmationInput!.focus()">
60
+ <label for="password" class="block mb-2 text-sm font-medium text-lightLoginViewTextColor dark:text-darkLoginViewTextColor">{{ $t('Password') }}</label>
61
+ <Input
62
+ v-model="password"
63
+ id="password"
64
+ name="password"
65
+ type="password"
66
+ passwordToggle
67
+ autocomplete="new-password"
68
+ :placeholder="$t('Enter your password')"
69
+ :invalid="Boolean(validationError)"
70
+ fullWidth
71
+ required
72
+ />
73
+ </div>
74
+ <div class="af-password-field" @keydown.enter="setPassword">
75
+ <label for="password_confirmation" class="block mb-2 text-sm font-medium text-lightLoginViewTextColor dark:text-darkLoginViewTextColor">{{ $t('Confirm password') }}</label>
76
+ <Input
77
+ ref="passwordConfirmationInput"
78
+ v-model="passwordConfirmation"
79
+ id="password_confirmation"
80
+ name="password_confirmation"
81
+ type="password"
82
+ passwordToggle
83
+ autocomplete="new-password"
84
+ :placeholder="$t('Confirm your password')"
85
+ :invalid="Boolean(validationError)"
86
+ fullWidth
87
+ required
88
+ />
89
+ </div>
78
90
  </template>
79
91
 
80
92
  <template v-if="!loading" #footer>
@@ -94,7 +106,7 @@
94
106
  {{ $t('Set password') }}
95
107
  </Button>
96
108
  </template>
97
- </AuthCard>
109
+ </DialogPanel>
98
110
  </div>
99
111
  </div>
100
112
 
@@ -108,10 +120,8 @@ import { useCoreStore } from '@/stores/core';
108
120
  import { useUserStore } from '@/stores/user';
109
121
  import { callAdminForthApi, loadFile, applyRegexValidation } from '@/utils';
110
122
  import { useRoute, useRouter } from 'vue-router';
111
- import { IconCheckCircleOutline, IconExclamationCircleOutline, IconUserAddOutline } from '@iconify-prerendered/vue-flowbite';
112
- import { Button, LinkButton, Spinner } from '@/afcl';
113
- import AuthCard from './AuthCard.vue';
114
- import PasswordField from './PasswordField.vue';
123
+ import { IconCircleAlert, IconCircleCheck, IconUserPlus } from '@iconify-prerendered/vue-lucide';
124
+ import { Button, DialogPanel, Input, LinkButton, Spinner } from '@/afcl';
115
125
 
116
126
  const { t } = useI18n();
117
127
 
@@ -123,10 +133,20 @@ const passwordSet = ref(false);
123
133
  const userStore = useUserStore();
124
134
  const coreStore = useCoreStore();
125
135
 
126
- const passwordConfirmationInput = ref<InstanceType<typeof PasswordField> | null>(null);
136
+ // class names apps restyle the card by, kept from the former AuthCard
137
+ const AUTH_CARD_CLASSES = {
138
+ header: 'af-auth-card-header',
139
+ icon: 'af-auth-card-icon',
140
+ titleWrap: 'af-auth-card-title-wrap',
141
+ title: 'af-auth-card-title',
142
+ subtitle: 'af-auth-card-subtitle',
143
+ body: 'af-auth-card-body',
144
+ error: 'af-auth-card-error',
145
+ footer: 'af-auth-card-footer',
146
+ };
147
+ const passwordConfirmationInput = ref<InstanceType<typeof Input> | null>(null);
127
148
  const password = ref("");
128
149
  const passwordConfirmation = ref("");
129
- const unmasked = ref(false);
130
150
 
131
151
  const route = useRoute();
132
152
  const router = useRouter();
@@ -192,7 +212,7 @@ const validationError = computed(() => {
192
212
 
193
213
  onMounted(async () => {
194
214
  loading.value = true;
195
- await coreStore.getPublicConfig();
215
+ await coreStore.fetchConfig({ redirectToLoginIfNotLoggedIn: false });
196
216
 
197
217
  // Check if token is present and valid
198
218
  const token = route.query.token;
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div class="relative flex items-center justify-center min-h-screen bg-gray-100 dark:bg-gray-800 relative w-screen h-screen"
2
+ <div class="relative flex items-center justify-center min-h-screen bg-lightHtml dark:bg-darkHtml w-screen h-screen"
3
3
  :style="coreStore.config?.loginBackgroundImage && backgroundPosition === 'over' ? {
4
4
  'background-image': 'url(' + loadFile(coreStore.config?.loginBackgroundImage) + ')',
5
5
  'background-size': 'cover',
@@ -26,55 +26,67 @@
26
26
  class="af-set-password-modal overflow-y-auto flex flex-grow
27
27
  overflow-x-hidden z-50 min-w-[350px] justify-center items-center md:inset-0 h-[calc(100%-1rem)] max-h-full">
28
28
  <div class="relative flex w-full max-h-full justify-center p-4">
29
- <AuthCard
30
- class="af-login-popup"
29
+ <DialogPanel
30
+ class="af-login-popup af-auth-card"
31
31
  :title="header.title"
32
32
  :subtitle="header.subtitle"
33
33
  :error="tokenValid && !passwordSet ? validationError || error : null"
34
+ :classes="AUTH_CARD_CLASSES"
34
35
  >
35
- <template #icon><IconUserAddOutline class="w-6 h-6" /></template>
36
+ <template #icon><IconUserPlus /></template>
36
37
 
37
38
  <div v-if="loading" class="flex justify-center py-6">
38
39
  <Spinner class="w-8 h-8" />
39
40
  </div>
40
41
 
41
- <p v-else-if="!tokenValid" role="alert" class="af-alert-error flex items-start gap-2.5 rounded-lg border border-red-200 bg-red-50 px-3.5 py-3 text-sm leading-5 text-red-800 dark:border-red-500/30 dark:bg-red-500/10 dark:text-red-300">
42
- <IconExclamationCircleOutline class="w-5 h-5 shrink-0" />
42
+ <p v-else-if="!tokenValid" role="alert" class="af-alert-error flex items-start gap-2.5 rounded-lg border border-lightDangerBorder bg-lightDangerSoft px-3.5 py-3 text-sm leading-5 text-lightDanger dark:border-darkDangerBorder dark:bg-darkDangerSoft dark:text-darkDanger">
43
+ <IconCircleAlert class="w-5 h-5 shrink-0" />
43
44
  {{ $t('This invitation link is invalid or has expired. Ask your administrator to send a new one.') }}
44
45
  </p>
45
46
 
46
47
  <template v-else-if="passwordSet">
47
- <p role="status" class="af-alert-success flex items-start gap-2.5 rounded-lg border border-green-200 bg-green-50 px-3.5 py-3 text-sm leading-5 text-green-800 dark:border-green-500/30 dark:bg-green-500/10 dark:text-green-300">
48
- <IconCheckCircleOutline class="w-5 h-5 shrink-0" />
48
+ <p role="status" class="af-alert-success flex items-start gap-2.5 rounded-lg border border-lightSuccessBorder bg-lightSuccessSoft px-3.5 py-3 text-sm leading-5 text-lightSuccess dark:border-darkSuccessBorder dark:bg-darkSuccessSoft dark:text-darkSuccess">
49
+ <IconCircleCheck class="w-5 h-5 shrink-0" />
49
50
  {{ isUserLoggedIn ? $t('Password set successfully to your new account.') : $t('Password set successfully! Log in with your email and the new password.') }}
50
51
  </p>
51
- <p v-if="isUserLoggedIn" class="text-sm text-gray-700 dark:text-gray-300">
52
+ <p v-if="isUserLoggedIn" class="text-sm text-lightLoginViewTextColor dark:text-darkLoginViewTextColor">
52
53
  {{ $t('You are still logged in to another account. Stay in it, or log out to log in to the new one.') }}
53
54
  </p>
54
55
  </template>
55
56
 
56
57
  <template v-else>
57
- <PasswordField
58
- v-model="password"
59
- id="password"
60
- :label="$t('Password')"
61
- :placeholder="$t('Enter your password')"
62
- :unmasked="unmasked"
63
- :invalid="Boolean(validationError)"
64
- @toggle="unmasked = !unmasked"
65
- @keydown.enter="passwordConfirmationInput!.focus()"
66
- />
67
- <PasswordField
68
- ref="passwordConfirmationInput"
69
- v-model="passwordConfirmation"
70
- id="password_confirmation"
71
- :label="$t('Confirm password')"
72
- :placeholder="$t('Confirm your password')"
73
- :unmasked="unmasked"
74
- :invalid="Boolean(validationError)"
75
- @toggle="unmasked = !unmasked"
76
- @keydown.enter="setPassword"
77
- />
58
+ <!-- enter is handled on the wrapper: Input spreads attrs on both its root and the input -->
59
+ <div class="af-password-field" @keydown.enter="passwordConfirmationInput!.focus()">
60
+ <label for="password" class="block mb-2 text-sm font-medium text-lightLoginViewTextColor dark:text-darkLoginViewTextColor">{{ $t('Password') }}</label>
61
+ <Input
62
+ v-model="password"
63
+ id="password"
64
+ name="password"
65
+ type="password"
66
+ passwordToggle
67
+ autocomplete="new-password"
68
+ :placeholder="$t('Enter your password')"
69
+ :invalid="Boolean(validationError)"
70
+ fullWidth
71
+ required
72
+ />
73
+ </div>
74
+ <div class="af-password-field" @keydown.enter="setPassword">
75
+ <label for="password_confirmation" class="block mb-2 text-sm font-medium text-lightLoginViewTextColor dark:text-darkLoginViewTextColor">{{ $t('Confirm password') }}</label>
76
+ <Input
77
+ ref="passwordConfirmationInput"
78
+ v-model="passwordConfirmation"
79
+ id="password_confirmation"
80
+ name="password_confirmation"
81
+ type="password"
82
+ passwordToggle
83
+ autocomplete="new-password"
84
+ :placeholder="$t('Confirm your password')"
85
+ :invalid="Boolean(validationError)"
86
+ fullWidth
87
+ required
88
+ />
89
+ </div>
78
90
  </template>
79
91
 
80
92
  <template v-if="!loading" #footer>
@@ -94,7 +106,7 @@
94
106
  {{ $t('Set password') }}
95
107
  </Button>
96
108
  </template>
97
- </AuthCard>
109
+ </DialogPanel>
98
110
  </div>
99
111
  </div>
100
112
 
@@ -108,10 +120,8 @@ import { useCoreStore } from '@/stores/core';
108
120
  import { useUserStore } from '@/stores/user';
109
121
  import { callAdminForthApi, loadFile, applyRegexValidation } from '@/utils';
110
122
  import { useRoute, useRouter } from 'vue-router';
111
- import { IconCheckCircleOutline, IconExclamationCircleOutline, IconUserAddOutline } from '@iconify-prerendered/vue-flowbite';
112
- import { Button, LinkButton, Spinner } from '@/afcl';
113
- import AuthCard from './AuthCard.vue';
114
- import PasswordField from './PasswordField.vue';
123
+ import { IconCircleAlert, IconCircleCheck, IconUserPlus } from '@iconify-prerendered/vue-lucide';
124
+ import { Button, DialogPanel, Input, LinkButton, Spinner } from '@/afcl';
115
125
 
116
126
  const { t } = useI18n();
117
127
 
@@ -123,10 +133,20 @@ const passwordSet = ref(false);
123
133
  const userStore = useUserStore();
124
134
  const coreStore = useCoreStore();
125
135
 
126
- const passwordConfirmationInput = ref<InstanceType<typeof PasswordField> | null>(null);
136
+ // class names apps restyle the card by, kept from the former AuthCard
137
+ const AUTH_CARD_CLASSES = {
138
+ header: 'af-auth-card-header',
139
+ icon: 'af-auth-card-icon',
140
+ titleWrap: 'af-auth-card-title-wrap',
141
+ title: 'af-auth-card-title',
142
+ subtitle: 'af-auth-card-subtitle',
143
+ body: 'af-auth-card-body',
144
+ error: 'af-auth-card-error',
145
+ footer: 'af-auth-card-footer',
146
+ };
147
+ const passwordConfirmationInput = ref<InstanceType<typeof Input> | null>(null);
127
148
  const password = ref("");
128
149
  const passwordConfirmation = ref("");
129
- const unmasked = ref(false);
130
150
 
131
151
  const route = useRoute();
132
152
  const router = useRouter();
@@ -192,7 +212,7 @@ const validationError = computed(() => {
192
212
 
193
213
  onMounted(async () => {
194
214
  loading.value = true;
195
- await coreStore.getPublicConfig();
215
+ await coreStore.fetchConfig({ redirectToLoginIfNotLoggedIn: false });
196
216
 
197
217
  // Check if token is present and valid
198
218
  const token = route.query.token;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adminforth/email-invite",
3
- "version": "1.3.1",
3
+ "version": "2.0.1",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
@@ -25,13 +25,13 @@
25
25
  },
26
26
  "devDependencies": {
27
27
  "@types/node": "^22.10.7",
28
- "adminforth": "^3.8.2",
28
+ "adminforth": "^4.7.3",
29
29
  "semantic-release": "^24.2.1",
30
30
  "semantic-release-slack-bot": "^4.0.2",
31
31
  "typescript": "^5.7.3"
32
32
  },
33
33
  "peerDependencies": {
34
- "adminforth": "^3.8.2"
34
+ "adminforth": "^4.6.0"
35
35
  },
36
36
  "publishConfig": {
37
37
  "access": "public"
@@ -1,37 +0,0 @@
1
- <template>
2
- <div class="af-auth-card relative flex flex-col w-full max-w-md overflow-hidden rounded-xl border border-lightDialogBreakLine bg-lightDialogBackgorund shadow-2xl dark:border-darkDialogBreakLine dark:bg-darkDialogBackgorund">
3
- <div class="af-auth-card-header flex items-start gap-3.5 border-b border-lightDialogBreakLine px-6 py-5 dark:border-darkDialogBreakLine">
4
- <div class="af-auth-card-icon flex w-10 h-10 shrink-0 items-center justify-center rounded-lg bg-lightPrimary/10 text-lightPrimary dark:bg-darkPrimary/20 dark:text-darkPrimary">
5
- <slot name="icon" />
6
- </div>
7
- <div class="af-auth-card-title-wrap flex-1 min-w-0">
8
- <slot name="title">
9
- <p class="af-auth-card-title text-lg leading-6 font-semibold text-lightDialogHeaderText dark:text-darkDialogHeaderText">{{ title }}</p>
10
- <p class="af-auth-card-subtitle mt-0.5 text-sm leading-5 text-gray-500 dark:text-gray-400">{{ subtitle }}</p>
11
- </slot>
12
- </div>
13
- </div>
14
-
15
- <div class="af-auth-card-body flex flex-col gap-4 px-6 pt-5 pb-6">
16
- <slot />
17
- <p v-if="error" role="alert" class="af-auth-card-error -mt-1.5 flex items-center gap-1.5 text-sm text-red-600 dark:text-red-400">
18
- <IconExclamationCircleOutline class="w-4 h-4 shrink-0" />
19
- {{ error }}
20
- </p>
21
- </div>
22
-
23
- <div v-if="$slots.footer" class="af-auth-card-footer flex flex-wrap items-center justify-end gap-x-3 gap-y-2 border-t border-lightDialogBreakLine bg-gray-50 py-3 pl-6 pr-5 dark:border-darkDialogBreakLine dark:bg-gray-800/50">
24
- <slot name="footer" />
25
- </div>
26
- </div>
27
- </template>
28
-
29
- <script setup lang="ts">
30
- import { IconExclamationCircleOutline } from '@iconify-prerendered/vue-flowbite';
31
-
32
- defineProps<{
33
- title: string;
34
- subtitle: string;
35
- error?: string | null;
36
- }>();
37
- </script>
@@ -1,55 +0,0 @@
1
- <template>
2
- <div class="af-password-field">
3
- <label :for="id" class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300">{{ label }}</label>
4
- <div class="relative">
5
- <input
6
- ref="input"
7
- v-model="model"
8
- :id="id"
9
- :name="id"
10
- :type="unmasked ? 'text' : 'password'"
11
- :placeholder="placeholder"
12
- autocomplete="new-password"
13
- required
14
- class="h-11 w-full rounded-lg border-2 bg-gray-50 pl-3.5 pr-12 text-base text-gray-900 placeholder-gray-400 outline-none focus:ring-4 dark:bg-gray-800 dark:text-white dark:placeholder-gray-500"
15
- :class="invalid
16
- ? 'border-red-500 focus:border-red-500 focus:ring-red-500/25 dark:border-red-400 dark:focus:border-red-400'
17
- : 'border-gray-300 focus:border-lightPrimary focus:ring-lightPrimary/25 dark:border-gray-600 dark:focus:border-darkPrimary dark:focus:ring-darkPrimary/25'"
18
- />
19
- <button
20
- type="button"
21
- :aria-label="unmasked ? $t('Hide password') : $t('Show password')"
22
- class="absolute right-1 top-1 inline-flex w-9 h-9 items-center justify-center rounded-lg text-gray-400 hover:text-gray-600 dark:hover:text-gray-200"
23
- @click="emit('toggle')"
24
- >
25
- <IconEyeOutline v-if="!unmasked" class="w-5 h-5" />
26
- <IconEyeSlashOutline v-else class="w-5 h-5" />
27
- </button>
28
- </div>
29
- </div>
30
- </template>
31
-
32
- <script setup lang="ts">
33
- import { ref } from 'vue';
34
- import { IconEyeOutline, IconEyeSlashOutline } from '@iconify-prerendered/vue-flowbite';
35
-
36
- const model = defineModel<string>({ required: true });
37
-
38
- defineProps<{
39
- id: string;
40
- label: string;
41
- placeholder: string;
42
- unmasked: boolean;
43
- invalid?: boolean;
44
- }>();
45
-
46
- const emit = defineEmits<{
47
- (e: 'toggle'): void;
48
- }>();
49
-
50
- const input = ref<HTMLInputElement | null>(null);
51
-
52
- defineExpose({
53
- focus: () => input.value!.focus(),
54
- });
55
- </script>
@@ -1,37 +0,0 @@
1
- <template>
2
- <div class="af-auth-card relative flex flex-col w-full max-w-md overflow-hidden rounded-xl border border-lightDialogBreakLine bg-lightDialogBackgorund shadow-2xl dark:border-darkDialogBreakLine dark:bg-darkDialogBackgorund">
3
- <div class="af-auth-card-header flex items-start gap-3.5 border-b border-lightDialogBreakLine px-6 py-5 dark:border-darkDialogBreakLine">
4
- <div class="af-auth-card-icon flex w-10 h-10 shrink-0 items-center justify-center rounded-lg bg-lightPrimary/10 text-lightPrimary dark:bg-darkPrimary/20 dark:text-darkPrimary">
5
- <slot name="icon" />
6
- </div>
7
- <div class="af-auth-card-title-wrap flex-1 min-w-0">
8
- <slot name="title">
9
- <p class="af-auth-card-title text-lg leading-6 font-semibold text-lightDialogHeaderText dark:text-darkDialogHeaderText">{{ title }}</p>
10
- <p class="af-auth-card-subtitle mt-0.5 text-sm leading-5 text-gray-500 dark:text-gray-400">{{ subtitle }}</p>
11
- </slot>
12
- </div>
13
- </div>
14
-
15
- <div class="af-auth-card-body flex flex-col gap-4 px-6 pt-5 pb-6">
16
- <slot />
17
- <p v-if="error" role="alert" class="af-auth-card-error -mt-1.5 flex items-center gap-1.5 text-sm text-red-600 dark:text-red-400">
18
- <IconExclamationCircleOutline class="w-4 h-4 shrink-0" />
19
- {{ error }}
20
- </p>
21
- </div>
22
-
23
- <div v-if="$slots.footer" class="af-auth-card-footer flex flex-wrap items-center justify-end gap-x-3 gap-y-2 border-t border-lightDialogBreakLine bg-gray-50 py-3 pl-6 pr-5 dark:border-darkDialogBreakLine dark:bg-gray-800/50">
24
- <slot name="footer" />
25
- </div>
26
- </div>
27
- </template>
28
-
29
- <script setup lang="ts">
30
- import { IconExclamationCircleOutline } from '@iconify-prerendered/vue-flowbite';
31
-
32
- defineProps<{
33
- title: string;
34
- subtitle: string;
35
- error?: string | null;
36
- }>();
37
- </script>
@@ -1,55 +0,0 @@
1
- <template>
2
- <div class="af-password-field">
3
- <label :for="id" class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300">{{ label }}</label>
4
- <div class="relative">
5
- <input
6
- ref="input"
7
- v-model="model"
8
- :id="id"
9
- :name="id"
10
- :type="unmasked ? 'text' : 'password'"
11
- :placeholder="placeholder"
12
- autocomplete="new-password"
13
- required
14
- class="h-11 w-full rounded-lg border-2 bg-gray-50 pl-3.5 pr-12 text-base text-gray-900 placeholder-gray-400 outline-none focus:ring-4 dark:bg-gray-800 dark:text-white dark:placeholder-gray-500"
15
- :class="invalid
16
- ? 'border-red-500 focus:border-red-500 focus:ring-red-500/25 dark:border-red-400 dark:focus:border-red-400'
17
- : 'border-gray-300 focus:border-lightPrimary focus:ring-lightPrimary/25 dark:border-gray-600 dark:focus:border-darkPrimary dark:focus:ring-darkPrimary/25'"
18
- />
19
- <button
20
- type="button"
21
- :aria-label="unmasked ? $t('Hide password') : $t('Show password')"
22
- class="absolute right-1 top-1 inline-flex w-9 h-9 items-center justify-center rounded-lg text-gray-400 hover:text-gray-600 dark:hover:text-gray-200"
23
- @click="emit('toggle')"
24
- >
25
- <IconEyeOutline v-if="!unmasked" class="w-5 h-5" />
26
- <IconEyeSlashOutline v-else class="w-5 h-5" />
27
- </button>
28
- </div>
29
- </div>
30
- </template>
31
-
32
- <script setup lang="ts">
33
- import { ref } from 'vue';
34
- import { IconEyeOutline, IconEyeSlashOutline } from '@iconify-prerendered/vue-flowbite';
35
-
36
- const model = defineModel<string>({ required: true });
37
-
38
- defineProps<{
39
- id: string;
40
- label: string;
41
- placeholder: string;
42
- unmasked: boolean;
43
- invalid?: boolean;
44
- }>();
45
-
46
- const emit = defineEmits<{
47
- (e: 'toggle'): void;
48
- }>();
49
-
50
- const input = ref<HTMLInputElement | null>(null);
51
-
52
- defineExpose({
53
- focus: () => input.value!.focus(),
54
- });
55
- </script>