Skip to content

Commit 8c87534

Browse files
committed
Introduce brand new project wizard
2 parents 99625bf + 58bf4ac commit 8c87534

27 files changed

+933
-1573
lines changed

portal/src/FormContainer.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ const FormContainer: React.FC<FormContainerProps> = function FormContainer(
151151

152152
return (
153153
<FormProvider
154+
loading={isUpdating}
154155
error={updateError ?? localError}
155156
rules={errorRules}
156157
fallbackErrorMessageID={fallbackErrorMessageID}

portal/src/FormDropdown.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,13 @@ export interface FormDropdownProps extends IDropdownProps {
1212
const FormDropdown: React.FC<FormDropdownProps> = function FormDropdown(
1313
props: FormDropdownProps
1414
) {
15-
const { parentJSONPointer, fieldName, errorRules, ...rest } = props;
15+
const {
16+
parentJSONPointer,
17+
fieldName,
18+
errorRules,
19+
disabled: ownDisabled,
20+
...rest
21+
} = props;
1622
const field = useMemo(
1723
() => ({
1824
parentJSONPointer,
@@ -21,8 +27,10 @@ const FormDropdown: React.FC<FormDropdownProps> = function FormDropdown(
2127
}),
2228
[parentJSONPointer, fieldName, errorRules]
2329
);
24-
const extraProps = useErrorMessageString(field);
25-
return <Dropdown {...rest} {...extraProps} />;
30+
const { disabled: ctxDisabled, ...extraProps } = useErrorMessageString(field);
31+
return (
32+
<Dropdown {...rest} {...extraProps} disabled={ownDisabled ?? ctxDisabled} />
33+
);
2634
};
2735

2836
export default FormDropdown;

portal/src/FormPhoneTextField.tsx

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,13 @@ export interface FormPhoneTextFieldProps extends PhoneTextFieldProps {
1111

1212
const FormPhoneTextField: React.FC<FormPhoneTextFieldProps> =
1313
function FormPhoneTextField(props: FormPhoneTextFieldProps) {
14-
const { parentJSONPointer, fieldName, errorRules, ...rest } = props;
14+
const {
15+
parentJSONPointer,
16+
fieldName,
17+
errorRules,
18+
disabled: ownDisabled,
19+
...rest
20+
} = props;
1521
const field = useMemo(
1622
() => ({
1723
parentJSONPointer,
@@ -20,8 +26,14 @@ const FormPhoneTextField: React.FC<FormPhoneTextFieldProps> =
2026
}),
2127
[parentJSONPointer, fieldName, errorRules]
2228
);
23-
const textFieldProps = useErrorMessage(field);
24-
return <PhoneTextField {...rest} {...textFieldProps} />;
29+
const { disabled: ctxDisabled, ...textFieldProps } = useErrorMessage(field);
30+
return (
31+
<PhoneTextField
32+
{...rest}
33+
{...textFieldProps}
34+
disabled={ownDisabled ?? ctxDisabled}
35+
/>
36+
);
2537
};
2638

2739
export default FormPhoneTextField;

portal/src/FormTextField.tsx

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,13 @@ export interface FormTextFieldProps extends ITextFieldProps {
1212
const FormTextField: React.FC<FormTextFieldProps> = function FormTextField(
1313
props: FormTextFieldProps
1414
) {
15-
const { parentJSONPointer, fieldName, errorRules, ...rest } = props;
15+
const {
16+
parentJSONPointer,
17+
fieldName,
18+
errorRules,
19+
disabled: ownDisabled,
20+
...rest
21+
} = props;
1622
const field = useMemo(
1723
() => ({
1824
parentJSONPointer,
@@ -21,8 +27,14 @@ const FormTextField: React.FC<FormTextFieldProps> = function FormTextField(
2127
}),
2228
[parentJSONPointer, fieldName, errorRules]
2329
);
24-
const textFieldProps = useErrorMessage(field);
25-
return <TextField {...rest} {...textFieldProps} />;
30+
const { disabled: ctxDisabled, ...textFieldProps } = useErrorMessage(field);
31+
return (
32+
<TextField
33+
{...rest}
34+
{...textFieldProps}
35+
disabled={ownDisabled ?? ctxDisabled}
36+
/>
37+
);
2638
};
2739

2840
export default FormTextField;

portal/src/ReactApp.tsx

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ import { ApolloProvider } from "@apollo/client";
99
import authgear from "@authgear/web";
1010
import { Helmet, HelmetProvider } from "react-helmet-async";
1111
import AppsScreen from "./graphql/portal/AppsScreen";
12-
import CreateAppScreen from "./graphql/portal/CreateAppScreen";
12+
import CreateProjectScreen from "./graphql/portal/CreateProjectScreen";
13+
import ProjectWizardScreen from "./graphql/portal/ProjectWizardScreen";
1314
import AppRoot from "./AppRoot";
1415
import MESSAGES from "./locale-data/en.json";
1516
import { client } from "./graphql/portal/apollo";
@@ -27,8 +28,7 @@ import {
2728
mergeSystemConfig,
2829
} from "./system-config";
2930
import { loadTheme, Link as FluentLink, ILinkProps } from "@fluentui/react";
30-
import OnboardingConfigAppScreen from "./graphql/portal/OnboardingConfigAppScreen";
31-
import OnboardingCompletionScreen from "./graphql/portal/OnboardingCompletionScreen";
31+
import ProjectWizardDoneScreen from "./graphql/portal/ProjectWizardDoneScreen";
3232
import OnboardingRedirect from "./OnboardingRedirect";
3333
import { ReactRouterLink, ReactRouterLinkProps } from "./ReactRouterLink";
3434
import { AppRoute } from "./AppRoute";
@@ -67,7 +67,7 @@ const ReactAppRoutes: React.FC = function ReactAppRoutes() {
6767
<AppRoute
6868
requireAuth={true}
6969
path="/projects/create"
70-
element={<CreateAppScreen />}
70+
element={<CreateProjectScreen />}
7171
/>
7272
<AppRoute
7373
requireAuth={true}
@@ -76,13 +76,13 @@ const ReactAppRoutes: React.FC = function ReactAppRoutes() {
7676
/>
7777
<AppRoute
7878
requireAuth={true}
79-
path="/project/:appID/onboarding"
80-
element={<OnboardingConfigAppScreen />}
79+
path="/project/:appID/wizard/*"
80+
element={<ProjectWizardScreen />}
8181
/>
8282
<AppRoute
8383
requireAuth={true}
84-
path="/project/:appID/done"
85-
element={<OnboardingCompletionScreen />}
84+
path="/project/:appID/wizard/done"
85+
element={<ProjectWizardDoneScreen />}
8686
/>
8787
<AppRoute path="/oauth-redirect" element={<OAuthRedirect />} />
8888
<AppRoute

portal/src/Tooltip.tsx

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,25 @@ export function useTooltipTargetElement(): UseTooltipTargetElementResult {
4242
};
4343
}
4444

45+
export interface TooltipIconProps {
46+
id?: string;
47+
className?: string;
48+
setRef?: React.RefCallback<unknown>;
49+
}
50+
51+
export function TooltipIcon(props: TooltipIconProps): React.ReactElement {
52+
const { id, setRef, className } = props;
53+
return (
54+
<Icon
55+
id={id}
56+
className={cn(className, styles.infoIcon)}
57+
/* @ts-expect-error */
58+
ref={setRef}
59+
iconName="info"
60+
/>
61+
);
62+
}
63+
4564
const Tooltip: React.FC<TooltipProps> = function Tooltip(props: TooltipProps) {
4665
const { className, tooltipMessageId, children } = props;
4766
const tooltipProps: ITooltipProps = React.useMemo(() => {
@@ -63,11 +82,7 @@ const Tooltip: React.FC<TooltipProps> = function Tooltip(props: TooltipProps) {
6382
tooltipProps={tooltipProps}
6483
directionalHint={DirectionalHint.bottomCenter}
6584
>
66-
{children ? (
67-
children
68-
) : (
69-
<Icon className={styles.infoIcon} iconName={"info"} />
70-
)}
85+
{children ? children : <TooltipIcon />}
7186
</TooltipHost>
7287
</div>
7388
);
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
.root {
2+
display: grid;
3+
padding: 24px;
4+
column-gap: 16px;
5+
row-gap: 20px;
6+
grid-template-columns: repeat(6, 80px);
7+
grid-auto-rows: auto;
8+
align-self: center;
9+
}
10+
11+
.content {
12+
grid-column: 1 / span 6;
13+
margin-top: 24px;
14+
padding: 16px;
15+
display: grid;
16+
row-gap: 16px;
17+
grid-template-columns: auto;
18+
grid-auto-rows: auto;
19+
background-color: white;
20+
}
21+
22+
.title {
23+
font-weight: 600;
24+
}
25+
26+
.buttons {
27+
display: flex;
28+
flex-direction: row-reverse;
29+
align-items: center;
30+
}
31+
32+
.backButton {
33+
margin: 0 10px;
34+
}
35+
36+
.skip {
37+
grid-column: 1 / span 6;
38+
font-size: smaller;
39+
}

portal/src/WizardContentLayout.tsx

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import React, { useCallback } from "react";
2+
import { useNavigate } from "react-router-dom";
3+
import {
4+
DefaultEffects,
5+
Text,
6+
DefaultButton,
7+
Link as FluentLink,
8+
} from "@fluentui/react";
9+
import { FormattedMessage } from "@oursky/react-messageformat";
10+
import ReactRouterLink from "./ReactRouterLink";
11+
import styles from "./WizardContentLayout.module.scss";
12+
13+
export interface WizardContentLayoutProps {
14+
title?: React.ReactNode;
15+
primaryButton?: React.ReactNode;
16+
backButtonDisabled?: boolean;
17+
children?: React.ReactNode;
18+
appID?: string;
19+
}
20+
21+
export default function WizardContentLayout(
22+
props: WizardContentLayoutProps
23+
): React.ReactElement {
24+
const navigate = useNavigate();
25+
const { title, children, primaryButton, backButtonDisabled, appID } = props;
26+
const onClickBackButton = useCallback(
27+
(e) => {
28+
e.preventDefault();
29+
e.stopPropagation();
30+
navigate(-1);
31+
},
32+
[navigate]
33+
);
34+
return (
35+
<div className={styles.root}>
36+
<div
37+
className={styles.content}
38+
style={{ boxShadow: DefaultEffects.elevation4 }}
39+
>
40+
<Text className={styles.title} variant="large" block={true}>
41+
{title}
42+
</Text>
43+
{children}
44+
<div className={styles.buttons}>
45+
{primaryButton}
46+
{backButtonDisabled !== true && (
47+
<DefaultButton
48+
className={styles.backButton}
49+
onClick={onClickBackButton}
50+
>
51+
<FormattedMessage id="back" />
52+
</DefaultButton>
53+
)}
54+
</div>
55+
</div>
56+
{appID != null && (
57+
<ReactRouterLink
58+
className={styles.skip}
59+
to={`/project/${appID}`}
60+
component={FluentLink}
61+
>
62+
<FormattedMessage id="WizardContentLayout.skip.label" />
63+
</ReactRouterLink>
64+
)}
65+
</div>
66+
);
67+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.root {
2+
background-color: #faf8f8;
3+
display: flex;
4+
flex-direction: column;
5+
height: 100vh;
6+
width: 100vw;
7+
}

portal/src/WizardScreenLayout.tsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import React from "react";
2+
import ScreenHeader from "./ScreenHeader";
3+
import styles from "./WizardScreenLayout.module.scss";
4+
5+
export interface WizardScreenLayoutProps {
6+
children?: React.ReactNode;
7+
}
8+
9+
export default function WizardScreenLayout(
10+
props: WizardScreenLayoutProps
11+
): React.ReactElement {
12+
const { children } = props;
13+
return (
14+
<div className={styles.root}>
15+
<ScreenHeader />
16+
{children}
17+
</div>
18+
);
19+
}

0 commit comments

Comments
 (0)