collection list, move, delete

This commit is contained in:
michalcourson
2026-02-20 20:21:08 -05:00
parent d6f4d4166b
commit 60355d176c
18 changed files with 437 additions and 2064 deletions

View File

@ -1,42 +1,152 @@
import { MemoryRouter as Router, Routes, Route } from 'react-router-dom';
import { useEffect, useState } from 'react';
import { Provider } from 'react-redux';
import Dialog from '@mui/material/Dialog';
import DialogTitle from '@mui/material/DialogTitle';
import DialogContent from '@mui/material/DialogContent';
import DialogActions from '@mui/material/DialogActions';
// import 'tailwindcss/tailwind.css';
import icon from '../../assets/icon.svg';
import './App.css';
import ClipList from './components/ClipList';
import { useAppDispatch } from './hooks';
import { useAppDispatch, useAppSelector } from './hooks';
import { store } from '../redux/main';
function MainPage() {
const [collection, setCollection] = useState<string | null>('Uncategorized');
const dispatch = useAppDispatch();
const collections = useAppSelector((state) =>
state.collections.map((col) => col.name),
);
const [selectedCollection, setSelectedCollection] = useState<string>(
collections[0] || 'Uncategorized',
);
const [newCollectionOpen, setNewCollectionOpen] = useState(false);
const [newCollectionName, setNewCollectionName] = useState<string>('');
useEffect(() => {
const fetchMetadata = async () => {
try {
const response = await fetch('http://localhost:5010/meta');
const data = await response.json();
// console.log('Fetched collections:', data.collections);
dispatch({ type: 'metadata/addNewClips', payload: data });
dispatch({ type: 'metadata/setAllData', payload: data });
} catch (error) {
console.error('Error fetching metadata:', error);
}
};
fetchMetadata();
const intervalId = setInterval(fetchMetadata, 5000);
return () => clearInterval(intervalId);
}, [dispatch]);
// 1. Set up the interval
const intervalId = setInterval(async () => {
fetchMetadata();
}, 5000); // 1000 milliseconds delay
useEffect(() => {
// Update selected collection if collections change
if (collections.length > 0 && !collections.includes(selectedCollection)) {
setSelectedCollection(collections[0]);
}
}, [collections, selectedCollection]);
// 2. Return a cleanup function to clear the interval when the component unmounts
return () => {
clearInterval(intervalId);
};
}, [dispatch]); //
return <ClipList collection={collection} />;
const handleNewCollectionSave = () => {
if (
newCollectionName.trim() &&
!collections.includes(newCollectionName.trim())
) {
dispatch({
type: 'metadata/addCollection',
payload: newCollectionName.trim(),
});
setSelectedCollection(newCollectionName.trim());
fetch('http://localhost:5010/meta/collections/add', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: newCollectionName.trim() }),
})
.then((res) => res.json())
.catch((err) => console.error('Error creating collection:', err));
}
setNewCollectionOpen(false);
setNewCollectionName('');
};
return (
<div className="min-h-screen min-w-screen bg-midnight text-offwhite relative">
{/* Left Nav Bar - sticky */}
<Dialog
open={newCollectionOpen}
onClose={() => setNewCollectionOpen(false)}
slotProps={{
paper: { sx: { backgroundColor: '#1a1a1a', color: 'white' } },
}}
>
<DialogTitle>Edit Clip Name</DialogTitle>
<DialogContent>
<input
autoFocus
className="font-bold text-lg bg-transparent outline-none border-b border-plum focus:border-plumDark text-white mb-1 w-full text-center"
type="text"
value={newCollectionName}
onChange={(e) => setNewCollectionName(e.target.value)}
onKeyDown={(e) => {
if (e.key === 'Enter') handleNewCollectionSave();
}}
aria-label="New collection name"
/>
</DialogContent>
<DialogActions>
<button
type="button"
onClick={() => {
setNewCollectionOpen(false);
setNewCollectionName('');
}}
className="bg-plum hover:bg-plumDark text-white font-bold h-10 px-4 rounded-md"
>
Cancel
</button>
<button
type="button"
onClick={handleNewCollectionSave}
className="bg-plum hover:bg-plumDark text-white font-bold h-10 px-4 rounded-md"
>
Save
</button>
</DialogActions>
</Dialog>
<nav
className="w-48 h-screen sticky top-0 left-0 border-r border-neutral-700 bg-midnight flex flex-col p-2"
style={{ position: 'absolute', top: 0, left: 0 }}
>
<div className="p-4 font-bold text-lg">Collections</div>
<li>
<button
type="button"
className="w-full rounded text-left px-4 py-2 mb-2 bg-plumDark text-offwhite font-semibold hover:bg-plum"
onClick={() => setNewCollectionOpen(true)}
>
+ Create Collection
</button>
</li>
<ul className="flex-1 overflow-y-auto">
{collections.map((col) => (
<li key={col}>
<button
type="button"
className={`w-full rounded text-left px-4 py-2 mt-2 hover:bg-plumDark ${selectedCollection === col ? 'bg-plum text-offwhite font-semibold' : 'text-offwhite'}`}
onClick={() => setSelectedCollection(col)}
>
{col}
</button>
</li>
))}
</ul>
</nav>
{/* Main Content */}
<div
className="absolute top-0 ml-[12rem] w-[calc(100%-12rem)] h-screen overflow-y-auto p-4"
// style={{ left: '12rem', width: 'calc(100% - 12rem)' }}
>
<ClipList collection={selectedCollection} />
</div>
</div>
);
}
export default function App() {