From fd966b9954c9a51df1a87d99e9b64e719d2af384 Mon Sep 17 00:00:00 2001 From: Karmaa Date: Sun, 16 Mar 2025 00:46:20 -0500 Subject: [PATCH] Fixed control v pasting formating. Reorganized location of scripts. Visbile password and confirm password. Guest login. OpenSSH key authentication. Optional to remember password. Serach for host viewer. --- package-lock.json | 241 +++++++++++++++++++++- package.json | 1 + src/App.jsx | 125 ++++++++---- src/{ => apps}/Launchpad.jsx | 15 +- src/apps/{ => ssh}/HostViewer.jsx | 32 ++- src/{ => apps/ssh}/Terminal.jsx | 54 +++-- src/{ => apps/user}/User.jsx | 30 ++- src/backend/database.cjs | 26 ++- src/backend/ssh.cjs | 1 + src/modals/AddHostModal.jsx | 108 +++++++--- src/modals/CreateUserModal.jsx | 68 +++++-- src/modals/EditHostModal.jsx | 290 ++++++++++++++++----------- src/modals/LoginUserModal.jsx | 52 +++-- src/modals/NoAuthenticationModal.jsx | 176 ++++++++++++++++ src/modals/ProfileModal.jsx | 6 +- src/{ => other}/Utils.jsx | 0 src/{ => ui}/TabList.jsx | 0 17 files changed, 976 insertions(+), 249 deletions(-) rename src/{ => apps}/Launchpad.jsx (94%) rename src/apps/{ => ssh}/HostViewer.jsx (81%) rename src/{ => apps/ssh}/Terminal.jsx (77%) rename src/{ => apps/user}/User.jsx (90%) create mode 100644 src/modals/NoAuthenticationModal.jsx rename src/{ => other}/Utils.jsx (100%) rename src/{ => ui}/TabList.jsx (100%) diff --git a/package-lock.json b/package-lock.json index 7c67313c..7615c7d0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.0", "@fontsource/inter": "^5.1.1", + "@mui/icons-material": "^6.4.7", "@mui/joy": "^5.0.0-beta.51", "@tailwindcss/vite": "^4.0.8", "@tiptap/extension-link": "^2.11.5", @@ -1321,6 +1322,32 @@ "url": "https://opencollective.com/mui-org" } }, + "node_modules/@mui/icons-material": { + "version": "6.4.7", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.4.7.tgz", + "integrity": "sha512-Rk8cs9ufQoLBw582Rdqq7fnSXXZTqhYRbpe1Y5SAz9lJKZP3CIdrj0PfG8HJLGw1hrsHFN/rkkm70IDzhJsG1g==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@mui/material": "^6.4.7", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@mui/joy": { "version": "5.0.0-beta.51", "resolved": "https://registry.npmjs.org/@mui/joy/-/joy-5.0.0-beta.51.tgz", @@ -1362,6 +1389,209 @@ } } }, + "node_modules/@mui/material": { + "version": "6.4.7", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.4.7.tgz", + "integrity": "sha512-K65StXUeGAtFJ4ikvHKtmDCO5Ab7g0FZUu2J5VpoKD+O6Y3CjLYzRi+TMlI3kaL4CL158+FccMoOd/eaddmeRQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.26.0", + "@mui/core-downloads-tracker": "^6.4.7", + "@mui/system": "^6.4.7", + "@mui/types": "^7.2.21", + "@mui/utils": "^6.4.6", + "@popperjs/core": "^2.11.8", + "@types/react-transition-group": "^4.4.12", + "clsx": "^2.1.1", + "csstype": "^3.1.3", + "prop-types": "^15.8.1", + "react-is": "^19.0.0", + "react-transition-group": "^4.4.5" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@mui/material-pigment-css": "^6.4.7", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@mui/material-pigment-css": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material/node_modules/@mui/core-downloads-tracker": { + "version": "6.4.7", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.4.7.tgz", + "integrity": "sha512-XjJrKFNt9zAKvcnoIIBquXyFyhfrHYuttqMsoDS7lM7VwufYG4fAPw4kINjBFg++fqXM2BNAuWR9J7XVIuKIKg==", + "license": "MIT", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + } + }, + "node_modules/@mui/material/node_modules/@mui/private-theming": { + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.4.6.tgz", + "integrity": "sha512-T5FxdPzCELuOrhpA2g4Pi6241HAxRwZudzAuL9vBvniuB5YU82HCmrARw32AuCiyTfWzbrYGGpZ4zyeqqp9RvQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.26.0", + "@mui/utils": "^6.4.6", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material/node_modules/@mui/styled-engine": { + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.4.6.tgz", + "integrity": "sha512-vSWYc9ZLX46be5gP+FCzWVn5rvDr4cXC5JBZwSIkYk9xbC7GeV+0kCvB8Q6XLFQJy+a62bbqtmdwS4Ghi9NBlQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.26.0", + "@emotion/cache": "^11.13.5", + "@emotion/serialize": "^1.3.3", + "@emotion/sheet": "^1.4.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.4.1", + "@emotion/styled": "^11.3.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, + "node_modules/@mui/material/node_modules/@mui/system": { + "version": "6.4.7", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.4.7.tgz", + "integrity": "sha512-7wwc4++Ak6tGIooEVA9AY7FhH2p9fvBMORT4vNLMAysH3Yus/9B9RYMbrn3ANgsOyvT3Z7nE+SP8/+3FimQmcg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.26.0", + "@mui/private-theming": "^6.4.6", + "@mui/styled-engine": "^6.4.6", + "@mui/types": "^7.2.21", + "@mui/utils": "^6.4.6", + "clsx": "^2.1.1", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material/node_modules/@mui/utils": { + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.4.6.tgz", + "integrity": "sha512-43nZeE1pJF2anGafNydUcYFPtHwAqiBiauRtaMvurdrZI3YrUjHkAu43RBsxef7OFtJMXGiHFvq43kb7lig0sA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.26.0", + "@mui/types": "^7.2.21", + "@types/prop-types": "^15.7.14", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-is": "^19.0.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material/node_modules/react-is": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.0.0.tgz", + "integrity": "sha512-H91OHcwjZsbq3ClIDHMzBShc1rotbfACdWENsmEf0IFvZ3FgGPtdHMcsv45bQ1hAbgdfiA8SnxTKfDS+x/8m2g==", + "license": "MIT", + "peer": true + }, "node_modules/@mui/private-theming": { "version": "5.16.14", "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.16.14.tgz", @@ -2583,7 +2813,6 @@ "version": "18.3.18", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz", "integrity": "sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==", - "devOptional": true, "license": "MIT", "dependencies": { "@types/prop-types": "*", @@ -2600,6 +2829,16 @@ "@types/react": "^18.0.0" } }, + "node_modules/@types/react-transition-group": { + "version": "4.4.12", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", + "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==", + "license": "MIT", + "peer": true, + "peerDependencies": { + "@types/react": "*" + } + }, "node_modules/@types/use-sync-external-store": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", diff --git a/package.json b/package.json index 4b432af5..0ab6013a 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.0", "@fontsource/inter": "^5.1.1", + "@mui/icons-material": "^6.4.7", "@mui/joy": "^5.0.0-beta.51", "@tailwindcss/vite": "^4.0.8", "@tiptap/extension-link": "^2.11.5", diff --git a/src/App.jsx b/src/App.jsx index 7dc8af8d..a2992c0c 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,14 +1,14 @@ import { useState, useEffect, useRef } from "react"; -import { NewTerminal } from "./Terminal.jsx"; -import { User } from "./User.jsx"; +import { NewTerminal } from "./apps/ssh/Terminal.jsx"; +import { User } from "./apps/user/User.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"; -import TabList from "./TabList.jsx"; -import Launchpad from "./Launchpad.jsx"; -import { Debounce } from './Utils'; +import TabList from "./ui/TabList.jsx"; +import Launchpad from "./apps/Launchpad.jsx"; +import { Debounce } from './other/Utils.jsx'; import TermixIcon from "./images/termix_icon.png"; import RocketIcon from './images/launchpad_rocket.png'; import ProfileIcon from './images/profile_icon.png'; @@ -16,6 +16,7 @@ import CreateUserModal from "./modals/CreateUserModal.jsx"; import ProfileModal from "./modals/ProfileModal.jsx"; import ErrorModal from "./modals/ErrorModal.jsx"; import EditHostModal from "./modals/EditHostModal.jsx"; +import NoAuthenticationModal from "./modals/NoAuthenticationModal.jsx"; function App() { const [isAddHostHidden, setIsAddHostHidden] = useState(true); @@ -36,6 +37,7 @@ function App() { port: 22, authMethod: "Select Auth", rememberHost: false, + storePassword: true, }); const [editHostForm, setEditHostForm] = useState({ name: "", @@ -45,6 +47,12 @@ function App() { port: 22, authMethod: "Select Auth", rememberHost: true, + storePassword: true, + }); + const [isNoAuthHidden, setIsNoAuthHidden] = useState(true); + const [authForm, setAuthForm] = useState({ + password: "", + rsaKey: "", }); const [loginUserForm, setLoginUserForm] = useState({ username: "", @@ -136,10 +144,19 @@ function App() { }, []); const handleAddHost = () => { - if (addHostForm.ip && addHostForm.user && ((addHostForm.authMethod === 'password' && addHostForm.password) || (addHostForm.authMethod === 'rsaKey' && addHostForm.rsaKey)) && addHostForm.port && addHostForm.authMethod !== 'Select Auth') { - connectToHost(); - if (addHostForm.rememberHost) { - handleSaveHost(); + if (addHostForm.ip && addHostForm.user && addHostForm.port && addHostForm.authMethod !== 'Select Auth') { + if (addHostForm.authMethod === 'password' && !addHostForm.password) { + setIsNoAuthHidden(false); + } else if (addHostForm.authMethod === 'rsaKey' && !addHostForm.rsaKey) { + setIsNoAuthHidden(false); + } else { + connectToHost(); + if (addHostForm.rememberHost) { + if (!addHostForm.storePassword) { + addHostForm.password = ''; + } + handleSaveHost(); + } } } else { alert("Please fill out all fields."); @@ -166,6 +183,24 @@ function App() { setAddHostForm({ name: "", ip: "", user: "", password: "", rsaKey: "", port: 22, authMethod: "Select Auth" }); } + const handleAuthSubmit = (form) => { + const updatedTerminals = terminals.map((terminal) => { + if (terminal.id === activeTab) { + return { + ...terminal, + hostConfig: { + ...terminal.hostConfig, + password: form.password, + rsaKey: form.rsaKey + } + }; + } + return terminal; + }); + setTerminals(updatedTerminals); + setIsNoAuthHidden(true); + }; + const connectToHostWithConfig = (hostConfig) => { const newTerminal = { id: nextId, @@ -195,23 +230,6 @@ 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) { @@ -231,6 +249,12 @@ function App() { } }; + const handleGuestLogin = () => { + if (userRef.current) { + userRef.current.loginAsGuest(); + } + } + const handleCreateUser = ({ username, password, onSuccess, onFailure }) => { if (userRef.current) { userRef.current.createUser({ @@ -287,21 +311,31 @@ function App() { } }; - const handleEditHost = () => { - if (editHostForm.ip && editHostForm.user && ((editHostForm.authMethod === 'password' && editHostForm.password) || (editHostForm.authMethod === 'rsaKey' && editHostForm.rsaKey)) && editHostForm.port && editHostForm.authMethod !== 'Select Auth') { - editHostForm.rememberHost = true; - - if (currentHostConfig) { - userRef.current.editHost({ - oldHostConfig: currentHostConfig, - newHostConfig: editHostForm, - }); - setIsEditHostHidden(true); - } else { - alert("Host not found"); + const handleEditHost = async () => { + try { + // Only clear the password if switching to RSA or storePassword is false + if (editHostForm.authMethod === 'rsaKey') { + editHostForm.password = ''; + } else if (!editHostForm.storePassword) { + editHostForm.password = ''; } - } else { - alert("Please fill out all fields."); + + await userRef.current.editHost({ + oldHostConfig: currentHostConfig, + newHostConfig: editHostForm, + }); + + // Refresh the updated config + const refreshedHosts = await userRef.current.getAllHosts(); + const updated = refreshedHosts.find( + (h) => h.config.ip === editHostForm.ip && h.config.user === editHostForm.user + ); + if (updated) { + setCurrentHostConfig(updated.config); + } + setIsEditHostHidden(true); + } catch (error) { + alert('Edit failed: ' + error); } }; @@ -439,12 +473,20 @@ function App() { key={terminal.id} hostConfig={terminal.hostConfig} isVisible={activeTab === terminal.id || splitTabIds.includes(terminal.id)} + setIsNoAuthHidden={setIsNoAuthHidden} ref={(ref) => { terminal.terminalRef = ref; }} /> ))} + @@ -495,8 +537,6 @@ function App() { isErrorHidden={isErrorHidden} deleteHost={deleteHost} editHost={updateEditHostForm} - createFolder={createFolder} - moveHostToFolder={moveHostToFolder} /> )} @@ -505,6 +545,7 @@ function App() { form={loginUserForm} setForm={setLoginUserForm} handleLoginUser={handleLoginUser} + handleGuestLogin={handleGuestLogin} setIsLoginUserHidden={setIsLoginUserHidden} setIsCreateUserHidden={setIsCreateUserHidden} /> diff --git a/src/Launchpad.jsx b/src/apps/Launchpad.jsx similarity index 94% rename from src/Launchpad.jsx rename to src/apps/Launchpad.jsx index c42d6d50..00413bbf 100644 --- a/src/Launchpad.jsx +++ b/src/apps/Launchpad.jsx @@ -2,14 +2,13 @@ import PropTypes from 'prop-types'; 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'; +import HostViewerIcon from '../images/host_viewer_icon.png'; +import theme from '../theme.js'; // Apps -import HostViewer from './apps/HostViewer'; +import HostViewer from './ssh/HostViewer.jsx'; -function Launchpad({ - onClose, +function Launchpad({onClose, getHosts, connectToHost, isAddHostHidden, @@ -18,8 +17,6 @@ function Launchpad({ isErrorHidden, deleteHost, editHost, - createFolder, - moveHostToFolder, }) { const launchpadRef = useRef(null); const [sidebarOpen, setSidebarOpen] = useState(false); @@ -174,8 +171,6 @@ function Launchpad({ setIsAddHostHidden={setIsAddHostHidden} deleteHost={deleteHost} editHost={editHost} - createFolder={createFolder} - moveHostToFolder={moveHostToFolder} onEditHostClick={handleEditHostClick} /> )} @@ -196,8 +191,6 @@ Launchpad.propTypes = { 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/apps/HostViewer.jsx b/src/apps/ssh/HostViewer.jsx similarity index 81% rename from src/apps/HostViewer.jsx rename to src/apps/ssh/HostViewer.jsx index a38dd7dd..6a2bf683 100644 --- a/src/apps/HostViewer.jsx +++ b/src/apps/ssh/HostViewer.jsx @@ -1,10 +1,12 @@ import PropTypes from "prop-types"; import { useState, useEffect, useRef } from "react"; -import { Button } from "@mui/joy"; +import { Button, Input } from "@mui/joy"; function HostViewer({ getHosts, connectToHost, setIsAddHostHidden, deleteHost, editHost }) { const [hosts, setHosts] = useState([]); + const [filteredHosts, setFilteredHosts] = useState([]); const [isLoading, setIsLoading] = useState(true); + const [searchTerm, setSearchTerm] = useState(""); const isMounted = useRef(true); const fetchHosts = async () => { @@ -12,12 +14,14 @@ function HostViewer({ getHosts, connectToHost, setIsAddHostHidden, deleteHost, e const savedHosts = await getHosts(); if (isMounted.current) { setHosts(savedHosts || []); + setFilteredHosts(savedHosts || []); setIsLoading(false); } } catch (error) { console.error("Host fetch failed:", error); if (isMounted.current) { setHosts([]); + setFilteredHosts([]); setIsLoading(false); } } @@ -37,10 +41,28 @@ function HostViewer({ getHosts, connectToHost, setIsAddHostHidden, deleteHost, e }; }, []); + useEffect(() => { + const filtered = hosts.filter((hostWrapper) => { + const hostConfig = hostWrapper.config || {}; + return hostConfig.name?.toLowerCase().includes(searchTerm.toLowerCase()) || hostConfig.ip?.toLowerCase().includes(searchTerm.toLowerCase()); + }); + setFilteredHosts(filtered); + }, [searchTerm, hosts]); + return (
-
-

Hosts

+
+ setSearchTerm(e.target.value)} + sx={{ + flex: 1, + backgroundColor: "#6e6e6e", + color: "#fff", + "&::placeholder": { color: "#ccc" }, + }} + /> @@ -216,20 +287,11 @@ const EditHostModal = ({ isHidden, form, setForm, handleEditHost, setIsEditHostH 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, + form: PropTypes.object.isRequired, setForm: PropTypes.func.isRequired, handleEditHost: PropTypes.func.isRequired, setIsEditHostHidden: PropTypes.func.isRequired, - hostConfig: PropTypes.object, + hostConfig: PropTypes.object }; export default EditHostModal; \ No newline at end of file diff --git a/src/modals/LoginUserModal.jsx b/src/modals/LoginUserModal.jsx index 9a0dc90d..efbe740f 100644 --- a/src/modals/LoginUserModal.jsx +++ b/src/modals/LoginUserModal.jsx @@ -1,10 +1,14 @@ 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 { Modal, Button, FormControl, FormLabel, Input, Stack, DialogTitle, DialogContent, ModalDialog, IconButton } from '@mui/joy'; import theme from '/src/theme'; -import {useEffect} from 'react'; +import { useEffect, useState } from 'react'; +import Visibility from '@mui/icons-material/Visibility'; +import VisibilityOff from '@mui/icons-material/VisibilityOff'; + +const LoginUserModal = ({ isHidden, form, setForm, handleLoginUser, handleGuestLogin, setIsLoginUserHidden, setIsCreateUserHidden }) => { + const [showPassword, setShowPassword] = useState(false); -const LoginUserModal = ({ isHidden, form, setForm, handleLoginUser, setIsLoginUserHidden, setIsCreateUserHidden }) => { const isFormValid = () => { if (!form.username || !form.password) return false; return true; @@ -64,15 +68,27 @@ const LoginUserModal = ({ isHidden, form, setForm, handleLoginUser, setIsLoginUs Password - setForm({ ...form, password: event.target.value })} - sx={{ - backgroundColor: theme.palette.general.primary, - color: theme.palette.text.primary, - }} - /> +
+ setForm({ ...form, password: event.target.value })} + sx={{ + backgroundColor: theme.palette.general.primary, + color: theme.palette.text.primary, + flex: 1, + }} + /> + setShowPassword(!showPassword)} + sx={{ + color: theme.palette.text.primary, + marginLeft: 1, + }} + > + {showPassword ? : } + +
+ @@ -115,6 +142,7 @@ LoginUserModal.propTypes = { form: PropTypes.object.isRequired, setForm: PropTypes.func.isRequired, handleLoginUser: PropTypes.func.isRequired, + handleGuestLogin: PropTypes.func.isRequired, setIsLoginUserHidden: PropTypes.func.isRequired, setIsCreateUserHidden: PropTypes.func.isRequired, }; diff --git a/src/modals/NoAuthenticationModal.jsx b/src/modals/NoAuthenticationModal.jsx new file mode 100644 index 00000000..24c753f5 --- /dev/null +++ b/src/modals/NoAuthenticationModal.jsx @@ -0,0 +1,176 @@ +import PropTypes from 'prop-types'; +import { CssVarsProvider } from '@mui/joy/styles'; +import { + Modal, + Button, + FormControl, + FormLabel, + Input, + Stack, + DialogTitle, + DialogContent, + ModalDialog, + IconButton, + Select, + Option, +} from '@mui/joy'; +import theme from '/src/theme'; +import { useState } from 'react'; +import Visibility from '@mui/icons-material/Visibility'; +import VisibilityOff from '@mui/icons-material/VisibilityOff'; + +const NoAuthenticationModal = ({ isHidden, form, setForm, setIsNoAuthHidden, handleAuthSubmit }) => { + const [showPassword, setShowPassword] = useState(false); + + const isFormValid = () => { + if (form.authMethod === 'Select Auth') return false; + if (form.authMethod === 'rsaKey' && !form.rsaKey) return false; + if (form.authMethod === 'password' && !form.password) return false; + return true; + }; + + const handleSubmit = (event) => { + event.preventDefault(); + if (isFormValid()) { + handleAuthSubmit(form); + setForm({ authMethod: 'Select Auth', password: '', rsaKey: '' }); + } + }; + + return ( + + { + if (reason !== 'backdropClick') { + setIsNoAuthHidden(true); + } + }} + disableBackdropClic + > + + Authentication Required + +
+ + + Authentication Method + + + {form.authMethod === 'password' && ( + + Password +
+ setForm({ ...form, password: e.target.value })} + required + sx={{ + backgroundColor: theme.palette.general.primary, + color: theme.palette.text.primary, + flex: 1, + }} + /> + setShowPassword(!showPassword)} + sx={{ + color: theme.palette.text.primary, + marginLeft: 1, + }} + > + {showPassword ? : } + +
+
+ )} + {form.authMethod === 'rsaKey' && ( + + RSA Key + { + const file = e.target.files[0]; + if (file) { + const reader = new FileReader(); + reader.onload = (event) => { + setForm({ ...form, rsaKey: event.target.result }); + }; + reader.readAsText(file); + } + }} + required + sx={{ + backgroundColor: theme.palette.general.primary, + color: theme.palette.text.primary, + padding: 1, + textAlign: 'center', + width: '100%', + minWidth: 'auto', + minHeight: 'auto', + }} + /> + + )} + +
+
+
+
+
+
+ ); +}; + +NoAuthenticationModal.propTypes = { + isHidden: PropTypes.bool.isRequired, + form: PropTypes.object.isRequired, + setForm: PropTypes.func.isRequired, + setIsNoAuthHidden: PropTypes.func.isRequired, + handleAuthSubmit: PropTypes.func.isRequired, +}; + +export default NoAuthenticationModal; \ No newline at end of file diff --git a/src/modals/ProfileModal.jsx b/src/modals/ProfileModal.jsx index 6566f3f8..38e59239 100644 --- a/src/modals/ProfileModal.jsx +++ b/src/modals/ProfileModal.jsx @@ -56,10 +56,12 @@ const ProfileModal = ({ isHidden, getUser, handleDeleteUser, handleLogoutUser, s borderRadius: 10, width: "100%", textAlign: "center", + display: "flex", + justifyContent: "center", + alignItems: "center", }} > - Username:
- {getUserName()} + User: {getUserName()} diff --git a/src/Utils.jsx b/src/other/Utils.jsx similarity index 100% rename from src/Utils.jsx rename to src/other/Utils.jsx diff --git a/src/TabList.jsx b/src/ui/TabList.jsx similarity index 100% rename from src/TabList.jsx rename to src/ui/TabList.jsx