Improved add host UI

This commit is contained in:
Karmaa
2025-03-19 22:02:44 -05:00
parent 0a50d5c85c
commit b5af67bdd6
3 changed files with 219 additions and 80 deletions

View File

@@ -2,7 +2,7 @@ import { useState, useEffect, useRef } from "react";
import { NewTerminal } from "./apps/ssh/Terminal.jsx"; import { NewTerminal } from "./apps/ssh/Terminal.jsx";
import { User } from "./apps/user/User.jsx"; import { User } from "./apps/user/User.jsx";
import AddHostModal from "./modals/AddHostModal.jsx"; import AddHostModal from "./modals/AddHostModal.jsx";
import LoginUserModal from "./modals/LoginUserModal.jsx"; import AuthModal from "./modals/AuthModal.jsx";
import { Button } from "@mui/joy"; import { Button } from "@mui/joy";
import { CssVarsProvider } from "@mui/joy"; import { CssVarsProvider } from "@mui/joy";
import theme from "./theme"; import theme from "./theme";
@@ -12,7 +12,6 @@ import { Debounce } from './other/Utils.jsx';
import TermixIcon from "./images/termix_icon.png"; import TermixIcon from "./images/termix_icon.png";
import RocketIcon from './images/launchpad_rocket.png'; import RocketIcon from './images/launchpad_rocket.png';
import ProfileIcon from './images/profile_icon.png'; import ProfileIcon from './images/profile_icon.png';
import CreateUserModal from "./modals/CreateUserModal.jsx";
import ProfileModal from "./modals/ProfileModal.jsx"; import ProfileModal from "./modals/ProfileModal.jsx";
import ErrorModal from "./modals/ErrorModal.jsx"; import ErrorModal from "./modals/ErrorModal.jsx";
import EditHostModal from "./modals/EditHostModal.jsx"; import EditHostModal from "./modals/EditHostModal.jsx";
@@ -20,8 +19,7 @@ import NoAuthenticationModal from "./modals/NoAuthenticationModal.jsx";
function App() { function App() {
const [isAddHostHidden, setIsAddHostHidden] = useState(true); const [isAddHostHidden, setIsAddHostHidden] = useState(true);
const [isLoginUserHidden, setIsLoginUserHidden] = useState(true); const [isAuthModalHidden, setIsAuthModalHidden] = useState(true);
const [isCreateUserHidden, setIsCreateUserHidden] = useState(true);
const [isProfileHidden, setIsProfileHidden] = useState(true); const [isProfileHidden, setIsProfileHidden] = useState(true);
const [isErrorHidden, setIsErrorHidden] = useState(true); const [isErrorHidden, setIsErrorHidden] = useState(true);
const [errorMessage, setErrorMessage] = useState(''); const [errorMessage, setErrorMessage] = useState('');
@@ -37,7 +35,7 @@ function App() {
password: "", password: "",
port: 22, port: 22,
authMethod: "Select Auth", authMethod: "Select Auth",
rememberHost: false, rememberHost: true,
storePassword: true, storePassword: true,
}); });
const [editHostForm, setEditHostForm] = useState({ const [editHostForm, setEditHostForm] = useState({
@@ -53,14 +51,6 @@ function App() {
}); });
const [isNoAuthHidden, setIsNoAuthHidden] = useState(true); const [isNoAuthHidden, setIsNoAuthHidden] = useState(true);
const [authForm, setAuthForm] = useState({ const [authForm, setAuthForm] = useState({
password: "",
rsaKey: "",
});
const [loginUserForm, setLoginUserForm] = useState({
username: "",
password: "",
});
const [createUserForm, setCreateUserForm] = useState({
username: "", username: "",
password: "", password: "",
}); });
@@ -133,13 +123,13 @@ function App() {
if (userRef.current?.getUser()) { if (userRef.current?.getUser()) {
setIsLoggingIn(false); setIsLoggingIn(false);
setIsLoginUserHidden(true); setIsAuthModalHidden(true);
return; return;
} }
if (!sessionToken) { if (!sessionToken) {
setIsLoggingIn(false); setIsLoggingIn(false);
setIsLoginUserHidden(false); setIsAuthModalHidden(false);
return; return;
} }
@@ -153,7 +143,7 @@ function App() {
clearInterval(attemptLoginInterval); clearInterval(attemptLoginInterval);
if (!userRef.current?.getUser()) { if (!userRef.current?.getUser()) {
localStorage.removeItem('sessionToken'); localStorage.removeItem('sessionToken');
setIsLoginUserHidden(false); setIsAuthModalHidden(false);
setIsLoggingIn(false); setIsLoggingIn(false);
setErrorMessage('Login timed out. Please try again.'); setErrorMessage('Login timed out. Please try again.');
setIsErrorHidden(false); setIsErrorHidden(false);
@@ -170,7 +160,7 @@ function App() {
if (!userRef.current?.getUser()) { if (!userRef.current?.getUser()) {
localStorage.removeItem('sessionToken'); localStorage.removeItem('sessionToken');
setIsLoginUserHidden(false); setIsAuthModalHidden(false);
setIsLoggingIn(false); setIsLoggingIn(false);
setErrorMessage('Login timed out. Please try again.'); setErrorMessage('Login timed out. Please try again.');
setIsErrorHidden(false); setIsErrorHidden(false);
@@ -186,7 +176,7 @@ function App() {
if (isComponentMounted) { if (isComponentMounted) {
clearTimeout(loginTimeout); clearTimeout(loginTimeout);
clearInterval(attemptLoginInterval); clearInterval(attemptLoginInterval);
setIsLoginUserHidden(true); setIsAuthModalHidden(true);
setIsLoggingIn(false); setIsLoggingIn(false);
setIsErrorHidden(true); setIsErrorHidden(true);
} }
@@ -200,7 +190,7 @@ function App() {
localStorage.removeItem('sessionToken'); localStorage.removeItem('sessionToken');
setErrorMessage(`Auto-login failed: ${error}`); setErrorMessage(`Auto-login failed: ${error}`);
setIsErrorHidden(false); setIsErrorHidden(false);
setIsLoginUserHidden(false); setIsAuthModalHidden(false);
setIsLoggingIn(false); setIsLoggingIn(false);
} }
} }
@@ -372,64 +362,49 @@ function App() {
} }
}; };
const handleLoginUser = ({ username, password, sessionToken, onSuccess, onFailure }) => { const handleLoginUser = ({ username, password, onSuccess, onFailure }) => {
if (userRef.current) { if (userRef.current) {
if (sessionToken) {
userRef.current.loginUser({
sessionToken,
onSuccess: () => {
setIsLoginUserHidden(true);
setIsLoggingIn(false);
if (onSuccess) onSuccess();
},
onFailure: (error) => {
localStorage.removeItem('sessionToken');
setIsLoginUserHidden(false);
setIsLoggingIn(false);
if (onFailure) onFailure(error);
},
});
} else {
userRef.current.loginUser({ userRef.current.loginUser({
username, username,
password, password,
onSuccess: () => { onSuccess: () => {
setIsLoginUserHidden(true); setIsAuthModalHidden(true);
setIsLoggingIn(false);
if (onSuccess) onSuccess(); if (onSuccess) onSuccess();
}, },
onFailure: (error) => { onFailure: (error) => {
setIsLoginUserHidden(false); setIsAuthModalHidden(false);
setIsLoggingIn(false);
if (onFailure) onFailure(error); if (onFailure) onFailure(error);
}, },
}); });
} }
}
}; };
const handleGuestLogin = async ({ onSuccess, onFailure }) => { const handleGuestLogin = async ({ onSuccess, onFailure }) => {
if (userRef.current) { if (userRef.current) {
try { try {
await userRef.current.loginAsGuest(); await userRef.current.loginAsGuest();
setIsLoginUserHidden(true); setIsAuthModalHidden(true);
setIsLoggingIn(false);
if (onSuccess) onSuccess(); if (onSuccess) onSuccess();
} catch (error) { } catch (error) {
setIsLoginUserHidden(false); setIsAuthModalHidden(false);
setIsLoggingIn(false);
if (onFailure) onFailure(error); if (onFailure) onFailure(error);
} }
} }
} };
const handleCreateUser = ({ username, password, onSuccess, onFailure }) => { const handleCreateUser = ({ username, password, onSuccess, onFailure }) => {
if (userRef.current) { if (userRef.current) {
userRef.current.createUser({ userRef.current.createUser({
username, username,
password, password,
onSuccess, onSuccess: () => {
onFailure, setIsAuthModalHidden(true);
if (onSuccess) onSuccess();
},
onFailure: (error) => {
setIsAuthModalHidden(false);
if (onFailure) onFailure(error);
},
}); });
} }
}; };
@@ -622,7 +597,7 @@ function App() {
{/* Profile Button */} {/* Profile Button */}
<Button <Button
disabled={isLoggingIn} disabled={isLoggingIn}
onClick={() => userRef.current?.getUser() ? setIsProfileHidden(false) : setIsLoginUserHidden(false)} onClick={() => userRef.current?.getUser() ? setIsProfileHidden(false) : setIsAuthModalHidden(false)}
sx={{ sx={{
backgroundColor: theme.palette.general.tertiary, backgroundColor: theme.palette.general.tertiary,
"&:hover": { backgroundColor: theme.palette.general.secondary }, "&:hover": { backgroundColor: theme.palette.general.secondary },
@@ -742,40 +717,31 @@ function App() {
setIsErrorHidden={setIsErrorHidden} setIsErrorHidden={setIsErrorHidden}
/> />
<LoginUserModal <AuthModal
isHidden={isLoginUserHidden} isHidden={isAuthModalHidden}
form={loginUserForm} form={authForm}
setForm={setLoginUserForm} setForm={setAuthForm}
handleLoginUser={handleLoginUser} handleLoginUser={handleLoginUser}
handleGuestLogin={handleGuestLogin} handleGuestLogin={handleGuestLogin}
setIsLoginUserHidden={setIsLoginUserHidden}
setIsCreateUserHidden={setIsCreateUserHidden}
/>
<CreateUserModal
isHidden={isCreateUserHidden}
form={createUserForm}
setForm={setCreateUserForm}
handleCreateUser={handleCreateUser} handleCreateUser={handleCreateUser}
setIsCreateUserHidden={setIsCreateUserHidden} setIsLoginUserHidden={setIsAuthModalHidden}
setIsLoginUserHidden={setIsLoginUserHidden}
/> />
{/* User component */} {/* User component */}
<User <User
ref={userRef} ref={userRef}
onLoginSuccess={() => { onLoginSuccess={() => {
setIsLoginUserHidden(true); setIsAuthModalHidden(true);
setIsLoggingIn(false); setIsLoggingIn(false);
setIsErrorHidden(true); setIsErrorHidden(true);
}} }}
onCreateSuccess={() => { onCreateSuccess={() => {
setIsCreateUserHidden(true); setIsAuthModalHidden(true);
handleLoginUser({ handleLoginUser({
username: createUserForm.username, username: authForm.username,
password: createUserForm.password, password: authForm.password,
onSuccess: () => { onSuccess: () => {
setIsLoginUserHidden(true); setIsAuthModalHidden(true);
setIsLoggingIn(false); setIsLoggingIn(false);
setIsErrorHidden(true); setIsErrorHidden(true);
}, },

View File

@@ -7,8 +7,6 @@ import {
FormLabel, FormLabel,
Input, Input,
Stack, Stack,
DialogTitle,
DialogContent,
ModalDialog, ModalDialog,
Select, Select,
Option, Option,
@@ -394,6 +392,7 @@ const AddHostModal = ({ isHidden, form, setForm, handleAddHost, setIsAddHostHidd
<Button <Button
onClick={handleSubmit} onClick={handleSubmit}
disabled={!isFormValid()}
sx={{ sx={{
backgroundColor: theme.palette.general.primary, backgroundColor: theme.palette.general.primary,
color: theme.palette.text.primary, color: theme.palette.text.primary,

174
src/modals/AuthModal.jsx Normal file
View File

@@ -0,0 +1,174 @@
import PropTypes from 'prop-types';
import { CssVarsProvider } from '@mui/joy/styles';
import { Modal, Button, FormControl, FormLabel, Input, Stack, ModalDialog, IconButton, Tabs, TabList, Tab, TabPanel } from '@mui/joy';
import theme from '/src/theme';
import { useEffect, useState } from 'react';
import Visibility from '@mui/icons-material/Visibility';
import VisibilityOff from '@mui/icons-material/VisibilityOff';
const AuthModal = ({ isHidden, form, setForm, handleLoginUser, handleGuestLogin, handleCreateUser, setIsLoginUserHidden }) => {
const [showPassword, setShowPassword] = useState(false);
const [confirmPassword, setConfirmPassword] = useState('');
const [activeTab, setActiveTab] = useState(0);
const [isLoading, setIsLoading] = useState(false);
const isLoginFormValid = () => {
return form.username?.trim() && form.password?.trim();
};
const isCreateFormValid = () => {
return form.username?.trim() && form.password?.trim() && confirmPassword?.trim() && form.password === confirmPassword;
};
const handleLogin = () => {
if (!isLoginFormValid()) {
alert("Please fill out all fields");
return;
}
setIsLoading(true);
handleLoginUser({
username: form.username.trim(),
password: form.password.trim(),
onSuccess: () => {
setIsLoading(false);
setIsLoginUserHidden(true);
},
onFailure: (error) => {
setIsLoading(false);
alert(error);
}
});
};
const handleCreate = () => {
if (!isCreateFormValid()) {
alert("Please fill out all fields and ensure passwords match");
return;
}
setIsLoading(true);
handleCreateUser({
username: form.username.trim(),
password: form.password.trim(),
onSuccess: () => {
setIsLoading(false);
setIsLoginUserHidden(true);
},
onFailure: (error) => {
setIsLoading(false);
alert(error);
}
});
};
useEffect(() => {
if (isHidden) {
setForm({ username: '', password: '' });
setConfirmPassword('');
setIsLoading(false);
setActiveTab(0);
}
}, [isHidden]);
return (
<CssVarsProvider theme={theme}>
<Modal open={!isHidden} onClose={() => setIsLoginUserHidden(true)}>
<ModalDialog layout="center" sx={{ backgroundColor: theme.palette.general.tertiary }}>
<Tabs value={activeTab} onChange={(e, val) => setActiveTab(val)}>
<TabList>
<Tab>Login</Tab>
<Tab>Create Account</Tab>
</TabList>
<div style={{ padding: '24px', backgroundColor: theme.palette.general.tertiary }}>
<TabPanel value={0}>
<Stack spacing={2}>
<FormControl>
<FormLabel>Username</FormLabel>
<Input
value={form.username}
onChange={(event) => setForm({ ...form, username: event.target.value })}
disabled={isLoading}
/>
</FormControl>
<FormControl>
<FormLabel>Password</FormLabel>
<div style={{ display: 'flex', alignItems: 'center' }}>
<Input
type={showPassword ? 'text' : 'password'}
value={form.password}
onChange={(event) => setForm({ ...form, password: event.target.value })}
disabled={isLoading}
/>
<IconButton onClick={() => setShowPassword(!showPassword)}>
{showPassword ? <VisibilityOff /> : <Visibility />}
</IconButton>
</div>
</FormControl>
<Button onClick={handleLogin} disabled={!isLoginFormValid() || isLoading}>
{isLoading ? "Logging in..." : "Login"}
</Button>
<Button onClick={handleGuestLogin} disabled={isLoading}>
{isLoading ? "Logging in as guest..." : "Login as Guest"}
</Button>
</Stack>
</TabPanel>
<TabPanel value={1}>
<Stack spacing={2}>
<FormControl>
<FormLabel>Username</FormLabel>
<Input
value={form.username}
onChange={(event) => setForm({ ...form, username: event.target.value })}
disabled={isLoading}
/>
</FormControl>
<FormControl>
<FormLabel>Password</FormLabel>
<div style={{ display: 'flex', alignItems: 'center' }}>
<Input
type={showPassword ? 'text' : 'password'}
value={form.password}
onChange={(event) => setForm({ ...form, password: event.target.value })}
disabled={isLoading}
/>
<IconButton onClick={() => setShowPassword(!showPassword)}>
{showPassword ? <VisibilityOff /> : <Visibility />}
</IconButton>
</div>
</FormControl>
<FormControl>
<FormLabel>Confirm Password</FormLabel>
<Input
type={showPassword ? 'text' : 'password'}
value={confirmPassword}
onChange={(event) => setConfirmPassword(event.target.value)}
disabled={isLoading}
/>
</FormControl>
<Button onClick={handleCreate} disabled={!isCreateFormValid() || isLoading}>
{isLoading ? "Creating account..." : "Create Account"}
</Button>
</Stack>
</TabPanel>
</div>
</Tabs>
</ModalDialog>
</Modal>
</CssVarsProvider>
);
};
AuthModal.propTypes = {
isHidden: PropTypes.bool.isRequired,
form: PropTypes.object.isRequired,
setForm: PropTypes.func.isRequired,
handleLoginUser: PropTypes.func.isRequired,
handleGuestLogin: PropTypes.func.isRequired,
handleCreateUser: PropTypes.func.isRequired,
setIsLoginUserHidden: PropTypes.func.isRequired,
};
export default AuthModal;