hotfix(admin/user): implement password reset and update user password

- Implemented password reset functionality for admin users.
- Updated the user edit and create forms to use a password meter component for password strength validation.
- Modified the `AdminuserController` to handle the new password field and update user passwords.
- Updated the `createUserValidator` and `updateUserValidator` to validate the new password field.
- Updated the password field to `new_password` in the `Edit.vue` and `Create.vue` components.
- Added `showRequiredMessage` prop to `SimplePasswordMeter` component.
- Added conditional rendering for password strength bar in `SimplePasswordMeter` component.
- Added `fieldLabel` prop to `SimplePasswordMeter` component.
- Updated form submission to handle errors and reset password field.
This commit is contained in:
Kaimbacher 2025-03-19 15:52:37 +01:00
parent 9f5d35f7ba
commit 70f016422c
5 changed files with 77 additions and 25 deletions

View file

@ -6,10 +6,29 @@ import FormField from '@/Components/FormField.vue';
import FormControl from '@/Components/FormControl.vue';
// Define props
const props = defineProps<{
modelValue: string;
errors: Partial<Record<"new_password" | "old_password" | "confirm_password", string>>;
}>();
// const props = defineProps<{
// modelValue: string,
// errors: Partial<Record<"new_password" | "old_password" | "confirm_password", string>>,
// showRequiredMessage: boolean,
// }>();
const props = defineProps({
modelValue: {
type: String,
},
errors: {
type: Object,
default: () => ({} as Partial<Record<"new_password" | "old_password" | "confirm_password", string>>),
},
showRequiredMessage: {
type: Boolean,
default:true,
},
fieldLabel: {
type: String,
default: 'New password',
}
});
const emit = defineEmits(['update:modelValue', 'score']);
@ -61,8 +80,8 @@ const passwordMetrics = computed<PasswordMetrics>(() => {
<template>
<!-- Password input Form -->
<FormField label="New password" help="Required. New password" :class="{'text-red-400': errors.new_password }">
<FormControl v-model="localPassword" :icon="mdiFormTextboxPassword" name="new_password" type="password" required
<FormField :label="fieldLabel" :help="showRequiredMessage ? 'Required. New password' : ''" :class="{'text-red-400': errors.new_password }">
<FormControl v-model="localPassword" :icon="mdiFormTextboxPassword" name="new_password" type="password" :required="showRequiredMessage"
:error="errors.new_password">
<!-- Secure Icon -->
<template #right>
@ -84,10 +103,10 @@ const passwordMetrics = computed<PasswordMetrics>(() => {
<div class="text-gray-700 text-sm">
{{ passwordMetrics.score }} / 6 points max
</div>
</FormField>
</FormField>
<!-- Password Strength Bar -->
<div class="po-password-strength-bar w-full h-2 rounded transition-all duration-200 mb-4"
<div v-if="passwordMetrics.score > 0"class="po-password-strength-bar w-full h-2 rounded transition-all duration-200 mb-4"
:class="passwordMetrics.scoreLabel" :style="{ width: `${(passwordMetrics.score / 6) * 100}%` }"
role="progressbar" :aria-valuenow="passwordMetrics.score" aria-valuemin="0" aria-valuemax="6"
:aria-label="`Password strength: ${passwordMetrics.scoreLabel || 'unknown'}`">

View file

@ -41,13 +41,28 @@ const form = useForm({
first_name: '',
last_name: '',
email: '',
password: '',
new_password: '',
password_confirmation: '',
roles: [],
});
const submit = async () => {
await router.post(stardust.route('settings.user.store'), form);
// await router.post(stardust.route('settings.user.store'), form);
await form.post(stardust.route('settings.user.store'), {
preserveScroll: true,
onSuccess: () => {
form.reset();
},
onError: () => {
if (form.errors.new_password) {
form.reset('new_password');
enabled.value = false;
// newPasswordInput.value.focus();
// newPasswordInput.value?.focus();
}
},
});
};
</script>
@ -109,7 +124,7 @@ const submit = async () => {
</FormControl>
</FormField>
<password-meter :password="form.password" @score="handleScore" /> -->
<PasswordMeter v-model:password="form.password" :errors="form.errors" @score="handleScore" />
<PasswordMeter v-model="form.new_password" :errors="form.errors" @score="handleScore" />
<FormField label="Password Confirmation" :class="{ 'text-red-400': errors.password_confirmation }">
<FormControl

View file

@ -42,14 +42,29 @@ const form = useForm({
first_name: props.user.first_name,
last_name: props.user.last_name,
email: props.user.email,
password: '',
new_password: '',
password_confirmation: '',
roles: props.userHasRoles, // fill actual user roles from db
});
const submit = async () => {
// await Inertia.post(stardust.route('user.store'), form);
await router.put(stardust.route('settings.user.update', [props.user.id]), form);
// await router.put(stardust.route('settings.user.update', [props.user.id]), form);
await form.put(stardust.route('settings.user.update', [props.user.id]), {
preserveScroll: true,
onSuccess: () => {
form.reset();
},
onError: () => {
if (form.errors.new_password) {
form.reset('new_password');
enabled.value = false;
// newPasswordInput.value.focus();
// newPasswordInput.value?.focus();
}
},
});
};
const handleScore = (score: number) => {
if (score >= 4){
@ -108,15 +123,16 @@ const handleScore = (score: number) => {
</FormControl>
</FormField>
<FormField label="Password" :class="{ 'text-red-400': errors.password }">
<!-- <FormField label="Password" :class="{ 'text-red-400': errors.password }">
<FormControl v-model="form.password" type="password" placeholder="Enter Password" :errors="errors.password">
<div class="text-red-400 text-sm" v-if="errors.password">
{{ errors.password }}
</div>
</FormControl>
</FormField>
</FormField> -->
<PasswordMeter field-label="Reset User Password" :show-required-message="false" ref="newPasswordInput" v-model="form.new_password" :errors="form.errors" @score="handleScore" />
<PasswordMeter v-model:password="form.password" :errors="form.errors" @score="handleScore" />
<FormField label="Password Confirmation" :class="{ 'text-red-400': errors.password_confirmation }">
<FormControl
@ -151,7 +167,7 @@ const handleScore = (score: number) => {
color="info"
label="Submit"
:class="{ 'opacity-25': form.processing }"
:disabled="form.processing == true|| (form.password != '' && enabled == false)"
:disabled="form.processing == true|| (form.new_password != '' && enabled == false)"
/>
</BaseButtons>
</template>