mirror of
https://github.com/OneUptime/oneuptime
synced 2024-11-21 14:49:07 +00:00
fix form issues
This commit is contained in:
parent
07eacc0a19
commit
173c03730a
@ -4,14 +4,20 @@ import LoginPage from './Pages/Login';
|
||||
import SsoLoginPage from './Pages/SsoLogin';
|
||||
import ForgotPasswordPage from './Pages/ForgotPassword';
|
||||
import RegisterPage from './Pages/Register';
|
||||
|
||||
import { DASHBOARD_URL } from 'CommonUI/src/Config';
|
||||
import 'CommonUI/src/Styles/theme.scss';
|
||||
import Navigation from 'CommonUI/src/Utils/Navigation';
|
||||
import VerifyEmail from './Pages/VerifyEmail';
|
||||
import User from 'CommonUI/src/Utils/User';
|
||||
|
||||
function App(): ReactElement {
|
||||
Navigation.setNavigateHook(useNavigate());
|
||||
Navigation.setLocation(useLocation());
|
||||
|
||||
if (User.isLoggedIn()) {
|
||||
Navigation.navigate(DASHBOARD_URL);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="App">
|
||||
<Routes>
|
||||
|
@ -1,28 +1,24 @@
|
||||
import React, { FunctionComponent, useState } from 'react';
|
||||
import BasicModelForm from 'CommonUI/src/Components/Forms/BasicModelForm';
|
||||
import React, { FunctionComponent } from 'react';
|
||||
import ModelForm, { FormType } from 'CommonUI/src/Components/Forms/ModelForm';
|
||||
import User from 'Common/Models/User';
|
||||
import FormValues from 'CommonUI/src/Components/Forms/Types/FormValues';
|
||||
import IdentityAPI from 'CommonUI/src/Utils/API/IdentityAPI';
|
||||
import Link from 'CommonUI/src/Components/Link/Link';
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import { JSONObject } from 'Common/Types/JSON';
|
||||
import FormFieldSchemaType from 'CommonUI/src/Components/Forms/Types/FormFieldSchemaType';
|
||||
import OneUptimeLogo from 'CommonUI/src/Images/logos/OneUptimePNG/7.png';
|
||||
import { DASHBOARD_URL } from 'CommonUI/src/Config';
|
||||
import { JSONObject } from 'Common/Types/JSON';
|
||||
import UserUtil from "CommonUI/src/Utils/User";
|
||||
import Navigation from 'CommonUI/src/Utils/Navigation';
|
||||
import Email from 'Common/Types/Email';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import Name from 'Common/Types/Name';
|
||||
import URL from 'Common/Types/API/URL';
|
||||
import { SIGNUP_API_URL } from "../Utils/ApiPaths";
|
||||
|
||||
const RegisterPage: FunctionComponent = () => {
|
||||
const [isLaoding, setIsLoading] = useState<boolean>(false);
|
||||
|
||||
const user: User = new User();
|
||||
|
||||
const submitForm: Function = async (values: FormValues<User>) => {
|
||||
setIsLoading(true);
|
||||
|
||||
await IdentityAPI.post<JSONObject>(new Route('/signup'), {
|
||||
user: values as JSONObject,
|
||||
});
|
||||
|
||||
setIsLoading(false);
|
||||
};
|
||||
const apiUrl: URL = SIGNUP_API_URL;
|
||||
|
||||
return (
|
||||
<div className="auth-page">
|
||||
@ -55,12 +51,21 @@ const RegisterPage: FunctionComponent = () => {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<BasicModelForm<User>
|
||||
<ModelForm<User>
|
||||
model={user}
|
||||
isLoading={isLaoding}
|
||||
id="register-form"
|
||||
showAsColumns={2}
|
||||
maxPrimaryButtonWidth={true}
|
||||
initialValues= {
|
||||
{
|
||||
email: '',
|
||||
name: '',
|
||||
companyName: '',
|
||||
companyPhoneNumber: '',
|
||||
password: '',
|
||||
confirmPassword: ''
|
||||
}
|
||||
}
|
||||
fields={[
|
||||
{
|
||||
field: {
|
||||
@ -89,7 +94,7 @@ const RegisterPage: FunctionComponent = () => {
|
||||
},
|
||||
fieldType:
|
||||
FormFieldSchemaType.Text,
|
||||
placeholder: 'Company Name',
|
||||
placeholder: 'Acme, Inc.',
|
||||
required: true,
|
||||
title: 'Company Name',
|
||||
},
|
||||
@ -101,7 +106,7 @@ const RegisterPage: FunctionComponent = () => {
|
||||
fieldType:
|
||||
FormFieldSchemaType.Text,
|
||||
required: true,
|
||||
placeholder: 'Phone Number',
|
||||
placeholder: '+1-123-456-7890',
|
||||
title: 'Phone Number',
|
||||
},
|
||||
{
|
||||
@ -136,8 +141,22 @@ const RegisterPage: FunctionComponent = () => {
|
||||
required: true,
|
||||
},
|
||||
]}
|
||||
onSubmit={submitForm}
|
||||
apiUrl={apiUrl}
|
||||
formType={FormType.Create}
|
||||
submitButtonText={'Sign Up'}
|
||||
onSuccess={(value: JSONObject) => {
|
||||
const user: User = User.fromJSON(value["user"] as JSONObject, User) as User;
|
||||
const token: string = value["token"] as string;
|
||||
|
||||
UserUtil.setAccessToken(token);
|
||||
UserUtil.setEmail(user.email as Email);
|
||||
UserUtil.setUserId(user.id as ObjectID);
|
||||
UserUtil.setName(user.name as Name);
|
||||
|
||||
// go to dashboard, user should be logged in.
|
||||
Navigation.navigate(DASHBOARD_URL);
|
||||
|
||||
}}
|
||||
/>
|
||||
|
||||
<div className="mt-5 text-center">
|
||||
|
5
Accounts/src/Utils/ApiPaths.ts
Normal file
5
Accounts/src/Utils/ApiPaths.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import URL from 'Common/Types/API/URL';
|
||||
import { IDENTITY_URL } from 'CommonUI/src/Config';
|
||||
|
||||
export const SIGNUP_API_URL: URL = IDENTITY_URL.addRoute(new Route("/signup"));
|
@ -63,5 +63,5 @@ module.exports = {
|
||||
writeToDisk: true,
|
||||
},
|
||||
},
|
||||
devtool: 'inline-source-map',
|
||||
devtool: 'eval-source-map',
|
||||
}
|
@ -27,6 +27,7 @@ import HashedString from '../Types/HashedString';
|
||||
import Email from '../Types/Email';
|
||||
import Phone from '../Types/Phone';
|
||||
import PositiveNumber from '../Types/PositiveNumber';
|
||||
import Route from '../Types/API/Route';
|
||||
|
||||
export type DbTypes =
|
||||
| string
|
||||
@ -100,6 +101,7 @@ export default class BaseModel extends BaseEntity {
|
||||
public slugifyColumn!: string | null;
|
||||
public saveSlugToColumn!: string | null;
|
||||
|
||||
public crudApiPath!: Route | null;
|
||||
// If this resource is by projectId, which column does projectId belong to?
|
||||
public projectIdColumn!: string | null;
|
||||
|
||||
@ -621,6 +623,10 @@ export default class BaseModel extends BaseEntity {
|
||||
return this.slugifyColumn;
|
||||
}
|
||||
|
||||
public getCrudApiPath(): Route | null {
|
||||
return this.crudApiPath;
|
||||
}
|
||||
|
||||
public getSaveSlugToColumn(): string | null {
|
||||
return this.saveSlugToColumn;
|
||||
}
|
||||
|
@ -6,7 +6,10 @@ import User from './User';
|
||||
import ColumnLength from '../Types/Database/ColumnLength';
|
||||
import Email from '../Types/Email';
|
||||
import TableColumn from '../Types/Database/TableColumn';
|
||||
import CrudApiEndpoint from '../Types/Database/CrudApiEndpoint';
|
||||
import Route from '../Types/API/Route';
|
||||
|
||||
@CrudApiEndpoint(new Route("/email-verification-token"))
|
||||
@Entity({
|
||||
name: 'EmailVerificationToken',
|
||||
})
|
||||
|
@ -9,7 +9,10 @@ import SlugifyColumn from '../Types/Database/SlugifyColumn';
|
||||
import URL from '../Types/API/URL';
|
||||
import User from './User';
|
||||
import TableColumn from '../Types/Database/TableColumn';
|
||||
import CrudApiEndpoint from '../Types/Database/CrudApiEndpoint';
|
||||
import Route from '../Types/API/Route';
|
||||
|
||||
@CrudApiEndpoint(new Route("/probe"))
|
||||
@SlugifyColumn('name', 'slug')
|
||||
@Entity({
|
||||
name: 'Probe',
|
||||
|
@ -6,7 +6,10 @@ import PositiveNumber from '../Types/PositiveNumber';
|
||||
import ObjectID from '../Types/ObjectID';
|
||||
import ColumnLength from '../Types/Database/ColumnLength';
|
||||
import TableColumn from '../Types/Database/TableColumn';
|
||||
import CrudApiEndpoint from '../Types/Database/CrudApiEndpoint';
|
||||
import Route from '../Types/API/Route';
|
||||
|
||||
@CrudApiEndpoint(new Route("/project"))
|
||||
@Entity({
|
||||
name: 'Project',
|
||||
})
|
||||
|
@ -14,7 +14,10 @@ import HashedString from '../Types/HashedString';
|
||||
import PublicRecordPermissions from '../Types/Database/AccessControls/Public/PublicRecordPermissions';
|
||||
import TableColumn from '../Types/Database/TableColumn';
|
||||
import PublicColumnPermissions from '../Types/Database/AccessControls/Public/PublicColumnPermissions';
|
||||
import CrudApiEndpoint from '../Types/Database/CrudApiEndpoint';
|
||||
import Route from '../Types/API/Route';
|
||||
|
||||
@CrudApiEndpoint(new Route("/user"))
|
||||
@PublicRecordPermissions({
|
||||
create: true,
|
||||
readAsList: false,
|
||||
|
@ -31,14 +31,30 @@ export default class HTTPResponse<
|
||||
public constructor(statusCode: number, data: JSONObjectOrArray) {
|
||||
this.statusCode = statusCode;
|
||||
this.jsonData = data;
|
||||
this.data = data as T;
|
||||
}
|
||||
|
||||
let obj!: T;
|
||||
public isSuccess(): boolean {
|
||||
return this.statusCode === 200;
|
||||
}
|
||||
|
||||
if (obj instanceof BaseModel) {
|
||||
// this.data = BaseModel.fromJSON(data) as T;
|
||||
this.data = data as T;
|
||||
} else {
|
||||
this.data = data as T;
|
||||
}
|
||||
public isFailure(): boolean {
|
||||
return this.statusCode !== 200;
|
||||
}
|
||||
|
||||
public isNotAuthorized(): boolean {
|
||||
return this.statusCode === 401;
|
||||
}
|
||||
|
||||
public isTooManyRequests(): boolean {
|
||||
return this.statusCode === 429;
|
||||
}
|
||||
|
||||
public isPaymentDeclined(): boolean {
|
||||
return this.statusCode === 402;
|
||||
}
|
||||
|
||||
public isServerError(): boolean {
|
||||
return this.statusCode === 500;
|
||||
}
|
||||
}
|
||||
|
@ -24,8 +24,8 @@ export default class Route {
|
||||
if (this.route.endsWith('/') && routeToBeAdded.trim().startsWith('/')) {
|
||||
routeToBeAdded = routeToBeAdded.trim().substring(1); // remove leading "/" from route
|
||||
}
|
||||
const newRoute: Route = new Route(this.route + routeToBeAdded);
|
||||
return newRoute;
|
||||
this.route = new Route(this.route + routeToBeAdded).route;
|
||||
return this;
|
||||
}
|
||||
|
||||
public toString(): string {
|
||||
|
@ -103,6 +103,16 @@ export default class URL extends DatabaseProperty {
|
||||
return new URL(protocol, hostname, route);
|
||||
}
|
||||
|
||||
public addRoute(route: Route | string): URL {
|
||||
if (typeof route === "string") {
|
||||
this.route.addRoute(new Route(route));
|
||||
} else {
|
||||
this.route.addRoute(route);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
protected static override toDatabase(
|
||||
value: URL | FindOperator<URL>
|
||||
): string | null {
|
||||
@ -113,6 +123,8 @@ export default class URL extends DatabaseProperty {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
protected static override fromDatabase(_value: string): URL | null {
|
||||
if (_value) {
|
||||
return URL.fromString(_value);
|
||||
|
7
Common/Types/Database/CrudApiEndpoint.ts
Normal file
7
Common/Types/Database/CrudApiEndpoint.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import Route from "../API/Route";
|
||||
|
||||
export default (apiPath: Route) => {
|
||||
return (ctr: Function) => {
|
||||
ctr.prototype.CrudApiPath = apiPath;
|
||||
};
|
||||
};
|
@ -192,7 +192,7 @@ export default class API {
|
||||
return await this.fetch(HTTPMethod.POST, url, data, headers);
|
||||
}
|
||||
|
||||
private static async fetch<
|
||||
public static async fetch<
|
||||
T extends JSONObject | JSONArray | BaseModel | Array<BaseModel>
|
||||
>(
|
||||
method: HTTPMethod,
|
||||
|
@ -79,17 +79,21 @@ app.use(
|
||||
res: ExpressResponse,
|
||||
next: NextFunction
|
||||
) => {
|
||||
logger.error("Code");
|
||||
logger.error(err);
|
||||
|
||||
if (res.headersSent) {
|
||||
return next(err);
|
||||
}
|
||||
|
||||
logger.error("Code");
|
||||
logger.error((err as Exception).code);
|
||||
if (err instanceof Exception) {
|
||||
logger.error("Exception Error")
|
||||
res.status((err as Exception).code);
|
||||
res.send({ error: (err as Exception).message });
|
||||
} else {
|
||||
res.status(500);
|
||||
res.status(400);
|
||||
res.send({ error: err });
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,8 @@ import ShortcutKey from '../ShortcutKey/ShortcutKey';
|
||||
import ButtonType from './ButtonTypes';
|
||||
import CSS from 'csstype';
|
||||
import Icon, { IconProp, SizeProp, ThickProp } from '../Icon/Icon';
|
||||
|
||||
import Loader, { LoaderType } from '../Loader/Loader';
|
||||
import { White } from '../../Utils/BrandColors';
|
||||
export enum ButtonStyleType {
|
||||
PRIMARY,
|
||||
SECONDRY,
|
||||
@ -164,7 +165,7 @@ const Button: FunctionComponent<ComponentProps> = ({
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{isLoading && <div>Implement Loader here</div>}
|
||||
{isLoading && <div><Loader loaderType={LoaderType.Beats} color={White} size={10} /></div>}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
@ -10,6 +10,7 @@ import { JSONObject } from 'Common/Types/JSON';
|
||||
import FormFieldSchemaType from './Types/FormFieldSchemaType';
|
||||
import Email from 'Common/Types/Email';
|
||||
import Link from '../Link/Link';
|
||||
import Alert, { AlertType } from '../Alerts/Alert';
|
||||
|
||||
export const DefaultValidateFunction: Function = (
|
||||
_values: FormValues<JSONObject>
|
||||
@ -32,6 +33,7 @@ export interface ComponentProps<T extends Object> {
|
||||
onCancel?: (() => void) | null;
|
||||
cancelButtonText?: string | null;
|
||||
maxPrimaryButtonWidth?: boolean;
|
||||
error: string | null
|
||||
}
|
||||
|
||||
function getFieldType(fieldType: FormFieldSchemaType): string {
|
||||
@ -50,9 +52,12 @@ function getFieldType(fieldType: FormFieldSchemaType): string {
|
||||
const BasicForm: Function = <T extends Object>(
|
||||
props: ComponentProps<T>
|
||||
): ReactElement => {
|
||||
|
||||
|
||||
const getFormField: Function = (
|
||||
field: DataField<T>,
|
||||
index: number
|
||||
index: number,
|
||||
isDisabled: boolean
|
||||
): ReactElement => {
|
||||
const fieldType: string = field.fieldType
|
||||
? getFieldType(field.fieldType)
|
||||
@ -65,10 +70,14 @@ const BasicForm: Function = <T extends Object>(
|
||||
if (props.showAsColumns && props.showAsColumns > 2) {
|
||||
throw new BadDataException(
|
||||
'showAsCOlumns should be <= 2. It is currently ' +
|
||||
props.showAsColumns
|
||||
props.showAsColumns
|
||||
);
|
||||
}
|
||||
|
||||
const fieldName: string = field.overideFieldKey
|
||||
? field.overideFieldKey
|
||||
: (Object.keys(field.field)[0] as string);
|
||||
|
||||
return (
|
||||
<div className="mb-3" key={index}>
|
||||
<label className="form-Label form-label justify-space-between width-max">
|
||||
@ -94,10 +103,9 @@ const BasicForm: Function = <T extends Object>(
|
||||
type={fieldType}
|
||||
tabIndex={index + 1}
|
||||
name={
|
||||
field.overideFieldKey
|
||||
? field.overideFieldKey
|
||||
: (Object.keys(field.field)[0] as string)
|
||||
fieldName
|
||||
}
|
||||
disabled={isDisabled}
|
||||
/>
|
||||
<ErrorMessage
|
||||
className="mt-1 text-danger"
|
||||
@ -119,17 +127,15 @@ const BasicForm: Function = <T extends Object>(
|
||||
if (field.validation) {
|
||||
if (field.validation.minLength) {
|
||||
if (content.trim().length < field.validation?.minLength) {
|
||||
return `${field.title || name} cannot be less than ${
|
||||
field.validation.minLength
|
||||
} characters.`;
|
||||
return `${field.title || name} cannot be less than ${field.validation.minLength
|
||||
} characters.`;
|
||||
}
|
||||
}
|
||||
|
||||
if (field.validation.maxLength) {
|
||||
if (content.trim().length > field.validation?.maxLength) {
|
||||
return `${field.title || name} cannot be more than ${
|
||||
field.validation.maxLength
|
||||
} characters.`;
|
||||
return `${field.title || name} cannot be more than ${field.validation.maxLength
|
||||
} characters.`;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -156,7 +162,7 @@ const BasicForm: Function = <T extends Object>(
|
||||
field.validation?.toMatchField &&
|
||||
entity[field.validation?.toMatchField] &&
|
||||
(entity[field.validation?.toMatchField] as string).trim() !==
|
||||
content.trim()
|
||||
content.trim()
|
||||
) {
|
||||
return `${field.title} should match ${field.validation?.toMatchField}`;
|
||||
}
|
||||
@ -179,69 +185,70 @@ const BasicForm: Function = <T extends Object>(
|
||||
values: FormValues<T>
|
||||
) => void | object | Promise<FormikErrors<FormValues<T>>>) &
|
||||
Function = (values: FormValues<T>): FormikErrors<FormValues<T>> => {
|
||||
const errors: JSONObject = {};
|
||||
const entries: JSONObject = { ...values } as JSONObject;
|
||||
|
||||
for (const field of props.fields) {
|
||||
const name: string = field.overideFieldKey
|
||||
? field.overideFieldKey
|
||||
: (Object.keys(field.field)[0] as string);
|
||||
if (name in values) {
|
||||
const content: string | undefined = entries[name]?.toString();
|
||||
const errors: JSONObject = {};
|
||||
const entries: JSONObject = { ...values } as JSONObject;
|
||||
|
||||
if (content) {
|
||||
// Check Required fields.
|
||||
const resultRequired: string | null = validateRequired(
|
||||
content,
|
||||
field
|
||||
);
|
||||
if (resultRequired) {
|
||||
errors[name] = resultRequired;
|
||||
}
|
||||
|
||||
// Check for valid email data.
|
||||
const resultValidateData: string | null = validateData(
|
||||
content,
|
||||
field
|
||||
);
|
||||
if (resultValidateData) {
|
||||
errors[name] = resultValidateData;
|
||||
}
|
||||
|
||||
const resultMatch: string | null = validateMatchField(
|
||||
content,
|
||||
field,
|
||||
entries
|
||||
);
|
||||
|
||||
if (resultMatch) {
|
||||
errors[name] = resultMatch;
|
||||
}
|
||||
|
||||
// check for length of content
|
||||
const result: string | null = validateLength(
|
||||
content,
|
||||
field
|
||||
);
|
||||
if (result) {
|
||||
errors[name] = result;
|
||||
for (const field of props.fields) {
|
||||
const name: string = field.overideFieldKey
|
||||
? field.overideFieldKey
|
||||
: (Object.keys(field.field)[0] as string);
|
||||
if (name in values) {
|
||||
const content: string | undefined = entries[name]?.toString();
|
||||
|
||||
if (content) {
|
||||
// Check Required fields.
|
||||
const resultRequired: string | null = validateRequired(
|
||||
content,
|
||||
field
|
||||
);
|
||||
if (resultRequired) {
|
||||
errors[name] = resultRequired;
|
||||
}
|
||||
|
||||
// Check for valid email data.
|
||||
const resultValidateData: string | null = validateData(
|
||||
content,
|
||||
field
|
||||
);
|
||||
if (resultValidateData) {
|
||||
errors[name] = resultValidateData;
|
||||
}
|
||||
|
||||
const resultMatch: string | null = validateMatchField(
|
||||
content,
|
||||
field,
|
||||
entries
|
||||
);
|
||||
|
||||
if (resultMatch) {
|
||||
errors[name] = resultMatch;
|
||||
}
|
||||
|
||||
// check for length of content
|
||||
const result: string | null = validateLength(
|
||||
content,
|
||||
field
|
||||
);
|
||||
if (result) {
|
||||
errors[name] = result;
|
||||
}
|
||||
}
|
||||
} else if (field.required) {
|
||||
errors[name] = `${field.title || name} is required.`;
|
||||
}
|
||||
} else if (field.required) {
|
||||
errors[name] = `${field.title || name} is required.`;
|
||||
}
|
||||
}
|
||||
|
||||
let customValidateResult: JSONObject = {};
|
||||
let customValidateResult: JSONObject = {};
|
||||
|
||||
if (props.onValidate) {
|
||||
customValidateResult = props.onValidate(values);
|
||||
}
|
||||
if (props.onValidate) {
|
||||
customValidateResult = props.onValidate(values);
|
||||
}
|
||||
|
||||
return { ...errors, ...customValidateResult } as FormikErrors<
|
||||
FormValues<T>
|
||||
>;
|
||||
};
|
||||
return { ...errors, ...customValidateResult } as FormikErrors<
|
||||
FormValues<T>
|
||||
>;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="row">
|
||||
@ -259,6 +266,7 @@ const BasicForm: Function = <T extends Object>(
|
||||
setSubmitting(false);
|
||||
}}
|
||||
>
|
||||
|
||||
<Form autoComplete="off">
|
||||
<h1>{props.title}</h1>
|
||||
|
||||
@ -266,40 +274,38 @@ const BasicForm: Function = <T extends Object>(
|
||||
<p className="description">{props.description}</p>
|
||||
)}
|
||||
|
||||
{props.error && <Alert title={props.error} type={AlertType.DANGER} />}
|
||||
|
||||
<div className={`col-lg-12 flex`}>
|
||||
<div
|
||||
className={`col-lg-${
|
||||
12 / (props.showAsColumns || 1)
|
||||
} ${
|
||||
(props.showAsColumns || 1) > 1
|
||||
className={`col-lg-${12 / (props.showAsColumns || 1)
|
||||
} ${(props.showAsColumns || 1) > 1
|
||||
? 'pr-10'
|
||||
: ''
|
||||
}`}
|
||||
}`}
|
||||
>
|
||||
{props.fields &&
|
||||
props.fields.map(
|
||||
(field: DataField<T>, i: number) => {
|
||||
if (
|
||||
i %
|
||||
(props.showAsColumns ||
|
||||
1) ===
|
||||
(props.showAsColumns ||
|
||||
1) ===
|
||||
0
|
||||
) {
|
||||
return getFormField(field, i);
|
||||
return getFormField(field, i, props.isLoading);
|
||||
}
|
||||
return <></>;
|
||||
return <div key={i}></div>;
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
{(props.showAsColumns || 1) > 1 && (
|
||||
<div
|
||||
className={`col-lg-${
|
||||
12 / (props.showAsColumns || 1)
|
||||
} ${
|
||||
(props.showAsColumns || 1) > 1
|
||||
className={`col-lg-${12 / (props.showAsColumns || 1)
|
||||
} ${(props.showAsColumns || 1) > 1
|
||||
? 'pl-10'
|
||||
: ''
|
||||
}`}
|
||||
}`}
|
||||
>
|
||||
{props.fields &&
|
||||
props.fields.map(
|
||||
@ -309,16 +315,17 @@ const BasicForm: Function = <T extends Object>(
|
||||
) => {
|
||||
if (
|
||||
i %
|
||||
(props.showAsColumns ||
|
||||
1) !==
|
||||
(props.showAsColumns ||
|
||||
1) !==
|
||||
0
|
||||
) {
|
||||
return getFormField(
|
||||
field,
|
||||
i
|
||||
i,
|
||||
props.isLoading
|
||||
);
|
||||
}
|
||||
return <></>;
|
||||
return <div key={i}></div>;
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
@ -342,6 +349,7 @@ const BasicForm: Function = <T extends Object>(
|
||||
title={props.submitButtonText || 'Submit'}
|
||||
type={ButtonTypes.Submit}
|
||||
id={`${props.id}-submit-button`}
|
||||
disabled={props.isLoading || false}
|
||||
isLoading={props.isLoading || false}
|
||||
buttonStyle={ButtonStyleType.PRIMARY}
|
||||
style={{
|
||||
|
@ -22,6 +22,7 @@ export interface ComponentProps<TBaseModel extends BaseModel> {
|
||||
onCancel?: () => void;
|
||||
cancelButtonText?: string;
|
||||
maxPrimaryButtonWidth?: boolean;
|
||||
error: string | null
|
||||
}
|
||||
|
||||
const BasicModelForm: Function = <TBaseModel extends BaseModel>(
|
||||
@ -75,6 +76,7 @@ const BasicModelForm: Function = <TBaseModel extends BaseModel>(
|
||||
onCancel={props.onCancel}
|
||||
cancelButtonText={props.cancelButtonText}
|
||||
maxPrimaryButtonWidth={props.maxPrimaryButtonWidth || false}
|
||||
error={props.error}
|
||||
></BasicForm>
|
||||
);
|
||||
};
|
||||
|
@ -4,10 +4,19 @@ import BaseModel from 'Common/Models/BaseModel';
|
||||
import FormValues from './Types/FormValues';
|
||||
import Fields from './Types/Fields';
|
||||
import BasicModelForm from './BasicModelForm';
|
||||
import { JSONArray, JSONObject, JSONObjectOrArray } from 'Common/Types/JSON';
|
||||
import URL from 'Common/Types/API/URL';
|
||||
import HTTPMethod from 'Common/Types/API/HTTPMethod';
|
||||
import API from '../../Utils/API/API';
|
||||
import HTTPResponse from 'Common/Types/API/HTTPResponse';
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import BadDataException from 'Common/Types/Exception/BadDataException';
|
||||
import { DASHBOARD_API_URL } from '../../Config';
|
||||
|
||||
|
||||
export enum FormType {
|
||||
Create,
|
||||
Update,
|
||||
Update
|
||||
}
|
||||
|
||||
export interface ComponentProps<TBaseModel extends BaseModel> {
|
||||
@ -23,30 +32,51 @@ export interface ComponentProps<TBaseModel extends BaseModel> {
|
||||
showAsColumns?: number;
|
||||
footer: ReactElement;
|
||||
onCancel?: () => void;
|
||||
onSuccess?: (data: TBaseModel) => void;
|
||||
onSuccess?: (data: TBaseModel | JSONObjectOrArray | Array<TBaseModel>) => void;
|
||||
cancelButtonText?: string;
|
||||
maxPrimaryButtonWidth?: boolean;
|
||||
apiUrl?: URL,
|
||||
formType: FormType
|
||||
}
|
||||
|
||||
const CreateModelForm: Function = <TBaseModel extends BaseModel>(
|
||||
props: ComponentProps<TBaseModel>
|
||||
): ReactElement => {
|
||||
const [isLoading] = useState<boolean>(false);
|
||||
// const [data, setData] = useState<TBaseModel>(props.model);
|
||||
const [isLoading, setLoading] = useState<boolean>(false);
|
||||
const [error, setError] = useState<string>('');
|
||||
|
||||
// useEffect(() => {
|
||||
const onSubmit = async (values: any) => {
|
||||
// Ping an API here.
|
||||
setLoading(true);
|
||||
let apiUrl = props.apiUrl;
|
||||
|
||||
// let httpMethod: HTTPMethod = HTTPMethod.POST;
|
||||
if (!apiUrl) {
|
||||
const apiPath: Route | null = props.model.getCrudApiPath();
|
||||
if (!apiPath) {
|
||||
throw new BadDataException("This model does not support CRUD operations.");
|
||||
}
|
||||
|
||||
/*
|
||||
* if (props.formType === FormType.Update) {
|
||||
* httpMethod = HTTPMethod.PUT;
|
||||
* setIsLoading(true);
|
||||
* // Need to fetch data;
|
||||
*/
|
||||
apiUrl = DASHBOARD_API_URL.addRoute(apiPath);
|
||||
}
|
||||
|
||||
// }
|
||||
const result: HTTPResponse<JSONObject | JSONArray | TBaseModel | Array<TBaseModel>> =
|
||||
await API.fetch<
|
||||
JSONObject |
|
||||
JSONArray |
|
||||
TBaseModel |
|
||||
Array<TBaseModel
|
||||
>>(props.formType === FormType.Create ? HTTPMethod.POST : HTTPMethod.PUT, apiUrl, values);
|
||||
|
||||
// }, []);
|
||||
setLoading(false);
|
||||
|
||||
if (result.isSuccess()) {
|
||||
if (props.onSuccess) {
|
||||
props.onSuccess(result.data);
|
||||
}
|
||||
} else {
|
||||
setError((result.data as JSONObject)["error"] as string)
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<BasicModelForm<TBaseModel>
|
||||
@ -60,14 +90,11 @@ const CreateModelForm: Function = <TBaseModel extends BaseModel>(
|
||||
isLoading={isLoading}
|
||||
submitButtonText={props.submitButtonText}
|
||||
cancelButtonText={props.cancelButtonText}
|
||||
onSubmit={async (_values: any) => {
|
||||
// Ping POST an API here.
|
||||
if (props.onSuccess) {
|
||||
props.onSuccess(props.model);
|
||||
}
|
||||
}}
|
||||
onSubmit={onSubmit}
|
||||
onValidate={props.onValidate}
|
||||
onCancel={props.onCancel}
|
||||
maxPrimaryButtonWidth={props.maxPrimaryButtonWidth}
|
||||
error={error}
|
||||
></BasicModelForm>
|
||||
);
|
||||
};
|
||||
|
@ -1,9 +1,11 @@
|
||||
import BarLoader from 'react-spinners/BarLoader';
|
||||
import BeatLoader from 'react-spinners/BeatLoader';
|
||||
import React, { FunctionComponent } from 'react';
|
||||
import Color from 'Common/Types/Color';
|
||||
|
||||
export enum LoaderType {
|
||||
Bar,
|
||||
Beats
|
||||
}
|
||||
|
||||
export interface ComponentProps {
|
||||
@ -17,10 +19,15 @@ const Loader: FunctionComponent<ComponentProps> = ({
|
||||
color = new Color('#000000'),
|
||||
loaderType = LoaderType.Bar,
|
||||
}: ComponentProps) => {
|
||||
|
||||
if (loaderType === LoaderType.Bar) {
|
||||
return <BarLoader height={4} width={size} color={color.toString()} />;
|
||||
}
|
||||
|
||||
if (loaderType === LoaderType.Beats) {
|
||||
return <BeatLoader size={size} color={color.toString()} />;
|
||||
}
|
||||
|
||||
return <></>;
|
||||
};
|
||||
|
||||
|
@ -72,8 +72,8 @@ export const HOME_HOSTNAME: Hostname = Hostname.fromString(
|
||||
|
||||
export const DASHBOARD_API_URL: URL = new URL(
|
||||
HTTP_PROTOCOL,
|
||||
INTEGRATION_HOSTNAME,
|
||||
INTEGRATION_ROUTE
|
||||
DASHBOARD_API_HOSTNAME,
|
||||
DASHBOARD_API_ROUTE
|
||||
);
|
||||
|
||||
export const IDENTITY_URL: URL = new URL(
|
||||
|
@ -1,5 +1,4 @@
|
||||
import User from '../User';
|
||||
import history from '../History';
|
||||
import Headers from 'Common/Types/API/Headers';
|
||||
import API from 'Common/Utils/API';
|
||||
import APIException from 'Common/Types/Exception/ApiException';
|
||||
@ -8,12 +7,18 @@ import Cookies from 'universal-cookie';
|
||||
import Protocol from 'Common/Types/API/Protocol';
|
||||
import Hostname from 'Common/Types/API/Hostname';
|
||||
import Route from 'Common/Types/API/Route';
|
||||
import URL from 'Common/Types/API/URL';
|
||||
import Navigation from '../Navigation';
|
||||
|
||||
class BaseAPI extends API {
|
||||
public constructor(protocol: Protocol, hostname: Hostname, route?: Route) {
|
||||
super(protocol, hostname, route);
|
||||
}
|
||||
|
||||
public static fromURL(url: URL) {
|
||||
return new BaseAPI(url.protocol, url.hostname, url.route);
|
||||
}
|
||||
|
||||
protected static override getHeaders(): Headers {
|
||||
let defaultHeaders: Headers = this.getDefaultHeaders();
|
||||
|
||||
@ -38,7 +43,7 @@ class BaseAPI extends API {
|
||||
cookies.remove('admin-data', { path: '/' });
|
||||
cookies.remove('data', { path: '/' });
|
||||
User.clear();
|
||||
history.push('/login');
|
||||
Navigation.navigate(new Route("/accounts/login"));
|
||||
}
|
||||
|
||||
return error;
|
@ -3,7 +3,7 @@ import {
|
||||
HTTP_PROTOCOL,
|
||||
DASHBOARD_API_ROUTE,
|
||||
} from '../../Config';
|
||||
import BaseAPI from './BaseAPI';
|
||||
import BaseAPI from './API';
|
||||
|
||||
class BackendAPI extends BaseAPI {
|
||||
public constructor() {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { IDENTITY_HOSTNAME, HTTP_PROTOCOL, IDENTITY_ROUTE } from '../../Config';
|
||||
import BaseAPI from './BaseAPI';
|
||||
import BaseAPI from './API';
|
||||
|
||||
class IdentityAPI extends BaseAPI {
|
||||
public constructor() {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { INTEGRATION_HOSTNAME, HTTP_PROTOCOL } from '../../Config';
|
||||
import BaseAPI from './BaseAPI';
|
||||
import BaseAPI from './API';
|
||||
|
||||
class BackendAPI extends BaseAPI {
|
||||
public constructor() {
|
||||
|
@ -2,6 +2,7 @@ import Color from 'Common/Types/Color';
|
||||
|
||||
export const Blue: Color = new Color('#7a7fdc');
|
||||
export const Black: Color = new Color('#000000');
|
||||
export const White: Color = new Color('#ffffff');
|
||||
export const Indigo: Color = new Color('#564ab1');
|
||||
export const Purple: Color = new Color('#6f42c1');
|
||||
export const Pink: Color = new Color('#e83e8c');
|
||||
|
@ -24,27 +24,27 @@ export default class User {
|
||||
}
|
||||
|
||||
public static setUserId(id: ObjectID): void {
|
||||
LocalStorage.setItem('id', id.toString());
|
||||
LocalStorage.setItem('user_id', id.toString());
|
||||
}
|
||||
|
||||
public static getUserId(): ObjectID {
|
||||
return new ObjectID((LocalStorage.getItem('id') as string) || '');
|
||||
return new ObjectID((LocalStorage.getItem('user_id') as string) || '');
|
||||
}
|
||||
|
||||
public static getName(): Name {
|
||||
return new Name((LocalStorage.getItem('name') as string) || '');
|
||||
return new Name((LocalStorage.getItem('user_name') as string) || '');
|
||||
}
|
||||
|
||||
public static setName(name: string): void {
|
||||
LocalStorage.setItem('name', name);
|
||||
public static setName(name: Name): void {
|
||||
LocalStorage.setItem('user_name', name.toString());
|
||||
}
|
||||
|
||||
public static getEmail(): Email {
|
||||
return new Email(LocalStorage.getItem('email') as string);
|
||||
return new Email(LocalStorage.getItem('user_email') as string);
|
||||
}
|
||||
|
||||
public static setEmail(email: Email): void {
|
||||
LocalStorage.setItem('email', email);
|
||||
LocalStorage.setItem('user_email', email);
|
||||
}
|
||||
|
||||
public static initialUrl(): URL {
|
||||
@ -73,7 +73,7 @@ export default class User {
|
||||
}
|
||||
|
||||
public static removeUserId(): void {
|
||||
LocalStorage.removeItem('id');
|
||||
LocalStorage.removeItem('user_id');
|
||||
}
|
||||
|
||||
public static removeAccessToken(): void {
|
||||
|
@ -17,7 +17,7 @@ import Logs from './Pages/Logs/Logs';
|
||||
import Navigation from 'CommonUI/src/Utils/Navigation';
|
||||
import RouteMap from './Utils/RouteMap';
|
||||
import PageMap from './Utils/PageMap';
|
||||
|
||||
import { ACCOUNTS_URL } from 'CommonUI/src/Config';
|
||||
// Settings Pages
|
||||
import Settings from './Pages/Settings/Settings';
|
||||
import SettingsDangerZone from './Pages/Settings/DangerZone';
|
||||
@ -25,11 +25,16 @@ import SettingsApiKeys from './Pages/Settings/APIKeys';
|
||||
import SettingsCreateAPIKey from './Pages/Settings/CreateAPIKey';
|
||||
// Import CSS
|
||||
import 'CommonUI/src/Styles/theme.scss';
|
||||
import User from 'CommonUI/src/Utils/User';
|
||||
|
||||
const App: FunctionComponent = () => {
|
||||
Navigation.setNavigateHook(useNavigate());
|
||||
Navigation.setLocation(useLocation());
|
||||
|
||||
if (!User.isLoggedIn()) {
|
||||
Navigation.navigate(ACCOUNTS_URL);
|
||||
}
|
||||
|
||||
return (
|
||||
<MasterPage>
|
||||
<Routes>
|
||||
|
@ -63,5 +63,5 @@ module.exports = {
|
||||
devServer: {
|
||||
historyApiFallback: true,
|
||||
},
|
||||
devtool: 'inline-source-map',
|
||||
devtool: 'eval-source-map',
|
||||
}
|
@ -46,7 +46,7 @@ router.post(
|
||||
const data: JSONObject = req.body;
|
||||
|
||||
const user: User = User.asPublicCreateable<User>(
|
||||
data['user'] as JSONObject,
|
||||
data as JSONObject,
|
||||
User
|
||||
);
|
||||
|
||||
|
@ -5,7 +5,6 @@ version: '3.7'
|
||||
|
||||
x-common-variables: &common-variables
|
||||
IS_SAAS_SERVICE: ${IS_SAAS_SERVICE}
|
||||
DEBUG: 'express:*'
|
||||
|
||||
services:
|
||||
##IMPORTANT:
|
||||
|
Loading…
Reference in New Issue
Block a user