mirror of
https://github.com/rowyio/rowy.git
synced 2025-12-29 00:16:39 +01:00
add cms setup stubs
This commit is contained in:
@@ -1,25 +1,36 @@
|
||||
import { ISetupStepBodyProps } from "@src/pages/Setup";
|
||||
|
||||
import { FormControlLabel, Checkbox, Typography, Link } from "@mui/material";
|
||||
import {
|
||||
FormControlLabel,
|
||||
Checkbox,
|
||||
Typography,
|
||||
Link,
|
||||
Button,
|
||||
} from "@mui/material";
|
||||
|
||||
import { useAppContext } from "@src/contexts/AppContext";
|
||||
import { EXTERNAL_LINKS } from "@src/constants/externalLinks";
|
||||
|
||||
export interface IStep0WelcomeProps extends ISetupStepBodyProps {
|
||||
handleContinue: () => void;
|
||||
}
|
||||
|
||||
export default function Step0Welcome({
|
||||
completion,
|
||||
setCompletion,
|
||||
}: ISetupStepBodyProps) {
|
||||
handleContinue,
|
||||
}: IStep0WelcomeProps) {
|
||||
const { projectId } = useAppContext();
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<Typography variant="body1" gutterBottom>
|
||||
You’ll be up and running in just a few minutes.
|
||||
<Typography variant="body1" paragraph>
|
||||
Get started with Rowy in just a few minutes.
|
||||
</Typography>
|
||||
<Typography variant="body1" gutterBottom>
|
||||
Configure your project’s backend functionality, Firestore Rules, and
|
||||
user management.
|
||||
<Typography variant="body1" paragraph>
|
||||
We have no access to your data and it always stays on your Firebase
|
||||
project.
|
||||
</Typography>
|
||||
<Typography variant="body1">
|
||||
Project: <b>{projectId}</b>
|
||||
@@ -67,6 +78,23 @@ export default function Step0Welcome({
|
||||
m: 0,
|
||||
}}
|
||||
/>
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
size="large"
|
||||
disabled={!completion.welcome}
|
||||
onClick={handleContinue}
|
||||
>
|
||||
Get started
|
||||
</Button>
|
||||
|
||||
<div>
|
||||
<Typography variant="body2" paragraph sx={{ mt: 4 }}>
|
||||
Want to invite your teammate to help with setup instead?
|
||||
</Typography>
|
||||
<Button>Send to teammate</Button>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
54
src/components/Setup/Step1Oauth.tsx
Normal file
54
src/components/Setup/Step1Oauth.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
import { ISetupStepBodyProps } from "@src/pages/Setup";
|
||||
|
||||
import { Typography, Link, Button } from "@mui/material";
|
||||
import InlineOpenInNewIcon from "@src/components/InlineOpenInNewIcon";
|
||||
|
||||
import SetupItem from "./SetupItem";
|
||||
|
||||
import { WIKI_LINKS } from "@src/constants/externalLinks";
|
||||
|
||||
export default function Step1Oauth({
|
||||
completion,
|
||||
setCompletion,
|
||||
}: ISetupStepBodyProps) {
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<Typography variant="inherit" paragraph>
|
||||
Allow Rowy to manage your Firebase Authentication, Firestore database,
|
||||
and Firebase Storage.
|
||||
</Typography>
|
||||
<Typography variant="inherit">
|
||||
Your data and code always stays on your Firebase project.{" "}
|
||||
<Link
|
||||
href={WIKI_LINKS.setup}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Learn more
|
||||
<InlineOpenInNewIcon />
|
||||
</Link>
|
||||
</Typography>
|
||||
</div>
|
||||
|
||||
<SetupItem
|
||||
title="Sign in with a Google account that has access to your Firebase project."
|
||||
status="incomplete"
|
||||
>
|
||||
<Button
|
||||
startIcon={
|
||||
<img
|
||||
src="https://www.gstatic.com/firebasejs/ui/2.0.0/images/auth/google.svg"
|
||||
alt="Google logo"
|
||||
width="20"
|
||||
height="20"
|
||||
/>
|
||||
}
|
||||
onClick={() => setCompletion((c) => ({ ...c, oauth: true }))}
|
||||
>
|
||||
Sign in with Google
|
||||
</Button>
|
||||
</SetupItem>
|
||||
</>
|
||||
);
|
||||
}
|
||||
47
src/components/Setup/Step2Project.tsx
Normal file
47
src/components/Setup/Step2Project.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
import { ISetupStepBodyProps } from "@src/pages/Setup";
|
||||
|
||||
import {
|
||||
Typography,
|
||||
TextField,
|
||||
MenuItem,
|
||||
Divider,
|
||||
Button,
|
||||
} from "@mui/material";
|
||||
import InlineOpenInNewIcon from "@src/components/InlineOpenInNewIcon";
|
||||
|
||||
import SetupItem from "./SetupItem";
|
||||
|
||||
import { WIKI_LINKS } from "@src/constants/externalLinks";
|
||||
|
||||
export default function Step1Oauth({
|
||||
completion,
|
||||
setCompletion,
|
||||
}: ISetupStepBodyProps) {
|
||||
return (
|
||||
<>
|
||||
<Typography variant="inherit">
|
||||
Select which Firebase project to set up Rowy on.
|
||||
</Typography>
|
||||
|
||||
<SetupItem
|
||||
title="Select an existing project or create a new one."
|
||||
status="incomplete"
|
||||
>
|
||||
<TextField label="Project" select style={{ minWidth: 200 }}>
|
||||
<MenuItem value="lorem">lorem</MenuItem>
|
||||
<MenuItem value="ipsum">ipsum</MenuItem>
|
||||
<MenuItem value="dolor">dolor</MenuItem>
|
||||
<MenuItem value="sit">sit</MenuItem>
|
||||
<MenuItem value="amet">amet</MenuItem>
|
||||
</TextField>
|
||||
|
||||
<Divider style={{ width: 200 }}>OR</Divider>
|
||||
|
||||
<Button onClick={() => setCompletion((c) => ({ ...c, project: true }))}>
|
||||
Create project in Firebase Console
|
||||
<InlineOpenInNewIcon />
|
||||
</Button>
|
||||
</SetupItem>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -29,7 +29,8 @@ import ScrollableDialogContent from "@src/components/Modal/ScrollableDialogConte
|
||||
import { SlideTransition } from "@src/components/Modal/SlideTransition";
|
||||
|
||||
import Step0Welcome from "@src/components/Setup/Step0Welcome";
|
||||
import Step1RowyRun, { checkRowyRun } from "@src/components/Setup/Step1RowyRun";
|
||||
import Step1Oauth from "@src/components/Setup/Step1Oauth";
|
||||
import Step2Project from "@src/components/Setup/Step2Project";
|
||||
// prettier-ignore
|
||||
import Step2ProjectOwner, { checkProjectOwner } from "@src/components/Setup/Step2ProjectOwner";
|
||||
import Step3Rules, { checkRules } from "@src/components/Setup/Step3Rules";
|
||||
@@ -47,7 +48,6 @@ export interface ISetupStep {
|
||||
title: React.ReactNode;
|
||||
description?: React.ReactNode;
|
||||
body: React.ReactNode;
|
||||
actions?: React.ReactNode;
|
||||
}
|
||||
|
||||
export interface ISetupStepBodyProps {
|
||||
@@ -69,25 +69,25 @@ const checkAllSteps = async (
|
||||
console.log("Check all steps");
|
||||
const completion: Record<string, boolean> = {};
|
||||
|
||||
const rowyRunValidation = await checkRowyRun(rowyRunUrl, signal);
|
||||
if (rowyRunValidation.isValidRowyRunUrl) {
|
||||
if (rowyRunValidation.isLatestVersion) completion.rowyRun = true;
|
||||
// const rowyRunValidation = await checkRowyRun(rowyRunUrl, signal);
|
||||
// if (rowyRunValidation.isValidRowyRunUrl) {
|
||||
// if (rowyRunValidation.isLatestVersion) completion.rowyRun = true;
|
||||
|
||||
const promises = [
|
||||
checkProjectOwner(rowyRunUrl, currentUser, userRoles, signal).then(
|
||||
(projectOwner) => {
|
||||
if (projectOwner) completion.projectOwner = true;
|
||||
}
|
||||
),
|
||||
checkRules(rowyRunUrl, authToken, signal).then((rules) => {
|
||||
if (rules) completion.rules = true;
|
||||
}),
|
||||
checkMigrate(rowyRunUrl, authToken, signal).then((requiresMigration) => {
|
||||
if (requiresMigration) completion.migrate = false;
|
||||
}),
|
||||
];
|
||||
await Promise.all(promises);
|
||||
}
|
||||
// const promises = [
|
||||
// checkProjectOwner(rowyRunUrl, currentUser, userRoles, signal).then(
|
||||
// (projectOwner) => {
|
||||
// if (projectOwner) completion.projectOwner = true;
|
||||
// }
|
||||
// ),
|
||||
// checkRules(rowyRunUrl, authToken, signal).then((rules) => {
|
||||
// if (rules) completion.rules = true;
|
||||
// }),
|
||||
// checkMigrate(rowyRunUrl, authToken, signal).then((requiresMigration) => {
|
||||
// if (requiresMigration) completion.migrate = false;
|
||||
// }),
|
||||
// ];
|
||||
// await Promise.all(promises);
|
||||
// }
|
||||
|
||||
return completion;
|
||||
};
|
||||
@@ -111,118 +111,34 @@ export default function SetupPage() {
|
||||
rules: false,
|
||||
});
|
||||
|
||||
const [checkingAllSteps, setCheckingAllSteps] = useState(false);
|
||||
useEffect(() => {
|
||||
const controller = new AbortController();
|
||||
const signal = controller.signal;
|
||||
// const [checkingAllSteps, setCheckingAllSteps] = useState(false);
|
||||
// useEffect(() => {
|
||||
// const controller = new AbortController();
|
||||
// const signal = controller.signal;
|
||||
|
||||
if (rowyRunUrl) {
|
||||
setCheckingAllSteps(true);
|
||||
getAuthToken().then((authToken) =>
|
||||
checkAllSteps(
|
||||
rowyRunUrl,
|
||||
currentUser,
|
||||
userRoles,
|
||||
authToken,
|
||||
signal
|
||||
).then((result) => {
|
||||
if (!signal.aborted) {
|
||||
setCompletion((c) => ({ ...c, ...result }));
|
||||
setCheckingAllSteps(false);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
// if (rowyRunUrl) {
|
||||
// setCheckingAllSteps(true);
|
||||
// getAuthToken().then((authToken) =>
|
||||
// checkAllSteps(
|
||||
// rowyRunUrl,
|
||||
// currentUser,
|
||||
// userRoles,
|
||||
// authToken,
|
||||
// signal
|
||||
// ).then((result) => {
|
||||
// if (!signal.aborted) {
|
||||
// setCompletion((c) => ({ ...c, ...result }));
|
||||
// setCheckingAllSteps(false);
|
||||
// }
|
||||
// })
|
||||
// );
|
||||
// }
|
||||
|
||||
return () => controller.abort();
|
||||
}, [rowyRunUrl, currentUser, userRoles, getAuthToken]);
|
||||
// return () => controller.abort();
|
||||
// }, [rowyRunUrl, currentUser, userRoles, getAuthToken]);
|
||||
|
||||
const stepProps = { completion, setCompletion, checkAllSteps, rowyRunUrl };
|
||||
|
||||
const steps: ISetupStep[] = [
|
||||
{
|
||||
id: "welcome",
|
||||
layout: "centered" as "centered",
|
||||
shortTitle: "Welcome",
|
||||
title: `Welcome to Rowy`,
|
||||
body: <Step0Welcome {...stepProps} />,
|
||||
actions: completion.welcome ? (
|
||||
<LoadingButton
|
||||
loading={checkingAllSteps}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
type="submit"
|
||||
>
|
||||
Get started
|
||||
</LoadingButton>
|
||||
) : (
|
||||
<Tooltip title="Please accept the terms and conditions">
|
||||
<div>
|
||||
<LoadingButton
|
||||
loading={checkingAllSteps}
|
||||
variant="contained"
|
||||
color="primary"
|
||||
disabled
|
||||
>
|
||||
Get started
|
||||
</LoadingButton>
|
||||
</div>
|
||||
</Tooltip>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: "rowyRun",
|
||||
shortTitle: `Rowy Run`,
|
||||
title: `Set up Rowy Run`,
|
||||
body: <Step1RowyRun {...stepProps} />,
|
||||
},
|
||||
{
|
||||
id: "projectOwner",
|
||||
shortTitle: `Project owner`,
|
||||
title: `Set up project owner`,
|
||||
body: <Step2ProjectOwner {...stepProps} />,
|
||||
},
|
||||
{
|
||||
id: "rules",
|
||||
shortTitle: `Rules`,
|
||||
title: `Set up Firestore Rules`,
|
||||
body: <Step3Rules {...stepProps} />,
|
||||
},
|
||||
completion.migrate !== undefined
|
||||
? {
|
||||
id: "migrate",
|
||||
shortTitle: `Migrate`,
|
||||
title: `Migrate to Rowy (optional)`,
|
||||
body: <Step4Migrate {...stepProps} />,
|
||||
}
|
||||
: ({} as ISetupStep),
|
||||
{
|
||||
id: "finish",
|
||||
layout: "centered" as "centered",
|
||||
shortTitle: `Finish`,
|
||||
title: `You’re all set up!`,
|
||||
body: <Step5Finish />,
|
||||
actions: (
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
component={Link}
|
||||
to={routes.home}
|
||||
sx={{ ml: 1 }}
|
||||
>
|
||||
Continue to Rowy
|
||||
</Button>
|
||||
),
|
||||
},
|
||||
].filter((x) => x.id);
|
||||
|
||||
const step =
|
||||
steps.find((step) => step.id === (stepId || steps[0].id)) ?? steps[0];
|
||||
const stepIndex = steps.findIndex(
|
||||
(step) => step.id === (stepId || steps[0].id)
|
||||
);
|
||||
const listedSteps = steps.filter((step) => step.layout !== "centered");
|
||||
|
||||
const handleContinue = () => {
|
||||
let nextIncompleteStepIndex = stepIndex + 1;
|
||||
while (completion[steps[nextIncompleteStepIndex]?.id]) {
|
||||
@@ -235,6 +151,78 @@ export default function SetupPage() {
|
||||
setStepId(nextStepId);
|
||||
};
|
||||
|
||||
const steps: ISetupStep[] = [
|
||||
{
|
||||
id: "welcome",
|
||||
layout: "centered" as "centered",
|
||||
shortTitle: "Welcome",
|
||||
title: `Welcome`,
|
||||
body: <Step0Welcome handleContinue={handleContinue} {...stepProps} />,
|
||||
actions: <></>,
|
||||
},
|
||||
// {
|
||||
// id: "rowyRun",
|
||||
// shortTitle: `Rowy Run`,
|
||||
// title: `Set up Rowy Run`,
|
||||
// body: <Step1RowyRun {...stepProps} />,
|
||||
// },
|
||||
// {
|
||||
// id: "projectOwner",
|
||||
// shortTitle: `Project owner`,
|
||||
// title: `Set up project owner`,
|
||||
// body: <Step2ProjectOwner {...stepProps} />,
|
||||
// },
|
||||
{
|
||||
id: "oauth",
|
||||
shortTitle: `Access`,
|
||||
title: `Allow Firebase access`,
|
||||
body: <Step1Oauth {...stepProps} />,
|
||||
},
|
||||
{
|
||||
id: "project",
|
||||
shortTitle: `Project`,
|
||||
title: `Select project`,
|
||||
body: <Step2Project {...stepProps} />,
|
||||
},
|
||||
{
|
||||
id: "rules",
|
||||
shortTitle: `Firestore Rules`,
|
||||
title: `Set up Firestore Rules`,
|
||||
body: <Step3Rules {...stepProps} />,
|
||||
},
|
||||
{
|
||||
id: "storageRules",
|
||||
shortTitle: `Storage Rules`,
|
||||
title: `Set up Firestore Rules`,
|
||||
body: <Step3Rules {...stepProps} />,
|
||||
},
|
||||
{
|
||||
id: "finish",
|
||||
layout: "centered" as "centered",
|
||||
shortTitle: `Finish`,
|
||||
title: `You’re all set up!`,
|
||||
body: <Step5Finish />,
|
||||
// actions: (
|
||||
// <Button
|
||||
// variant="contained"
|
||||
// color="primary"
|
||||
// component={Link}
|
||||
// to={routes.home}
|
||||
// sx={{ ml: 1 }}
|
||||
// >
|
||||
// Continue to Rowy
|
||||
// </Button>
|
||||
// ),
|
||||
},
|
||||
].filter((x) => x.id);
|
||||
|
||||
const step =
|
||||
steps.find((step) => step.id === (stepId || steps[0].id)) ?? steps[0];
|
||||
const stepIndex = steps.findIndex(
|
||||
(step) => step.id === (stepId || steps[0].id)
|
||||
);
|
||||
const listedSteps = steps.filter((step) => step.layout !== "centered");
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
<BrandedBackground />
|
||||
@@ -243,18 +231,17 @@ export default function SetupPage() {
|
||||
elevation={4}
|
||||
sx={{
|
||||
backgroundColor: (theme) =>
|
||||
alpha(theme.palette.background.paper, 0.5),
|
||||
alpha(theme.palette.background.paper, 0.75),
|
||||
backdropFilter: "blur(20px) saturate(150%)",
|
||||
|
||||
maxWidth: BASE_WIDTH,
|
||||
width: (theme) => `calc(100vw - ${theme.spacing(2)})`,
|
||||
maxHeight: (theme) =>
|
||||
height: (theme) =>
|
||||
`calc(${
|
||||
fullScreenHeight > 0 ? `${fullScreenHeight}px` : "100vh"
|
||||
} - ${theme.spacing(
|
||||
2
|
||||
)} - env(safe-area-inset-top) - env(safe-area-inset-bottom))`,
|
||||
height: BASE_WIDTH * 0.75,
|
||||
resize: "both",
|
||||
|
||||
p: 0,
|
||||
@@ -393,31 +380,32 @@ export default function SetupPage() {
|
||||
</>
|
||||
)}
|
||||
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
try {
|
||||
handleContinue();
|
||||
} catch (e: any) {
|
||||
throw new Error(e.message);
|
||||
}
|
||||
return false;
|
||||
}}
|
||||
>
|
||||
<DialogActions>
|
||||
{step.actions ?? (
|
||||
<LoadingButton
|
||||
{step.layout !== "centered" && (
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
try {
|
||||
handleContinue();
|
||||
} catch (e: any) {
|
||||
throw new Error(e.message);
|
||||
}
|
||||
return false;
|
||||
}}
|
||||
>
|
||||
<DialogActions>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
size="large"
|
||||
type="submit"
|
||||
loading={checkingAllSteps}
|
||||
// loading={checkingAllSteps}
|
||||
disabled={!completion[stepId]}
|
||||
>
|
||||
Continue
|
||||
</LoadingButton>
|
||||
)}
|
||||
</DialogActions>
|
||||
</form>
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</form>
|
||||
)}
|
||||
</Paper>
|
||||
</Wrapper>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user