mirror of
https://github.com/shuaiplus/nodewarden.git
synced 2026-06-20 13:00:39 +00:00
feat: add loading state management for TOTP and import/export operations
This commit is contained in:
+26
-1
@@ -172,9 +172,11 @@ export default function App() {
|
|||||||
const [pendingTotp, setPendingTotp] = useState<PendingTotp | null>(null);
|
const [pendingTotp, setPendingTotp] = useState<PendingTotp | null>(null);
|
||||||
const [totpCode, setTotpCode] = useState('');
|
const [totpCode, setTotpCode] = useState('');
|
||||||
const [rememberDevice, setRememberDevice] = useState(true);
|
const [rememberDevice, setRememberDevice] = useState(true);
|
||||||
|
const [totpSubmitting, setTotpSubmitting] = useState(false);
|
||||||
|
|
||||||
const [disableTotpOpen, setDisableTotpOpen] = useState(false);
|
const [disableTotpOpen, setDisableTotpOpen] = useState(false);
|
||||||
const [disableTotpPassword, setDisableTotpPassword] = useState('');
|
const [disableTotpPassword, setDisableTotpPassword] = useState('');
|
||||||
|
const [disableTotpSubmitting, setDisableTotpSubmitting] = useState(false);
|
||||||
const [recoverValues, setRecoverValues] = useState({ email: '', password: '', recoveryCode: '' });
|
const [recoverValues, setRecoverValues] = useState({ email: '', password: '', recoveryCode: '' });
|
||||||
const [themePreference, setThemePreference] = useState<ThemePreference>(() => readThemePreference());
|
const [themePreference, setThemePreference] = useState<ThemePreference>(() => readThemePreference());
|
||||||
const [systemTheme, setSystemTheme] = useState<'light' | 'dark'>(() => resolveSystemTheme());
|
const [systemTheme, setSystemTheme] = useState<'light' | 'dark'>(() => resolveSystemTheme());
|
||||||
@@ -433,16 +435,20 @@ export default function App() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function handleTotpVerify() {
|
async function handleTotpVerify() {
|
||||||
|
if (totpSubmitting) return;
|
||||||
if (!pendingTotp) return;
|
if (!pendingTotp) return;
|
||||||
if (!totpCode.trim()) {
|
if (!totpCode.trim()) {
|
||||||
pushToast('error', t('txt_please_input_totp_code'));
|
pushToast('error', t('txt_please_input_totp_code'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
setTotpSubmitting(true);
|
||||||
try {
|
try {
|
||||||
const login = await performTotpLogin(pendingTotp, totpCode, rememberDevice);
|
const login = await performTotpLogin(pendingTotp, totpCode, rememberDevice);
|
||||||
await finalizeLogin(login);
|
await finalizeLogin(login);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
pushToast('error', error instanceof Error ? error.message : t('txt_totp_verify_failed'));
|
pushToast('error', error instanceof Error ? error.message : t('txt_totp_verify_failed'));
|
||||||
|
} finally {
|
||||||
|
setTotpSubmitting(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -631,11 +637,13 @@ export default function App() {
|
|||||||
onConfirmTotp={() => {}}
|
onConfirmTotp={() => {}}
|
||||||
onCancelTotp={() => {}}
|
onCancelTotp={() => {}}
|
||||||
onUseRecoveryCode={() => {}}
|
onUseRecoveryCode={() => {}}
|
||||||
|
totpSubmitting={false}
|
||||||
disableTotpOpen={false}
|
disableTotpOpen={false}
|
||||||
disableTotpPassword=""
|
disableTotpPassword=""
|
||||||
onDisableTotpPasswordChange={() => {}}
|
onDisableTotpPasswordChange={() => {}}
|
||||||
onConfirmDisableTotp={() => {}}
|
onConfirmDisableTotp={() => {}}
|
||||||
onCancelDisableTotp={() => {}}
|
onCancelDisableTotp={() => {}}
|
||||||
|
disableTotpSubmitting={false}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1288,21 +1296,25 @@ export default function App() {
|
|||||||
onRememberDeviceChange={setRememberDevice}
|
onRememberDeviceChange={setRememberDevice}
|
||||||
onConfirmTotp={() => void handleTotpVerify()}
|
onConfirmTotp={() => void handleTotpVerify()}
|
||||||
onCancelTotp={() => {
|
onCancelTotp={() => {
|
||||||
|
if (totpSubmitting) return;
|
||||||
setPendingTotp(null);
|
setPendingTotp(null);
|
||||||
setTotpCode('');
|
setTotpCode('');
|
||||||
setRememberDevice(true);
|
setRememberDevice(true);
|
||||||
}}
|
}}
|
||||||
onUseRecoveryCode={() => {
|
onUseRecoveryCode={() => {
|
||||||
|
if (totpSubmitting) return;
|
||||||
setPendingTotp(null);
|
setPendingTotp(null);
|
||||||
setTotpCode('');
|
setTotpCode('');
|
||||||
setRememberDevice(true);
|
setRememberDevice(true);
|
||||||
navigate('/recover-2fa');
|
navigate('/recover-2fa');
|
||||||
}}
|
}}
|
||||||
|
totpSubmitting={totpSubmitting}
|
||||||
disableTotpOpen={false}
|
disableTotpOpen={false}
|
||||||
disableTotpPassword=""
|
disableTotpPassword=""
|
||||||
onDisableTotpPasswordChange={() => {}}
|
onDisableTotpPasswordChange={() => {}}
|
||||||
onConfirmDisableTotp={() => {}}
|
onConfirmDisableTotp={() => {}}
|
||||||
onCancelDisableTotp={() => {}}
|
onCancelDisableTotp={() => {}}
|
||||||
|
disableTotpSubmitting={false}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@@ -1341,14 +1353,27 @@ export default function App() {
|
|||||||
onConfirmTotp={() => {}}
|
onConfirmTotp={() => {}}
|
||||||
onCancelTotp={() => {}}
|
onCancelTotp={() => {}}
|
||||||
onUseRecoveryCode={() => {}}
|
onUseRecoveryCode={() => {}}
|
||||||
|
totpSubmitting={false}
|
||||||
disableTotpOpen={disableTotpOpen}
|
disableTotpOpen={disableTotpOpen}
|
||||||
disableTotpPassword={disableTotpPassword}
|
disableTotpPassword={disableTotpPassword}
|
||||||
onDisableTotpPasswordChange={setDisableTotpPassword}
|
onDisableTotpPasswordChange={setDisableTotpPassword}
|
||||||
onConfirmDisableTotp={() => void accountSecurityActions.disableTotp()}
|
onConfirmDisableTotp={() => {
|
||||||
|
if (disableTotpSubmitting) return;
|
||||||
|
void (async () => {
|
||||||
|
setDisableTotpSubmitting(true);
|
||||||
|
try {
|
||||||
|
await accountSecurityActions.disableTotp();
|
||||||
|
} finally {
|
||||||
|
setDisableTotpSubmitting(false);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}}
|
||||||
onCancelDisableTotp={() => {
|
onCancelDisableTotp={() => {
|
||||||
|
if (disableTotpSubmitting) return;
|
||||||
setDisableTotpOpen(false);
|
setDisableTotpOpen(false);
|
||||||
setDisableTotpPassword('');
|
setDisableTotpPassword('');
|
||||||
}}
|
}}
|
||||||
|
disableTotpSubmitting={disableTotpSubmitting}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -27,11 +27,13 @@ interface AppGlobalOverlaysProps {
|
|||||||
onConfirmTotp: () => void;
|
onConfirmTotp: () => void;
|
||||||
onCancelTotp: () => void;
|
onCancelTotp: () => void;
|
||||||
onUseRecoveryCode: () => void;
|
onUseRecoveryCode: () => void;
|
||||||
|
totpSubmitting: boolean;
|
||||||
disableTotpOpen: boolean;
|
disableTotpOpen: boolean;
|
||||||
disableTotpPassword: string;
|
disableTotpPassword: string;
|
||||||
onDisableTotpPasswordChange: (value: string) => void;
|
onDisableTotpPasswordChange: (value: string) => void;
|
||||||
onConfirmDisableTotp: () => void;
|
onConfirmDisableTotp: () => void;
|
||||||
onCancelDisableTotp: () => void;
|
onCancelDisableTotp: () => void;
|
||||||
|
disableTotpSubmitting: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function AppGlobalOverlays(props: AppGlobalOverlaysProps) {
|
export default function AppGlobalOverlays(props: AppGlobalOverlaysProps) {
|
||||||
@@ -57,12 +59,14 @@ export default function AppGlobalOverlays(props: AppGlobalOverlaysProps) {
|
|||||||
confirmText={t('txt_verify')}
|
confirmText={t('txt_verify')}
|
||||||
cancelText={t('txt_cancel')}
|
cancelText={t('txt_cancel')}
|
||||||
showIcon={false}
|
showIcon={false}
|
||||||
|
confirmDisabled={props.totpSubmitting}
|
||||||
|
cancelDisabled={props.totpSubmitting}
|
||||||
onConfirm={props.onConfirmTotp}
|
onConfirm={props.onConfirmTotp}
|
||||||
onCancel={props.onCancelTotp}
|
onCancel={props.onCancelTotp}
|
||||||
afterActions={(
|
afterActions={(
|
||||||
<div className="dialog-extra">
|
<div className="dialog-extra">
|
||||||
<div className="dialog-divider" />
|
<div className="dialog-divider" />
|
||||||
<button type="button" className="btn btn-secondary dialog-btn" onClick={props.onUseRecoveryCode}>
|
<button type="button" className="btn btn-secondary dialog-btn" disabled={props.totpSubmitting} onClick={props.onUseRecoveryCode}>
|
||||||
{t('txt_use_recovery_code')}
|
{t('txt_use_recovery_code')}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -86,6 +90,8 @@ export default function AppGlobalOverlays(props: AppGlobalOverlaysProps) {
|
|||||||
cancelText={t('txt_cancel')}
|
cancelText={t('txt_cancel')}
|
||||||
danger
|
danger
|
||||||
showIcon={false}
|
showIcon={false}
|
||||||
|
confirmDisabled={props.disableTotpSubmitting}
|
||||||
|
cancelDisabled={props.disableTotpSubmitting}
|
||||||
onConfirm={props.onConfirmDisableTotp}
|
onConfirm={props.onConfirmDisableTotp}
|
||||||
onCancel={props.onCancelDisableTotp}
|
onCancel={props.onCancelDisableTotp}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -528,6 +528,7 @@ export default function BackupCenterPage(props: BackupCenterPageProps) {
|
|||||||
allowChecksumMismatch: boolean = false,
|
allowChecksumMismatch: boolean = false,
|
||||||
knownIntegrity?: BackupFileIntegrityCheckResult
|
knownIntegrity?: BackupFileIntegrityCheckResult
|
||||||
) {
|
) {
|
||||||
|
if (importing) return;
|
||||||
if (!selectedFile) {
|
if (!selectedFile) {
|
||||||
const message = t('txt_backup_file_required');
|
const message = t('txt_backup_file_required');
|
||||||
setLocalError(message);
|
setLocalError(message);
|
||||||
@@ -654,6 +655,7 @@ export default function BackupCenterPage(props: BackupCenterPageProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function handleDeleteRemote(path: string) {
|
async function handleDeleteRemote(path: string) {
|
||||||
|
if (deletingRemotePath) return;
|
||||||
if (!savedSelectedDestination) return;
|
if (!savedSelectedDestination) return;
|
||||||
setDeletingRemotePath(path);
|
setDeletingRemotePath(path);
|
||||||
setLocalError('');
|
setLocalError('');
|
||||||
@@ -723,6 +725,7 @@ export default function BackupCenterPage(props: BackupCenterPageProps) {
|
|||||||
allowChecksumMismatch: boolean = false,
|
allowChecksumMismatch: boolean = false,
|
||||||
knownIntegrity?: BackupFileIntegrityCheckResult
|
knownIntegrity?: BackupFileIntegrityCheckResult
|
||||||
) {
|
) {
|
||||||
|
if (restoringRemotePath) return;
|
||||||
if (!savedSelectedDestination) return;
|
if (!savedSelectedDestination) return;
|
||||||
setConfirmRemoteReplaceOpen(false);
|
setConfirmRemoteReplaceOpen(false);
|
||||||
setConfirmIntegrityWarningOpen(false);
|
setConfirmIntegrityWarningOpen(false);
|
||||||
@@ -896,9 +899,12 @@ export default function BackupCenterPage(props: BackupCenterPageProps) {
|
|||||||
message={selectedFile ? t('txt_backup_selected_file_name', { name: selectedFile.name }) : t('txt_backup_restore_note')}
|
message={selectedFile ? t('txt_backup_selected_file_name', { name: selectedFile.name }) : t('txt_backup_restore_note')}
|
||||||
confirmText={t('txt_backup_import')}
|
confirmText={t('txt_backup_import')}
|
||||||
cancelText={t('txt_cancel')}
|
cancelText={t('txt_cancel')}
|
||||||
|
confirmDisabled={importing}
|
||||||
|
cancelDisabled={importing}
|
||||||
danger
|
danger
|
||||||
onConfirm={() => void runLocalRestore(false)}
|
onConfirm={() => void runLocalRestore(false)}
|
||||||
onCancel={() => {
|
onCancel={() => {
|
||||||
|
if (importing) return;
|
||||||
setConfirmLocalRestoreOpen(false);
|
setConfirmLocalRestoreOpen(false);
|
||||||
resetSelectedFile();
|
resetSelectedFile();
|
||||||
resetPendingIntegrityWarning();
|
resetPendingIntegrityWarning();
|
||||||
@@ -959,6 +965,8 @@ export default function BackupCenterPage(props: BackupCenterPageProps) {
|
|||||||
variant="warning"
|
variant="warning"
|
||||||
confirmText={t('txt_backup_restore_checksum_warning_confirm')}
|
confirmText={t('txt_backup_restore_checksum_warning_confirm')}
|
||||||
cancelText={t('txt_cancel')}
|
cancelText={t('txt_cancel')}
|
||||||
|
confirmDisabled={importing || !!restoringRemotePath}
|
||||||
|
cancelDisabled={importing || !!restoringRemotePath}
|
||||||
danger
|
danger
|
||||||
onConfirm={() => {
|
onConfirm={() => {
|
||||||
if (!pendingRestoreIntegrity) return;
|
if (!pendingRestoreIntegrity) return;
|
||||||
@@ -984,6 +992,8 @@ export default function BackupCenterPage(props: BackupCenterPageProps) {
|
|||||||
message={t('txt_backup_remote_delete_confirm_message', { name: pendingRemoteDeletePath.split('/').pop() || pendingRemoteDeletePath })}
|
message={t('txt_backup_remote_delete_confirm_message', { name: pendingRemoteDeletePath.split('/').pop() || pendingRemoteDeletePath })}
|
||||||
confirmText={t('txt_delete')}
|
confirmText={t('txt_delete')}
|
||||||
cancelText={t('txt_cancel')}
|
cancelText={t('txt_cancel')}
|
||||||
|
confirmDisabled={!!deletingRemotePath}
|
||||||
|
cancelDisabled={!!deletingRemotePath}
|
||||||
danger
|
danger
|
||||||
onConfirm={() => void handleDeleteRemote(pendingRemoteDeletePath)}
|
onConfirm={() => void handleDeleteRemote(pendingRemoteDeletePath)}
|
||||||
onCancel={() => {
|
onCancel={() => {
|
||||||
@@ -1001,6 +1011,8 @@ export default function BackupCenterPage(props: BackupCenterPageProps) {
|
|||||||
})}
|
})}
|
||||||
confirmText={t('txt_delete')}
|
confirmText={t('txt_delete')}
|
||||||
cancelText={t('txt_cancel')}
|
cancelText={t('txt_cancel')}
|
||||||
|
confirmDisabled={savingSettings}
|
||||||
|
cancelDisabled={savingSettings}
|
||||||
danger
|
danger
|
||||||
onConfirm={() => void handleDeleteDestination()}
|
onConfirm={() => void handleDeleteDestination()}
|
||||||
onCancel={() => {
|
onCancel={() => {
|
||||||
|
|||||||
@@ -468,6 +468,7 @@ export default function ImportPage({ onImport, onImportEncryptedRaw, accountKeys
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function handlePasswordImportConfirm() {
|
async function handlePasswordImportConfirm() {
|
||||||
|
if (isPasswordSubmitting) return;
|
||||||
if (!pendingPasswordImport) return;
|
if (!pendingPasswordImport) return;
|
||||||
setIsPasswordSubmitting(true);
|
setIsPasswordSubmitting(true);
|
||||||
try {
|
try {
|
||||||
@@ -486,6 +487,7 @@ export default function ImportPage({ onImport, onImportEncryptedRaw, accountKeys
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function handleZipPasswordImportConfirm() {
|
async function handleZipPasswordImportConfirm() {
|
||||||
|
if (isZipPasswordSubmitting) return;
|
||||||
if (!pendingZipFile) return;
|
if (!pendingZipFile) return;
|
||||||
setIsZipPasswordSubmitting(true);
|
setIsZipPasswordSubmitting(true);
|
||||||
try {
|
try {
|
||||||
@@ -558,6 +560,7 @@ export default function ImportPage({ onImport, onImportEncryptedRaw, accountKeys
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function handleExportConfirmPassword() {
|
async function handleExportConfirmPassword() {
|
||||||
|
if (isExporting) return;
|
||||||
const masterPassword = String(exportAuthPassword || '').trim();
|
const masterPassword = String(exportAuthPassword || '').trim();
|
||||||
if (!masterPassword) {
|
if (!masterPassword) {
|
||||||
onNotify('error', t('txt_master_password_is_required'));
|
onNotify('error', t('txt_master_password_is_required'));
|
||||||
@@ -736,6 +739,8 @@ export default function ImportPage({ onImport, onImportEncryptedRaw, accountKeys
|
|||||||
confirmText={isExporting ? t('txt_loading') : t('txt_verify')}
|
confirmText={isExporting ? t('txt_loading') : t('txt_verify')}
|
||||||
cancelText={t('txt_cancel')}
|
cancelText={t('txt_cancel')}
|
||||||
showIcon={false}
|
showIcon={false}
|
||||||
|
confirmDisabled={isExporting}
|
||||||
|
cancelDisabled={isExporting}
|
||||||
onConfirm={() => void handleExportConfirmPassword()}
|
onConfirm={() => void handleExportConfirmPassword()}
|
||||||
onCancel={() => {
|
onCancel={() => {
|
||||||
if (isExporting) return;
|
if (isExporting) return;
|
||||||
@@ -761,6 +766,8 @@ export default function ImportPage({ onImport, onImportEncryptedRaw, accountKeys
|
|||||||
confirmText={isPasswordSubmitting ? t('txt_loading') : t('txt_import')}
|
confirmText={isPasswordSubmitting ? t('txt_loading') : t('txt_import')}
|
||||||
cancelText={t('txt_cancel')}
|
cancelText={t('txt_cancel')}
|
||||||
showIcon={false}
|
showIcon={false}
|
||||||
|
confirmDisabled={isPasswordSubmitting}
|
||||||
|
cancelDisabled={isPasswordSubmitting}
|
||||||
onConfirm={() => void handlePasswordImportConfirm()}
|
onConfirm={() => void handlePasswordImportConfirm()}
|
||||||
onCancel={() => {
|
onCancel={() => {
|
||||||
if (isPasswordSubmitting) return;
|
if (isPasswordSubmitting) return;
|
||||||
@@ -787,6 +794,8 @@ export default function ImportPage({ onImport, onImportEncryptedRaw, accountKeys
|
|||||||
confirmText={isZipPasswordSubmitting ? t('txt_loading') : t('txt_import')}
|
confirmText={isZipPasswordSubmitting ? t('txt_loading') : t('txt_import')}
|
||||||
cancelText={t('txt_cancel')}
|
cancelText={t('txt_cancel')}
|
||||||
showIcon={false}
|
showIcon={false}
|
||||||
|
confirmDisabled={isZipPasswordSubmitting}
|
||||||
|
cancelDisabled={isZipPasswordSubmitting}
|
||||||
onConfirm={() => void handleZipPasswordImportConfirm()}
|
onConfirm={() => void handleZipPasswordImportConfirm()}
|
||||||
onCancel={() => {
|
onCancel={() => {
|
||||||
if (isZipPasswordSubmitting) return;
|
if (isZipPasswordSubmitting) return;
|
||||||
|
|||||||
@@ -1009,6 +1009,7 @@ function folderName(id: string | null | undefined): string {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<VaultDialogs
|
<VaultDialogs
|
||||||
|
busy={busy}
|
||||||
fieldModalOpen={fieldModalOpen}
|
fieldModalOpen={fieldModalOpen}
|
||||||
fieldType={fieldType}
|
fieldType={fieldType}
|
||||||
fieldLabel={fieldLabel}
|
fieldLabel={fieldLabel}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { FIELD_TYPE_OPTIONS, toBooleanFieldValue } from '@/components/vault/vaul
|
|||||||
import { t } from '@/lib/i18n';
|
import { t } from '@/lib/i18n';
|
||||||
|
|
||||||
interface VaultDialogsProps {
|
interface VaultDialogsProps {
|
||||||
|
busy: boolean;
|
||||||
fieldModalOpen: boolean;
|
fieldModalOpen: boolean;
|
||||||
fieldType: CustomFieldType;
|
fieldType: CustomFieldType;
|
||||||
fieldLabel: string;
|
fieldLabel: string;
|
||||||
@@ -108,6 +109,8 @@ export default function VaultDialogs(props: VaultDialogsProps) {
|
|||||||
message={t('txt_archive_item_message')}
|
message={t('txt_archive_item_message')}
|
||||||
confirmText={t('txt_archive')}
|
confirmText={t('txt_archive')}
|
||||||
cancelText={t('txt_cancel')}
|
cancelText={t('txt_cancel')}
|
||||||
|
confirmDisabled={props.busy}
|
||||||
|
cancelDisabled={props.busy}
|
||||||
onConfirm={props.onConfirmArchive}
|
onConfirm={props.onConfirmArchive}
|
||||||
onCancel={props.onCancelArchive}
|
onCancel={props.onCancelArchive}
|
||||||
/>
|
/>
|
||||||
@@ -118,11 +121,22 @@ export default function VaultDialogs(props: VaultDialogsProps) {
|
|||||||
message={t('txt_archive_selected_items_message', { count: props.selectedCount })}
|
message={t('txt_archive_selected_items_message', { count: props.selectedCount })}
|
||||||
confirmText={t('txt_archive')}
|
confirmText={t('txt_archive')}
|
||||||
cancelText={t('txt_cancel')}
|
cancelText={t('txt_cancel')}
|
||||||
|
confirmDisabled={props.busy}
|
||||||
|
cancelDisabled={props.busy}
|
||||||
onConfirm={props.onConfirmBulkArchive}
|
onConfirm={props.onConfirmBulkArchive}
|
||||||
onCancel={props.onCancelBulkArchive}
|
onCancel={props.onCancelBulkArchive}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ConfirmDialog open={props.pendingDeleteOpen} title={t('txt_delete_item')} message={t('txt_are_you_sure_you_want_to_delete_this_item')} danger onConfirm={props.onConfirmDelete} onCancel={props.onCancelDelete} />
|
<ConfirmDialog
|
||||||
|
open={props.pendingDeleteOpen}
|
||||||
|
title={t('txt_delete_item')}
|
||||||
|
message={t('txt_are_you_sure_you_want_to_delete_this_item')}
|
||||||
|
danger
|
||||||
|
confirmDisabled={props.busy}
|
||||||
|
cancelDisabled={props.busy}
|
||||||
|
onConfirm={props.onConfirmDelete}
|
||||||
|
onCancel={props.onCancelDelete}
|
||||||
|
/>
|
||||||
|
|
||||||
<ConfirmDialog
|
<ConfirmDialog
|
||||||
open={props.bulkDeleteOpen}
|
open={props.bulkDeleteOpen}
|
||||||
@@ -133,11 +147,23 @@ export default function VaultDialogs(props: VaultDialogsProps) {
|
|||||||
: t('txt_are_you_sure_you_want_to_delete_count_selected_items', { count: props.selectedCount })
|
: t('txt_are_you_sure_you_want_to_delete_count_selected_items', { count: props.selectedCount })
|
||||||
}
|
}
|
||||||
danger
|
danger
|
||||||
|
confirmDisabled={props.busy}
|
||||||
|
cancelDisabled={props.busy}
|
||||||
onConfirm={props.onConfirmBulkDelete}
|
onConfirm={props.onConfirmBulkDelete}
|
||||||
onCancel={props.onCancelBulkDelete}
|
onCancel={props.onCancelBulkDelete}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ConfirmDialog open={props.moveOpen} title={t('txt_move_selected_items')} message={t('txt_choose_destination_folder')} confirmText={t('txt_move')} cancelText={t('txt_cancel')} onConfirm={props.onConfirmMove} onCancel={props.onCancelMove}>
|
<ConfirmDialog
|
||||||
|
open={props.moveOpen}
|
||||||
|
title={t('txt_move_selected_items')}
|
||||||
|
message={t('txt_choose_destination_folder')}
|
||||||
|
confirmText={t('txt_move')}
|
||||||
|
cancelText={t('txt_cancel')}
|
||||||
|
confirmDisabled={props.busy}
|
||||||
|
cancelDisabled={props.busy}
|
||||||
|
onConfirm={props.onConfirmMove}
|
||||||
|
onCancel={props.onCancelMove}
|
||||||
|
>
|
||||||
<label className="field">
|
<label className="field">
|
||||||
<span>{t('txt_folder')}</span>
|
<span>{t('txt_folder')}</span>
|
||||||
<select className="input" value={props.moveFolderId} onInput={(e) => props.onMoveFolderIdChange((e.currentTarget as HTMLSelectElement).value)}>
|
<select className="input" value={props.moveFolderId} onInput={(e) => props.onMoveFolderIdChange((e.currentTarget as HTMLSelectElement).value)}>
|
||||||
@@ -151,14 +177,34 @@ export default function VaultDialogs(props: VaultDialogsProps) {
|
|||||||
</label>
|
</label>
|
||||||
</ConfirmDialog>
|
</ConfirmDialog>
|
||||||
|
|
||||||
<ConfirmDialog open={props.createFolderOpen} title={t('txt_create_folder')} message={t('txt_enter_a_folder_name')} confirmText={t('txt_create')} cancelText={t('txt_cancel')} onConfirm={props.onConfirmCreateFolder} onCancel={props.onCancelCreateFolder}>
|
<ConfirmDialog
|
||||||
|
open={props.createFolderOpen}
|
||||||
|
title={t('txt_create_folder')}
|
||||||
|
message={t('txt_enter_a_folder_name')}
|
||||||
|
confirmText={t('txt_create')}
|
||||||
|
cancelText={t('txt_cancel')}
|
||||||
|
confirmDisabled={props.busy}
|
||||||
|
cancelDisabled={props.busy}
|
||||||
|
onConfirm={props.onConfirmCreateFolder}
|
||||||
|
onCancel={props.onCancelCreateFolder}
|
||||||
|
>
|
||||||
<label className="field">
|
<label className="field">
|
||||||
<span>{t('txt_folder_name')}</span>
|
<span>{t('txt_folder_name')}</span>
|
||||||
<input className="input" value={props.newFolderName} onInput={(e) => props.onNewFolderNameChange((e.currentTarget as HTMLInputElement).value)} />
|
<input className="input" value={props.newFolderName} onInput={(e) => props.onNewFolderNameChange((e.currentTarget as HTMLInputElement).value)} />
|
||||||
</label>
|
</label>
|
||||||
</ConfirmDialog>
|
</ConfirmDialog>
|
||||||
|
|
||||||
<ConfirmDialog open={props.renameFolderOpen} title={t('txt_edit')} message={t('txt_enter_a_folder_name')} confirmText={t('txt_save')} cancelText={t('txt_cancel')} onConfirm={props.onConfirmRenameFolder} onCancel={props.onCancelRenameFolder}>
|
<ConfirmDialog
|
||||||
|
open={props.renameFolderOpen}
|
||||||
|
title={t('txt_edit')}
|
||||||
|
message={t('txt_enter_a_folder_name')}
|
||||||
|
confirmText={t('txt_save')}
|
||||||
|
cancelText={t('txt_cancel')}
|
||||||
|
confirmDisabled={props.busy}
|
||||||
|
cancelDisabled={props.busy}
|
||||||
|
onConfirm={props.onConfirmRenameFolder}
|
||||||
|
onCancel={props.onCancelRenameFolder}
|
||||||
|
>
|
||||||
<label className="field">
|
<label className="field">
|
||||||
<span>{t('txt_folder_name')}</span>
|
<span>{t('txt_folder_name')}</span>
|
||||||
<input className="input" value={props.renameFolderName} onInput={(e) => props.onRenameFolderNameChange((e.currentTarget as HTMLInputElement).value)} />
|
<input className="input" value={props.renameFolderName} onInput={(e) => props.onRenameFolderNameChange((e.currentTarget as HTMLInputElement).value)} />
|
||||||
@@ -172,13 +218,37 @@ export default function VaultDialogs(props: VaultDialogsProps) {
|
|||||||
confirmText={t('txt_delete')}
|
confirmText={t('txt_delete')}
|
||||||
cancelText={t('txt_cancel')}
|
cancelText={t('txt_cancel')}
|
||||||
danger
|
danger
|
||||||
|
confirmDisabled={props.busy}
|
||||||
|
cancelDisabled={props.busy}
|
||||||
onConfirm={props.onConfirmDeleteFolder}
|
onConfirm={props.onConfirmDeleteFolder}
|
||||||
onCancel={props.onCancelDeleteFolder}
|
onCancel={props.onCancelDeleteFolder}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ConfirmDialog open={props.deleteAllFoldersOpen} title={t('txt_delete_all_folders')} message={t('txt_delete_all_folders_message')} confirmText={t('txt_delete')} cancelText={t('txt_cancel')} danger onConfirm={props.onConfirmDeleteAllFolders} onCancel={props.onCancelDeleteAllFolders} />
|
<ConfirmDialog
|
||||||
|
open={props.deleteAllFoldersOpen}
|
||||||
|
title={t('txt_delete_all_folders')}
|
||||||
|
message={t('txt_delete_all_folders_message')}
|
||||||
|
confirmText={t('txt_delete')}
|
||||||
|
cancelText={t('txt_cancel')}
|
||||||
|
danger
|
||||||
|
confirmDisabled={props.busy}
|
||||||
|
cancelDisabled={props.busy}
|
||||||
|
onConfirm={props.onConfirmDeleteAllFolders}
|
||||||
|
onCancel={props.onCancelDeleteAllFolders}
|
||||||
|
/>
|
||||||
|
|
||||||
<ConfirmDialog open={props.repromptOpen} title={t('txt_unlock_item')} message={t('txt_enter_master_password_to_view_this_item')} confirmText={t('txt_unlock')} cancelText={t('txt_cancel')} showIcon={false} onConfirm={props.onConfirmReprompt} onCancel={props.onCancelReprompt}>
|
<ConfirmDialog
|
||||||
|
open={props.repromptOpen}
|
||||||
|
title={t('txt_unlock_item')}
|
||||||
|
message={t('txt_enter_master_password_to_view_this_item')}
|
||||||
|
confirmText={t('txt_unlock')}
|
||||||
|
cancelText={t('txt_cancel')}
|
||||||
|
showIcon={false}
|
||||||
|
confirmDisabled={props.busy}
|
||||||
|
cancelDisabled={props.busy}
|
||||||
|
onConfirm={props.onConfirmReprompt}
|
||||||
|
onCancel={props.onCancelReprompt}
|
||||||
|
>
|
||||||
<label className="field">
|
<label className="field">
|
||||||
<span>{t('txt_master_password')}</span>
|
<span>{t('txt_master_password')}</span>
|
||||||
<input className="input" type="password" value={props.repromptPassword} onInput={(e) => props.onRepromptPasswordChange((e.currentTarget as HTMLInputElement).value)} />
|
<input className="input" type="password" value={props.repromptPassword} onInput={(e) => props.onRepromptPasswordChange((e.currentTarget as HTMLInputElement).value)} />
|
||||||
|
|||||||
Reference in New Issue
Block a user