Dine360-Ads-Frontend/src/Pages/ManageFilesPage.jsx
MOHAN 56dd06b0f7 Complete UI/UX redesign — vibrant dark theme with orange/amber brand
- New collapsible sidebar (64px → 220px on hover) replaces top header
- Vibrant dark purple base (#0c0a1e) with glassmorphism cards
- Orange/amber gradient brand color throughout
- Redesigned Login page with floating orbs and glass card
- New AdminDashboard with gradient icon cards
- Partners/Clients/Mapping cards with modern hover effects
- AddPartner/ManagePartner forms with section-based glass layout
- ManageFilesPage with drag-upload zone and media grid
- ManageFilesOrderPage with collapsible screen groups and toggle UI
- SettingsPage with card-per-setting layout
- Shared CSS design system (glass-card, btn-primary, input-field, etc.)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 16:53:05 +05:30

137 lines
5.5 KiB
JavaScript

import { useEffect, useState } from "react";
import { motion } from "framer-motion";
import { FaUpload, FaTrash, FaFileImage, FaFileVideo } from "react-icons/fa";
import { api } from "../API/api";
import { useLoading } from "../Context/LoadingContext";
const ManageFiles = ({ id }) => {
const BACKEND = "https://backend.dine360ads.com/";
const [files, setFiles] = useState([]);
const [upd, setupd] = useState(0);
const [uploading, setUploading] = useState(false);
const { setLoading } = useLoading();
useEffect(() => { fetchFiles(); }, [id, upd]);
const fetchFiles = async () => {
try {
setLoading(true);
const res = await api.get(`/client/files/${id}`);
setFiles(res);
} catch (e) { console.error(e); }
setLoading(false);
};
const deleteFiles = async (fileid) => {
try {
setLoading(true);
await api.delete(`/files/del/${fileid}`);
setupd(upd + 1);
} catch (e) { console.error(e); }
setLoading(false);
};
const handleFileUpload = async (file) => {
if (!file) return;
setUploading(true);
const fd = new FormData();
fd.append("file", file);
fd.append("file_name", file.name);
fd.append("client_id", id);
try {
setLoading(true);
await api.post("/files/upload", fd, { headers: { "Content-Type": "multipart/form-data" } });
fetchFiles();
} catch (e) { console.error(e); }
finally { setUploading(false); setLoading(false); }
};
const handleMultipleUpload = async (e) => {
const filesToUpload = e.target.files;
if (!filesToUpload?.length) return;
setUploading(true);
for (let i = 0; i < filesToUpload.length; i++) {
await handleFileUpload(filesToUpload[i]);
}
setUploading(false);
};
return (
<div>
<h2 className="text-white font-bold text-lg mb-4">Media Files</h2>
{/* Upload zone */}
<label
className="flex flex-col items-center justify-center gap-3 rounded-2xl p-8 mb-6 cursor-pointer transition-all"
style={{
background: "rgba(249,115,22,0.04)",
border: "2px dashed rgba(249,115,22,0.2)",
}}
onMouseEnter={e => { e.currentTarget.style.borderColor = "rgba(249,115,22,0.45)"; e.currentTarget.style.background = "rgba(249,115,22,0.08)"; }}
onMouseLeave={e => { e.currentTarget.style.borderColor = "rgba(249,115,22,0.2)"; e.currentTarget.style.background = "rgba(249,115,22,0.04)"; }}
>
<div
className="w-12 h-12 rounded-xl flex items-center justify-center"
style={{ background: "linear-gradient(135deg, #f97316, #f59e0b)", boxShadow: "0 4px 15px rgba(249,115,22,0.3)" }}
>
<FaUpload className="w-5 h-5 text-white" />
</div>
<div className="text-center">
<p className="text-white font-semibold text-sm">{uploading ? "Uploading..." : "Click to upload files"}</p>
<p className="text-xs mt-1" style={{ color: "#475569" }}>Images and videos supported</p>
</div>
<input type="file" onChange={handleMultipleUpload} className="hidden" multiple />
</label>
{/* File list */}
{files.length === 0 ? (
<p className="text-center py-8 text-sm" style={{ color: "#334155" }}>No files uploaded yet.</p>
) : (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
{files.map((file, index) => {
const fileName = file.file_name;
const fileUrl = BACKEND + file.file_path;
const isImage = /\.(jpe?g|png|gif)$/i.test(fileUrl);
const isVideo = /\.(mp4|webm|ogg|mkv)$/i.test(fileUrl);
return (
<motion.div
key={index}
className="rounded-2xl overflow-hidden"
style={{ background: "rgba(255,255,255,0.04)", border: "1px solid rgba(255,255,255,0.08)" }}
initial={{ opacity: 0, scale: 0.97 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ delay: index * 0.04 }}
>
<div className="relative h-36 bg-black flex items-center justify-center" style={{ background: "rgba(0,0,0,0.3)" }}>
{isImage && <img src={fileUrl} alt={fileName} className="h-full w-full object-cover" />}
{isVideo && <video className="h-full w-full object-cover" controls><source src={fileUrl} type="video/mp4" /></video>}
{!isImage && !isVideo && (
<div className="flex flex-col items-center gap-2 opacity-40">
<FaFileImage className="w-8 h-8 text-white" />
</div>
)}
</div>
<div className="p-3 flex items-center justify-between gap-2">
<p className="text-xs text-white truncate flex-1">{fileName}</p>
<button
className="w-7 h-7 rounded-lg flex items-center justify-center flex-shrink-0 transition-all"
style={{ background: "rgba(239,68,68,0.12)", border: "1px solid rgba(239,68,68,0.18)" }}
onClick={() => { if (confirm("Delete this file?")) deleteFiles(file.transid); }}
onMouseEnter={e => { e.currentTarget.style.background = "rgba(239,68,68,0.28)"; }}
onMouseLeave={e => { e.currentTarget.style.background = "rgba(239,68,68,0.12)"; }}
>
<FaTrash className="w-3 h-3 text-red-400" />
</button>
</div>
</motion.div>
);
})}
</div>
)}
</div>
);
};
export default ManageFiles;