Wrap first login popup in a form so enter works to change password (#6769)

# Description of Changes

> [!note]
> GitHub absolutely mangles the diff unless you change to ignore
whitespace changes

This page has never had the Enter key bound to the Change Password
button:

<img width="673" height="745" alt="image"
src="https://github.com/user-attachments/assets/7a1b06f0-2945-4270-a795-f799ec556c12"
/>

This PR changes the modal to be properly wrapped in a form so key
commands work correctly on it.
This commit is contained in:
James Brunton
2026-06-22 13:59:28 +00:00
committed by GitHub
parent 72f8705460
commit 9aee85d55e
2 changed files with 167 additions and 157 deletions
@@ -31,7 +31,9 @@ function FirstLoginForm({
const [loading, setLoading] = useState(false);
const [error, setError] = useState("");
const handleSubmit = async () => {
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
// Validation
if (
(!usingDefaultCredentials && !currentPassword) ||
@@ -115,102 +117,104 @@ function FirstLoginForm({
return (
<div className={styles.securitySlideContent}>
<div className={styles.securityCard}>
<Stack gap="md">
<div className={styles.securityAlertRow}>
<LocalIcon
icon="info-rounded"
width={20}
height={20}
style={{ color: "#3B82F6", flexShrink: 0 }}
/>
<span>
{t(
"firstLogin.welcomeMessage",
"For security reasons, you must change your password on your first login.",
)}
</span>
</div>
<form onSubmit={handleSubmit}>
<Stack gap="md">
<div className={styles.securityAlertRow}>
<LocalIcon
icon="info-rounded"
width={20}
height={20}
style={{ color: "#3B82F6", flexShrink: 0 }}
/>
<span>
{t(
"firstLogin.welcomeMessage",
"For security reasons, you must change your password on your first login.",
)}
</span>
</div>
<Text size="sm" fw={500}>
{t("firstLogin.loggedInAs", "Logged in as")}:{" "}
<strong>{username}</strong>
</Text>
<Text size="sm" fw={500}>
{t("firstLogin.loggedInAs", "Logged in as")}:{" "}
<strong>{username}</strong>
</Text>
{error && (
<Alert
icon={
<LocalIcon icon="error-rounded" width="1rem" height="1rem" />
}
color="red"
variant="light"
>
{error}
</Alert>
)}
{error && (
<Alert
icon={
<LocalIcon icon="error-rounded" width="1rem" height="1rem" />
}
color="red"
variant="light"
>
{error}
</Alert>
)}
{/* Only show current password field if not using default credentials */}
{!usingDefaultCredentials && (
<PasswordInput
label={t("firstLogin.currentPassword", "Current Password")}
placeholder={t(
"firstLogin.enterCurrentPassword",
"Enter your current password",
)}
value={currentPassword}
onChange={(e) => setCurrentPassword(e.currentTarget.value)}
required
styles={{
input: { height: 44 },
}}
/>
)}
{/* Only show current password field if not using default credentials */}
{!usingDefaultCredentials && (
<PasswordInput
label={t("firstLogin.currentPassword", "Current Password")}
label={t("firstLogin.newPassword", "New Password")}
placeholder={t(
"firstLogin.enterCurrentPassword",
"Enter your current password",
"firstLogin.enterNewPassword",
"Enter new password (min 8 characters)",
)}
value={currentPassword}
onChange={(e) => setCurrentPassword(e.currentTarget.value)}
value={newPassword}
onChange={(e) => setNewPassword(e.currentTarget.value)}
minLength={8}
required
styles={{
input: { height: 44 },
}}
/>
)}
<PasswordInput
label={t("firstLogin.newPassword", "New Password")}
placeholder={t(
"firstLogin.enterNewPassword",
"Enter new password (min 8 characters)",
)}
value={newPassword}
onChange={(e) => setNewPassword(e.currentTarget.value)}
minLength={8}
required
styles={{
input: { height: 44 },
}}
/>
<PasswordInput
label={t("firstLogin.confirmPassword", "Confirm New Password")}
placeholder={t(
"firstLogin.reEnterNewPassword",
"Re-enter new password",
)}
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.currentTarget.value)}
required
minLength={8}
styles={{
input: { height: 44 },
}}
/>
<PasswordInput
label={t("firstLogin.confirmPassword", "Confirm New Password")}
placeholder={t(
"firstLogin.reEnterNewPassword",
"Re-enter new password",
)}
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.currentTarget.value)}
required
minLength={8}
styles={{
input: { height: 44 },
}}
/>
<Button
fullWidth
onClick={handleSubmit}
loading={loading}
disabled={
!newPassword ||
!confirmPassword ||
newPassword.length < 8 ||
confirmPassword.length < 8
}
size="md"
mt="xs"
>
{t("firstLogin.changePassword", "Change Password")}
</Button>
</Stack>
<Button
type="submit"
fullWidth
loading={loading}
disabled={
!newPassword ||
!confirmPassword ||
newPassword.length < 8 ||
confirmPassword.length < 8
}
size="md"
mt="xs"
>
{t("firstLogin.changePassword", "Change Password")}
</Button>
</Stack>
</form>
</div>
</div>
);
@@ -37,7 +37,9 @@ export default function FirstLoginModal({
const [loading, setLoading] = useState(false);
const [error, setError] = useState("");
const handleSubmit = async () => {
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
// Validation
if (!currentPassword || !newPassword || !confirmPassword) {
setError(t("firstLogin.allFieldsRequired", "All fields are required"));
@@ -125,86 +127,90 @@ export default function FirstLoginModal({
size="md"
zIndex={Z_INDEX_OVER_FULLSCREEN_SURFACE}
>
<Stack gap="md">
<Alert
icon={<LocalIcon icon="info-rounded" width="1rem" height="1rem" />}
title={t("firstLogin.welcomeTitle", "Welcome!")}
color="blue"
>
<Text size="sm">
{t(
"firstLogin.welcomeMessage",
"For security reasons, you must change your password on your first login.",
)}
</Text>
</Alert>
<Text size="sm" fw={500}>
{t("firstLogin.loggedInAs", "Logged in as")}:{" "}
<strong>{username}</strong>
</Text>
{error && (
<form onSubmit={handleSubmit}>
<Stack gap="md">
<Alert
icon={<LocalIcon icon="error-rounded" width="1rem" height="1rem" />}
title={t("firstLogin.error", "Error")}
color="red"
icon={<LocalIcon icon="info-rounded" width="1rem" height="1rem" />}
title={t("firstLogin.welcomeTitle", "Welcome!")}
color="blue"
>
{error}
<Text size="sm">
{t(
"firstLogin.welcomeMessage",
"For security reasons, you must change your password on your first login.",
)}
</Text>
</Alert>
)}
<PasswordInput
label={t("firstLogin.currentPassword", "Current Password")}
placeholder={t(
"firstLogin.enterCurrentPassword",
"Enter your current password",
<Text size="sm" fw={500}>
{t("firstLogin.loggedInAs", "Logged in as")}:{" "}
<strong>{username}</strong>
</Text>
{error && (
<Alert
icon={
<LocalIcon icon="error-rounded" width="1rem" height="1rem" />
}
title={t("firstLogin.error", "Error")}
color="red"
>
{error}
</Alert>
)}
value={currentPassword}
onChange={(e) => setCurrentPassword(e.currentTarget.value)}
required
/>
<PasswordInput
label={t("firstLogin.newPassword", "New Password")}
placeholder={t(
"firstLogin.enterNewPassword",
"Enter new password (min 8 characters)",
)}
value={newPassword}
onChange={(e) => setNewPassword(e.currentTarget.value)}
minLength={8}
required
/>
<PasswordInput
label={t("firstLogin.currentPassword", "Current Password")}
placeholder={t(
"firstLogin.enterCurrentPassword",
"Enter your current password",
)}
value={currentPassword}
onChange={(e) => setCurrentPassword(e.currentTarget.value)}
required
/>
<PasswordInput
label={t("firstLogin.confirmPassword", "Confirm New Password")}
placeholder={t(
"firstLogin.reEnterNewPassword",
"Re-enter new password",
)}
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.currentTarget.value)}
minLength={8}
required
/>
<PasswordInput
label={t("firstLogin.newPassword", "New Password")}
placeholder={t(
"firstLogin.enterNewPassword",
"Enter new password (min 8 characters)",
)}
value={newPassword}
onChange={(e) => setNewPassword(e.currentTarget.value)}
minLength={8}
required
/>
<Button
fullWidth
onClick={handleSubmit}
loading={loading}
disabled={
!currentPassword ||
!newPassword ||
!confirmPassword ||
newPassword.length < 8 ||
confirmPassword.length < 8
}
mt="md"
>
{t("firstLogin.changePassword", "Change Password")}
</Button>
</Stack>
<PasswordInput
label={t("firstLogin.confirmPassword", "Confirm New Password")}
placeholder={t(
"firstLogin.reEnterNewPassword",
"Re-enter new password",
)}
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.currentTarget.value)}
minLength={8}
required
/>
<Button
type="submit"
fullWidth
loading={loading}
disabled={
!currentPassword ||
!newPassword ||
!confirmPassword ||
newPassword.length < 8 ||
confirmPassword.length < 8
}
mt="md"
>
{t("firstLogin.changePassword", "Change Password")}
</Button>
</Stack>
</form>
</Modal>
);
}