Compare commits
2 Commits
fcbf534f5d
...
7e0baebc20
| Author | SHA1 | Date | |
|---|---|---|---|
| 7e0baebc20 | |||
| 2520c2b248 |
+57
-34
@@ -1,5 +1,3 @@
|
|||||||
|
|
||||||
|
|
||||||
import React, { useEffect, useState, useCallback, useRef } from 'react';
|
import React, { useEffect, useState, useCallback, useRef } from 'react';
|
||||||
import { Playlist, ServerType, SyncStrategy, PlexServerConnection, PathMappingConfig, PathMappingMode, SyncState, ScheduleSettings, ScheduleMode, BackupSettings } from './types';
|
import { Playlist, ServerType, SyncStrategy, PlexServerConnection, PathMappingConfig, PathMappingMode, SyncState, ScheduleSettings, ScheduleMode, BackupSettings } from './types';
|
||||||
import { apiService } from './services/api';
|
import { apiService } from './services/api';
|
||||||
@@ -17,7 +15,7 @@ import { SYNC_BANNER_PADDING_X, SYNC_BANNER_PADDING_Y, SYNC_BANNER_MIN_WIDTH } f
|
|||||||
import ServerPanel from './components/ServerPanel';
|
import ServerPanel from './components/ServerPanel';
|
||||||
import StrategySelector from './components/StrategySelector';
|
import StrategySelector from './components/StrategySelector';
|
||||||
import ConnectionModal from './components/ConnectionModal';
|
import ConnectionModal from './components/ConnectionModal';
|
||||||
import { ArrowLeftRight, ShieldCheck, X, Server, ServerOff, Clock, Eye, EyeOff, Type, Code2 } from 'lucide-react';
|
import { ArrowLeftRight, ShieldCheck, X, Server, ServerOff, Clock, Eye, EyeOff, Type, Code2, Archive } from 'lucide-react';
|
||||||
|
|
||||||
interface Toast {
|
interface Toast {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -542,6 +540,24 @@ const App: React.FC = () => {
|
|||||||
|
|
||||||
const pathMappingInfo = getPathMappingDisplayInfo(pathMappingConfig);
|
const pathMappingInfo = getPathMappingDisplayInfo(pathMappingConfig);
|
||||||
|
|
||||||
|
// Helper: Calculate Backup Info
|
||||||
|
const getBackupDisplayInfo = (settings: BackupSettings) => {
|
||||||
|
if (!settings.enabled) {
|
||||||
|
return {
|
||||||
|
label: 'Backups',
|
||||||
|
value: 'Disabled',
|
||||||
|
active: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
label: 'Backups',
|
||||||
|
value: `Keep ${settings.retentionCount}`,
|
||||||
|
active: true
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const backupInfo = getBackupDisplayInfo(backupSettings);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen flex flex-col bg-gray-900 text-gray-100 font-sans overflow-hidden bg-[radial-gradient(ellipse_at_top,_var(--tw-gradient-stops))] from-gray-800 via-gray-900 to-black">
|
<div className="min-h-screen flex flex-col bg-gray-900 text-gray-100 font-sans overflow-hidden bg-[radial-gradient(ellipse_at_top,_var(--tw-gradient-stops))] from-gray-800 via-gray-900 to-black">
|
||||||
|
|
||||||
@@ -619,39 +635,46 @@ const App: React.FC = () => {
|
|||||||
|
|
||||||
{/* Normal Toolbar Right */}
|
{/* Normal Toolbar Right */}
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
{/* Path Mapping Info */}
|
|
||||||
<div className="flex flex-col items-end hidden md:flex border-r border-gray-700/50 pr-4 mr-1">
|
|
||||||
<span className="text-[10px] uppercase font-bold text-gray-500 tracking-wider">
|
|
||||||
{pathMappingInfo.label}
|
|
||||||
</span>
|
|
||||||
<div className={`text-xs font-mono flex items-center gap-1.5 ${pathMappingInfo.active ? 'text-plex-orange' : 'text-gray-600'}`}>
|
|
||||||
{pathMappingInfo.active && <pathMappingInfo.Icon size={12} />}
|
|
||||||
<span>{pathMappingInfo.value}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Schedule Info */}
|
{/* Unified Status Dock */}
|
||||||
<div className="flex flex-col items-end mr-2 md:mr-0 hidden md:flex">
|
<div className="hidden md:flex items-center bg-gray-900/40 border border-gray-700/50 rounded-lg p-1 mr-2 backdrop-blur-sm shadow-sm transition-all hover:bg-gray-900/60 hover:border-gray-600/50">
|
||||||
<span className="text-[10px] uppercase font-bold text-gray-500 tracking-wider">
|
|
||||||
{scheduleInfo.label}
|
|
||||||
</span>
|
|
||||||
<div className="text-xs font-mono flex items-center gap-1.5">
|
|
||||||
{/* Schedule Part */}
|
|
||||||
<div className={`flex items-center gap-1.5 ${scheduleInfo.active ? 'text-plex-orange' : 'text-gray-600'}`}>
|
|
||||||
{scheduleInfo.active && <Clock size={12} />}
|
|
||||||
<span>{scheduleInfo.value}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Watch Part */}
|
{/* Path Mapping Section */}
|
||||||
<span className="text-gray-700 mx-0.5">|</span>
|
<div className="flex flex-col px-3 py-0.5 border-r border-gray-700/30 min-w-[90px] group/item">
|
||||||
<div
|
<span className="text-[9px] font-bold text-gray-500 uppercase tracking-widest group-hover/item:text-gray-400 transition-colors">Mapping</span>
|
||||||
className={`flex items-center gap-1 ${scheduleInfo.autoWatch ? 'text-plex-orange' : 'text-gray-600'}`}
|
<div className={`flex items-center gap-1.5 text-xs font-medium ${pathMappingInfo.active ? 'text-blue-400' : 'text-gray-600'}`}>
|
||||||
title={scheduleInfo.autoWatch ? "Local Playlist Monitoring Enabled" : "Local Playlist Monitoring Disabled"}
|
<pathMappingInfo.Icon size={12} strokeWidth={2.5} />
|
||||||
>
|
<span className="truncate">{pathMappingInfo.value === 'Not Set' ? 'None' : pathMappingInfo.value}</span>
|
||||||
{scheduleInfo.autoWatch ? <Eye size={12} /> : <EyeOff size={12} />}
|
</div>
|
||||||
<span className="text-[10px] font-sans font-bold">WATCH</span>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
{/* Backup Section */}
|
||||||
|
<div className="flex flex-col px-3 py-0.5 border-r border-gray-700/30 min-w-[90px] group/item">
|
||||||
|
<span className="text-[9px] font-bold text-gray-500 uppercase tracking-widest group-hover/item:text-gray-400 transition-colors">Backup</span>
|
||||||
|
<div className={`flex items-center gap-1.5 text-xs font-medium ${backupInfo.active ? 'text-indigo-400' : 'text-gray-600'}`}>
|
||||||
|
<Archive size={12} strokeWidth={2.5} />
|
||||||
|
<span>{backupInfo.active ? backupInfo.value.replace('Keep ', 'Retain: ') : 'Disabled'}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Schedule Section */}
|
||||||
|
<div className="flex flex-col px-3 py-0.5 min-w-[140px] group/item">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<span className="text-[9px] font-bold text-gray-500 uppercase tracking-widest group-hover/item:text-gray-400 transition-colors">Auto-Sync</span>
|
||||||
|
{/* Watch Indicator Badge */}
|
||||||
|
<div
|
||||||
|
className={`flex items-center gap-1 px-1 rounded-[2px] transition-colors ${scheduleInfo.autoWatch ? 'text-plex-orange bg-plex-orange/10' : 'text-gray-700 bg-gray-800'}`}
|
||||||
|
title={scheduleInfo.autoWatch ? "Watch Mode: Active" : "Watch Mode: Disabled"}
|
||||||
|
>
|
||||||
|
{scheduleInfo.autoWatch ? <Eye size={9} /> : <EyeOff size={9} />}
|
||||||
|
<span className="text-[8px] font-bold uppercase">Watch</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={`flex items-center gap-1.5 text-xs font-medium mt-0.5 ${scheduleInfo.active ? 'text-green-400' : 'text-gray-600'}`}>
|
||||||
|
<Clock size={12} strokeWidth={2.5} />
|
||||||
|
<span className="truncate max-w-[120px]">{scheduleInfo.active ? scheduleInfo.value : 'Disabled'}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Connection Status Button */}
|
{/* Connection Status Button */}
|
||||||
|
|||||||
Reference in New Issue
Block a user