the registration process is a bit of an embedded 'micro app' (#841)

This commit is contained in:
Michael Quigley 2025-01-30 13:54:15 -05:00
parent c5381b5ae3
commit f3adf0cae9
No known key found for this signature in database
GPG Key ID: 9B60314A9DD20A62

View File

@ -1,12 +1,19 @@
import {Box, Button, Checkbox, Container, FormControlLabel, TextField, Typography} from "@mui/material";
import {Box, Button, Checkbox, Container, FormControlLabel, Grid2, TextField, Typography} from "@mui/material";
import zroket from "./assets/zrok-1.0.0-rocket-purple.svg";
import {useParams} from "react-router";
import {useFormik} from "formik";
import * as Yup from 'yup';
import {useRef, useState} from "react";
import {useEffect, useRef, useState} from "react";
import {AccountApi, MetadataApi} from "./api";
import ClipboardText from "./ClipboardText.tsx";
const Register = () => {
const { regToken } = useParams();
interface SetPasswordFormProps {
email: string;
touLink: string;
register: (v) => void;
}
const SetPasswordForm = ({ email, touLink, register }: SetPasswordFormProps) => {
const [checked, setChecked] = useState<boolean>(false);
const checkedRef = useRef<boolean>();
checkedRef.current = checked;
@ -19,6 +26,7 @@ const Register = () => {
},
onSubmit: v => {
console.log(v);
register(v);
},
validationSchema: Yup.object({
password: Yup.string()
@ -43,45 +51,193 @@ const Register = () => {
})
});
return (
<Box component="form" onSubmit={form.handleSubmit}>
<Typography component="div" align="center"><h2>Welcome to zrok!</h2></Typography>
<Typography component="p" align="center" sx={{ mb: 2 }}><code>{email}</code></Typography>
<TextField
fullWidth
type="password"
id="password"
name="password"
label="Create a Password"
value={form.values.password}
onChange={form.handleChange}
onBlur={form.handleBlur}
error={form.errors.password !== undefined}
helperText={form.errors.password}
sx={{ mt: 2 }}
/>
<TextField
fullWidth
type="password"
id="confirm"
name="confirm"
label="Confirm Your Password"
value={form.values.confirm}
onChange={form.handleChange}
onBlur={form.handleBlur}
error={form.errors.confirm !== undefined}
helperText={form.errors.confirm}
sx={{ mt: 2 }}
/>
<FormControlLabel control={<Checkbox checked={checked} onChange={toggleChecked} />} label={<p>I accept the <span dangerouslySetInnerHTML={{__html: touLink as string}}></span></p>} sx={{ mt: 2 }} />
<Button type="submit" fullWidth variant="contained" sx={{ mt: 3, mb: 2 }} style={{ color: "#9bf316" }} disabled={!checked}>
Register Account
</Button>
</Box>
);
}
interface RegistrationCompleteProps {
token: string;
}
const RegistrationComplete = ({ token }: RegistrationCompleteProps) => {
return (
<Box component="div">
<Container>
<Box sx={{ display: "flex", alignItems: "center" }}>
<Typography component="div">
<h2>Registration completed!</h2>
</Typography>
</Box>
</Container>
<Container>
<Box>
<Typography component="p">
Your account was created successfully!
</Typography>
</Box>
<Box sx={{ mt: 3 }}>
<Typography component="p">
Your new account token is: <code>{token}</code> <ClipboardText text={token} />
</Typography>
</Box>
<Box sx={{ mt: 3 }}>
<Typography component="p">
You can create an environment using your account token, like this:
</Typography>
</Box>
<Box sx={{ mt: 3 }}>
<Typography component="p">
<code>$ zrok enable {token}</code> <ClipboardText text={"zrok enable " + token} />
</Typography>
</Box>
<Box sx={{ mt: 3 }}>
<Typography component="p">
<strong>Your account token is a secret (like a password).
Please do not share it with anyone!</strong>
</Typography>
</Box>
<Box sx={{ mt: 3 }}>
<Typography component="p">
You can use your new password to log into the console here:
</Typography>
</Box>
<Box sx={{ mt: 3 }}>
<Typography component="p">
<a href={window.location.origin}>{window.location.origin}</a>
</Typography>
</Box>
<Box sx={{ mt: 3 }}>
<Typography component="p">
<h3>Enjoy zrok!</h3>
</Typography>
</Box>
</Container>
</Box>
);
}
const InvalidToken = () => {
return (
<Box component="div">
<Container>
<Box sx={{ display: "flex", alignItems: "center" }}>
<Typography component="div"><h2 style={{ color: "red" }} align="center">Invalid registration token?!</h2></Typography>
</Box>
</Container>
<Container>
<Box>
<Typography component="p">
Your registration token may have expired!
</Typography>
</Box>
<Box sx={{ mt: 3 }}>
<Typography component="p">
Please use the <code>zrok invite</code> command to
generate a new registration request and try again.
</Typography>
</Box>
</Container>
</Box>
);
}
const Register = () => {
const { regToken } = useParams();
const [component, setComponent] = useState<React.JSX.Element>(null);
const [error, setError] = useState<boolean>(false);
const [email, setEmail] = useState<string>();
const [touLink, setTouLink] = useState<string>();
const doRegistration = (v) => {
new AccountApi().register({body: {token: regToken, password: v.password}})
.then(d => {
console.log(d);
setComponent(<RegistrationComplete token={d.token!} />);
})
.catch(e => {
console.log("doRegistration", e);
});
}
useEffect(() => {
if(regToken) {
new AccountApi().verify({body: {token: regToken}})
.then((d) => {
console.log(d);
setEmail(d.email);
})
.catch(e => {
setError(true);
console.log("error", e);
});
}
}, [regToken]);
useEffect(() => {
if(email) {
new MetadataApi()._configuration()
.then(d => {
setTouLink(d.touLink);
})
.catch(e => {
e.response.json().then(ex => {
console.log("register", ex.message);
})
});
}
}, [email]);
useEffect(() => {
if(!error && email && touLink) {
setComponent(<SetPasswordForm email={email!} touLink={touLink!} register={doRegistration} />);
} else {
if(error) {
setComponent(<InvalidToken />);
}
}
}, [touLink, error]);
return (
<Typography component="div">
<Container maxWidth="xs">
<Container maxWidth="sm">
<Box sx={{marginTop: 8, display: "flex", flexDirection: "column", alignItems: "center"}}>
<img src={zroket} height="300"/>
<h1 style={{ color: "#241775" }}>z r o k</h1>
<Box component="form" onSubmit={form.handleSubmit}>
<Typography component="div" align="center"><h2>Welcome to zrok!</h2></Typography>
<TextField
fullWidth
type="password"
id="password"
name="password"
label="Create a Password"
value={form.values.password}
onChange={form.handleChange}
onBlur={form.handleBlur}
error={form.errors.password !== undefined}
helperText={form.errors.password}
sx={{ mt: 2 }}
/>
<TextField
fullWidth
type="password"
id="confirm"
name="confirm"
label="Confirm Your Password"
value={form.values.confirm}
onChange={form.handleChange}
onBlur={form.handleBlur}
error={form.errors.confirm !== undefined}
helperText={form.errors.confirm}
sx={{ mt: 2 }}
/>
<FormControlLabel control={<Checkbox checked={checked} onChange={toggleChecked} />} label="I accept the Terms of Service" sx={{ mt: 2 }} />
<Button type="submit" fullWidth variant="contained" sx={{ mt: 3, mb: 2 }} style={{ color: "#9bf316" }} disabled={!checked}>
Register Account
</Button>
</Box>
{component}
</Box>
</Container>
</Typography>