navix/frontend/src/App.jsx
2025-07-04 11:36:51 +03:00

183 lines
4.9 KiB
JavaScript

import { useEffect, useState } from 'react';
import './App.css';
import SectionGrid from './components/SectionGrid';
import AppModal from './components/AppModal';
import ConfirmDialog from './components/ConfirmDialog';
import Clock from './components/Clock';
import { ToastContainer, toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
import { IoIosAdd } from 'react-icons/io';
import CustomToast from './components/CustomToast';
import {
fetchSections,
addAppToSection,
editAppInSection,
deleteAppFromSection,
} from './services/api';
function App() {
const [sections, setSections] = useState([]);
const [editData, setEditData] = useState(null);
const [showAdd, setShowAdd] = useState(false);
const [confirmData, setConfirmData] = useState(null);
const [searchTerm, setSearchTerm] = useState('');
const loadSections = () => {
fetchSections()
.then(data => {
const filtered = (data.sections || []).filter(section => (section.apps?.length ?? 0) > 0);
setSections(filtered);
})
.catch(err => console.error('Failed to fetch sections:', err));
};
useEffect(() => {
loadSections();
}, []);
const handleDelete = (app) => {
setConfirmData({
message: `Are you sure you want to delete "${app.name}"?`,
app,
});
};
const confirmDelete = async () => {
if (!confirmData?.app) return;
try {
await deleteAppFromSection({
section: confirmData.app.section,
app: confirmData.app,
});
toast(<CustomToast type="delete" message={`App "${confirmData.app.name}" deleted successfully!`} />);
loadSections();
} catch (err) {
console.error('Failed to delete app:', err);
toast.error('Failed to delete app');
} finally {
setConfirmData(null);
}
};
const handleAddSubmit = async (data) => {
try {
await addAppToSection(data);
toast(<CustomToast type="success" message={`App "${data.app.name}" added successfully!`} />);
setShowAdd(false);
loadSections();
} catch (err) {
console.error('Failed to add app:', err);
toast.error('Failed to add app');
}
};
const handleEditSubmit = async (data) => {
try {
await editAppInSection({
section: data.section,
app: data.app,
original_name: data.original_name || data.app.name,
});
toast(<CustomToast type="edit" message={`App "${data.app.name}" updated successfully!`} />);
setEditData(null);
loadSections();
} catch (err) {
console.error('Failed to update app:', err);
toast.error('Failed to update app');
}
};
const filteredSections = sections.map(section => ({
...section,
apps: section.apps.filter(app =>
app.name.toLowerCase().includes(searchTerm.toLowerCase())
),
})).filter(section => section.apps.length > 0);
return (
<div className="App">
<Clock />
<h1 className="main-title">
<img src="/navix-logo.svg" alt="Navix logo" className="navix-logo" />
Navix
</h1>
{/* 🔍 Search Box */}
<div style={{ display: 'flex', justifyContent: 'flex-start', marginLeft: '2rem' }}>
<input
type="text"
placeholder="Search apps..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
className="search-input"
/>
</div>
{showAdd && (
<AppModal
mode="add"
onSubmit={handleAddSubmit}
onClose={() => setShowAdd(false)}
sections={sections}
/>
)}
{editData && (
<AppModal
mode="edit"
initialData={editData}
onSubmit={handleEditSubmit}
onClose={() => setEditData(null)}
sections={sections}
/>
)}
{confirmData && (
<ConfirmDialog
message={confirmData.message}
onConfirm={confirmDelete}
onCancel={() => setConfirmData(null)}
/>
)}
{filteredSections.map((section) => (
<SectionGrid
key={section.name}
section={section}
onEdit={(app) =>
setEditData({ ...app, section: section.name, original_name: app.name })
}
onDelete={handleDelete}
/>
))}
<IoIosAdd className="add-button" onClick={() => setShowAdd(true)} />
<ToastContainer
position="bottom-right"
autoClose={3000}
hideProgressBar={false}
newestOnTop
closeOnClick
pauseOnFocusLoss
draggable
pauseOnHover
theme="dark"
toastStyle={{
backgroundColor: 'black',
borderRadius: '12px',
minHeight: '110px',
padding: '20px',
color: '#fff',
display: 'flex',
alignItems: 'center',
}}
bodyClassName="toast-body"
/>
</div>
);
}
export default App;