mirror of
https://github.com/shuaiplus/nodewarden.git
synced 2026-06-20 21:00:41 +00:00
feat: refactor authentication forms to use <form> elements for better submission handling
This commit is contained in:
@@ -64,22 +64,29 @@ export default function AuthViews(props: AuthViewsProps) {
|
|||||||
return (
|
return (
|
||||||
<div className="auth-page">
|
<div className="auth-page">
|
||||||
<StandalonePageFrame title={t('txt_unlock_vault')}>
|
<StandalonePageFrame title={t('txt_unlock_vault')}>
|
||||||
<p className="muted standalone-muted">{props.emailForLock}</p>
|
<form
|
||||||
<PasswordField
|
onSubmit={(e) => {
|
||||||
label={t('txt_master_password')}
|
e.preventDefault();
|
||||||
value={props.unlockPassword}
|
props.onSubmitUnlock();
|
||||||
autoFocus
|
}}
|
||||||
onInput={props.onChangeUnlock}
|
>
|
||||||
/>
|
<p className="muted standalone-muted">{props.emailForLock}</p>
|
||||||
<button type="button" className="btn btn-primary full" onClick={props.onSubmitUnlock}>
|
<PasswordField
|
||||||
<Unlock size={16} className="btn-icon" />
|
label={t('txt_master_password')}
|
||||||
{t('txt_unlock')}
|
value={props.unlockPassword}
|
||||||
</button>
|
autoFocus
|
||||||
<div className="or">{t('txt_or')}</div>
|
onInput={props.onChangeUnlock}
|
||||||
<button type="button" className="btn btn-secondary full" onClick={props.onLogout}>
|
/>
|
||||||
<LogOut size={16} className="btn-icon" />
|
<button type="submit" className="btn btn-primary full">
|
||||||
{t('txt_log_out')}
|
<Unlock size={16} className="btn-icon" />
|
||||||
</button>
|
{t('txt_unlock')}
|
||||||
|
</button>
|
||||||
|
<div className="or">{t('txt_or')}</div>
|
||||||
|
<button type="button" className="btn btn-secondary full" onClick={props.onLogout}>
|
||||||
|
<LogOut size={16} className="btn-icon" />
|
||||||
|
{t('txt_log_out')}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
</StandalonePageFrame>
|
</StandalonePageFrame>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -89,56 +96,63 @@ export default function AuthViews(props: AuthViewsProps) {
|
|||||||
return (
|
return (
|
||||||
<div className="auth-page">
|
<div className="auth-page">
|
||||||
<StandalonePageFrame title={t('txt_create_account')}>
|
<StandalonePageFrame title={t('txt_create_account')}>
|
||||||
<label className="field">
|
<form
|
||||||
<span>{t('txt_name')}</span>
|
onSubmit={(e) => {
|
||||||
<input
|
e.preventDefault();
|
||||||
className="input"
|
props.onSubmitRegister();
|
||||||
value={props.registerValues.name}
|
}}
|
||||||
onInput={(e) =>
|
>
|
||||||
props.onChangeRegister({ ...props.registerValues, name: (e.currentTarget as HTMLInputElement).value })
|
<label className="field">
|
||||||
}
|
<span>{t('txt_name')}</span>
|
||||||
|
<input
|
||||||
|
className="input"
|
||||||
|
value={props.registerValues.name}
|
||||||
|
onInput={(e) =>
|
||||||
|
props.onChangeRegister({ ...props.registerValues, name: (e.currentTarget as HTMLInputElement).value })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label className="field">
|
||||||
|
<span>{t('txt_email')}</span>
|
||||||
|
<input
|
||||||
|
className="input"
|
||||||
|
type="email"
|
||||||
|
value={props.registerValues.email}
|
||||||
|
onInput={(e) =>
|
||||||
|
props.onChangeRegister({ ...props.registerValues, email: (e.currentTarget as HTMLInputElement).value })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<PasswordField
|
||||||
|
label={t('txt_master_password')}
|
||||||
|
value={props.registerValues.password}
|
||||||
|
onInput={(v) => props.onChangeRegister({ ...props.registerValues, password: v })}
|
||||||
/>
|
/>
|
||||||
</label>
|
<PasswordField
|
||||||
<label className="field">
|
label={t('txt_confirm_master_password')}
|
||||||
<span>{t('txt_email')}</span>
|
value={props.registerValues.password2}
|
||||||
<input
|
onInput={(v) => props.onChangeRegister({ ...props.registerValues, password2: v })}
|
||||||
className="input"
|
|
||||||
type="email"
|
|
||||||
value={props.registerValues.email}
|
|
||||||
onInput={(e) =>
|
|
||||||
props.onChangeRegister({ ...props.registerValues, email: (e.currentTarget as HTMLInputElement).value })
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</label>
|
<label className="field">
|
||||||
<PasswordField
|
<span>{t('txt_invite_code_optional')}</span>
|
||||||
label={t('txt_master_password')}
|
<input
|
||||||
value={props.registerValues.password}
|
className="input"
|
||||||
onInput={(v) => props.onChangeRegister({ ...props.registerValues, password: v })}
|
value={props.registerValues.inviteCode}
|
||||||
/>
|
onInput={(e) =>
|
||||||
<PasswordField
|
props.onChangeRegister({ ...props.registerValues, inviteCode: (e.currentTarget as HTMLInputElement).value })
|
||||||
label={t('txt_confirm_master_password')}
|
}
|
||||||
value={props.registerValues.password2}
|
/>
|
||||||
onInput={(v) => props.onChangeRegister({ ...props.registerValues, password2: v })}
|
</label>
|
||||||
/>
|
<button type="submit" className="btn btn-primary full">
|
||||||
<label className="field">
|
<UserPlus size={16} className="btn-icon" />
|
||||||
<span>{t('txt_invite_code_optional')}</span>
|
{t('txt_create_account')}
|
||||||
<input
|
</button>
|
||||||
className="input"
|
<div className="or">{t('txt_or')}</div>
|
||||||
value={props.registerValues.inviteCode}
|
<button type="button" className="btn btn-secondary full" onClick={props.onGotoLogin}>
|
||||||
onInput={(e) =>
|
<ArrowLeft size={16} className="btn-icon" />
|
||||||
props.onChangeRegister({ ...props.registerValues, inviteCode: (e.currentTarget as HTMLInputElement).value })
|
{t('txt_back_to_login')}
|
||||||
}
|
</button>
|
||||||
/>
|
</form>
|
||||||
</label>
|
|
||||||
<button type="button" className="btn btn-primary full" onClick={props.onSubmitRegister}>
|
|
||||||
<UserPlus size={16} className="btn-icon" />
|
|
||||||
{t('txt_create_account')}
|
|
||||||
</button>
|
|
||||||
<div className="or">{t('txt_or')}</div>
|
|
||||||
<button type="button" className="btn btn-secondary full" onClick={props.onGotoLogin}>
|
|
||||||
<ArrowLeft size={16} className="btn-icon" />
|
|
||||||
{t('txt_back_to_login')}
|
|
||||||
</button>
|
|
||||||
</StandalonePageFrame>
|
</StandalonePageFrame>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -147,30 +161,37 @@ export default function AuthViews(props: AuthViewsProps) {
|
|||||||
return (
|
return (
|
||||||
<div className="auth-page">
|
<div className="auth-page">
|
||||||
<StandalonePageFrame title={t('txt_log_in')}>
|
<StandalonePageFrame title={t('txt_log_in')}>
|
||||||
<label className="field">
|
<form
|
||||||
<span>{t('txt_email')}</span>
|
onSubmit={(e) => {
|
||||||
<input
|
e.preventDefault();
|
||||||
className="input"
|
props.onSubmitLogin();
|
||||||
type="email"
|
}}
|
||||||
value={props.loginValues.email}
|
>
|
||||||
onInput={(e) => props.onChangeLogin({ ...props.loginValues, email: (e.currentTarget as HTMLInputElement).value })}
|
<label className="field">
|
||||||
|
<span>{t('txt_email')}</span>
|
||||||
|
<input
|
||||||
|
className="input"
|
||||||
|
type="email"
|
||||||
|
value={props.loginValues.email}
|
||||||
|
onInput={(e) => props.onChangeLogin({ ...props.loginValues, email: (e.currentTarget as HTMLInputElement).value })}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<PasswordField
|
||||||
|
label={t('txt_master_password')}
|
||||||
|
value={props.loginValues.password}
|
||||||
|
onInput={(v) => props.onChangeLogin({ ...props.loginValues, password: v })}
|
||||||
|
autoFocus
|
||||||
/>
|
/>
|
||||||
</label>
|
<button type="submit" className="btn btn-primary full">
|
||||||
<PasswordField
|
<LogIn size={16} className="btn-icon" />
|
||||||
label={t('txt_master_password')}
|
{t('txt_log_in')}
|
||||||
value={props.loginValues.password}
|
</button>
|
||||||
onInput={(v) => props.onChangeLogin({ ...props.loginValues, password: v })}
|
<div className="or">{t('txt_or')}</div>
|
||||||
autoFocus
|
<button type="button" className="btn btn-secondary full" onClick={props.onGotoRegister}>
|
||||||
/>
|
<UserPlus size={16} className="btn-icon" />
|
||||||
<button type="button" className="btn btn-primary full" onClick={props.onSubmitLogin}>
|
{t('txt_create_account')}
|
||||||
<LogIn size={16} className="btn-icon" />
|
</button>
|
||||||
{t('txt_log_in')}
|
</form>
|
||||||
</button>
|
|
||||||
<div className="or">{t('txt_or')}</div>
|
|
||||||
<button type="button" className="btn btn-secondary full" onClick={props.onGotoRegister}>
|
|
||||||
<UserPlus size={16} className="btn-icon" />
|
|
||||||
{t('txt_create_account')}
|
|
||||||
</button>
|
|
||||||
</StandalonePageFrame>
|
</StandalonePageFrame>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -20,14 +20,19 @@ export default function ConfirmDialog(props: ConfirmDialogProps) {
|
|||||||
if (!props.open) return null;
|
if (!props.open) return null;
|
||||||
return (
|
return (
|
||||||
<div className="dialog-mask">
|
<div className="dialog-mask">
|
||||||
<div className="dialog-card">
|
<form
|
||||||
|
className="dialog-card"
|
||||||
|
onSubmit={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
props.onConfirm();
|
||||||
|
}}
|
||||||
|
>
|
||||||
<h3 className="dialog-title">{props.title}</h3>
|
<h3 className="dialog-title">{props.title}</h3>
|
||||||
<div className="dialog-message">{props.message}</div>
|
<div className="dialog-message">{props.message}</div>
|
||||||
{props.children}
|
{props.children}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="submit"
|
||||||
className={`btn ${props.danger ? 'btn-danger' : 'btn-primary'} dialog-btn`}
|
className={`btn ${props.danger ? 'btn-danger' : 'btn-primary'} dialog-btn`}
|
||||||
onClick={props.onConfirm}
|
|
||||||
>
|
>
|
||||||
<Check size={14} className="btn-icon" />
|
<Check size={14} className="btn-icon" />
|
||||||
{props.confirmText || t('txt_yes')}
|
{props.confirmText || t('txt_yes')}
|
||||||
@@ -37,7 +42,7 @@ export default function ConfirmDialog(props: ConfirmDialogProps) {
|
|||||||
{props.cancelText || t('txt_no')}
|
{props.cancelText || t('txt_no')}
|
||||||
</button>
|
</button>
|
||||||
{props.afterActions}
|
{props.afterActions}
|
||||||
</div>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -92,7 +92,12 @@ export default function PublicSendPage(props: PublicSendPageProps) {
|
|||||||
{loading && <p className="muted">{t('txt_loading')}</p>}
|
{loading && <p className="muted">{t('txt_loading')}</p>}
|
||||||
|
|
||||||
{!loading && needPassword && (
|
{!loading && needPassword && (
|
||||||
<>
|
<form
|
||||||
|
onSubmit={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
void loadSend(password);
|
||||||
|
}}
|
||||||
|
>
|
||||||
<label className="field">
|
<label className="field">
|
||||||
<span>{t('txt_password')}</span>
|
<span>{t('txt_password')}</span>
|
||||||
<div className="password-wrap">
|
<div className="password-wrap">
|
||||||
@@ -104,10 +109,10 @@ export default function PublicSendPage(props: PublicSendPageProps) {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
<button type="button" className="btn btn-primary full" disabled={busy} onClick={() => void loadSend(password)}>
|
<button type="submit" className="btn btn-primary full" disabled={busy}>
|
||||||
<Lock size={14} className="btn-icon" /> {t('txt_unlock_send')}
|
<Lock size={14} className="btn-icon" /> {t('txt_unlock_send')}
|
||||||
</button>
|
</button>
|
||||||
</>
|
</form>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!loading && sendData && (
|
{!loading && sendData && (
|
||||||
|
|||||||
@@ -16,52 +16,59 @@ export default function RecoverTwoFactorPage(props: RecoverTwoFactorPageProps) {
|
|||||||
return (
|
return (
|
||||||
<div className="auth-page">
|
<div className="auth-page">
|
||||||
<StandalonePageFrame title={t('txt_recover_two_step_login')}>
|
<StandalonePageFrame title={t('txt_recover_two_step_login')}>
|
||||||
<p className="muted standalone-muted">{t('txt_use_your_one_time_recovery_code_to_disable_two_step_verification')}</p>
|
<form
|
||||||
|
onSubmit={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
props.onSubmit();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<p className="muted standalone-muted">{t('txt_use_your_one_time_recovery_code_to_disable_two_step_verification')}</p>
|
||||||
|
|
||||||
<label className="field">
|
<label className="field">
|
||||||
<span>{t('txt_email')}</span>
|
<span>{t('txt_email')}</span>
|
||||||
<input
|
|
||||||
className="input"
|
|
||||||
type="email"
|
|
||||||
value={props.values.email}
|
|
||||||
onInput={(e) => props.onChange({ ...props.values, email: (e.currentTarget as HTMLInputElement).value })}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<label className="field">
|
|
||||||
<span>{t('txt_master_password')}</span>
|
|
||||||
<div className="password-wrap">
|
|
||||||
<input
|
<input
|
||||||
className="input"
|
className="input"
|
||||||
type={showPassword ? 'text' : 'password'}
|
type="email"
|
||||||
value={props.values.password}
|
value={props.values.email}
|
||||||
onInput={(e) => props.onChange({ ...props.values, password: (e.currentTarget as HTMLInputElement).value })}
|
onInput={(e) => props.onChange({ ...props.values, email: (e.currentTarget as HTMLInputElement).value })}
|
||||||
/>
|
/>
|
||||||
<button type="button" className="eye-btn" onClick={() => setShowPassword((v) => !v)}>
|
</label>
|
||||||
{showPassword ? <EyeOff size={16} /> : <Eye size={16} />}
|
|
||||||
|
<label className="field">
|
||||||
|
<span>{t('txt_master_password')}</span>
|
||||||
|
<div className="password-wrap">
|
||||||
|
<input
|
||||||
|
className="input"
|
||||||
|
type={showPassword ? 'text' : 'password'}
|
||||||
|
value={props.values.password}
|
||||||
|
onInput={(e) => props.onChange({ ...props.values, password: (e.currentTarget as HTMLInputElement).value })}
|
||||||
|
/>
|
||||||
|
<button type="button" className="eye-btn" onClick={() => setShowPassword((v) => !v)}>
|
||||||
|
{showPassword ? <EyeOff size={16} /> : <Eye size={16} />}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label className="field">
|
||||||
|
<span>{t('txt_recovery_code')}</span>
|
||||||
|
<input
|
||||||
|
className="input"
|
||||||
|
value={props.values.recoveryCode}
|
||||||
|
onInput={(e) => props.onChange({ ...props.values, recoveryCode: (e.currentTarget as HTMLInputElement).value.toUpperCase() })}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div className="field-grid">
|
||||||
|
<button type="submit" className="btn btn-primary">
|
||||||
|
<Send size={14} className="btn-icon" />
|
||||||
|
{t('txt_submit')}
|
||||||
|
</button>
|
||||||
|
<button type="button" className="btn btn-secondary" onClick={props.onCancel}>
|
||||||
|
<X size={14} className="btn-icon" />
|
||||||
|
{t('txt_cancel')}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</form>
|
||||||
|
|
||||||
<label className="field">
|
|
||||||
<span>{t('txt_recovery_code')}</span>
|
|
||||||
<input
|
|
||||||
className="input"
|
|
||||||
value={props.values.recoveryCode}
|
|
||||||
onInput={(e) => props.onChange({ ...props.values, recoveryCode: (e.currentTarget as HTMLInputElement).value.toUpperCase() })}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<div className="field-grid">
|
|
||||||
<button type="button" className="btn btn-primary" onClick={props.onSubmit}>
|
|
||||||
<Send size={14} className="btn-icon" />
|
|
||||||
{t('txt_submit')}
|
|
||||||
</button>
|
|
||||||
<button type="button" className="btn btn-secondary" onClick={props.onCancel}>
|
|
||||||
<X size={14} className="btn-icon" />
|
|
||||||
{t('txt_cancel')}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</StandalonePageFrame>
|
</StandalonePageFrame>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user