diff --git a/cshare-app/src/components/FileDetailSlideOver.jsx b/cshare-app/src/components/FileDetailSlideOver.jsx
new file mode 100644
index 0000000..c04735a
--- /dev/null
+++ b/cshare-app/src/components/FileDetailSlideOver.jsx
@@ -0,0 +1,418 @@
+import { useState, useEffect, useRef, useMemo } from 'react';
+import { CopyIcon, CheckIcon, TrashIcon, EyeIcon, ClockIcon, DownloadIcon, XIcon } from './Icons';
+import { formatFileSize, formatDate, formatDateRelative, getFileIcon, getFileIconColor } from '../data/mockData';
+import { useToast } from './ToastContext';
+import { getFileAnalytics } from '../lib/api';
+
+const MOCK_CHART_DATA = [
+ { day: 'Mon', views: 12, downloads: 3 },
+ { day: 'Tue', views: 18, downloads: 5 },
+ { day: 'Wed', views: 8, downloads: 2 },
+ { day: 'Thu', views: 22, downloads: 7 },
+ { day: 'Fri', views: 15, downloads: 4 },
+ { day: 'Sat', views: 6, downloads: 1 },
+ { day: 'Sun', views: 10, downloads: 3 },
+];
+
+function MiniChart({ data }) {
+ const [hoveredIdx, setHoveredIdx] = useState(null);
+ const width = 360;
+ const height = 100;
+ const padding = 15;
+
+ if (!data || data.length === 0) return null;
+
+ const maxVal = Math.max(...data.map(d => Math.max(d.views, d.downloads)), 1);
+
+ const chartWidth = width - padding * 2;
+ const chartHeight = height - padding * 2;
+
+ // Coordinates for points
+ const points = data.map((d, i) => {
+ const x = padding + (i / (data.length - 1)) * chartWidth;
+ const yViews = padding + chartHeight - (d.views / maxVal) * chartHeight;
+ const yDownloads = padding + chartHeight - (d.downloads / maxVal) * chartHeight;
+ return { x, yViews, yDownloads, ...d };
+ });
+
+ // Construct smooth lines or simple linear svg paths
+ const viewsLine = points.map((p, i) => `${i === 0 ? 'M' : 'L'} ${p.x} ${p.yViews}`).join(' ');
+ const downloadsLine = points.map((p, i) => `${i === 0 ? 'M' : 'L'} ${p.x} ${p.yDownloads}`).join(' ');
+
+ // Construct area paths (fill under the line)
+ const viewsArea = points.length > 0
+ ? `${viewsLine} L ${points[points.length - 1].x} ${height - padding} L ${points[0].x} ${height - padding} Z`
+ : '';
+ const downloadsArea = points.length > 0
+ ? `${downloadsLine} L ${points[points.length - 1].x} ${height - padding} L ${points[0].x} ${height - padding} Z`
+ : '';
+
+ return (
+
+
+
+
+ {/* Hover Tooltip */}
+ {hoveredIdx !== null && (
+
+ {points[hoveredIdx].day}
+ Views: {points[hoveredIdx].views}
+ Downloads: {points[hoveredIdx].downloads}
+
+ )}
+
+
+ {/* Legend & X Axis */}
+
+ {data.map((d, i) => (
+
+ {d.day}
+
+ ))}
+
+
+
+ Views
+
+
+ Downloads
+
+
+
+ );
+}
+
+export default function FileDetailSlideOver({ file, onClose, onDelete }) {
+ const panelRef = useRef(null);
+ const [copied, setCopied] = useState(false);
+ const addToast = useToast();
+ const [analytics, setAnalytics] = useState(null);
+ const [analyticsLoading, setAnalyticsLoading] = useState(true);
+
+ useEffect(() => {
+ if (!file?.id) return;
+ setAnalyticsLoading(true);
+ getFileAnalytics(file.id)
+ .then((data) => {
+ setAnalytics(data);
+ })
+ .catch((err) => {
+ console.error(err);
+ })
+ .finally(() => {
+ setAnalyticsLoading(false);
+ });
+ }, [file?.id]);
+
+ const daysOfWeek = useMemo(() => ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], []);
+ const chartData = useMemo(() => {
+ if (!analytics?.dailyBreakdown) {
+ return MOCK_CHART_DATA;
+ }
+ return analytics.dailyBreakdown.slice(-7).map((item) => {
+ const d = new Date(item.date + 'T00:00:00');
+ return {
+ day: daysOfWeek[d.getDay()],
+ views: item.views,
+ downloads: item.downloads,
+ };
+ });
+ }, [analytics, daysOfWeek]);
+
+ useEffect(() => {
+ const handleKey = (e) => { if (e.key === 'Escape') onClose(); };
+ document.addEventListener('keydown', handleKey);
+ document.body.style.overflow = 'hidden';
+ return () => {
+ document.removeEventListener('keydown', handleKey);
+ document.body.style.overflow = '';
+ };
+ }, [onClose]);
+
+ if (!file) return null;
+
+ const copyLink = () => {
+ navigator.clipboard?.writeText(file.link);
+ setCopied(true);
+ addToast('Link copied to clipboard', 'success');
+ setTimeout(() => setCopied(false), 2000);
+ };
+
+ const daysUntilExpiry = Math.ceil((new Date(file.expiresAt) - new Date()) / (1000 * 60 * 60 * 24));
+ const isExpiringSoon = daysUntilExpiry <= 3 && daysUntilExpiry > 0;
+
+ return (
+
+ {/* Backdrop */}
+
+
+ {/* Panel */}
+
e.stopPropagation()}
+ >
+ {/* Top accent */}
+
+
+
+
+ {file.name}
+
+
+
+
+
+ {/* Content */}
+
+ {/* File type badge */}
+
+
+ {getFileIcon(file.type)}
+
+
+
+ {file.type.split('/')[1]?.toUpperCase() || 'FILE'} File
+
+
+ {formatFileSize(file.size)}
+
+
+
+
+ {/* Expiry badge */}
+
+
+
+
Expires
+
+ {formatDateRelative(file.expiresAt)} — {formatDate(file.expiresAt)}
+
+
+
+
+ {/* Stats row */}
+
+
+
+
+ Views
+
+
{file.views}
+
+
+
+
+ Downloads
+
+
{file.downloads}
+
+
+
+ {/* Mini chart */}
+
+
+ Last 7 Days
+
+
+
+
+
+
+ {/* Share link */}
+
+
+ Share Link
+
+
+
+ {file.link}
+
+
+
+
+
+ {/* Details grid */}
+
+
+
Uploaded
+
{formatDate(file.uploadedAt)}
+
+
+
+
Type
+
{file.type.split('/')[1]?.toUpperCase() || 'FILE'}
+
+
+
Size
+
{formatFileSize(file.size)}
+
+
+
+
+ {/* Footer actions */}
+
+
+
+
+
+
+ );
+}