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

View File

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