ChangePasswordForm
DomainCurrent password + new password + confirm fields with match validation and server-error banner.
<%- include('modules/domain/common/auth/ChangePasswordForm', {
action: '/account/change-password'
}) %>
<%- include('modules/domain/common/auth/ChangePasswordForm', {
action: '/account/change-password',
errors: { confirmPassword: "Passwords don't match." }
}) %>
<%
var _action = locals.action || '#';
var _method = locals.method || 'post';
var _error = locals.error || '';
var _errors = locals.errors || {};
var _formId = locals.formId || ('chgpw-form-' + Math.random().toString(36).substr(2, 9));
%>
<form id="<%= _formId %>" action="<%= _action %>" method="<%= _method %>" novalidate class="space-y-4<%= locals.className ? ' ' + locals.className : '' %>">
<% if (_error) { %>
<div role="alert" class="flex items-start gap-3 rounded-lg border p-3 bg-error-subtle border-error text-error-fg text-sm">
<i class="fa-solid fa-circle-xmark mt-0.5 shrink-0" aria-hidden="true"></i>
<span><%= _error %></span>
</div>
<% } %>
<%- include('../../../ui/Input', {
id: 'current-password',
label: 'Current Password',
type: 'password',
name: 'currentPassword',
required: true,
iconLeft: '<i class="fa-solid fa-lock text-xs" aria-hidden="true"></i>',
error: _errors.currentPassword
}) %>
<%- include('../../../ui/Input', {
id: 'new-password',
label: 'New Password',
type: 'password',
name: 'newPassword',
required: true,
iconLeft: '<i class="fa-solid fa-lock text-xs" aria-hidden="true"></i>',
error: _errors.newPassword
}) %>
<%- include('../../../ui/Input', {
id: 'confirm-password',
label: 'Confirm New Password',
type: 'password',
name: 'confirmPassword',
required: true,
iconLeft: '<i class="fa-solid fa-lock text-xs" aria-hidden="true"></i>',
error: _errors.confirmPassword
}) %>
<%- include('../../../ui/Button', {
type: 'submit',
fullWidth: true,
children: 'Update Password'
}) %>
</form>
<script>
(function () {
var form = document.getElementById(<%- include('../../../ui/_script-json', { value: String(_formId) }) %>);
if (!form || form.__kuiChangePwBound) return;
form.__kuiChangePwBound = true;
function setError(name, idBase, message) {
var input = form.querySelector('[name="' + name + '"]');
if (!input) return;
var wrapper = input.closest('.space-y-1');
if (!wrapper) return;
var msgArea = wrapper.querySelector('.flex.items-center.justify-between .flex-1');
if (!msgArea) return;
var existing = msgArea.querySelector('[data-kui-client-error]');
if (message) {
input.setAttribute('aria-invalid', 'true');
var errId = idBase + '-error';
var desc = input.getAttribute('aria-describedby') || '';
if (desc.indexOf(errId) === -1) input.setAttribute('aria-describedby', (desc + ' ' + errId).trim());
input.classList.remove('border-border', 'bg-surface-base');
input.classList.add('border-error', 'ring-1', 'ring-error', 'bg-error-subtle');
if (existing) { existing.textContent = message; }
else {
var p = document.createElement('p');
p.id = errId; p.setAttribute('data-kui-client-error', '1');
p.setAttribute('role', 'alert'); p.className = 'text-xs text-error'; p.textContent = message;
msgArea.appendChild(p);
}
} else if (existing) {
input.setAttribute('aria-invalid', 'false');
input.classList.remove('border-error', 'ring-1', 'ring-error', 'bg-error-subtle');
input.classList.add('border-border', 'bg-surface-base');
existing.remove();
}
}
form.addEventListener('submit', function (ev) {
var cur = (form.querySelector('[name="currentPassword"]') || {}).value || '';
var nw = (form.querySelector('[name="newPassword"]') || {}).value || '';
var cf = (form.querySelector('[name="confirmPassword"]') || {}).value || '';
var hasError = false;
if (!cur) { setError('currentPassword', 'current-password', 'Current password is required.'); hasError = true; }
else { setError('currentPassword', 'current-password', null); }
if (!nw) { setError('newPassword', 'new-password', 'New password is required.'); hasError = true; }
else if (nw.length < 8) { setError('newPassword', 'new-password', 'Password must be at least 8 characters.'); hasError = true; }
else { setError('newPassword', 'new-password', null); }
if (!cf) { setError('confirmPassword', 'confirm-password', 'Please confirm your new password.'); hasError = true; }
else if (nw !== cf) { setError('confirmPassword', 'confirm-password', "Passwords don't match."); hasError = true; }
else { setError('confirmPassword', 'confirm-password', null); }
if (hasError) ev.preventDefault();
});
})();
</script>