mirror of
https://github.com/OneUptime/oneuptime
synced 2024-11-21 22:59:07 +00:00
Refactor form to allow dynamic children
This commit is contained in:
parent
02abfb53fa
commit
deed690345
8
Accounts/package-lock.json
generated
8
Accounts/package-lock.json
generated
@ -112,9 +112,7 @@
|
|||||||
"formik": "^2.2.9",
|
"formik": "^2.2.9",
|
||||||
"prop-types": "^15.8.1",
|
"prop-types": "^15.8.1",
|
||||||
"react": "^18.1.0",
|
"react": "^18.1.0",
|
||||||
"react-dom": "^18.1.0",
|
"react-scripts": "5.0.1",
|
||||||
"react-router": "^6.3.0",
|
|
||||||
"react-router-dom": "^6.3.0",
|
|
||||||
"redux": "^4.2.0",
|
"redux": "^4.2.0",
|
||||||
"universal-cookie": "^4.0.4",
|
"universal-cookie": "^4.0.4",
|
||||||
"web-vitals": "^2.1.4",
|
"web-vitals": "^2.1.4",
|
||||||
@ -18439,9 +18437,7 @@
|
|||||||
"formik": "^2.2.9",
|
"formik": "^2.2.9",
|
||||||
"prop-types": "^15.8.1",
|
"prop-types": "^15.8.1",
|
||||||
"react": "^18.1.0",
|
"react": "^18.1.0",
|
||||||
"react-dom": "^18.1.0",
|
"react-scripts": "5.0.1",
|
||||||
"react-router": "^6.3.0",
|
|
||||||
"react-router-dom": "^6.3.0",
|
|
||||||
"redux": "^4.2.0",
|
"redux": "^4.2.0",
|
||||||
"universal-cookie": "^4.0.4",
|
"universal-cookie": "^4.0.4",
|
||||||
"web-vitals": "^2.1.4",
|
"web-vitals": "^2.1.4",
|
||||||
|
@ -61,17 +61,10 @@ body {
|
|||||||
|
|
||||||
.brand {
|
.brand {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 5%;
|
top: -5%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translateX(-55%);
|
transform: translateX(-50%);
|
||||||
|
margin-bottom: 20px;
|
||||||
@include responsive($xs) {
|
|
||||||
top: 6%;
|
|
||||||
}
|
|
||||||
|
|
||||||
@include responsive($lg) {
|
|
||||||
top: 5%;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
img {
|
||||||
width: 200px;
|
width: 200px;
|
||||||
@ -137,6 +130,19 @@ form {
|
|||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
color: lighten($bgDark, 20%);
|
color: lighten($bgDark, 20%);
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
span:last-child {
|
||||||
|
color: $lightBlue;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: $transition;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: darken($lightBlue, 50%);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
input {
|
input {
|
||||||
@ -248,6 +254,7 @@ form {
|
|||||||
|
|
||||||
span {
|
span {
|
||||||
color: lighten($bgDark, 20%);
|
color: lighten($bgDark, 20%);
|
||||||
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
@ -282,7 +289,7 @@ form {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
@include responsive($xs) {
|
@include responsive($xs) {
|
||||||
padding-bottom: 15px;
|
padding-bottom: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
|
@ -3,6 +3,7 @@ import { Link } from 'react-router-dom';
|
|||||||
import BasicModelForm from 'CommonUI/src/Components/Forms/BasicModelForm';
|
import BasicModelForm from 'CommonUI/src/Components/Forms/BasicModelForm';
|
||||||
import User from 'Common/Models/User';
|
import User from 'Common/Models/User';
|
||||||
import FormValues from 'CommonUI/src/Components/Forms/Types/FormValues';
|
import FormValues from 'CommonUI/src/Components/Forms/Types/FormValues';
|
||||||
|
import Route from 'Common/Types/API/Route';
|
||||||
|
|
||||||
const LoginPage: FunctionComponent = () => {
|
const LoginPage: FunctionComponent = () => {
|
||||||
const user: User = new User();
|
const user: User = new User();
|
||||||
@ -24,6 +25,11 @@ const LoginPage: FunctionComponent = () => {
|
|||||||
password: true,
|
password: true,
|
||||||
},
|
},
|
||||||
title: 'Password',
|
title: 'Password',
|
||||||
|
sideLink: {
|
||||||
|
text: 'Forgot password?',
|
||||||
|
url: new Route('/forgot-password'),
|
||||||
|
openLinkInNewTab: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
onSubmit={(values: FormValues<User>) => {
|
onSubmit={(values: FormValues<User>) => {
|
||||||
@ -31,7 +37,22 @@ const LoginPage: FunctionComponent = () => {
|
|||||||
}}
|
}}
|
||||||
submitButtonText={'Login'}
|
submitButtonText={'Login'}
|
||||||
title={'Sign in to your account'}
|
title={'Sign in to your account'}
|
||||||
/>
|
>
|
||||||
|
<div className="actions">
|
||||||
|
<p>
|
||||||
|
<Link to="/forgot-password">Forgot your password?</Link>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<Link to="/login/sso">
|
||||||
|
Use single sign-on (SSO) instead
|
||||||
|
</Link>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<span>Don't have an account? </span>{' '}
|
||||||
|
<Link to="/register">Sign up</Link>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</BasicModelForm>
|
||||||
|
|
||||||
<div className="footer">
|
<div className="footer">
|
||||||
<p>
|
<p>
|
||||||
|
5920
CommonUI/package-lock.json
generated
5920
CommonUI/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -18,15 +18,12 @@
|
|||||||
"Common": "file:../Common",
|
"Common": "file:../Common",
|
||||||
"formik": "^2.2.9",
|
"formik": "^2.2.9",
|
||||||
"prop-types": "^15.8.1",
|
"prop-types": "^15.8.1",
|
||||||
|
"react": "^18.1.0",
|
||||||
|
"react-scripts": "5.0.1",
|
||||||
"redux": "^4.2.0",
|
"redux": "^4.2.0",
|
||||||
"universal-cookie": "^4.0.4",
|
"universal-cookie": "^4.0.4",
|
||||||
"web-vitals": "^2.1.4",
|
"web-vitals": "^2.1.4",
|
||||||
"yup": "^0.32.11",
|
"yup": "^0.32.11"
|
||||||
"react": "^18.1.0",
|
|
||||||
"react-dom": "^18.1.0",
|
|
||||||
"react-router": "^6.3.0",
|
|
||||||
"react-router-dom": "^6.3.0",
|
|
||||||
"react-scripts": "5.0.1"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^17.0.23",
|
"@types/node": "^17.0.23",
|
||||||
|
@ -7,7 +7,6 @@ import Fields from './Types/Fields';
|
|||||||
import DataField from './Types/Field';
|
import DataField from './Types/Field';
|
||||||
import ButtonTypes from '../Basic/Button/ButtonTypes';
|
import ButtonTypes from '../Basic/Button/ButtonTypes';
|
||||||
import BadDataException from 'Common/Types/Exception/BadDataException';
|
import BadDataException from 'Common/Types/Exception/BadDataException';
|
||||||
|
|
||||||
export interface ComponentProps<T extends Object> {
|
export interface ComponentProps<T extends Object> {
|
||||||
id: string;
|
id: string;
|
||||||
initialValues: FormValues<T>;
|
initialValues: FormValues<T>;
|
||||||
@ -18,19 +17,36 @@ export interface ComponentProps<T extends Object> {
|
|||||||
model: T;
|
model: T;
|
||||||
submitButtonText?: string;
|
submitButtonText?: string;
|
||||||
title?: string;
|
title?: string;
|
||||||
|
children: ReactElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
const BasicForm = <T extends Object>(
|
const BasicForm = <T extends Object>(
|
||||||
props: ComponentProps<T>
|
props: ComponentProps<T>
|
||||||
): ReactElement => {
|
): ReactElement => {
|
||||||
const getFormField = (field: DataField<T>, index: number): ReactElement => {
|
const getFormField = (field: DataField<T>, index: number): ReactElement => {
|
||||||
let fieldType = 'text';
|
const fieldType = 'text';
|
||||||
if (Object.keys(field.field).length === 0) {
|
if (Object.keys(field.field).length === 0) {
|
||||||
throw new BadDataException('Object cannot be without Field');
|
throw new BadDataException('Object cannot be without Field');
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div key={index}>
|
<div key={index}>
|
||||||
<label>{field.title}</label>
|
<label>
|
||||||
|
<span>{field.title}</span>
|
||||||
|
{
|
||||||
|
<span>
|
||||||
|
<a
|
||||||
|
href={field.sideLink?.url.toString()}
|
||||||
|
target={`${
|
||||||
|
field.sideLink?.openLinkInNewTab
|
||||||
|
? '_blank'
|
||||||
|
: '_self'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{field.sideLink?.text}
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
</label>
|
||||||
<p>{field.description}</p>
|
<p>{field.description}</p>
|
||||||
<Field
|
<Field
|
||||||
placeholder={field.placeholder}
|
placeholder={field.placeholder}
|
||||||
@ -60,35 +76,24 @@ const BasicForm = <T extends Object>(
|
|||||||
props.onSubmit(values);
|
props.onSubmit(values);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{({ isSubmitting }) => (
|
{({ isSubmitting }) => {
|
||||||
<Form>
|
return (
|
||||||
<h1>{props.title}</h1>
|
<Form>
|
||||||
{props.fields &&
|
<h1>{props.title}</h1>
|
||||||
props.fields.map((field: DataField<T>, i) => {
|
{props.fields &&
|
||||||
return getFormField(field, i);
|
props.fields.map((field: DataField<T>, i) => {
|
||||||
})}
|
return getFormField(field, i);
|
||||||
<div className="remember">
|
})}
|
||||||
<input type="checkbox" id="remember" />
|
<Button
|
||||||
<label htmlFor="remember">
|
title={props.submitButtonText || 'Submit'}
|
||||||
Stay signed in for a week
|
disabled={isSubmitting}
|
||||||
</label>
|
type={ButtonTypes.Submit}
|
||||||
</div>
|
id={`${props.id}-submit-button`}
|
||||||
<Button
|
/>
|
||||||
title={props.submitButtonText || 'Submit'}
|
{props.children}
|
||||||
disabled={isSubmitting}
|
</Form>
|
||||||
type={ButtonTypes.Submit}
|
);
|
||||||
id={`${props.id}-submit-button`}
|
}}
|
||||||
/>
|
|
||||||
<div className="actions">
|
|
||||||
<p>Forgot your password?</p>
|
|
||||||
<p>Use single sign-on (SSO) instead</p>
|
|
||||||
<p>
|
|
||||||
<span>Don't have an account? </span> Sign
|
|
||||||
up
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</Form>
|
|
||||||
)}
|
|
||||||
</Formik>
|
</Formik>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -13,6 +13,7 @@ export interface ComponentProps<T extends BaseModel> {
|
|||||||
fields: Fields<T>;
|
fields: Fields<T>;
|
||||||
submitButtonText?: string;
|
submitButtonText?: string;
|
||||||
title?: string;
|
title?: string;
|
||||||
|
children: ReactElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
const BasicModelForm = <TBaseModel extends BaseModel>(
|
const BasicModelForm = <TBaseModel extends BaseModel>(
|
||||||
@ -58,7 +59,9 @@ const BasicModelForm = <TBaseModel extends BaseModel>(
|
|||||||
model={props.model}
|
model={props.model}
|
||||||
submitButtonText={props.submitButtonText || 'Save'}
|
submitButtonText={props.submitButtonText || 'Save'}
|
||||||
title={props.title || ''}
|
title={props.title || ''}
|
||||||
/>
|
>
|
||||||
|
{props.children}
|
||||||
|
</BasicForm>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import Route from 'Common/Types/API/Route';
|
||||||
|
import URL from 'Common/Types/API/URL';
|
||||||
import SelectFormFields from './SelectFormField';
|
import SelectFormFields from './SelectFormField';
|
||||||
|
|
||||||
export default interface Field<TEntity> {
|
export default interface Field<TEntity> {
|
||||||
@ -5,4 +7,9 @@ export default interface Field<TEntity> {
|
|||||||
description?: string;
|
description?: string;
|
||||||
field: SelectFormFields<TEntity>;
|
field: SelectFormFields<TEntity>;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
|
sideLink?: {
|
||||||
|
text: string;
|
||||||
|
url: Route | URL;
|
||||||
|
openLinkInNewTab?: boolean;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
import Hostname from 'Common/Types/API/Hostname';
|
import Hostname from 'Common/Types/API/Hostname';
|
||||||
|
import Route from 'Common/Types/API/Route';
|
||||||
|
import URL from 'Common/Types/API/URL';
|
||||||
import Email from 'Common/Types/Email';
|
import Email from 'Common/Types/Email';
|
||||||
import Name from 'Common/Types/Name';
|
import Name from 'Common/Types/Name';
|
||||||
import ObjectID from 'Common/Types/ObjectID';
|
import ObjectID from 'Common/Types/ObjectID';
|
||||||
@ -10,6 +12,8 @@ type FormFieldType =
|
|||||||
| ObjectID
|
| ObjectID
|
||||||
| Hostname
|
| Hostname
|
||||||
| Email
|
| Email
|
||||||
| Name;
|
| Name
|
||||||
|
| Route
|
||||||
|
| URL;
|
||||||
|
|
||||||
export default FormFieldType;
|
export default FormFieldType;
|
||||||
|
@ -3,6 +3,7 @@ enum FormType {
|
|||||||
Name,
|
Name,
|
||||||
Hostname,
|
Hostname,
|
||||||
URL,
|
URL,
|
||||||
|
Route,
|
||||||
String,
|
String,
|
||||||
Number,
|
Number,
|
||||||
Password,
|
Password,
|
||||||
|
Loading…
Reference in New Issue
Block a user