new share modal ported to typescript (#221)

This commit is contained in:
Michael Quigley 2024-11-19 13:47:43 -05:00
parent 0f2418e921
commit 79449859de
No known key found for this signature in database
GPG Key ID: 9B60314A9DD20A62
7 changed files with 254 additions and 5 deletions

View File

@ -12,6 +12,7 @@
"@emotion/styled": "^11.13.0",
"@mui/icons-material": "^6.1.7",
"@mui/material": "^6.1.7",
"formik": "^2.4.6",
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
@ -1703,6 +1704,16 @@
"dev": true,
"license": "MIT"
},
"node_modules/@types/hoist-non-react-statics": {
"version": "3.3.5",
"resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz",
"integrity": "sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==",
"license": "MIT",
"dependencies": {
"@types/react": "*",
"hoist-non-react-statics": "^3.3.0"
}
},
"node_modules/@types/json-schema": {
"version": "7.0.15",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
@ -2301,6 +2312,15 @@
"dev": true,
"license": "MIT"
},
"node_modules/deepmerge": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz",
"integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/dom-helpers": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
@ -2696,6 +2716,31 @@
"dev": true,
"license": "ISC"
},
"node_modules/formik": {
"version": "2.4.6",
"resolved": "https://registry.npmjs.org/formik/-/formik-2.4.6.tgz",
"integrity": "sha512-A+2EI7U7aG296q2TLGvNapDNTZp1khVt5Vk0Q/fyfSROss0V/V6+txt2aJnwEos44IxTCW/LYAi/zgWzlevj+g==",
"funding": [
{
"type": "individual",
"url": "https://opencollective.com/formik"
}
],
"license": "Apache-2.0",
"dependencies": {
"@types/hoist-non-react-statics": "^3.3.1",
"deepmerge": "^2.1.1",
"hoist-non-react-statics": "^3.3.0",
"lodash": "^4.17.21",
"lodash-es": "^4.17.21",
"react-fast-compare": "^2.0.1",
"tiny-warning": "^1.0.2",
"tslib": "^2.0.0"
},
"peerDependencies": {
"react": ">=16.8.0"
}
},
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@ -3014,6 +3059,18 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"license": "MIT"
},
"node_modules/lodash-es": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
"license": "MIT"
},
"node_modules/lodash.merge": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
@ -3374,6 +3431,12 @@
"react": "^18.3.1"
}
},
"node_modules/react-fast-compare": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz",
"integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==",
"license": "MIT"
},
"node_modules/react-is": {
"version": "18.3.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
@ -3616,6 +3679,12 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/tiny-warning": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
"integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==",
"license": "MIT"
},
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@ -3642,6 +3711,12 @@
"typescript": ">=4.2.0"
}
},
"node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"license": "0BSD"
},
"node_modules/type-check": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",

View File

@ -14,6 +14,7 @@
"@emotion/styled": "^11.13.0",
"@mui/icons-material": "^6.1.7",
"@mui/material": "^6.1.7",
"formik": "^2.4.6",
"react": "^18.3.1",
"react-dom": "^18.3.1"
},

View File

@ -3,6 +3,7 @@ import {AppBar, Box, Button, Card, Chip, Grid2, Toolbar, Typography} from "@mui/
import LanIcon from "@mui/icons-material/Lan";
import {AccessDetail} from "./api";
import DeleteIcon from "@mui/icons-material/Delete";
import {GetAgentApi} from "./model/api.ts";
interface AccessCardProps {
accessObject: AgentObject;
@ -10,6 +11,14 @@ interface AccessCardProps {
function AccessCard({ accessObject }: AccessCardProps) {
let access = (accessObject.v as AccessDetail);
const releaseAccess = () => {
GetAgentApi().agentReleaseAccess({frontendToken: access.frontendToken})
.catch(e => {
console.log(e);
});
}
return (
<Card>
<AppBar position="sticky">
@ -34,7 +43,7 @@ function AccessCard({ accessObject }: AccessCardProps) {
</Box>
<Grid2 container sx={{ flexGrow: 1 }}>
<Grid2 display="flex" justifyContent="right" size="grow">
<Button variant="contained"><DeleteIcon /></Button>
<Button variant="contained" onClick={releaseAccess}><DeleteIcon /></Button>
</Grid2>
</Grid2>
</Card>

View File

@ -3,10 +3,19 @@ import {GetAgentApi} from "./model/api.ts";
import NavBar from "./NavBar.tsx";
import {AgentObject, buildOverview} from "./model/overview.ts";
import Overview from "./Overview.tsx";
import NewShareModal from "./NewShareModal.tsx";
const AgentUi = () => {
const [version, setVersion] = useState("unset");
const [overview, setOverview] = useState(new Array<AgentObject>());
const [newShareOpen, setNewShareOpen] = useState(false);
const openNewShare = () => {
setNewShareOpen(true);
}
const closeNewShare = () => {
setNewShareOpen(false);
}
useEffect(() => {
GetAgentApi().agentVersion()
@ -40,8 +49,9 @@ const AgentUi = () => {
return (
<>
<NavBar version={version} />
<NavBar version={version} shareClick={openNewShare} />
<Overview overview={overview} />
<NewShareModal isOpen={newShareOpen} close={closeNewShare} />
</>
);
}

View File

@ -5,9 +5,10 @@ import ShareIcon from "@mui/icons-material/Share";
interface NavBarProps {
version: string;
shareClick: () => void;
}
function NavBar({ version }: NavBarProps) {
function NavBar({ version, shareClick }: NavBarProps) {
return (
<Box ssx={{ flexGrow: 1 }}>
<AppBar position="static">
@ -20,7 +21,7 @@ function NavBar({ version }: NavBarProps) {
</Typography>
<Grid2 container sx={{ flexGrow: 1 }}>
<Grid2 display="flex" justifyContent="right" size="grow">
<Button color="inherit"><ShareIcon /></Button>
<Button color="inherit" onClick={shareClick}><ShareIcon /></Button>
</Grid2>
<Grid2 display="flex" justifyContent="right">
<Button color="inherit" ><LanIcon /></Button>

View File

@ -0,0 +1,145 @@
import {useFormik} from "formik";
import {GetAgentApi} from "./model/api.ts";
import {useState} from "react";
import {Box, Button, Checkbox, FormControlLabel, MenuItem, Modal, TextField} from "@mui/material";
import {modalStyle} from "./model/theme.ts";
interface NewShareModalProps {
close: () => void;
isOpen: boolean;
}
function NewShareModal({ close, isOpen }: NewShareModalProps) {
const [errorMessage, setErrorMessage] = useState(<></>);
const form = useFormik({
initialValues: {
shareMode: "public",
backendMode: "proxy",
target: "",
insecure: false,
},
onSubmit: v => {
setErrorMessage(<></>);
switch(v.shareMode) {
case "public":
GetAgentApi().agentSharePublic(v)
.then(r => {
close();
})
.catch(e => {
e.response().json().then(ex => {
setErrorMessage(<p>{ex.message}</p>);
console.log(ex.message);
})
});
break;
case "private":
GetAgentApi().agentSharePrivate(v)
.then(r => {
close();
})
.catch(e => {
e.response().json().then(ex => {
setErrorMessage(<p>{ex.message}</p>);
console.log(ex.message);
})
});
break;
}
},
});
return (
<Modal open={isOpen} onClose={close}>
<Box sx={{ ...modalStyle }}>
<h2>Share...</h2>
{errorMessage}
<form onSubmit={form.handleSubmit}>
<TextField
fullWidth
select
id="shareMode"
name="shareMode"
label="Share Mode"
value={form.values.shareMode}
onChange={form.handleChange}
onBlur={form.handleBlur}
sx={{ mt: 2 }}
>
<MenuItem value="public">public</MenuItem>
<MenuItem value="private">private</MenuItem>
</TextField>
{form.values.shareMode === "public" && (
<TextField
fullWidth select
id="backendMode"
name="backendMode"
label="Backend Mode"
value={form.values.backendMode}
onChange={form.handleChange}
onBlur={form.handleBlur}
sx={{ mt: 2 }}
>
<MenuItem value="proxy">proxy</MenuItem>
<MenuItem value="web">web</MenuItem>
<MenuItem value="caddy">caddy</MenuItem>
<MenuItem value="drive">drive</MenuItem>
</TextField>
)}
{form.values.shareMode === "private" && (
<TextField
fullWidth select
id="backendMode"
name="backendMode"
label="Backend Mode"
value={form.values.backendMode}
onChange={form.handleChange}
onBlur={form.handleBlur}
sx={{ mt: 2 }}
>
<MenuItem value="proxy">proxy</MenuItem>
<MenuItem value="web">web</MenuItem>
<MenuItem value="tcpTunnel">tcpTunnel</MenuItem>
<MenuItem value="udpTunnel">udpTunnel</MenuItem>
<MenuItem value="caddy">caddy</MenuItem>
<MenuItem value="drive">drive</MenuItem>
<MenuItem value="socks">socks</MenuItem>
<MenuItem value="vpn">vpn</MenuItem>
</TextField>
)}
<TextField
fullWidth
id="target"
name="target"
label="Target"
value={form.values.target}
onChange={form.handleChange}
onBlur={form.handleBlur}
sx={{ mt: 2 }}
/>
{form.values.backendMode === "proxy" && (
<Box>
<FormControlLabel
control={<Checkbox
id="insecure"
name="insecure"
label="Insecure"
checked={form.values.insecure}
onChange={form.handleChange}
onBlur={form.handleBlur}
/>}
label="Insecure"
sx={{ mt: 2 }}
/>
</Box>
)}
<Button color="primary" variant="contained" type="submit" sx={{ mt: 2 }}>Create Share</Button>
</form>
</Box>
</Modal>
);
}
export default NewShareModal;

View File

@ -4,6 +4,7 @@ import {ShareDetail} from "./api";
import {AppBar, Box, Button, Card, Chip, Grid2, Toolbar, Typography} from "@mui/material";
import ShareIcon from "@mui/icons-material/Share";
import DeleteIcon from "@mui/icons-material/Delete";
import {GetAgentApi} from "./model/api.ts";
interface ShareCardProps {
shareObject: AgentObject;
@ -16,6 +17,13 @@ function ShareCard({ shareObject }: ShareCardProps) {
frontends.push(<a key={share.token} href={fe} target="_">{fe}</a>);
});
const releaseShare = () => {
GetAgentApi().agentReleaseShare({token: share.token})
.catch(e => {
console.log(e);
});
}
return (
<Card>
<AppBar position="sticky">
@ -46,7 +54,7 @@ function ShareCard({ shareObject }: ShareCardProps) {
</Box>
<Grid2 container sx={{ flexGrow: 1 }}>
<Grid2 display="flex" justifyContent="right" size="grow">
<Button variant="contained"><DeleteIcon /></Button>
<Button variant="contained" onClick={releaseShare}><DeleteIcon /></Button>
</Grid2>
</Grid2>
</Card>