diff --git a/src/App.jsx b/src/App.jsx index 5f8bc445..2a1c665e 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,8 +1,8 @@ import { useState, useEffect, useRef } from "react"; import { NewTerminal } from "./Terminal.jsx"; import { User } from "./User.jsx"; -import AddHostModal from "./AddHostModal.jsx"; -import LoginUserModal from "./LoginUserModal.jsx"; +import AddHostModal from "./modals/AddHostModal.jsx"; +import LoginUserModal from "./modals/LoginUserModal.jsx"; import { Button } from "@mui/joy"; import { CssVarsProvider } from "@mui/joy"; import theme from "./theme"; @@ -12,9 +12,10 @@ import { Debounce } from './Utils'; import TermixIcon from "./images/termix_icon.png"; import RocketIcon from './images/launchpad_rocket.png'; import ProfileIcon from './images/profile_icon.png'; -import CreateUserModal from "./CreateUserModal.jsx"; -import ProfileModal from "./ProfileModal.jsx"; -import ErrorModal from "./ErrorModal.jsx"; +import CreateUserModal from "./modals/CreateUserModal.jsx"; +import ProfileModal from "./modals/ProfileModal.jsx"; +import ErrorModal from "./modals/ErrorModal.jsx"; +import EditHostModal from "./modals/EditHostModal.jsx"; function App() { const [isAddHostHidden, setIsAddHostHidden] = useState(true); @@ -36,6 +37,15 @@ function App() { authMethod: "Select Auth", rememberHost: false, }); + const [editHostForm, setEditHostForm] = useState({ + name: "", + ip: "", + user: "", + password: "", + port: 22, + authMethod: "Select Auth", + rememberHost: true, + }); const [loginUserForm, setLoginUserForm] = useState({ username: "", password: "", @@ -46,6 +56,8 @@ function App() { }); const [isLaunchpadOpen, setIsLaunchpadOpen] = useState(false); const [splitTabIds, setSplitTabIds] = useState([]); + const [isEditHostHidden, setIsEditHostHidden] = useState(true); + const [currentHostConfig, setCurrentHostConfig] = useState(null); useEffect(() => { const handleKeyDown = (e) => { @@ -183,6 +195,23 @@ function App() { } } + const createFolder = (folderName) => { + if (userRef.current) { + userRef.current.createFolder({ + folderName, + }); + } + } + + const moveHostToFolder = (folderName, hostConfig) => { + if (userRef.current) { + userRef.current.moveHostToFolder({ + folderName, + hostConfig, + }); + } + } + const handleLoginUser = ({ username, password, sessionToken, onSuccess, onFailure }) => { if (userRef.current) { if (sessionToken) { @@ -241,6 +270,43 @@ function App() { } } + const deleteHost = (hostConfig) => { + if (userRef.current) { + userRef.current.deleteHost({ + hostConfig, + }); + } + } + + const updateEditHostForm = (hostConfig) => { + if (hostConfig) { + setCurrentHostConfig(hostConfig); + setIsEditHostHidden(false); + } else { + console.error("hostConfig is null"); + } + }; + + const handleEditHost = () => { + if (editHostForm.ip && editHostForm.user && ((editHostForm.authMethod === 'password' && editHostForm.password) || (editHostForm.authMethod === 'rsaKey' && editHostForm.rsaKey)) && editHostForm.port && editHostForm.authMethod !== 'Select Auth') { + const user = getUser(); + editHostForm.rememberHost = true; + + if (user && currentHostConfig) { + userRef.current.editExistingHost({ + userId: user.id, + oldHostConfig: currentHostConfig, + newHostConfig: editHostForm, + }); + setIsEditHostHidden(true); + } else { + console.error("User or currentHostConfig is null"); + } + } else { + alert("Please fill out all fields."); + } + }; + const closeTab = (id) => { const newTerminals = terminals.filter((t) => t.id !== id); setTerminals(newTerminals); @@ -392,6 +458,14 @@ function App() { handleAddHost={handleAddHost} setIsAddHostHidden={setIsAddHostHidden} /> + setIsLaunchpadOpen(false)} getHosts={getHosts} connectToHost={connectToHostWithConfig} + isAddHostHidden={isAddHostHidden} + setIsAddHostHidden={setIsAddHostHidden} + isEditHostHidden={isEditHostHidden} + isErrorHidden={isErrorHidden} + deleteHost={deleteHost} + editHost={updateEditHostForm} + createFolder={createFolder} + moveHostToFolder={moveHostToFolder} /> )} diff --git a/src/Apps/HostViewer.jsx b/src/Apps/HostViewer.jsx deleted file mode 100644 index bce47eb0..00000000 --- a/src/Apps/HostViewer.jsx +++ /dev/null @@ -1,115 +0,0 @@ -import PropTypes from "prop-types"; -import { useState, useEffect, useRef } from "react"; -import { Button } from "@mui/joy"; - -function HostViewer({ getHosts, connectToHost }) { - const [hosts, setHosts] = useState([]); - const [initialLoadComplete, setInitialLoadComplete] = useState(false); - const isMounted = useRef(true); - - useEffect(() => { - isMounted.current = true; - - async function fetchInitialHosts() { - try { - const savedHosts = await getHosts(); - if (isMounted.current) { - setHosts(savedHosts || []); - setInitialLoadComplete(true); - } - } catch (error) { - console.error("Initial host fetch failed:", error); - if (isMounted.current) { - setHosts([]); - setInitialLoadComplete(true); - } - } - } - - // Immediate first fetch - fetchInitialHosts(); - - // Periodic updates - const intervalId = setInterval(async () => { - try { - const savedHosts = await getHosts(); - if (isMounted.current) { - setHosts(savedHosts || []); - } - } catch (error) { - console.error("Periodic host update failed:", error); - } - }, 2000); - - return () => { - isMounted.current = false; - clearInterval(intervalId); - }; - }, [getHosts]); - - return ( -
-
-

Saved Hosts

-
-
- {!initialLoadComplete ? ( -
-
-
-
-
-
-
-
-
- ) : hosts.length > 0 ? ( -
- {hosts.map((hostWrapper, index) => { - const hostConfig = hostWrapper.hostConfig || {}; - - const formattedHostConfig = { - name: hostConfig.name || "Unknown Host Name", - ip: hostConfig.ip || "Unknown IP", - user: hostConfig.user || "Unknown User", - password: hostConfig.password || undefined, - rsaKey: hostConfig.rsaKey || undefined, - port: hostConfig.port ? String(hostConfig.port) : "22", - }; - - const displayName = hostConfig.name ? hostConfig.name : hostConfig.ip; - - return ( -
-
-

{displayName}

-

- {hostConfig.user ? `${hostConfig.user}@${hostConfig.ip}` : hostConfig.ip}:{hostConfig.port} -

-
- -
- ); - })} -
- ) : ( -

Hosts are loading...

- )} -
-
- ); -} - -HostViewer.propTypes = { - getHosts: PropTypes.func.isRequired, - connectToHost: PropTypes.func.isRequired, -}; - -export default HostViewer; \ No newline at end of file diff --git a/src/Launchpad.jsx b/src/Launchpad.jsx index 7aa6e391..76408d4e 100644 --- a/src/Launchpad.jsx +++ b/src/Launchpad.jsx @@ -1,17 +1,40 @@ import PropTypes from 'prop-types'; -import { useEffect, useRef } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { CssVarsProvider } from '@mui/joy/styles'; +import { Button } from '@mui/joy'; +import HostViewerIcon from './images/host_viewer_icon.png'; import theme from './theme'; // Apps -import HostViewer from './Apps/HostViewer'; +import HostViewer from './apps/HostViewer'; -function Launchpad({ onClose, getHosts, connectToHost }) { +function Launchpad({ + onClose, + getHosts, + connectToHost, + isAddHostHidden, + setIsAddHostHidden, + isEditHostHidden, + isErrorHidden, + deleteHost, + editHost, + createFolder, + moveHostToFolder, + }) { const launchpadRef = useRef(null); + const [sidebarOpen, setSidebarOpen] = useState(false); + const [activeApp, setActiveApp] = useState('hostViewer'); useEffect(() => { const handleClickOutside = (event) => { - if (launchpadRef.current && !launchpadRef.current.contains(event.target)) { + // Close the launchpad when neither form is visible and no error is showing + if ( + launchpadRef.current && + !launchpadRef.current.contains(event.target) && + isAddHostHidden && // Only close if addHost form is hidden + isEditHostHidden && // Only close if editHost form is hidden + isErrorHidden // Only close if error is hidden + ) { onClose(); } }; @@ -21,7 +44,12 @@ function Launchpad({ onClose, getHosts, connectToHost }) { return () => { document.removeEventListener("mousedown", handleClickOutside); }; - }, [onClose]); + }, [onClose, isAddHostHidden, isEditHostHidden, isErrorHidden]); + + const handleEditHostClick = () => { + setIsAddHostHidden(false); // Open the form for editing + setActiveApp('hostViewer'); // Set active app to HostViewer + }; return ( @@ -47,16 +75,112 @@ function Launchpad({ onClose, getHosts, connectToHost }) { height: "75%", backgroundColor: theme.palette.general.tertiary, display: "flex", - alignItems: "center", - justifyContent: "center", borderRadius: "8px", boxShadow: "0 4px 10px rgba(0, 0, 0, 0.3)", border: `1px solid ${theme.palette.general.secondary}`, color: theme.palette.text.primary, - padding: 3, + padding: 0, }} > - + {/* Sidebar */} +
+ {/* Sidebar Toggle Button */} + + + {/* HostViewer Button */} + +
+ + {/* Main Content */} +
+ {activeApp === 'hostViewer' && ( + + )} +
@@ -65,8 +189,16 @@ function Launchpad({ onClose, getHosts, connectToHost }) { Launchpad.propTypes = { onClose: PropTypes.func.isRequired, - connectToHost: PropTypes.func.isRequired, getHosts: PropTypes.func.isRequired, + connectToHost: PropTypes.func.isRequired, + isAddHostHidden: PropTypes.bool.isRequired, + setIsAddHostHidden: PropTypes.func.isRequired, + isEditHostHidden: PropTypes.bool.isRequired, + isErrorHidden: PropTypes.bool.isRequired, + deleteHost: PropTypes.func.isRequired, + editHost: PropTypes.func.isRequired, + createFolder: PropTypes.func.isRequired, + moveHostToFolder: PropTypes.func.isRequired, }; export default Launchpad; \ No newline at end of file diff --git a/src/User.jsx b/src/User.jsx index 0c217213..9dbcca2a 100644 --- a/src/User.jsx +++ b/src/User.jsx @@ -133,7 +133,11 @@ export const User = forwardRef(({ onLoginSuccess, onCreateSuccess, onDeleteSucce }); socketRef.current.once("hostsFound", (data) => { - resolve(data); + if (data && Array.isArray(data)) { + resolve(data); + } else { + reject("Invalid data received."); + } }); socketRef.current.once("error", (error) => { @@ -149,6 +153,68 @@ export const User = forwardRef(({ onLoginSuccess, onCreateSuccess, onDeleteSucce }); }; + const deleteHost = (hostConfig) => { + if (currentUser.current?.id && socketRef.current) { + socketRef.current.emit("deleteHost", { + userId: currentUser.current.id, + hostConfig: hostConfig, + }); + + socketRef.current.once("error", (error) => { + onFailure(error); + }); + } else { + onFailure("No user is currently logged in."); + } + } + + const editExistingHost = ({ userId, oldHostConfig, newHostConfig }) => { + if (currentUser.current?.id && socketRef.current) { + socketRef.current.emit("editHost", { + userId: userId, + oldHostConfig: oldHostConfig, + newHostConfig: newHostConfig, + }); + + socketRef.current.once("error", (error) => { + onFailure(error); + }); + } else { + onFailure("No user is currently logged in."); + } + }; + + const createFolder = (folderName) => { + if (currentUser.current?.id && socketRef.current) { + socketRef.current.emit("createFolder", { + userId: currentUser.current.id, + folderName: folderName, + }); + + socketRef.current.once("error", (error) => { + onFailure(error); + }); + } else { + onFailure("No user is currently logged in."); + } + } + + const moveHostToFolder = (folderName, hostConfig) => { + if (currentUser.current?.id && socketRef.current) { + socketRef.current.emit("moveHostToFolder", { + userId: currentUser.current.id, + folderName: folderName, + hostConfig: hostConfig, + }); + + socketRef.current.once("error", (error) => { + onFailure(error); + }); + } else { + onFailure("No user is currently logged in."); + } + } + useImperativeHandle(ref, () => ({ createUser, loginUser, @@ -157,6 +223,10 @@ export const User = forwardRef(({ onLoginSuccess, onCreateSuccess, onDeleteSucce saveHost, getUser, getAllHosts, + deleteHost, + editExistingHost, + createFolder, + moveHostToFolder, })); return
; diff --git a/src/apps/HostViewer.jsx b/src/apps/HostViewer.jsx new file mode 100644 index 00000000..8bae1e6f --- /dev/null +++ b/src/apps/HostViewer.jsx @@ -0,0 +1,135 @@ +import PropTypes from "prop-types"; +import { useState, useEffect, useRef } from "react"; +import { Button } from "@mui/joy"; + +function HostViewer({ getHosts, connectToHost, setIsAddHostHidden, deleteHost, editHost }) { + const [hosts, setHosts] = useState([]); + const [initialLoadComplete, setInitialLoadComplete] = useState(false); + const isMounted = useRef(true); + + useEffect(() => { + isMounted.current = true; + + async function fetchInitialHosts() { + try { + const savedHosts = await getHosts(); + if (isMounted.current) { + setHosts(savedHosts || []); + setInitialLoadComplete(true); + } + } catch (error) { + console.error("Initial host fetch failed:", error); + if (isMounted.current) { + setHosts([]); + setInitialLoadComplete(true); + } + } + } + + fetchInitialHosts(); + + const intervalId = setInterval(async () => { + try { + const savedHosts = await getHosts(); + if (isMounted.current) { + setHosts(savedHosts || []); + } + } catch (error) { + console.error("Periodic host update failed:", error); + } + }, 2000); + + return () => { + isMounted.current = false; + clearInterval(intervalId); + }; + }, [getHosts]); + + return ( +
+
+

Hosts

+ +
+
+ {hosts.length > 0 ? ( +
+ {hosts.map((hostWrapper, index) => { + const hostConfig = hostWrapper.hostConfig || {}; + + return ( +
+
+

{hostConfig.name || hostConfig.ip}

+

+ {hostConfig.user ? `${hostConfig.user}@${hostConfig.ip}` : hostConfig.ip}:{hostConfig.port} +

+
+
+ + + +
+
+ ); + })} +
+ ) : ( +

Hosts are either loading or do not exist...

+ )} +
+
+ ); +} + +HostViewer.propTypes = { + getHosts: PropTypes.func.isRequired, + connectToHost: PropTypes.func.isRequired, + setIsAddHostHidden: PropTypes.func.isRequired, + deleteHost: PropTypes.func.isRequired, + editHost: PropTypes.func.isRequired, +}; + +export default HostViewer; \ No newline at end of file diff --git a/src/backend/database.cjs b/src/backend/database.cjs index 7c13f0f2..c2c3cf2e 100644 --- a/src/backend/database.cjs +++ b/src/backend/database.cjs @@ -141,6 +141,93 @@ async function getHosts(userId) { } } +async function deleteHost(userId, hostConfig) { + try { + const user = await User.findById(userId); + if (user) { + user.sshConnections = user.sshConnections.filter(connection => { + const matches = + connection.name === hostConfig.name && + connection.ip === hostConfig.ip && + connection.port === hostConfig.port && + connection.user === hostConfig.user; + + return !matches; + }); + + await user.save(); + return { success: true }; + } else { + return { error: 'User not found' }; + } + } catch (err) { + return { error: 'Error deleting host: ' + err.message }; + } +} + +async function editHost(userId, oldHostConfig, newHostConfig) { + try { + const user = await User.findById(userId); + if (user) { + user.sshConnections = user.sshConnections.map(connection => { + const matches = + connection.hostConfig.name === oldHostConfig.name && + connection.hostConfig.ip === oldHostConfig.ip && + connection.hostConfig.port === oldHostConfig.port && + connection.hostConfig.user === oldHostConfig.user; + + if (matches) { + return { hostConfig: newHostConfig }; + } else { + return connection; + } + }); + + await user.save(); + return { success: true }; + } else { + return { error: 'User not found' }; + } + } catch (err) { + return { error: 'Error editing host: ' + err.message }; + } +} + +async function createFolder(userId, folderName) { + try { + const user = await User.findById(userId); + if (user) { + user.sshConnections.push({ folderName, connections: [] }); + await user.save(); + return { success: true }; + } else { + return { error: 'User not found' }; + } + } catch (err) { + return { error: 'Error creating folder: ' + err.message }; + } +} + +async function moveHostToFolder(userId, hostConfig, folderName) { + try { + const user = await User.findById(userId); + if (user) { + const folder = user.sshConnections.find(folder => folder.folderName === folderName); + if (folder) { + folder.connections.push(hostConfig); + await user.save(); + return { success: true }; + } else { + return { error: 'Folder not found' }; + } + } else { + return { error: 'User not found' }; + } + } catch (err) { + return { error: 'Error moving host to folder: ' + err.message }; + } +} + dbNamespace.on("connection", (socket) => { console.log("New socket connection established on"); @@ -202,6 +289,50 @@ dbNamespace.on("connection", (socket) => { socket.emit(result.error ? "error" : "hostsFound", result); console.log(result.error || `Hosts found`); }); + + socket.on("deleteHost", async (data) => { + const { userId, hostConfig } = data; + if (!userId || !hostConfig) { + socket.emit("error", "User ID and host config are required"); + return; + } + const result = await deleteHost(userId, hostConfig); + socket.emit(result.error ? "error" : "hostDeleted", result); + console.log(result.error || `Host deleted`); + }); + + socket.on("editHost", async (data) => { + const { userId, oldHostConfig, newHostConfig } = data; + if (!userId || !oldHostConfig || !newHostConfig) { + socket.emit("error", "User ID, old host config, and new host config are required"); + return; + } + const result = await editHost(userId, oldHostConfig, newHostConfig); + socket.emit(result.error ? "error" : "hostEdited", result); + console.log(result.error || `Host edited`); + }); + + socket.on("createFolder", async (data) => { + const { userId, folderName } = data; + if (!userId || !folderName) { + socket.emit("error", "User ID and folder name are required"); + return; + } + const result = await createFolder(userId, folderName); + socket.emit(result.error ? "error" : "folderCreated", result); + console.log(result.error || `Folder created`); + }); + + socket.on("moveHostToFolder", async (data) => { + const { userId, hostConfig, folderName } = data; + if (!userId || !hostConfig || !folderName) { + socket.emit("error", "User ID, host config, and folder name are required"); + return; + } + const result = await moveHostToFolder(userId, hostConfig, folderName); + socket.emit(result.error ? "error" : "hostMoved", result); + console.log(result.error || `Host moved to folder`); + }); }); server.listen(8082, '0.0.0.0', async () => { diff --git a/src/images/host_viewer_icon.png b/src/images/host_viewer_icon.png new file mode 100644 index 00000000..03bc917c Binary files /dev/null and b/src/images/host_viewer_icon.png differ diff --git a/src/AddHostModal.jsx b/src/modals/AddHostModal.jsx similarity index 99% rename from src/AddHostModal.jsx rename to src/modals/AddHostModal.jsx index da3537a7..41296f84 100644 --- a/src/AddHostModal.jsx +++ b/src/modals/AddHostModal.jsx @@ -14,7 +14,7 @@ import { Option, Checkbox } from '@mui/joy'; -import theme from './theme'; +import theme from '/src/theme'; const AddHostModal = ({ isHidden, form, setForm, handleAddHost, setIsAddHostHidden }) => { const handleFileChange = (e) => { diff --git a/src/CreateUserModal.jsx b/src/modals/CreateUserModal.jsx similarity index 99% rename from src/CreateUserModal.jsx rename to src/modals/CreateUserModal.jsx index c9e6d797..e0c24e33 100644 --- a/src/CreateUserModal.jsx +++ b/src/modals/CreateUserModal.jsx @@ -1,7 +1,7 @@ import PropTypes from 'prop-types'; import { CssVarsProvider } from '@mui/joy/styles'; import { Modal, Button, FormControl, FormLabel, Input, Stack, DialogTitle, DialogContent, ModalDialog } from '@mui/joy'; -import theme from './theme'; +import theme from '/src/theme'; import { useEffect } from 'react'; const CreateUserModal = ({ isHidden, form, setForm, handleCreateUser, setIsCreateUserHidden, setIsLoginUserHidden }) => { diff --git a/src/modals/EditHostModal.jsx b/src/modals/EditHostModal.jsx new file mode 100644 index 00000000..4fefa74c --- /dev/null +++ b/src/modals/EditHostModal.jsx @@ -0,0 +1,231 @@ +import PropTypes from 'prop-types'; +import { useEffect } from 'react'; +import { CssVarsProvider } from '@mui/joy/styles'; +import { + Modal, + Button, + FormControl, + FormLabel, + Input, + Stack, + DialogTitle, + DialogContent, + ModalDialog, + Select, + Option, + Checkbox +} from '@mui/joy'; +import theme from '/src/theme'; + +const EditHostModal = ({ isHidden, form, setForm, handleEditHost, setIsEditHostHidden, hostConfig }) => { + const handleFileChange = (e) => { + const file = e.target.files[0]; + if (file) { + if (file.name.endsWith('.rsa') || file.name.endsWith('.key') || file.name.endsWith('.pem') || file.name.endsWith('.der') || file.name.endsWith('.p8') || file.name.endsWith('.ssh')) { + const reader = new FileReader(); + reader.onload = (event) => { + setForm({ ...form, rsaKey: event.target.result }); + }; + reader.readAsText(file); + } else { + alert("Please upload a valid RSA private key file."); + } + } + }; + + const isFormValid = () => { + if (form.authMethod === 'Select Auth') return false; + if (!form.ip || !form.user || !form.port) return false; + if (form.authMethod === 'rsaKey' && !form.rsaKey) return false; + if (form.authMethod === 'password' && !form.password) return false; + return true; + }; + + useEffect(() => { + if (hostConfig) { + setForm({ + name: hostConfig.name || '', + ip: hostConfig.ip || '', + user: hostConfig.user || '', + password: hostConfig.password || '', + rsaKey: hostConfig.rsaKey || '', + port: Number(hostConfig.port) || 22, + authMethod: hostConfig.password ? 'password' : 'rsaKey', + rememberHost: hostConfig.rememberHost || false, + }); + } + }, [hostConfig, setForm]); + + return ( + + setIsEditHostHidden(true)}> + + Edit Host + +
{ + event.preventDefault(); + if (isFormValid()) handleEditHost(); + }} + > + + + Host Name + setForm({ ...form, name: e.target.value })} + sx={{ + backgroundColor: theme.palette.general.primary, + color: theme.palette.text.primary, + }} + /> + + + Host IP + setForm({ ...form, ip: e.target.value })} + required + sx={{ + backgroundColor: theme.palette.general.primary, + color: theme.palette.text.primary, + }} + /> + + + Host User + setForm({ ...form, user: e.target.value })} + required + sx={{ + backgroundColor: theme.palette.general.primary, + color: theme.palette.text.primary, + }} + /> + + + Authentication Method + + + {form.authMethod === 'password' && ( + + Host Password + setForm({ ...form, password: e.target.value })} + required + sx={{ + backgroundColor: theme.palette.general.primary, + color: theme.palette.text.primary, + }} + /> + + )} + {form.authMethod === 'rsaKey' && ( + + RSA Key + + + )} + 65535}> + Host Port + setForm({ ...form, port: e.target.value })} + min={1} + max={65535} + required + sx={{ + backgroundColor: theme.palette.general.primary, + color: theme.palette.text.primary, + }} + /> + + + +
+
+
+
+
+ ); +}; + +EditHostModal.propTypes = { + isHidden: PropTypes.bool.isRequired, + form: PropTypes.shape({ + name: PropTypes.string, + ip: PropTypes.string.isRequired, + user: PropTypes.string.isRequired, + password: PropTypes.string, + rsaKey: PropTypes.string, + port: PropTypes.number.isRequired, + authMethod: PropTypes.string.isRequired, + rememberHost: PropTypes.bool, + }).isRequired, + setForm: PropTypes.func.isRequired, + handleEditHost: PropTypes.func.isRequired, + setIsEditHostHidden: PropTypes.func.isRequired, + hostConfig: PropTypes.object, +}; + +export default EditHostModal; \ No newline at end of file diff --git a/src/ErrorModal.jsx b/src/modals/ErrorModal.jsx similarity index 98% rename from src/ErrorModal.jsx rename to src/modals/ErrorModal.jsx index 272b6675..46590b07 100644 --- a/src/ErrorModal.jsx +++ b/src/modals/ErrorModal.jsx @@ -1,7 +1,7 @@ import PropTypes from 'prop-types'; import { CssVarsProvider } from '@mui/joy/styles'; import { Modal, Button, DialogTitle, DialogContent, ModalDialog } from '@mui/joy'; -import theme from './theme'; +import theme from '/src/theme'; const ErrorModal = ({ isHidden, errorMessage, setIsErrorHidden }) => { return ( diff --git a/src/LoginUserModal.jsx b/src/modals/LoginUserModal.jsx similarity index 99% rename from src/LoginUserModal.jsx rename to src/modals/LoginUserModal.jsx index 44ec49bc..9a0dc90d 100644 --- a/src/LoginUserModal.jsx +++ b/src/modals/LoginUserModal.jsx @@ -1,7 +1,7 @@ import PropTypes from 'prop-types'; import { CssVarsProvider } from '@mui/joy/styles'; import { Modal, Button, FormControl, FormLabel, Input, Stack, DialogTitle, DialogContent, ModalDialog } from '@mui/joy'; -import theme from './theme'; +import theme from '/src/theme'; import {useEffect} from 'react'; const LoginUserModal = ({ isHidden, form, setForm, handleLoginUser, setIsLoginUserHidden, setIsCreateUserHidden }) => { diff --git a/src/ProfileModal.jsx b/src/modals/ProfileModal.jsx similarity index 99% rename from src/ProfileModal.jsx rename to src/modals/ProfileModal.jsx index 17806b4f..198ef423 100644 --- a/src/ProfileModal.jsx +++ b/src/modals/ProfileModal.jsx @@ -1,7 +1,7 @@ import PropTypes from 'prop-types'; import { CssVarsProvider } from '@mui/joy/styles'; import {Modal, Button, DialogTitle, DialogContent, ModalDialog, Stack } from '@mui/joy'; -import theme from './theme'; +import theme from '/src/theme'; const ProfileModal = ({ isHidden, getUser, handleDeleteUser, handleLogoutUser, setIsProfileHidden }) => { const handleDelete = () => {