Improved and fixed editing and adding host UI
This commit is contained in:
78
src/App.jsx
78
src/App.jsx
@@ -222,13 +222,12 @@ function App() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleAddHost = () => {
|
const handleAddHost = () => {
|
||||||
if (addHostForm.ip && addHostForm.user && addHostForm.port) {
|
if (!addHostForm.ip?.trim() || !addHostForm.user?.trim() || !addHostForm.port) {
|
||||||
if (!addHostForm.rememberHost) {
|
alert("Please fill out all required fields (IP, User, Port).");
|
||||||
connectToHost();
|
|
||||||
setIsAddHostHidden(true);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (addHostForm.rememberHost) {
|
||||||
if (addHostForm.authMethod === 'Select Auth') {
|
if (addHostForm.authMethod === 'Select Auth') {
|
||||||
alert("Please select an authentication method.");
|
alert("Please select an authentication method.");
|
||||||
return;
|
return;
|
||||||
@@ -237,33 +236,42 @@ function App() {
|
|||||||
setIsNoAuthHidden(false);
|
setIsNoAuthHidden(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (addHostForm.authMethod === 'rsaKey' && !addHostForm.rsaKey) {
|
if (addHostForm.authMethod === 'key' && !addHostForm.privateKey) {
|
||||||
setIsNoAuthHidden(false);
|
setIsNoAuthHidden(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
connectToHost();
|
connectToHost();
|
||||||
|
if (addHostForm.rememberHost) {
|
||||||
if (!addHostForm.storePassword) {
|
if (!addHostForm.storePassword) {
|
||||||
addHostForm.password = '';
|
addHostForm.password = '';
|
||||||
|
addHostForm.privateKey = '';
|
||||||
}
|
}
|
||||||
handleSaveHost();
|
handleSaveHost();
|
||||||
setIsAddHostHidden(true);
|
|
||||||
} else {
|
|
||||||
alert("Please fill out all required fields (IP, User, Port).");
|
|
||||||
}
|
}
|
||||||
|
setIsAddHostHidden(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const connectToHost = () => {
|
const connectToHost = () => {
|
||||||
const hostConfig = {
|
const hostConfig = {
|
||||||
name: addHostForm.name || '',
|
name: addHostForm.name || '',
|
||||||
folder: addHostForm.folder || '',
|
folder: addHostForm.folder || '',
|
||||||
ip: addHostForm.ip,
|
ip: addHostForm.ip.trim(),
|
||||||
user: addHostForm.user,
|
user: addHostForm.user.trim(),
|
||||||
port: String(addHostForm.port),
|
port: String(addHostForm.port),
|
||||||
password: addHostForm.rememberHost && addHostForm.authMethod === 'password' ? addHostForm.password : undefined,
|
|
||||||
rsaKey: addHostForm.rememberHost && addHostForm.authMethod === 'rsaKey' ? addHostForm.rsaKey : undefined,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (addHostForm.rememberHost && addHostForm.storePassword) {
|
||||||
|
if (addHostForm.authMethod === 'password') {
|
||||||
|
hostConfig.password = addHostForm.password;
|
||||||
|
} else if (addHostForm.authMethod === 'key') {
|
||||||
|
hostConfig.privateKey = addHostForm.privateKey;
|
||||||
|
hostConfig.keyType = addHostForm.keyType;
|
||||||
|
hostConfig.passphrase = addHostForm.passphrase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const newTerminal = {
|
const newTerminal = {
|
||||||
id: nextId,
|
id: nextId,
|
||||||
title: hostConfig.name || hostConfig.ip,
|
title: hostConfig.name || hostConfig.ip,
|
||||||
@@ -273,9 +281,21 @@ function App() {
|
|||||||
setTerminals([...terminals, newTerminal]);
|
setTerminals([...terminals, newTerminal]);
|
||||||
setActiveTab(nextId);
|
setActiveTab(nextId);
|
||||||
setNextId(nextId + 1);
|
setNextId(nextId + 1);
|
||||||
setIsAddHostHidden(true);
|
setAddHostForm({
|
||||||
setAddHostForm({ name: "", folder: "", ip: "", user: "", password: "", rsaKey: "", port: 22, authMethod: "Select Auth", rememberHost: false, storePassword: true });
|
name: "",
|
||||||
}
|
folder: "",
|
||||||
|
ip: "",
|
||||||
|
user: "",
|
||||||
|
password: "",
|
||||||
|
privateKey: "",
|
||||||
|
keyType: "",
|
||||||
|
passphrase: "",
|
||||||
|
port: 22,
|
||||||
|
authMethod: "Select Auth",
|
||||||
|
rememberHost: true,
|
||||||
|
storePassword: true
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const handleAuthSubmit = (form) => {
|
const handleAuthSubmit = (form) => {
|
||||||
const updatedTerminals = terminals.map((terminal) => {
|
const updatedTerminals = terminals.map((terminal) => {
|
||||||
@@ -327,21 +347,30 @@ function App() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const handleSaveHost = () => {
|
const handleSaveHost = () => {
|
||||||
let hostConfig = {
|
const hostConfig = {
|
||||||
name: addHostForm.name || addHostForm.ip,
|
name: addHostForm.name || addHostForm.ip,
|
||||||
folder: addHostForm.folder,
|
folder: addHostForm.folder,
|
||||||
ip: addHostForm.ip,
|
ip: addHostForm.ip.trim(),
|
||||||
user: addHostForm.user,
|
user: addHostForm.user.trim(),
|
||||||
password: addHostForm.authMethod === 'password' ? addHostForm.password : undefined,
|
|
||||||
rsaKey: addHostForm.authMethod === 'rsaKey' ? addHostForm.rsaKey : undefined,
|
|
||||||
port: String(addHostForm.port),
|
port: String(addHostForm.port),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (addHostForm.storePassword) {
|
||||||
|
if (addHostForm.authMethod === 'password') {
|
||||||
|
hostConfig.password = addHostForm.password;
|
||||||
|
} else if (addHostForm.authMethod === 'key') {
|
||||||
|
hostConfig.privateKey = addHostForm.privateKey;
|
||||||
|
hostConfig.keyType = addHostForm.keyType;
|
||||||
|
hostConfig.passphrase = addHostForm.passphrase;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (userRef.current) {
|
if (userRef.current) {
|
||||||
userRef.current.saveHost({
|
userRef.current.saveHost({
|
||||||
hostConfig,
|
hostConfig,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const handleLoginUser = ({ username, password, sessionToken, onSuccess, onFailure }) => {
|
const handleLoginUser = ({ username, password, sessionToken, onSuccess, onFailure }) => {
|
||||||
if (userRef.current) {
|
if (userRef.current) {
|
||||||
@@ -472,7 +501,6 @@ function App() {
|
|||||||
|
|
||||||
updateEditHostForm(oldConfig);
|
updateEditHostForm(oldConfig);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Edit failed:', error);
|
|
||||||
setErrorMessage(`Edit failed: ${error}`);
|
setErrorMessage(`Edit failed: ${error}`);
|
||||||
setIsErrorHidden(false);
|
setIsErrorHidden(false);
|
||||||
setIsEditing(false);
|
setIsEditing(false);
|
||||||
@@ -658,10 +686,10 @@ function App() {
|
|||||||
)}
|
)}
|
||||||
<NoAuthenticationModal
|
<NoAuthenticationModal
|
||||||
isHidden={isNoAuthHidden}
|
isHidden={isNoAuthHidden}
|
||||||
|
setIsHidden={setIsNoAuthHidden}
|
||||||
form={authForm}
|
form={authForm}
|
||||||
setForm={setAuthForm}
|
setForm={setAuthForm}
|
||||||
setIsNoAuthHidden={setIsNoAuthHidden}
|
onAuthenticate={handleAuthSubmit}
|
||||||
handleAuthSubmit={handleAuthSubmit}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -681,7 +709,7 @@ function App() {
|
|||||||
setForm={setEditHostForm}
|
setForm={setEditHostForm}
|
||||||
handleEditHost={handleEditHost}
|
handleEditHost={handleEditHost}
|
||||||
setIsEditHostHidden={setIsEditHostHidden}
|
setIsEditHostHidden={setIsEditHostHidden}
|
||||||
hostConfig={currentHostConfig}
|
hostConfig={currentHostConfig || {}}
|
||||||
/>
|
/>
|
||||||
<ProfileModal
|
<ProfileModal
|
||||||
isHidden={isProfileHidden}
|
isHidden={isProfileHidden}
|
||||||
|
|||||||
@@ -222,7 +222,6 @@ export const User = forwardRef(({ onLoginSuccess, onCreateSuccess, onDeleteSucce
|
|||||||
if (!currentUser.current) return onFailure("Not authenticated");
|
if (!currentUser.current) return onFailure("Not authenticated");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.log('Editing host with configs:', { oldHostConfig, newHostConfig });
|
|
||||||
const response = await new Promise((resolve) => {
|
const response = await new Promise((resolve) => {
|
||||||
socketRef.current.emit("editHost", {
|
socketRef.current.emit("editHost", {
|
||||||
userId: currentUser.current.id,
|
userId: currentUser.current.id,
|
||||||
|
|||||||
@@ -24,20 +24,7 @@ import { useState } from 'react';
|
|||||||
import Visibility from '@mui/icons-material/Visibility';
|
import Visibility from '@mui/icons-material/Visibility';
|
||||||
import VisibilityOff from '@mui/icons-material/VisibilityOff';
|
import VisibilityOff from '@mui/icons-material/VisibilityOff';
|
||||||
|
|
||||||
const AddHostModal = ({ isHidden, setIsAddHostHidden, handleAddHost }) => {
|
const AddHostModal = ({ isHidden, form, setForm, handleAddHost, setIsAddHostHidden }) => {
|
||||||
const [form, setForm] = useState({
|
|
||||||
name: '',
|
|
||||||
folder: '',
|
|
||||||
ip: '',
|
|
||||||
user: '',
|
|
||||||
port: 22,
|
|
||||||
password: '',
|
|
||||||
privateKey: '',
|
|
||||||
keyType: '',
|
|
||||||
passphrase: '',
|
|
||||||
authMethod: 'Select Auth',
|
|
||||||
rememberHost: true
|
|
||||||
});
|
|
||||||
const [showPassword, setShowPassword] = useState(false);
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
const [showPassphrase, setShowPassphrase] = useState(false);
|
const [showPassphrase, setShowPassphrase] = useState(false);
|
||||||
const [activeTab, setActiveTab] = useState(0);
|
const [activeTab, setActiveTab] = useState(0);
|
||||||
@@ -100,14 +87,23 @@ const AddHostModal = ({ isHidden, setIsAddHostHidden, handleAddHost }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const isFormValid = () => {
|
const isFormValid = () => {
|
||||||
if (!form.ip || !form.user || !form.port) return false;
|
const { ip, user, port, authMethod, password, privateKey } = form;
|
||||||
const portNum = Number(form.port);
|
|
||||||
|
// Basic validation for required fields
|
||||||
|
if (!ip?.trim() || !user?.trim() || !port) return false;
|
||||||
|
|
||||||
|
// Port validation
|
||||||
|
const portNum = Number(port);
|
||||||
if (isNaN(portNum) || portNum < 1 || portNum > 65535) return false;
|
if (isNaN(portNum) || portNum < 1 || portNum > 65535) return false;
|
||||||
|
|
||||||
|
// If not remembering host, only basic fields are required
|
||||||
|
if (!form.rememberHost) return true;
|
||||||
|
|
||||||
|
// Auth method validation only if remembering host
|
||||||
if (form.rememberHost) {
|
if (form.rememberHost) {
|
||||||
if (form.authMethod === 'Select Auth') return false;
|
if (authMethod === 'Select Auth') return false;
|
||||||
if (form.authMethod === 'key' && !form.privateKey) return false;
|
if (authMethod === 'password' && !password?.trim()) return false;
|
||||||
if (form.authMethod === 'password' && !form.password) return false;
|
if (authMethod === 'key' && !privateKey?.trim()) return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -115,28 +111,11 @@ const AddHostModal = ({ isHidden, setIsAddHostHidden, handleAddHost }) => {
|
|||||||
|
|
||||||
const handleSubmit = (event) => {
|
const handleSubmit = (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (isFormValid()) {
|
if (!form.ip?.trim() || !form.user?.trim() || !form.port) {
|
||||||
if (!form.rememberHost) {
|
alert("Please fill out all required fields (IP, User, Port).");
|
||||||
handleAddHost();
|
return;
|
||||||
} else {
|
|
||||||
handleAddHost();
|
|
||||||
}
|
|
||||||
|
|
||||||
setForm({
|
|
||||||
name: '',
|
|
||||||
folder: '',
|
|
||||||
ip: '',
|
|
||||||
user: '',
|
|
||||||
password: '',
|
|
||||||
privateKey: '',
|
|
||||||
keyType: '',
|
|
||||||
port: 22,
|
|
||||||
authMethod: 'Select Auth',
|
|
||||||
rememberHost: false,
|
|
||||||
storePassword: true,
|
|
||||||
});
|
|
||||||
setIsAddHostHidden(true);
|
|
||||||
}
|
}
|
||||||
|
handleAddHost();
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -146,15 +125,18 @@ const AddHostModal = ({ isHidden, setIsAddHostHidden, handleAddHost }) => {
|
|||||||
display: 'flex',
|
display: 'flex',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
|
backdropFilter: 'blur(5px)',
|
||||||
|
backgroundColor: 'rgba(0, 0, 0, 0.2)',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ModalDialog
|
<ModalDialog
|
||||||
layout="center"
|
layout="center"
|
||||||
|
variant="outlined"
|
||||||
sx={{
|
sx={{
|
||||||
backgroundColor: theme.palette.general.tertiary,
|
backgroundColor: theme.palette.general.tertiary,
|
||||||
borderColor: theme.palette.general.secondary,
|
borderColor: theme.palette.general.secondary,
|
||||||
color: theme.palette.text.primary,
|
color: theme.palette.text.primary,
|
||||||
padding: 3,
|
padding: 0,
|
||||||
borderRadius: 10,
|
borderRadius: 10,
|
||||||
maxWidth: '500px',
|
maxWidth: '500px',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
@@ -164,47 +146,45 @@ const AddHostModal = ({ isHidden, setIsAddHostHidden, handleAddHost }) => {
|
|||||||
mx: 2,
|
mx: 2,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DialogTitle sx={{ mb: 2 }}>Add Host</DialogTitle>
|
|
||||||
<DialogContent>
|
|
||||||
<form onSubmit={handleSubmit}>
|
|
||||||
<Tabs
|
<Tabs
|
||||||
value={activeTab}
|
value={activeTab}
|
||||||
onChange={(e, val) => setActiveTab(val)}
|
onChange={(e, val) => setActiveTab(val)}
|
||||||
sx={{
|
sx={{
|
||||||
backgroundColor: theme.palette.general.disabled,
|
|
||||||
borderRadius: '8px',
|
|
||||||
padding: '8px',
|
|
||||||
marginBottom: '16px',
|
|
||||||
width: '100%',
|
width: '100%',
|
||||||
|
mb: 0,
|
||||||
|
backgroundColor: theme.palette.general.tertiary,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<TabList
|
<TabList
|
||||||
sx={{
|
sx={{
|
||||||
width: '100%',
|
width: '100%',
|
||||||
gap: 0,
|
gap: 0,
|
||||||
mb: 2,
|
borderTopLeftRadius: 10,
|
||||||
|
borderTopRightRadius: 10,
|
||||||
|
backgroundColor: theme.palette.general.primary,
|
||||||
'& button': {
|
'& button': {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
bgcolor: 'transparent',
|
bgcolor: 'transparent',
|
||||||
color: theme.palette.text.secondary,
|
color: theme.palette.text.secondary,
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
bgcolor: 'rgba(255, 255, 255, 0.1)',
|
bgcolor: theme.palette.general.disabled,
|
||||||
},
|
},
|
||||||
'&.Mui-selected': {
|
'&.Mui-selected': {
|
||||||
bgcolor: theme.palette.general.primary,
|
bgcolor: theme.palette.general.tertiary,
|
||||||
color: theme.palette.text.primary,
|
color: theme.palette.text.primary,
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
bgcolor: theme.palette.general.primary,
|
bgcolor: theme.palette.general.tertiary,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Tab>Basic Info</Tab>
|
<Tab sx={{ flex: 1 }}>Basic Info</Tab>
|
||||||
<Tab>Connection</Tab>
|
<Tab sx={{ flex: 1 }}>Connection</Tab>
|
||||||
<Tab>Authentication</Tab>
|
<Tab sx={{ flex: 1 }}>Authentication</Tab>
|
||||||
</TabList>
|
</TabList>
|
||||||
|
|
||||||
|
<div style={{ padding: '24px', backgroundColor: theme.palette.general.tertiary }}>
|
||||||
<TabPanel value={0}>
|
<TabPanel value={0}>
|
||||||
<Stack spacing={2}>
|
<Stack spacing={2}>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
@@ -229,6 +209,22 @@ const AddHostModal = ({ isHidden, setIsAddHostHidden, handleAddHost }) => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
<FormControl>
|
||||||
|
<FormLabel>Remember Host</FormLabel>
|
||||||
|
<Checkbox
|
||||||
|
checked={Boolean(form.rememberHost)}
|
||||||
|
onChange={(e) => setForm({
|
||||||
|
...form,
|
||||||
|
rememberHost: e.target.checked,
|
||||||
|
})}
|
||||||
|
sx={{
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
'&.Mui-checked': {
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
</Stack>
|
</Stack>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
|
|
||||||
@@ -278,46 +274,6 @@ const AddHostModal = ({ isHidden, setIsAddHostHidden, handleAddHost }) => {
|
|||||||
|
|
||||||
<TabPanel value={2}>
|
<TabPanel value={2}>
|
||||||
<Stack spacing={2}>
|
<Stack spacing={2}>
|
||||||
<FormControl>
|
|
||||||
<FormLabel>Remember Host</FormLabel>
|
|
||||||
<Checkbox
|
|
||||||
checked={form.rememberHost}
|
|
||||||
onChange={(e) => setForm({
|
|
||||||
...form,
|
|
||||||
rememberHost: e.target.checked,
|
|
||||||
|
|
||||||
...((!e.target.checked) && {
|
|
||||||
authMethod: 'Select Auth',
|
|
||||||
password: '',
|
|
||||||
privateKey: '',
|
|
||||||
keyType: '',
|
|
||||||
passphrase: '',
|
|
||||||
storePassword: true
|
|
||||||
})
|
|
||||||
})}
|
|
||||||
sx={{
|
|
||||||
color: theme.palette.text.primary,
|
|
||||||
'&.Mui-checked': {
|
|
||||||
color: theme.palette.text.primary,
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
{form.rememberHost && (
|
|
||||||
<>
|
|
||||||
<FormControl>
|
|
||||||
<FormLabel>Store Password</FormLabel>
|
|
||||||
<Checkbox
|
|
||||||
checked={form.storePassword}
|
|
||||||
onChange={(e) => setForm({ ...form, storePassword: e.target.checked })}
|
|
||||||
sx={{
|
|
||||||
color: theme.palette.text.primary,
|
|
||||||
'&.Mui-checked': {
|
|
||||||
color: theme.palette.text.primary,
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<FormControl error={!form.authMethod || form.authMethod === 'Select Auth'}>
|
<FormControl error={!form.authMethod || form.authMethod === 'Select Auth'}>
|
||||||
<FormLabel>Authentication Method</FormLabel>
|
<FormLabel>Authentication Method</FormLabel>
|
||||||
<Select
|
<Select
|
||||||
@@ -361,7 +317,7 @@ const AddHostModal = ({ isHidden, setIsAddHostHidden, handleAddHost }) => {
|
|||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{form.authMethod === 'key' && form.rememberHost && (
|
{form.authMethod === 'key' && (
|
||||||
<Stack spacing={2}>
|
<Stack spacing={2}>
|
||||||
<FormControl error={!form.privateKey}>
|
<FormControl error={!form.privateKey}>
|
||||||
<FormLabel>SSH Key</FormLabel>
|
<FormLabel>SSH Key</FormLabel>
|
||||||
@@ -376,7 +332,7 @@ const AddHostModal = ({ isHidden, setIsAddHostHidden, handleAddHost }) => {
|
|||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
height: '40px',
|
height: '40px',
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
backgroundColor: 'rgba(0, 0, 0, 0.3)',
|
backgroundColor: theme.palette.general.disabled,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -416,15 +372,28 @@ const AddHostModal = ({ isHidden, setIsAddHostHidden, handleAddHost }) => {
|
|||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
</>
|
|
||||||
|
{form.rememberHost && (
|
||||||
|
<FormControl>
|
||||||
|
<FormLabel>Store Password</FormLabel>
|
||||||
|
<Checkbox
|
||||||
|
checked={Boolean(form.storePassword)}
|
||||||
|
onChange={(e) => setForm({ ...form, storePassword: e.target.checked })}
|
||||||
|
sx={{
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
'&.Mui-checked': {
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</Tabs>
|
</div>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
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,
|
||||||
@@ -435,15 +404,14 @@ const AddHostModal = ({ isHidden, setIsAddHostHidden, handleAddHost }) => {
|
|||||||
backgroundColor: 'rgba(255, 255, 255, 0.1)',
|
backgroundColor: 'rgba(255, 255, 255, 0.1)',
|
||||||
color: 'rgba(255, 255, 255, 0.3)',
|
color: 'rgba(255, 255, 255, 0.3)',
|
||||||
},
|
},
|
||||||
marginTop: 3,
|
marginTop: 1,
|
||||||
width: '100%',
|
width: '100%',
|
||||||
height: '40px',
|
height: '40px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Add Host
|
Add Host
|
||||||
</Button>
|
</Button>
|
||||||
</form>
|
</Tabs>
|
||||||
</DialogContent>
|
|
||||||
</ModalDialog>
|
</ModalDialog>
|
||||||
</Modal>
|
</Modal>
|
||||||
</CssVarsProvider>
|
</CssVarsProvider>
|
||||||
@@ -452,8 +420,10 @@ const AddHostModal = ({ isHidden, setIsAddHostHidden, handleAddHost }) => {
|
|||||||
|
|
||||||
AddHostModal.propTypes = {
|
AddHostModal.propTypes = {
|
||||||
isHidden: PropTypes.bool.isRequired,
|
isHidden: PropTypes.bool.isRequired,
|
||||||
setIsAddHostHidden: PropTypes.func.isRequired,
|
form: PropTypes.object.isRequired,
|
||||||
|
setForm: PropTypes.func.isRequired,
|
||||||
handleAddHost: PropTypes.func.isRequired,
|
handleAddHost: PropTypes.func.isRequired,
|
||||||
|
setIsAddHostHidden: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AddHostModal;
|
export default AddHostModal;
|
||||||
@@ -36,7 +36,8 @@ const EditHostModal = ({ isHidden, hostConfig, setIsEditHostHidden, handleEditHo
|
|||||||
keyType: hostConfig?.keyType || '',
|
keyType: hostConfig?.keyType || '',
|
||||||
passphrase: '',
|
passphrase: '',
|
||||||
authMethod: hostConfig?.authMethod || 'Select Auth',
|
authMethod: hostConfig?.authMethod || 'Select Auth',
|
||||||
storePassword: true
|
storePassword: true,
|
||||||
|
rememberHost: true
|
||||||
});
|
});
|
||||||
const [showPassword, setShowPassword] = useState(false);
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
const [showPassphrase, setShowPassphrase] = useState(false);
|
const [showPassphrase, setShowPassphrase] = useState(false);
|
||||||
@@ -51,11 +52,13 @@ const EditHostModal = ({ isHidden, hostConfig, setIsEditHostHidden, handleEditHo
|
|||||||
ip: hostConfig.ip || '',
|
ip: hostConfig.ip || '',
|
||||||
user: hostConfig.user || '',
|
user: hostConfig.user || '',
|
||||||
password: hostConfig.password || '',
|
password: hostConfig.password || '',
|
||||||
rsaKey: hostConfig.rsaKey || '',
|
privateKey: hostConfig.privateKey || '',
|
||||||
|
keyType: hostConfig.keyType || '',
|
||||||
|
passphrase: hostConfig.passphrase || '',
|
||||||
port: hostConfig.port || 22,
|
port: hostConfig.port || 22,
|
||||||
authMethod: hostConfig.password ? 'password' : hostConfig.rsaKey ? 'rsaKey' : 'Select Auth',
|
authMethod: hostConfig.password ? 'password' : hostConfig.privateKey ? 'key' : 'Select Auth',
|
||||||
rememberHost: true,
|
rememberHost: true,
|
||||||
storePassword: !!(hostConfig.password || hostConfig.rsaKey),
|
storePassword: !!(hostConfig.password || hostConfig.privateKey),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [isHidden, hostConfig]);
|
}, [isHidden, hostConfig]);
|
||||||
@@ -118,20 +121,28 @@ const EditHostModal = ({ isHidden, hostConfig, setIsEditHostHidden, handleEditHo
|
|||||||
...prev,
|
...prev,
|
||||||
storePassword: Boolean(checked),
|
storePassword: Boolean(checked),
|
||||||
password: checked ? prev.password : "",
|
password: checked ? prev.password : "",
|
||||||
rsaKey: checked ? prev.rsaKey : "",
|
privateKey: checked ? prev.privateKey : "",
|
||||||
|
passphrase: checked ? prev.passphrase : "",
|
||||||
authMethod: checked ? prev.authMethod : "Select Auth"
|
authMethod: checked ? prev.authMethod : "Select Auth"
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
const isFormValid = () => {
|
const isFormValid = () => {
|
||||||
const { ip, user, port, authMethod, password, rsaKey, storePassword } = form;
|
const { ip, user, port, authMethod, password, privateKey } = form;
|
||||||
|
|
||||||
|
// Basic validation for required fields
|
||||||
if (!ip?.trim() || !user?.trim() || !port) return false;
|
if (!ip?.trim() || !user?.trim() || !port) return false;
|
||||||
|
|
||||||
|
// Port validation
|
||||||
const portNum = Number(port);
|
const portNum = Number(port);
|
||||||
if (isNaN(portNum) || portNum < 1 || portNum > 65535) return false;
|
if (isNaN(portNum) || portNum < 1 || portNum > 65535) return false;
|
||||||
|
|
||||||
if (Boolean(storePassword) && authMethod === 'password' && !password?.trim()) return false;
|
// Auth method validation only if storing password
|
||||||
if (Boolean(storePassword) && authMethod === 'rsaKey' && !rsaKey && !hostConfig?.rsaKey) return false;
|
if (form.storePassword) {
|
||||||
if (Boolean(storePassword) && authMethod === 'Select Auth') return false;
|
if (authMethod === 'Select Auth') return false;
|
||||||
|
if (authMethod === 'password' && !password?.trim()) return false;
|
||||||
|
if (authMethod === 'key' && !privateKey?.trim()) return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
@@ -142,15 +153,25 @@ const EditHostModal = ({ isHidden, hostConfig, setIsEditHostHidden, handleEditHo
|
|||||||
|
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
try {
|
try {
|
||||||
await handleEditHost(hostConfig, {
|
const newConfig = {
|
||||||
name: form.name || form.ip,
|
name: form.name || form.ip,
|
||||||
folder: form.folder,
|
folder: form.folder,
|
||||||
ip: form.ip,
|
ip: form.ip,
|
||||||
user: form.user,
|
user: form.user,
|
||||||
password: form.authMethod === 'password' ? form.password : undefined,
|
|
||||||
rsaKey: form.authMethod === 'rsaKey' ? form.rsaKey : undefined,
|
|
||||||
port: String(form.port),
|
port: String(form.port),
|
||||||
});
|
};
|
||||||
|
|
||||||
|
if (form.storePassword) {
|
||||||
|
if (form.authMethod === 'password') {
|
||||||
|
newConfig.password = form.password;
|
||||||
|
} else if (form.authMethod === 'key') {
|
||||||
|
newConfig.privateKey = form.privateKey;
|
||||||
|
newConfig.keyType = form.keyType;
|
||||||
|
newConfig.passphrase = form.passphrase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await handleEditHost(hostConfig, newConfig);
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
@@ -162,11 +183,9 @@ const EditHostModal = ({ isHidden, hostConfig, setIsEditHostHidden, handleEditHo
|
|||||||
open={!isHidden}
|
open={!isHidden}
|
||||||
onClose={() => !isLoading && setIsEditHostHidden(true)}
|
onClose={() => !isLoading && setIsEditHostHidden(true)}
|
||||||
sx={{
|
sx={{
|
||||||
position: 'fixed',
|
|
||||||
inset: 0,
|
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
backdropFilter: 'blur(5px)',
|
backdropFilter: 'blur(5px)',
|
||||||
backgroundColor: 'rgba(0, 0, 0, 0.2)',
|
backgroundColor: 'rgba(0, 0, 0, 0.2)',
|
||||||
}}
|
}}
|
||||||
@@ -178,7 +197,7 @@ const EditHostModal = ({ isHidden, hostConfig, setIsEditHostHidden, handleEditHo
|
|||||||
backgroundColor: theme.palette.general.tertiary,
|
backgroundColor: theme.palette.general.tertiary,
|
||||||
borderColor: theme.palette.general.secondary,
|
borderColor: theme.palette.general.secondary,
|
||||||
color: theme.palette.text.primary,
|
color: theme.palette.text.primary,
|
||||||
padding: 3,
|
padding: 0,
|
||||||
borderRadius: 10,
|
borderRadius: 10,
|
||||||
maxWidth: '500px',
|
maxWidth: '500px',
|
||||||
width: '100%',
|
width: '100%',
|
||||||
@@ -188,47 +207,45 @@ const EditHostModal = ({ isHidden, hostConfig, setIsEditHostHidden, handleEditHo
|
|||||||
mx: 2,
|
mx: 2,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DialogTitle sx={{ mb: 2 }}>Edit Host</DialogTitle>
|
|
||||||
<DialogContent>
|
|
||||||
<form onSubmit={handleSubmit}>
|
|
||||||
<Tabs
|
<Tabs
|
||||||
value={activeTab}
|
value={activeTab}
|
||||||
onChange={(e, val) => setActiveTab(val)}
|
onChange={(e, val) => setActiveTab(val)}
|
||||||
sx={{
|
sx={{
|
||||||
backgroundColor: theme.palette.general.disabled,
|
|
||||||
borderRadius: '8px',
|
|
||||||
padding: '8px',
|
|
||||||
marginBottom: '16px',
|
|
||||||
width: '100%',
|
width: '100%',
|
||||||
|
mb: 0,
|
||||||
|
backgroundColor: theme.palette.general.tertiary,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<TabList
|
<TabList
|
||||||
sx={{
|
sx={{
|
||||||
width: '100%',
|
width: '100%',
|
||||||
gap: 0,
|
gap: 0,
|
||||||
mb: 2,
|
borderTopLeftRadius: 10,
|
||||||
|
borderTopRightRadius: 10,
|
||||||
|
backgroundColor: theme.palette.general.primary,
|
||||||
'& button': {
|
'& button': {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
bgcolor: 'transparent',
|
bgcolor: 'transparent',
|
||||||
color: theme.palette.text.secondary,
|
color: theme.palette.text.secondary,
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
bgcolor: 'rgba(255, 255, 255, 0.1)',
|
bgcolor: theme.palette.general.disabled,
|
||||||
},
|
},
|
||||||
'&.Mui-selected': {
|
'&.Mui-selected': {
|
||||||
bgcolor: theme.palette.general.primary,
|
bgcolor: theme.palette.general.tertiary,
|
||||||
color: theme.palette.text.primary,
|
color: theme.palette.text.primary,
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
bgcolor: theme.palette.general.primary,
|
bgcolor: theme.palette.general.tertiary,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Tab>Basic Info</Tab>
|
<Tab sx={{ flex: 1 }}>Basic Info</Tab>
|
||||||
<Tab>Connection</Tab>
|
<Tab sx={{ flex: 1 }}>Connection</Tab>
|
||||||
<Tab>Authentication</Tab>
|
<Tab sx={{ flex: 1 }}>Authentication</Tab>
|
||||||
</TabList>
|
</TabList>
|
||||||
|
|
||||||
|
<div style={{ padding: '24px', backgroundColor: theme.palette.general.tertiary }}>
|
||||||
<TabPanel value={0}>
|
<TabPanel value={0}>
|
||||||
<Stack spacing={2}>
|
<Stack spacing={2}>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
@@ -303,19 +320,20 @@ const EditHostModal = ({ isHidden, hostConfig, setIsEditHostHidden, handleEditHo
|
|||||||
<FormControl>
|
<FormControl>
|
||||||
<FormLabel>Store Password</FormLabel>
|
<FormLabel>Store Password</FormLabel>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={form.storePassword}
|
checked={Boolean(form.storePassword)}
|
||||||
onChange={(e) => handleStorePasswordChange(e.target.checked)}
|
onChange={(e) => handleStorePasswordChange(e.target.checked)}
|
||||||
sx={{
|
sx={{
|
||||||
color: theme.palette.text.primary,
|
color: theme.palette.text.primary,
|
||||||
'&.Mui-checked': {
|
'&.Mui-checked': {
|
||||||
color: theme.palette.text.primary
|
color: theme.palette.text.primary,
|
||||||
}
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
{form.storePassword && (
|
{form.storePassword && (
|
||||||
<FormControl error={form.authMethod === 'Select Auth'}>
|
<>
|
||||||
|
<FormControl error={form.storePassword && (!form.authMethod || form.authMethod === 'Select Auth')}>
|
||||||
<FormLabel>Authentication Method</FormLabel>
|
<FormLabel>Authentication Method</FormLabel>
|
||||||
<Select
|
<Select
|
||||||
value={form.authMethod}
|
value={form.authMethod}
|
||||||
@@ -330,16 +348,15 @@ const EditHostModal = ({ isHidden, hostConfig, setIsEditHostHidden, handleEditHo
|
|||||||
<Option value="key">SSH Key</Option>
|
<Option value="key">SSH Key</Option>
|
||||||
</Select>
|
</Select>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
|
||||||
|
|
||||||
{form.authMethod === 'password' && form.storePassword && (
|
{form.authMethod === 'password' && (
|
||||||
<FormControl error={!form.password}>
|
<FormControl error={form.storePassword && !form.password}>
|
||||||
<FormLabel>Password</FormLabel>
|
<FormLabel>Password</FormLabel>
|
||||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||||
<Input
|
<Input
|
||||||
type={showPassword ? 'text' : 'password'}
|
type={showPassword ? 'text' : 'password'}
|
||||||
value={form.password}
|
value={form.password}
|
||||||
onChange={(e) => setForm((prev) => ({ ...prev, password: e.target.value }))}
|
onChange={(e) => setForm(prev => ({ ...prev, password: e.target.value }))}
|
||||||
sx={{
|
sx={{
|
||||||
backgroundColor: theme.palette.general.primary,
|
backgroundColor: theme.palette.general.primary,
|
||||||
color: theme.palette.text.primary,
|
color: theme.palette.text.primary,
|
||||||
@@ -359,50 +376,9 @@ const EditHostModal = ({ isHidden, hostConfig, setIsEditHostHidden, handleEditHo
|
|||||||
</FormControl>
|
</FormControl>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{form.authMethod === 'rsaKey' && form.storePassword && (
|
{form.authMethod === 'key' && (
|
||||||
<FormControl error={!form.rsaKey && !hostConfig?.rsaKey}>
|
|
||||||
<FormLabel>Public Key</FormLabel>
|
|
||||||
<Button
|
|
||||||
component="label"
|
|
||||||
sx={{
|
|
||||||
backgroundColor: theme.palette.general.primary,
|
|
||||||
color: theme.palette.text.primary,
|
|
||||||
width: '100%',
|
|
||||||
display: 'flex',
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
|
||||||
height: '40px',
|
|
||||||
'&:hover': {
|
|
||||||
backgroundColor: theme.palette.general.disabled,
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{form.rsaKey ? 'Change Public Key File' : 'Upload Public Key File'}
|
|
||||||
<Input
|
|
||||||
type="file"
|
|
||||||
onChange={handleFileChange}
|
|
||||||
sx={{ display: 'none' }}
|
|
||||||
/>
|
|
||||||
</Button>
|
|
||||||
{hostConfig?.rsaKey && !form.rsaKey && (
|
|
||||||
<FormLabel
|
|
||||||
sx={{
|
|
||||||
color: theme.palette.text.secondary,
|
|
||||||
fontSize: '0.875rem',
|
|
||||||
mt: 1,
|
|
||||||
display: 'block',
|
|
||||||
textAlign: 'center'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Existing key detected. Upload to replace.
|
|
||||||
</FormLabel>
|
|
||||||
)}
|
|
||||||
</FormControl>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{form.authMethod === 'key' && form.storePassword && (
|
|
||||||
<Stack spacing={2}>
|
<Stack spacing={2}>
|
||||||
<FormControl error={!form.privateKey && !hostConfig?.privateKey}>
|
<FormControl error={form.storePassword && !form.privateKey}>
|
||||||
<FormLabel>SSH Key</FormLabel>
|
<FormLabel>SSH Key</FormLabel>
|
||||||
<Button
|
<Button
|
||||||
component="label"
|
component="label"
|
||||||
@@ -415,7 +391,7 @@ const EditHostModal = ({ isHidden, hostConfig, setIsEditHostHidden, handleEditHo
|
|||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
height: '40px',
|
height: '40px',
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
backgroundColor: 'rgba(0, 0, 0, 0.3)',
|
backgroundColor: theme.palette.general.disabled,
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -468,32 +444,33 @@ const EditHostModal = ({ isHidden, hostConfig, setIsEditHostHidden, handleEditHo
|
|||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
)}
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</Tabs>
|
</div>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
onClick={handleSubmit}
|
||||||
disabled={!isFormValid() || isLoading}
|
disabled={isLoading}
|
||||||
sx={{
|
sx={{
|
||||||
backgroundColor: theme.palette.general.primary,
|
backgroundColor: theme.palette.general.primary,
|
||||||
color: theme.palette.text.primary,
|
color: theme.palette.text.primary,
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
backgroundColor: theme.palette.general.disabled
|
backgroundColor: theme.palette.general.disabled,
|
||||||
},
|
},
|
||||||
'&:disabled': {
|
'&:disabled': {
|
||||||
backgroundColor: 'rgba(255, 255, 255, 0.1)',
|
backgroundColor: 'rgba(255, 255, 255, 0.1)',
|
||||||
color: 'rgba(255, 255, 255, 0.3)',
|
color: 'rgba(255, 255, 255, 0.3)',
|
||||||
},
|
},
|
||||||
marginTop: 3,
|
marginTop: 1,
|
||||||
width: '100%',
|
width: '100%',
|
||||||
height: '40px',
|
height: '40px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{isLoading ? "Saving..." : "Save Changes"}
|
{isLoading ? "Saving changes..." : "Save changes"}
|
||||||
</Button>
|
</Button>
|
||||||
</form>
|
</Tabs>
|
||||||
</DialogContent>
|
|
||||||
</ModalDialog>
|
</ModalDialog>
|
||||||
</Modal>
|
</Modal>
|
||||||
</CssVarsProvider>
|
</CssVarsProvider>
|
||||||
|
|||||||
Reference in New Issue
Block a user