Files
nodewarden/webapp/src/components/backup-center/BackupDestinationSidebar.tsx
T
shuaiplus f0ace28bf2 feat: add shared API utilities for handling requests and responses
- Introduced `shared.ts` with utility functions for API interactions, including JSON parsing, error handling, and content disposition parsing.
- Added `vault.ts` to manage vault-related operations such as folder and cipher management, including creation, deletion, and bulk operations.
- Implemented encryption and decryption methods for secure data handling within the vault.
- Created `backup-settings-repair.ts` to automatically repair backup settings for admin profiles if needed.
2026-03-15 04:17:09 +08:00

71 lines
2.9 KiB
TypeScript

import { Plus } from 'lucide-preact';
import type { BackupDestinationRecord, BackupDestinationType } from '@/lib/api/backup';
import { formatDateTime, getDestinationTypeLabel } from '@/lib/backup-center';
import { t } from '@/lib/i18n';
interface BackupDestinationSidebarProps {
destinations: BackupDestinationRecord[];
selectedDestinationId: string | null;
disableWhileBusy: boolean;
showAddChooser: boolean;
onSelectDestination: (destinationId: string) => void;
onToggleAddChooser: () => void;
onAddDestination: (type: BackupDestinationType) => void;
}
export function BackupDestinationSidebar(props: BackupDestinationSidebarProps) {
return (
<aside className="backup-destination-sidebar">
<div className="section-head">
<h3>{t('txt_backup_destinations_title')}</h3>
</div>
<div className="backup-destination-list">
{props.destinations.map((destination) => {
const isSelected = destination.id === props.selectedDestinationId;
const isScheduled = destination.schedule.enabled;
return (
<button
key={destination.id}
type="button"
className={`backup-destination-item ${isSelected ? 'active' : ''}`}
onClick={() => props.onSelectDestination(destination.id)}
>
<span className="backup-destination-top">
<span className="backup-destination-name">{destination.name || getDestinationTypeLabel(destination.type)}</span>
<span className="backup-destination-type">{getDestinationTypeLabel(destination.type)}</span>
</span>
<span className="backup-destination-meta">
{isScheduled ? t('txt_backup_destination_active_badge') : t('txt_backup_destination_idle_badge')}
</span>
<span className="backup-destination-meta">
{destination.runtime.lastSuccessAt
? t('txt_backup_destination_last_success', { time: formatDateTime(destination.runtime.lastSuccessAt) })
: t('txt_backup_destination_never_run')}
</span>
</button>
);
})}
</div>
<div className="actions backup-destination-addbar">
<button type="button" className="btn btn-secondary small" disabled={props.disableWhileBusy} onClick={props.onToggleAddChooser}>
<Plus size={14} className="btn-icon" />
{t('txt_backup_add_destination')}
</button>
</div>
{props.showAddChooser ? (
<div className="backup-add-chooser">
<button type="button" className="btn btn-secondary small" onClick={() => props.onAddDestination('webdav')}>
{t('txt_backup_protocol_webdav')}
</button>
<button type="button" className="btn btn-secondary small" onClick={() => props.onAddDestination('e3')}>
{t('txt_backup_protocol_e3')}
</button>
</div>
) : null}
</aside>
);
}