Squashed 'sample-front-end/' changes from 0881bf1..601ffe4
601ffe4 fix: Refine UI layout and visual elements 4689aaa feat: Add request timeout and cancellation to API calls git-subtree-dir: sample-front-end git-subtree-split: 601ffe468a78955839eef6c839314d9b96ea204d
This commit is contained in:
+43
-21
@@ -2,17 +2,18 @@
|
||||
import React from 'react';
|
||||
import { Playlist, ServerType, PlexServerConnection } from '../types';
|
||||
import PlaylistCard from './PlaylistCard';
|
||||
import { RefreshCw, Server, Cloud, WifiOff } from 'lucide-react';
|
||||
import { RefreshCw, Server, Cloud, WifiOff, X } from 'lucide-react';
|
||||
|
||||
interface ServerPanelProps {
|
||||
type: ServerType;
|
||||
playlists: Playlist[];
|
||||
isLoading: boolean;
|
||||
onRefresh: () => void;
|
||||
onCancel?: () => void;
|
||||
serverInfo?: PlexServerConnection;
|
||||
}
|
||||
|
||||
const ServerPanel: React.FC<ServerPanelProps> = ({ type, playlists, isLoading, onRefresh, serverInfo }) => {
|
||||
const ServerPanel: React.FC<ServerPanelProps> = ({ type, playlists, isLoading, onRefresh, onCancel, serverInfo }) => {
|
||||
const isLocal = type === ServerType.LOCAL;
|
||||
|
||||
let Icon = isLocal ? Server : Cloud;
|
||||
@@ -65,21 +66,30 @@ const ServerPanel: React.FC<ServerPanelProps> = ({ type, playlists, isLoading, o
|
||||
}
|
||||
}
|
||||
|
||||
// Handle Refresh/Cancel Click
|
||||
const handleAction = () => {
|
||||
if (isLoading && onCancel) {
|
||||
onCancel();
|
||||
} else {
|
||||
onRefresh();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`flex flex-row md:flex-col h-full ${bgGradient} rounded-2xl border ${borderColor} backdrop-blur-xl shadow-xl overflow-hidden transition-all duration-300`}>
|
||||
|
||||
{/* Header */}
|
||||
{/* Mobile: Order Last (Right side), Vertical Text */}
|
||||
{/* Desktop: Order First (Top side), Horizontal Text */}
|
||||
<div className={`
|
||||
relative flex-none
|
||||
order-last md:order-first
|
||||
w-[72px] md:w-full
|
||||
h-full md:h-auto md:min-h-[80px]
|
||||
flex flex-col md:flex-row items-center justify-between
|
||||
py-6 md:py-0 md:px-8
|
||||
bg-gray-800/60 border-l md:border-l-0 md:border-b border-white/5
|
||||
`}>
|
||||
<div
|
||||
className={`
|
||||
relative flex-none
|
||||
order-last md:order-first
|
||||
w-[72px] md:w-full
|
||||
h-full md:h-auto md:min-h-[80px]
|
||||
flex flex-col md:flex-row items-center justify-between
|
||||
py-6 md:py-0 md:px-8
|
||||
bg-gray-800/60 border-l md:border-l-0 md:border-b border-white/5
|
||||
`}
|
||||
>
|
||||
|
||||
{/* Title Group */}
|
||||
<div className="flex flex-col md:flex-row items-center md:space-x-4 overflow-hidden w-full md:w-auto h-full md:h-full md:py-4">
|
||||
@@ -89,9 +99,8 @@ const ServerPanel: React.FC<ServerPanelProps> = ({ type, playlists, isLoading, o
|
||||
<Icon size={22} strokeWidth={2} />
|
||||
</div>
|
||||
|
||||
{/* Text Container - Vertical on Mobile, Horizontal on Desktop */}
|
||||
{/* Text Container */}
|
||||
<div className="flex-1 min-w-0 flex flex-col justify-center items-center md:items-start h-full md:h-auto w-full md:w-auto">
|
||||
{/* Use vertical-rl for vertical text flow, rotate-180 to flip it bottom-up */}
|
||||
<div className="flex flex-col justify-center w-full md:w-auto [writing-mode:vertical-rl] rotate-180 md:[writing-mode:horizontal-tb] md:rotate-0 items-center md:items-start gap-1 md:gap-0">
|
||||
<h2 className="text-sm md:text-lg font-bold text-gray-100 tracking-wide whitespace-nowrap" title={displayTitle}>
|
||||
{displayTitle}
|
||||
@@ -103,14 +112,27 @@ const ServerPanel: React.FC<ServerPanelProps> = ({ type, playlists, isLoading, o
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Refresh Button */}
|
||||
{/* Refresh/Stop Button */}
|
||||
<button
|
||||
onClick={onRefresh}
|
||||
disabled={isLoading}
|
||||
className="flex-shrink-0 p-2.5 text-gray-400 hover:text-white hover:bg-white/10 rounded-full transition-all active:scale-90 disabled:opacity-50 disabled:cursor-not-allowed mt-4 md:mt-0 md:ml-4"
|
||||
title="Refresh Playlists"
|
||||
onClick={handleAction}
|
||||
className={`flex-shrink-0 p-2.5 rounded-full transition-all active:scale-90 mt-4 md:mt-0 md:ml-4 border border-transparent group relative
|
||||
${isLoading
|
||||
? 'text-plex-orange bg-plex-orange/10 border-plex-orange/20 hover:bg-red-500/10 hover:border-red-500/30'
|
||||
: 'text-gray-400 hover:text-white hover:bg-white/10'
|
||||
}
|
||||
`}
|
||||
title={isLoading ? "Cancel Refresh" : "Refresh Playlists"}
|
||||
>
|
||||
<RefreshCw size={20} className={isLoading ? 'animate-spin text-plex-orange' : ''} strokeWidth={2} />
|
||||
{isLoading ? (
|
||||
<div className="relative flex items-center justify-center">
|
||||
{/* Outer Spinner */}
|
||||
<RefreshCw size={20} strokeWidth={2} className="animate-spin opacity-40 group-hover:opacity-20 transition-opacity" />
|
||||
{/* Inner Cancel X */}
|
||||
<X size={12} strokeWidth={3} className="absolute text-plex-orange group-hover:text-red-400 transition-colors" />
|
||||
</div>
|
||||
) : (
|
||||
<RefreshCw size={20} strokeWidth={2} />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user