mirror of
https://github.com/shuaiplus/nodewarden.git
synced 2026-06-20 21:00:41 +00:00
feat: add backup recommendations and update backup strategy UI
- Introduced new backup recommendations feature with interfaces for recommended storage providers. - Updated i18n translations for backup strategy to reflect new terminology and improved descriptions. - Enhanced types with optional private and public keys in user profiles. - Redesigned backup-related styles for better layout and responsiveness. - Updated TypeScript configuration to include shared modules. - Configured Vite to resolve shared modules and allow filesystem access. - Added cron triggers for periodic tasks in Wrangler configuration.
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
import { Plus } from 'lucide-preact';
|
||||
import type { BackupDestinationRecord, BackupDestinationType } from '@/lib/api';
|
||||
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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user