mirror of
https://github.com/shuaiplus/nodewarden.git
synced 2026-06-20 21:00:41 +00:00
feat: implement archive and bulk archive functionality with confirmation dialogs
This commit is contained in:
@@ -79,7 +79,9 @@ export default function VaultPage(props: VaultPageProps) {
|
|||||||
const [fieldLabel, setFieldLabel] = useState('');
|
const [fieldLabel, setFieldLabel] = useState('');
|
||||||
const [fieldValue, setFieldValue] = useState('');
|
const [fieldValue, setFieldValue] = useState('');
|
||||||
const [localError, setLocalError] = useState('');
|
const [localError, setLocalError] = useState('');
|
||||||
|
const [pendingArchive, setPendingArchive] = useState<Cipher | null>(null);
|
||||||
const [pendingDelete, setPendingDelete] = useState<Cipher | null>(null);
|
const [pendingDelete, setPendingDelete] = useState<Cipher | null>(null);
|
||||||
|
const [bulkArchiveOpen, setBulkArchiveOpen] = useState(false);
|
||||||
const [bulkDeleteOpen, setBulkDeleteOpen] = useState(false);
|
const [bulkDeleteOpen, setBulkDeleteOpen] = useState(false);
|
||||||
const [moveOpen, setMoveOpen] = useState(false);
|
const [moveOpen, setMoveOpen] = useState(false);
|
||||||
const [moveFolderId, setMoveFolderId] = useState('__none__');
|
const [moveFolderId, setMoveFolderId] = useState('__none__');
|
||||||
@@ -684,6 +686,20 @@ function folderName(id: string | null | undefined): string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function confirmArchiveSelected(): Promise<void> {
|
||||||
|
if (!pendingArchive) return;
|
||||||
|
setBusy(true);
|
||||||
|
try {
|
||||||
|
await props.onArchive(pendingArchive);
|
||||||
|
setPendingArchive(null);
|
||||||
|
if (isMobileLayout && selectedCipherId === pendingArchive.id) {
|
||||||
|
setMobilePanel('list');
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
setBusy(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function confirmBulkArchive(): Promise<void> {
|
async function confirmBulkArchive(): Promise<void> {
|
||||||
const ids = Object.entries(selectedMap)
|
const ids = Object.entries(selectedMap)
|
||||||
.filter(([, selected]) => selected)
|
.filter(([, selected]) => selected)
|
||||||
@@ -693,6 +709,7 @@ function folderName(id: string | null | undefined): string {
|
|||||||
try {
|
try {
|
||||||
await props.onBulkArchive(ids);
|
await props.onBulkArchive(ids);
|
||||||
setSelectedMap({});
|
setSelectedMap({});
|
||||||
|
setBulkArchiveOpen(false);
|
||||||
} finally {
|
} finally {
|
||||||
setBusy(false);
|
setBusy(false);
|
||||||
}
|
}
|
||||||
@@ -795,7 +812,7 @@ function folderName(id: string | null | undefined): string {
|
|||||||
onToggleCreateMenu={() => setCreateMenuOpen((open) => !open)}
|
onToggleCreateMenu={() => setCreateMenuOpen((open) => !open)}
|
||||||
onStartCreate={startCreate}
|
onStartCreate={startCreate}
|
||||||
onBulkRestore={() => void confirmBulkRestore()}
|
onBulkRestore={() => void confirmBulkRestore()}
|
||||||
onBulkArchive={() => void confirmBulkArchive()}
|
onBulkArchive={() => setBulkArchiveOpen(true)}
|
||||||
onBulkUnarchive={() => void confirmBulkUnarchive()}
|
onBulkUnarchive={() => void confirmBulkUnarchive()}
|
||||||
onOpenMove={() => {
|
onOpenMove={() => {
|
||||||
setMoveFolderId('__none__');
|
setMoveFolderId('__none__');
|
||||||
@@ -888,7 +905,7 @@ function folderName(id: string | null | undefined): string {
|
|||||||
attachmentDownloadPercent={props.attachmentDownloadPercent}
|
attachmentDownloadPercent={props.attachmentDownloadPercent}
|
||||||
onStartEdit={startEdit}
|
onStartEdit={startEdit}
|
||||||
onDelete={setPendingDelete}
|
onDelete={setPendingDelete}
|
||||||
onArchive={props.onArchive}
|
onArchive={(cipher) => setPendingArchive(cipher)}
|
||||||
onUnarchive={props.onUnarchive}
|
onUnarchive={props.onUnarchive}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@@ -902,6 +919,8 @@ function folderName(id: string | null | undefined): string {
|
|||||||
fieldType={fieldType}
|
fieldType={fieldType}
|
||||||
fieldLabel={fieldLabel}
|
fieldLabel={fieldLabel}
|
||||||
fieldValue={fieldValue}
|
fieldValue={fieldValue}
|
||||||
|
archiveConfirmOpen={!!pendingArchive}
|
||||||
|
bulkArchiveOpen={bulkArchiveOpen}
|
||||||
pendingDeleteOpen={!!pendingDelete}
|
pendingDeleteOpen={!!pendingDelete}
|
||||||
bulkDeleteOpen={bulkDeleteOpen}
|
bulkDeleteOpen={bulkDeleteOpen}
|
||||||
sidebarTrashMode={sidebarFilter.kind === 'trash'}
|
sidebarTrashMode={sidebarFilter.kind === 'trash'}
|
||||||
@@ -944,6 +963,10 @@ function folderName(id: string | null | undefined): string {
|
|||||||
onFieldTypeChange={setFieldType}
|
onFieldTypeChange={setFieldType}
|
||||||
onFieldLabelChange={setFieldLabel}
|
onFieldLabelChange={setFieldLabel}
|
||||||
onFieldValueChange={setFieldValue}
|
onFieldValueChange={setFieldValue}
|
||||||
|
onConfirmArchive={() => void confirmArchiveSelected()}
|
||||||
|
onCancelArchive={() => setPendingArchive(null)}
|
||||||
|
onConfirmBulkArchive={() => void confirmBulkArchive()}
|
||||||
|
onCancelBulkArchive={() => setBulkArchiveOpen(false)}
|
||||||
onConfirmDelete={() => void deleteSelected()}
|
onConfirmDelete={() => void deleteSelected()}
|
||||||
onCancelDelete={() => setPendingDelete(null)}
|
onCancelDelete={() => setPendingDelete(null)}
|
||||||
onConfirmBulkDelete={() => void confirmBulkDelete()}
|
onConfirmBulkDelete={() => void confirmBulkDelete()}
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ interface VaultDialogsProps {
|
|||||||
fieldType: CustomFieldType;
|
fieldType: CustomFieldType;
|
||||||
fieldLabel: string;
|
fieldLabel: string;
|
||||||
fieldValue: string;
|
fieldValue: string;
|
||||||
|
archiveConfirmOpen: boolean;
|
||||||
|
bulkArchiveOpen: boolean;
|
||||||
pendingDeleteOpen: boolean;
|
pendingDeleteOpen: boolean;
|
||||||
bulkDeleteOpen: boolean;
|
bulkDeleteOpen: boolean;
|
||||||
sidebarTrashMode: boolean;
|
sidebarTrashMode: boolean;
|
||||||
@@ -26,6 +28,10 @@ interface VaultDialogsProps {
|
|||||||
onFieldTypeChange: (value: CustomFieldType) => void;
|
onFieldTypeChange: (value: CustomFieldType) => void;
|
||||||
onFieldLabelChange: (value: string) => void;
|
onFieldLabelChange: (value: string) => void;
|
||||||
onFieldValueChange: (value: string) => void;
|
onFieldValueChange: (value: string) => void;
|
||||||
|
onConfirmArchive: () => void;
|
||||||
|
onCancelArchive: () => void;
|
||||||
|
onConfirmBulkArchive: () => void;
|
||||||
|
onCancelBulkArchive: () => void;
|
||||||
onConfirmDelete: () => void;
|
onConfirmDelete: () => void;
|
||||||
onCancelDelete: () => void;
|
onCancelDelete: () => void;
|
||||||
onConfirmBulkDelete: () => void;
|
onConfirmBulkDelete: () => void;
|
||||||
@@ -88,6 +94,26 @@ export default function VaultDialogs(props: VaultDialogsProps) {
|
|||||||
)}
|
)}
|
||||||
</ConfirmDialog>
|
</ConfirmDialog>
|
||||||
|
|
||||||
|
<ConfirmDialog
|
||||||
|
open={props.archiveConfirmOpen}
|
||||||
|
title={t('txt_archive_item')}
|
||||||
|
message={t('txt_archive_item_message')}
|
||||||
|
confirmText={t('txt_archive')}
|
||||||
|
cancelText={t('txt_cancel')}
|
||||||
|
onConfirm={props.onConfirmArchive}
|
||||||
|
onCancel={props.onCancelArchive}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ConfirmDialog
|
||||||
|
open={props.bulkArchiveOpen}
|
||||||
|
title={t('txt_archive_selected_items')}
|
||||||
|
message={t('txt_archive_selected_items_message', { count: props.selectedCount })}
|
||||||
|
confirmText={t('txt_archive')}
|
||||||
|
cancelText={t('txt_cancel')}
|
||||||
|
onConfirm={props.onConfirmBulkArchive}
|
||||||
|
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 onConfirm={props.onConfirmDelete} onCancel={props.onCancelDelete} />
|
||||||
|
|
||||||
<ConfirmDialog
|
<ConfirmDialog
|
||||||
|
|||||||
@@ -281,6 +281,10 @@ const messages: Record<Locale, Record<string, string>> = {
|
|||||||
txt_delete_item_failed: "Delete item failed",
|
txt_delete_item_failed: "Delete item failed",
|
||||||
txt_delete_permanently: "Delete Permanently",
|
txt_delete_permanently: "Delete Permanently",
|
||||||
txt_archive: "Archive",
|
txt_archive: "Archive",
|
||||||
|
txt_archive_item: "Archive Item",
|
||||||
|
txt_archive_item_message: "After archiving, this item will be excluded from general search results and autofill suggestions.",
|
||||||
|
txt_archive_selected_items: "Archive Items",
|
||||||
|
txt_archive_selected_items_message: "After archiving, {count} selected items will be excluded from general search results and autofill suggestions.",
|
||||||
txt_archived: "Archived",
|
txt_archived: "Archived",
|
||||||
txt_archive_selected: "Archive",
|
txt_archive_selected: "Archive",
|
||||||
txt_item_archived: "Item archived",
|
txt_item_archived: "Item archived",
|
||||||
@@ -1376,6 +1380,10 @@ zhCNOverrides.txt_import_export_title = '导入导出';
|
|||||||
zhCNOverrides.txt_new_type_header = '新建{type}';
|
zhCNOverrides.txt_new_type_header = '新建{type}';
|
||||||
zhCNOverrides.txt_edit_type_header = '编辑{type}';
|
zhCNOverrides.txt_edit_type_header = '编辑{type}';
|
||||||
zhCNOverrides.txt_archive = '归档';
|
zhCNOverrides.txt_archive = '归档';
|
||||||
|
zhCNOverrides.txt_archive_item = '归档项目';
|
||||||
|
zhCNOverrides.txt_archive_item_message = '归档后,此项目将被排除在一般搜索结果和自动填充建议之外。';
|
||||||
|
zhCNOverrides.txt_archive_selected_items = '归档项目';
|
||||||
|
zhCNOverrides.txt_archive_selected_items_message = '归档后,所选的 {count} 个项目将被排除在一般搜索结果和自动填充建议之外。';
|
||||||
zhCNOverrides.txt_archived = '已归档';
|
zhCNOverrides.txt_archived = '已归档';
|
||||||
zhCNOverrides.txt_archive_selected = '归档';
|
zhCNOverrides.txt_archive_selected = '归档';
|
||||||
zhCNOverrides.txt_item_archived = '项目已归档';
|
zhCNOverrides.txt_item_archived = '项目已归档';
|
||||||
|
|||||||
Reference in New Issue
Block a user