fix(ui): autocomplete und Formulare für Passwort-Felder
PIN, Backup-Export/Import und API-Key entsprechen Chrome-DOM-Empfehlungen. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -273,6 +273,7 @@ export default function AuthOnboarding({ onAuthenticated, onOpenDemo }: AuthOnbo
|
|||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
|
name="new-pin"
|
||||||
inputMode="numeric"
|
inputMode="numeric"
|
||||||
pattern="[0-9]*"
|
pattern="[0-9]*"
|
||||||
maxLength={8}
|
maxLength={8}
|
||||||
@@ -282,6 +283,7 @@ export default function AuthOnboarding({ onAuthenticated, onOpenDemo }: AuthOnbo
|
|||||||
onChange={(e) => setPinInput(e.target.value.replace(/\D/g, ''))}
|
onChange={(e) => setPinInput(e.target.value.replace(/\D/g, ''))}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
required
|
required
|
||||||
|
autoComplete="new-password"
|
||||||
style={{ width: '100%', padding: '12px', boxSizing: 'border-box' }}
|
style={{ width: '100%', padding: '12px', boxSizing: 'border-box' }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -322,6 +324,7 @@ export default function AuthOnboarding({ onAuthenticated, onOpenDemo }: AuthOnbo
|
|||||||
<div className="input-group">
|
<div className="input-group">
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
|
name="pin"
|
||||||
inputMode="numeric"
|
inputMode="numeric"
|
||||||
pattern="[0-9]*"
|
pattern="[0-9]*"
|
||||||
maxLength={8}
|
maxLength={8}
|
||||||
@@ -331,6 +334,7 @@ export default function AuthOnboarding({ onAuthenticated, onOpenDemo }: AuthOnbo
|
|||||||
onChange={(e) => setPinLoginInput(e.target.value.replace(/\D/g, ''))}
|
onChange={(e) => setPinLoginInput(e.target.value.replace(/\D/g, ''))}
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
required
|
required
|
||||||
|
autoComplete="current-password"
|
||||||
style={{ width: '100%', padding: '12px', boxSizing: 'border-box' }}
|
style={{ width: '100%', padding: '12px', boxSizing: 'border-box' }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -58,6 +58,16 @@ export default function LogbookBackupPanel({ logbookId, onRestored }: LogbookBac
|
|||||||
const [error, setError] = useState<string | null>(null)
|
const [error, setError] = useState<string | null>(null)
|
||||||
const [success, setSuccess] = useState<string | null>(null)
|
const [success, setSuccess] = useState<string | null>(null)
|
||||||
|
|
||||||
|
const handleExportSubmit = async (e: React.FormEvent) => {
|
||||||
|
e.preventDefault()
|
||||||
|
await handleExport()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleImportSubmit = async (e: React.FormEvent) => {
|
||||||
|
e.preventDefault()
|
||||||
|
await handleRestore()
|
||||||
|
}
|
||||||
|
|
||||||
const handleExport = async () => {
|
const handleExport = async () => {
|
||||||
setError(null)
|
setError(null)
|
||||||
setSuccess(null)
|
setSuccess(null)
|
||||||
@@ -209,40 +219,45 @@ export default function LogbookBackupPanel({ logbookId, onRestored }: LogbookBac
|
|||||||
</h4>
|
</h4>
|
||||||
<p className="text-muted backup-section-desc">{t('settings.backup_export_desc')}</p>
|
<p className="text-muted backup-section-desc">{t('settings.backup_export_desc')}</p>
|
||||||
|
|
||||||
<div className="input-group">
|
<form onSubmit={handleExportSubmit} className="backup-export-form">
|
||||||
<label htmlFor="backup-export-passphrase">{t('settings.backup_passphrase')}</label>
|
<div className="input-group">
|
||||||
<input
|
<label htmlFor="backup-export-passphrase">{t('settings.backup_passphrase')}</label>
|
||||||
id="backup-export-passphrase"
|
<input
|
||||||
type="password"
|
id="backup-export-passphrase"
|
||||||
className="input-text"
|
name="backup-export-passphrase"
|
||||||
value={exportPassphrase}
|
type="password"
|
||||||
onChange={(e) => setExportPassphrase(e.target.value)}
|
className="input-text"
|
||||||
placeholder={t('settings.backup_passphrase_placeholder')}
|
value={exportPassphrase}
|
||||||
autoComplete="new-password"
|
onChange={(e) => setExportPassphrase(e.target.value)}
|
||||||
disabled={exporting}
|
placeholder={t('settings.backup_passphrase_placeholder')}
|
||||||
/>
|
autoComplete="new-password"
|
||||||
</div>
|
disabled={exporting}
|
||||||
<div className="input-group">
|
required
|
||||||
<label htmlFor="backup-export-confirm">{t('settings.backup_passphrase_confirm')}</label>
|
/>
|
||||||
<input
|
</div>
|
||||||
id="backup-export-confirm"
|
<div className="input-group">
|
||||||
type="password"
|
<label htmlFor="backup-export-confirm">{t('settings.backup_passphrase_confirm')}</label>
|
||||||
className="input-text"
|
<input
|
||||||
value={exportConfirm}
|
id="backup-export-confirm"
|
||||||
onChange={(e) => setExportConfirm(e.target.value)}
|
name="backup-export-confirm"
|
||||||
autoComplete="new-password"
|
type="password"
|
||||||
disabled={exporting}
|
className="input-text"
|
||||||
/>
|
value={exportConfirm}
|
||||||
</div>
|
onChange={(e) => setExportConfirm(e.target.value)}
|
||||||
<button
|
autoComplete="new-password"
|
||||||
type="button"
|
disabled={exporting}
|
||||||
className="btn primary"
|
required
|
||||||
onClick={handleExport}
|
/>
|
||||||
disabled={exporting || !exportPassphrase || !exportConfirm}
|
</div>
|
||||||
>
|
<button
|
||||||
<Download size={16} />
|
type="submit"
|
||||||
{exporting ? t('settings.backup_exporting') : t('settings.backup_export_btn')}
|
className="btn primary"
|
||||||
</button>
|
disabled={exporting || !exportPassphrase || !exportConfirm}
|
||||||
|
>
|
||||||
|
<Download size={16} />
|
||||||
|
{exporting ? t('settings.backup_exporting') : t('settings.backup_export_btn')}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section className="backup-section backup-section--import" aria-labelledby="backup-import-heading">
|
<section className="backup-section backup-section--import" aria-labelledby="backup-import-heading">
|
||||||
@@ -252,58 +267,61 @@ export default function LogbookBackupPanel({ logbookId, onRestored }: LogbookBac
|
|||||||
</h4>
|
</h4>
|
||||||
<p className="text-muted backup-section-desc">{t('settings.backup_restore_desc')}</p>
|
<p className="text-muted backup-section-desc">{t('settings.backup_restore_desc')}</p>
|
||||||
|
|
||||||
<div className="input-group">
|
<form onSubmit={handleImportSubmit} className="backup-import-form">
|
||||||
<label htmlFor="backup-import-file">{t('settings.backup_file_label')}</label>
|
<div className="input-group">
|
||||||
<input
|
<label htmlFor="backup-import-file">{t('settings.backup_file_label')}</label>
|
||||||
id="backup-import-file"
|
<input
|
||||||
ref={fileInputRef}
|
id="backup-import-file"
|
||||||
type="file"
|
ref={fileInputRef}
|
||||||
accept=".daagbok.json,application/json"
|
type="file"
|
||||||
className="input-text"
|
accept=".daagbok.json,application/json"
|
||||||
onChange={handleFileChange}
|
className="input-text"
|
||||||
disabled={importing}
|
onChange={handleFileChange}
|
||||||
/>
|
disabled={importing}
|
||||||
</div>
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
{importFile && (
|
{importFile && (
|
||||||
<>
|
<>
|
||||||
<div className="input-group">
|
<div className="input-group">
|
||||||
<label htmlFor="backup-import-passphrase">{t('settings.backup_passphrase')}</label>
|
<label htmlFor="backup-import-passphrase">{t('settings.backup_passphrase')}</label>
|
||||||
<input
|
<input
|
||||||
id="backup-import-passphrase"
|
id="backup-import-passphrase"
|
||||||
type="password"
|
name="backup-import-passphrase"
|
||||||
className="input-text"
|
type="password"
|
||||||
value={importPassphrase}
|
className="input-text"
|
||||||
onChange={(e) => {
|
value={importPassphrase}
|
||||||
setImportPassphrase(e.target.value)
|
onChange={(e) => {
|
||||||
setImportPreview(null)
|
setImportPassphrase(e.target.value)
|
||||||
}}
|
setImportPreview(null)
|
||||||
autoComplete="current-password"
|
}}
|
||||||
disabled={importing}
|
autoComplete="current-password"
|
||||||
/>
|
disabled={importing}
|
||||||
</div>
|
required
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="backup-actions-row">
|
<div className="backup-actions-row">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="btn secondary"
|
className="btn secondary"
|
||||||
onClick={handlePreviewImport}
|
onClick={handlePreviewImport}
|
||||||
disabled={previewing || importing || !importPassphrase}
|
disabled={previewing || importing || !importPassphrase}
|
||||||
>
|
>
|
||||||
{previewing ? t('settings.backup_previewing') : t('settings.backup_preview_btn')}
|
{previewing ? t('settings.backup_previewing') : t('settings.backup_preview_btn')}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="submit"
|
||||||
className="btn primary"
|
className="btn primary"
|
||||||
onClick={() => handleRestore()}
|
disabled={importing || !importPassphrase}
|
||||||
disabled={importing || !importPassphrase}
|
>
|
||||||
>
|
<Upload size={16} />
|
||||||
<Upload size={16} />
|
{importing ? t('settings.backup_restoring') : t('settings.backup_restore_btn')}
|
||||||
{importing ? t('settings.backup_restoring') : t('settings.backup_restore_btn')}
|
</button>
|
||||||
</button>
|
</div>
|
||||||
</div>
|
</>
|
||||||
</>
|
)}
|
||||||
)}
|
</form>
|
||||||
|
|
||||||
{importPreview && (
|
{importPreview && (
|
||||||
<div className="backup-preview glass">
|
<div className="backup-preview glass">
|
||||||
|
|||||||
@@ -293,12 +293,14 @@ export default function SettingsForm({ logbookId, onLogbookRestored }: SettingsF
|
|||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
id="owm-api-key"
|
id="owm-api-key"
|
||||||
|
name="owm-api-key"
|
||||||
type="password"
|
type="password"
|
||||||
className="input-text"
|
className="input-text"
|
||||||
placeholder="e.g. 8b6a7f...d8"
|
placeholder="e.g. 8b6a7f...d8"
|
||||||
value={apiKey}
|
value={apiKey}
|
||||||
onChange={(e) => setApiKey(e.target.value)}
|
onChange={(e) => setApiKey(e.target.value)}
|
||||||
disabled={saving}
|
disabled={saving}
|
||||||
|
autoComplete="off"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user