-
Notifications
You must be signed in to change notification settings - Fork 73
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
🚧 trying to manage server validation error from response body rebase rebase
- Loading branch information
1 parent
d72b707
commit 01da96f
Showing
5 changed files
with
236 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
182 changes: 182 additions & 0 deletions
182
portafly/src/components/pages/product/CreateProductPage.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,182 @@ | ||
import React, { | ||
useState, FormEventHandler, FocusEventHandler, useEffect | ||
} from 'react' | ||
|
||
import { | ||
PageSection, | ||
TextContent, | ||
Text, | ||
Card, | ||
CardBody, | ||
Form, | ||
FormGroup, | ||
TextInput, | ||
TextArea, | ||
ActionGroup, | ||
Button | ||
} from '@patternfly/react-core' | ||
import { useTranslation } from 'i18n/useTranslation' | ||
import { useHistory, Redirect } from 'react-router' | ||
import { createProduct, NewProduct } from 'dal/products' | ||
import { useAsync } from 'react-async' | ||
import { useAlertsContext } from 'components/util' | ||
import { ValidationException } from 'utils' | ||
|
||
type Validations = Record<string, { | ||
validation: 'default' | 'success' | 'error', | ||
errors?: string[] | ||
}> | ||
|
||
const CreateProductPage = () => { | ||
// @ts-ignore missing strings file | ||
const { t } = useTranslation('createProduct') | ||
const { goBack } = useHistory() | ||
const { addAlert } = useAlertsContext() | ||
|
||
const [name, setName] = useState('') | ||
const [systemName, setSystemName] = useState('') | ||
const [description, setDescription] = useState('') | ||
const [validations, setValidations] = useState<Validations>({ | ||
name: { validation: 'default' }, | ||
system_name: { validation: 'default' }, | ||
description: { validation: 'default' } | ||
}) | ||
|
||
const { | ||
isPending, error, run, data | ||
} = useAsync({ deferFn: createProduct }) | ||
|
||
useEffect(() => { | ||
if (error) { | ||
if (Object.prototype.hasOwnProperty.call(error, 'validationErrors')) { | ||
const { validationErrors } = (error as unknown as ValidationException) | ||
const newValidations: Validations = {} | ||
Object.keys(validationErrors).forEach((id) => { | ||
newValidations[id] = { | ||
validation: 'error', | ||
errors: validationErrors[id] | ||
} | ||
}) | ||
setValidations({ ...validations, ...newValidations }) | ||
} else { | ||
addAlert({ id: String(Date.now()), title: error.message, variant: 'danger' }) | ||
} | ||
} | ||
}, [error]) | ||
|
||
if (data) { | ||
const { service } = data as NewProduct | ||
return <Redirect to={`/products/${service.id}`} /> | ||
} | ||
|
||
const isValid = validations.name.validation === 'success' && validations.system_name.validation !== 'error' | ||
|
||
const validate = (inputName: string) => { | ||
switch (inputName) { | ||
case 'name': | ||
return name.length > 5 ? 'success' : 'error' | ||
case 'system_name': | ||
// eslint-disable-next-line no-nested-ternary | ||
return !systemName ? 'default' : (systemName.length > 5 ? 'success' : 'error') | ||
case 'description': | ||
return description.length > 0 ? 'success' : 'default' | ||
default: | ||
return 'default' | ||
} | ||
} | ||
|
||
const onBlur: FocusEventHandler = (ev) => { | ||
const { name: inputName } = ev.currentTarget as HTMLInputElement | ||
|
||
const newValidations = { ...validations } | ||
|
||
newValidations[inputName] = { | ||
validation: validate(inputName) | ||
} | ||
|
||
setValidations(newValidations) | ||
} | ||
|
||
const onSubmit: FormEventHandler = (ev) => { | ||
ev.preventDefault() | ||
const formData = new FormData(ev.currentTarget as HTMLFormElement) | ||
run(formData) | ||
} | ||
|
||
return ( | ||
<> | ||
<PageSection variant="light"> | ||
<TextContent> | ||
<Text component="h1">{t('New API Product')}</Text> | ||
</TextContent> | ||
</PageSection> | ||
|
||
<PageSection> | ||
<Card> | ||
<CardBody> | ||
<Form onSubmit={onSubmit}> | ||
<FormGroup | ||
label={t('Name')} | ||
fieldId="name" | ||
helperTextInvalid={validations.name.errors?.flat()} | ||
validated={validations.name.validation} | ||
isRequired | ||
> | ||
<TextInput | ||
validated={validations.name.validation} | ||
id="name" | ||
type="text" | ||
name="name" | ||
value={name} | ||
onChange={setName} | ||
onBlur={onBlur} | ||
isRequired | ||
/> | ||
</FormGroup> | ||
<FormGroup | ||
label={t('System name')} | ||
fieldId="system_name" | ||
helperText={t('Only ASCII letters, numbers...')} | ||
helperTextInvalid={validations.system_name.errors?.flat()} | ||
validated={validations.system_name.validation} | ||
> | ||
<TextInput | ||
validated={validations.system_name.validation} | ||
id="system_name" | ||
type="text" | ||
name="system_name" | ||
value={systemName} | ||
onChange={setSystemName} | ||
onBlur={onBlur} | ||
/> | ||
</FormGroup> | ||
<FormGroup | ||
label={t('Description')} | ||
fieldId="description" | ||
helperTextInvalid={validations.description.errors?.flat()} | ||
validated={validations.description.validation} | ||
> | ||
<TextArea | ||
validated={validations.description.validation} | ||
id="description" | ||
name="description" | ||
value={description} | ||
onChange={setDescription} | ||
onBlur={onBlur} | ||
/> | ||
</FormGroup> | ||
<ActionGroup> | ||
<Button type="submit" isDisabled={isPending || !isValid} variant="primary">{t('Create')}</Button> | ||
<Button onClick={goBack} variant="link">{t('Cancel')}</Button> | ||
</ActionGroup> | ||
</Form> | ||
</CardBody> | ||
</Card> | ||
</PageSection> | ||
</> | ||
) | ||
} | ||
|
||
// Default export needed for React.lazy | ||
// eslint-disable-next-line import/no-default-export | ||
export default CreateProductPage |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import { craftRequest, postData } from 'utils' | ||
import { DeferFn } from 'react-async' | ||
|
||
type CreateProductParams = { | ||
name: string | ||
description?: string | ||
deployment_option?: string | ||
backend_version?: string | ||
system_name?: string | ||
} | ||
|
||
export interface NewProduct { | ||
service: { | ||
id: number, | ||
name: string, | ||
state: string, | ||
system_name: string, | ||
backend_version: string, | ||
deployment_option: string, | ||
support_email: string, | ||
description: string, | ||
intentions_required: boolean, | ||
buyers_manage_apps: boolean, | ||
buyers_manage_keys: boolean, | ||
referrer_filters_required: boolean, | ||
custom_keys_enabled: boolean, | ||
buyer_key_regenerate_enabled: boolean, | ||
mandatory_app_key: boolean, | ||
buyer_can_select_plan: boolean, | ||
buyer_plan_change_permission: string, | ||
created_at: string, | ||
updated_at: string, | ||
links: Array<{ rel: string, href: string }> | ||
} | ||
} | ||
|
||
export type CreateProductValidationErrors = { | ||
name?: string[], | ||
system_name?: string[] | ||
} | ||
|
||
const createProduct: DeferFn<NewProduct> = async ([data]) => { | ||
const request = craftRequest('/admin/api/services.json') | ||
return postData(request, data) | ||
} | ||
|
||
export { createProduct } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './createProduct' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export interface IProduct { | ||
id: number | ||
name: string | ||
} |