diff --git a/app/views/install/installer.phtml b/app/views/install/installer.phtml index cfc8ceb934..1c003b4369 100644 --- a/app/views/install/installer.phtml +++ b/app/views/install/installer.phtml @@ -208,7 +208,10 @@ $installerVersion = @filemtime(__DIR__ . '/installer/js/installer.js') ?: time() diff --git a/app/views/install/installer/css/styles.css b/app/views/install/installer/css/styles.css index 86955395f8..9fa320b3b4 100644 --- a/app/views/install/installer/css/styles.css +++ b/app/views/install/installer/css/styles.css @@ -1222,23 +1222,47 @@ body { display: flex; align-items: flex-start; gap: var(--space-3); - margin-top: var(--space-2); color: var(--fgcolor-error); max-height: 0; opacity: 0; overflow: hidden; - padding-inline-start: var(--space-2); + /*padding-inline-start: var(--space-2);*/ transition: max-height var(--duration-extended) var(--ease-standard), opacity var(--duration-extended) var(--ease-standard); } -.field-error-text { - line-height: 1.4; +.field-error-icon { + display: flex; + flex-shrink: 0; + align-items: center; +} + +.field-error-icon svg { + width: 1rem; + height: 1rem; } .field-error.is-visible { - max-height: 64px; opacity: 1; + max-height: 64px; +} + +.field-helper { + display: flex; + align-items: flex-start; + gap: var(--space-3); + color: var(--fgcolor-neutral-secondary); +} + +.field-helper-icon { + display: flex; + flex-shrink: 0; + align-items: center; +} + +.field-helper-icon svg { + width: 1rem; + height: 1rem; } .input-field { diff --git a/app/views/install/installer/js/modules/ui.js b/app/views/install/installer/js/modules/ui.js index 9db7488efb..19aa3b63a0 100644 --- a/app/views/install/installer/js/modules/ui.js +++ b/app/views/install/installer/js/modules/ui.js @@ -4,10 +4,15 @@ const clearFieldErrors = (root) => { if (!root) return; - root.querySelectorAll('.field-error').forEach((node) => node.remove()); + root.querySelectorAll('.field-error').forEach((node) => { + node.classList.remove('is-visible'); + }); root.querySelectorAll('.input-field.is-error, .input-action.is-error').forEach((node) => { node.classList.remove('is-error'); }); + root.querySelectorAll('.field-helper').forEach((helper) => { + helper.style.display = ''; + }); }; const setFieldError = (input, message) => { @@ -18,35 +23,39 @@ let errorText = error?.querySelector('.field-error-text'); const hasSameMessage = Boolean(errorText && errorText.textContent === message); const alreadyVisible = Boolean(error && error.classList.contains('is-visible')); + + if (hasSameMessage && alreadyVisible) { + return; + } + if (!error) { const template = document.getElementById('field-error-template'); if (template && template.content) { const fragment = template.content.cloneNode(true); error = fragment.querySelector('.field-error'); group.appendChild(fragment); - } else { - error = document.createElement('div'); - error.className = 'field-error typography-text-xs-400 text-error'; - const text = document.createElement('span'); - text.className = 'field-error-text'; - error.appendChild(text); - group.appendChild(error); } - errorText = error.querySelector('.field-error-text'); + errorText = error?.querySelector('.field-error-text'); } if (errorText) { errorText.textContent = message; } - if (!hasSameMessage || !alreadyVisible) { + + if (!alreadyVisible) { requestAnimationFrame(() => { error.classList.add('is-visible'); }); } + input.classList.add('is-error'); const actionWrapper = input.closest('.input-action'); if (actionWrapper) { actionWrapper.classList.add('is-error'); } + const helper = group.querySelector('.field-helper'); + if (helper) { + helper.style.display = 'none'; + } }; const bindErrorClear = (input) => { @@ -56,15 +65,16 @@ const error = group?.querySelector('.field-error'); if (error) { error.classList.remove('is-visible'); - setTimeout(() => { - error.remove(); - }, TIMINGS?.errorClear ?? 0); } input.classList.remove('is-error'); const actionWrapper = input.closest('.input-action'); if (actionWrapper) { actionWrapper.classList.remove('is-error'); } + const helper = group?.querySelector('.field-helper'); + if (helper) { + helper.style.display = ''; + } }; input.addEventListener('input', handler); input.addEventListener('change', handler); diff --git a/app/views/install/installer/js/steps.js b/app/views/install/installer/js/steps.js index 66b95e31eb..bbdb79a941 100644 --- a/app/views/install/installer/js/steps.js +++ b/app/views/install/installer/js/steps.js @@ -434,12 +434,12 @@ const password = root?.querySelector('#account-password'); if (!name || !name.value.trim()) { - setFieldError?.(name, 'Please enter a name'); + setFieldError?.(name, 'This field is required'); valid = false; } if (!email || !email.value.trim()) { - setFieldError?.(email, 'Please enter an email address'); + setFieldError?.(email, 'This field is required'); valid = false; } else if (!isValidEmail?.(email.value.trim())) { setFieldError?.(email, 'Please enter a valid email address'); @@ -448,10 +448,10 @@ const passwordValue = password?.value ?? ''; if (!password || !/\S/.test(passwordValue)) { - setFieldError?.(password, 'Please enter a password'); + setFieldError?.(password, 'This field is required'); valid = false; } else if (!isValidPassword?.(passwordValue)) { - setFieldError?.(password, 'Password must be at least 8 characters'); + setFieldError?.(password, 'Password must be at least 8 characters long'); valid = false; } diff --git a/app/views/install/installer/templates/steps/step-3.phtml b/app/views/install/installer/templates/steps/step-3.phtml index 22b2f830ad..7ad151a1fb 100644 --- a/app/views/install/installer/templates/steps/step-3.phtml +++ b/app/views/install/installer/templates/steps/step-3.phtml @@ -49,7 +49,7 @@ $accountNameValue = htmlspecialchars($defaultAccountName, ENT_QUOTES, 'UTF-8'); id="account-password" name="accountPassword" class="input-action-input typography-text-m-400 text-neutral-primary" - placeholder="Password must be at least 8 characters long" + placeholder="Enter password" autocomplete="new-password" >
@@ -63,6 +63,12 @@ $accountNameValue = htmlspecialchars($defaultAccountName, ENT_QUOTES, 'UTF-8');
+
+ + + + Password must be at least 8 characters long +