diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 9a8559d..0d90a6d 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -10,7 +10,8 @@ "dependencies": { "react": "^19.1.0", "react-beautiful-dnd": "^13.1.1", - "react-dom": "^19.1.0" + "react-dom": "^19.1.0", + "react-icons": "^5.5.0" }, "devDependencies": { "@eslint/js": "^9.25.0", @@ -2393,6 +2394,14 @@ "react": "^19.1.0" } }, + "node_modules/react-icons": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.5.0.tgz", + "integrity": "sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", diff --git a/frontend/package.json b/frontend/package.json index 9a6dfe2..e2a3347 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -12,7 +12,8 @@ "dependencies": { "react": "^19.1.0", "react-beautiful-dnd": "^13.1.1", - "react-dom": "^19.1.0" + "react-dom": "^19.1.0", + "react-icons": "^5.5.0" }, "devDependencies": { "@eslint/js": "^9.25.0", diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index adc33df..40e526d 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -2,7 +2,7 @@ import { useEffect, useState } from 'react'; import './App.css'; import SectionGrid from './components/SectionGrid'; -import ControlPanel from './components/ControlPanel'; +import AddAppModal from './components/AddAppModal'; function App() { const [sections, setSections] = useState([]); @@ -20,7 +20,7 @@ function App() { return (

🔷 Navix

- + window.location.reload()} /> {sections.map((section) => ( ))} diff --git a/frontend/src/components/AddAppModal.jsx b/frontend/src/components/AddAppModal.jsx new file mode 100644 index 0000000..5826ad3 --- /dev/null +++ b/frontend/src/components/AddAppModal.jsx @@ -0,0 +1,66 @@ +import { useState } from 'react'; +import { addAppToSection } from '../services/api'; +import '../style/AddAppModal.css'; +import { IoIosAddCircleOutline } from "react-icons/io"; + +function AddAppModal({ onAdded }) { + const [open, setOpen] = useState(false); + const [spin, setSpin] = useState(false); + + const [section, setSection] = useState(''); + const [name, setName] = useState(''); + const [icon, setIcon] = useState(''); + const [description, setDescription] = useState(''); + const [url, setUrl] = useState(''); + + const handleOpen = () => { + setSpin(true); + setTimeout(() => { + setSpin(false); + setOpen(true); + }, 500); // match spin animation duration + }; + + const handleSubmit = async (e) => { + e.preventDefault(); + try { + await addAppToSection({ section, app: { name, icon, description, url } }); + setOpen(false); + setSection(''); + setName(''); + setIcon(''); + setDescription(''); + setUrl(''); + if (onAdded) onAdded(); + } catch (err) { + alert('Failed to add app'); + } + }; + + return ( + <> + + + {open && ( +
setOpen(false)}> +
e.stopPropagation()}> +

Add New App

+
+ setSection(e.target.value)} required /> + setName(e.target.value)} required /> + setIcon(e.target.value)} /> + setDescription(e.target.value)} /> + setUrl(e.target.value)} required /> + +
+
+
+ )} + + ); +} + +export default AddAppModal; diff --git a/frontend/src/components/ControlPanel.jsx b/frontend/src/components/ControlPanel.jsx deleted file mode 100644 index d8b5b11..0000000 --- a/frontend/src/components/ControlPanel.jsx +++ /dev/null @@ -1,45 +0,0 @@ -import { useState } from 'react'; -import { addAppToSection } from '../services/api'; -import '../style/ControlPanel.css'; - -function ControlPanel({ onUpdate }) { - const [section, setSection] = useState(''); - const [name, setName] = useState(''); - const [icon, setIcon] = useState(''); - const [description, setDescription] = useState(''); - const [url, setUrl] = useState(''); - - const handleSubmit = async (e) => { - e.preventDefault(); - if (!section || !name || !url) return; - - try { - await addAppToSection({ - section, - app: { name, icon, description, url } - }); - - if (onUpdate) onUpdate(); - setSection(''); - setName(''); - setIcon(''); - setDescription(''); - setUrl(''); - } catch (err) { - console.error('Failed to add app:', err); - } - }; - - return ( -
- setSection(e.target.value)} /> - setName(e.target.value)} /> - setIcon(e.target.value)} /> - setDescription(e.target.value)} /> - setUrl(e.target.value)} /> - -
- ); -} - -export default ControlPanel; diff --git a/frontend/src/style/AddAppModal.css b/frontend/src/style/AddAppModal.css new file mode 100644 index 0000000..067a32e --- /dev/null +++ b/frontend/src/style/AddAppModal.css @@ -0,0 +1,84 @@ +.add-button { + position: fixed; + bottom: 2rem; + right: 2rem; + background-color: #007bff; + color: white; + font-size: 2rem; + border-radius: 50%; + width: 60px; + height: 60px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + z-index: 999; + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3); + transition: background-color 0.3s ease; +} + +.add-button:hover { + background-color: #0056b3; +} + +.modal-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; +} + +.modal { + background: #1e1e1e; + padding: 2rem; + border-radius: 12px; + width: 340px; + height: 420px; + box-shadow: 0 0 10px #000; + + /* Flexbox centering */ + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.modal input { + width: 90%; + height: 35px; + padding: 0.5rem; + margin-bottom: 0.75rem; + background-color: #2c2c2c; + border: none; + color: white; + border-radius: 6px; +} + +.modal button { + width: 90%; + padding: 0.5rem; + background-color: #007bff; + color: white; + border: none; + border-radius: 6px; + cursor: pointer; +} + +.modal button:hover { + background-color: #0056b3; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +.add-button.spin { + animation: spin 0.5s linear; +} diff --git a/frontend/src/style/ControlPanel.css b/frontend/src/style/ControlPanel.css deleted file mode 100644 index ed27afc..0000000 --- a/frontend/src/style/ControlPanel.css +++ /dev/null @@ -1,27 +0,0 @@ -.control-panel { - display: flex; - flex-direction: column; - gap: 0.5rem; - margin-bottom: 2rem; - background-color: #1e1e1e; - padding: 1rem; - border-radius: 8px; - max-width: 400px; -} - -.control-panel input, -.control-panel button { - padding: 0.5rem; - border: none; - border-radius: 4px; -} - -.control-panel button { - background-color: #007bff; - color: white; - cursor: pointer; -} - -.control-panel button:hover { - background-color: #0056b3; -}