From f98d0de927102cc6fb31f6f0d23582114a143b5f Mon Sep 17 00:00:00 2001 From: Kiril Kartunov Date: Fri, 16 Sep 2022 11:12:04 +0300 Subject: [PATCH 1/9] init badge creation --- .../create-badge/CreateBadgePage.module.scss | 14 ---- .../pages/create-badge/CreateBadgePage.tsx | 22 +++++-- .../CreateBadgeForm.module.scss | 2 + .../create-badge-form/CreateBadgeForm.tsx | 49 ++++++++++++++ .../create-badge-form.config.tsx | 66 +++++++++++++++++++ .../create-badge-request.model.ts | 5 ++ .../create-badge-store/create-badge.store.ts | 9 +++ .../create-badge-store/index.ts | 2 + .../create-badge.functions.ts | 5 ++ .../create-badge-functions/index.ts | 2 + .../create-badge/create-badge-form/index.ts | 2 + 11 files changed, 158 insertions(+), 20 deletions(-) create mode 100644 src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/CreateBadgeForm.module.scss create mode 100644 src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/CreateBadgeForm.tsx create mode 100644 src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-form.config.tsx create mode 100644 src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-functions/create-badge-store/create-badge-request.model.ts create mode 100644 src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-functions/create-badge-store/create-badge.store.ts create mode 100644 src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-functions/create-badge-store/index.ts create mode 100644 src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-functions/create-badge.functions.ts create mode 100644 src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-functions/index.ts create mode 100644 src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/index.ts diff --git a/src-ts/tools/gamification-admin/pages/create-badge/CreateBadgePage.module.scss b/src-ts/tools/gamification-admin/pages/create-badge/CreateBadgePage.module.scss index 7da13b030..abde53a3a 100644 --- a/src-ts/tools/gamification-admin/pages/create-badge/CreateBadgePage.module.scss +++ b/src-ts/tools/gamification-admin/pages/create-badge/CreateBadgePage.module.scss @@ -1,17 +1,3 @@ -.contentLayout { - width: 100%; - padding-bottom: 0; - - .contentLayout-outer { - width: 100%; - - .contentLayout-inner { - width: 100%; - overflow: visible; - } - } -} - .container { display: flex; } diff --git a/src-ts/tools/gamification-admin/pages/create-badge/CreateBadgePage.tsx b/src-ts/tools/gamification-admin/pages/create-badge/CreateBadgePage.tsx index 020b7f8eb..8756dc77f 100644 --- a/src-ts/tools/gamification-admin/pages/create-badge/CreateBadgePage.tsx +++ b/src-ts/tools/gamification-admin/pages/create-badge/CreateBadgePage.tsx @@ -1,8 +1,9 @@ -import { FC, useMemo } from 'react' +import { Dispatch, FC, SetStateAction, useMemo, useState } from 'react' -import { Breadcrumb, BreadcrumbItemModel, ContentLayout } from '../../../../lib' +import { Breadcrumb, BreadcrumbItemModel, ContentLayout, FormDefinition, formOnReset, formGetInputFields } from '../../../../lib' import { baseUrl } from '../../gamification-admin.routes' import { toolTitle } from '../../GamificationAdmin' +import { CreateBadgeForm, createBadgeFormDef } from './create-badge-form' import styles from './CreateBadgePage.module.scss' @@ -12,16 +13,25 @@ const CreateBadgePage: FC = () => { { name: 'create badge', url: '#' }, ], []) + const [formDef, setFormDef]: [FormDefinition, Dispatch>] + = useState({ ...createBadgeFormDef }) + + function onSave(): void { + const updatedForm: FormDefinition = { ...formDef } + formOnReset(formGetInputFields(updatedForm.groups || [])) + setFormDef(updatedForm) + } + return (
- +
) diff --git a/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/CreateBadgeForm.module.scss b/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/CreateBadgeForm.module.scss new file mode 100644 index 000000000..ca132e9b9 --- /dev/null +++ b/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/CreateBadgeForm.module.scss @@ -0,0 +1,2 @@ +@import '../../../../../lib/styles/includes'; + diff --git a/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/CreateBadgeForm.tsx b/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/CreateBadgeForm.tsx new file mode 100644 index 000000000..5baf46a94 --- /dev/null +++ b/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/CreateBadgeForm.tsx @@ -0,0 +1,49 @@ +import { FC, useContext } from 'react' + +import { Form, FormDefinition, formGetInputModel, FormInputModel, profileContext, ProfileContextData } from '../../../../../lib' + +import { CreateBadgeFormField } from './create-badge-form.config' +import { CreateBadgeRequest } from './create-badge-functions' +import { createBadgeSubmitRequestAsync } from './create-badge-functions/create-badge-store' +import styles from './CreateBadgeForm.module.scss' + +export interface CreateBadgeFormProps { + formDef: FormDefinition + onSave: () => void +} + +const CreateBadgeForm: FC = (props: CreateBadgeFormProps) => { + + const { profile }: ProfileContextData = useContext(profileContext) + + function generateRequest(inputs: ReadonlyArray): CreateBadgeRequest { + const badgeName: string = formGetInputModel(inputs, CreateBadgeFormField.badgeName).value as string + const badgeDesc: string = formGetInputModel(inputs, CreateBadgeFormField.badgeDesc).value as string + const badgeActive: string = formGetInputModel(inputs, CreateBadgeFormField.badgeActive).value as string + return { + badgeActive, + badgeName, + badgeDesc, + } + } + + async function saveAsync(request: CreateBadgeRequest): Promise { + return createBadgeSubmitRequestAsync(request) + .then(() => { + props.onSave() + }) + } + + return ( + <> +
+ + ) +} + +export default CreateBadgeForm diff --git a/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-form.config.tsx b/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-form.config.tsx new file mode 100644 index 000000000..df79932da --- /dev/null +++ b/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-form.config.tsx @@ -0,0 +1,66 @@ +import { FormDefinition, validatorRequired, IconOutline, RadioButton } from '../../../../../lib' + +export enum CreateBadgeFormField { + badgeActive = 'badgeActive', + badgeName = 'badgeName', + badgeDesc = 'badgeDesc', +} + +export const createBadgeFormDef: FormDefinition = { + buttons: { + primaryGroup: [ + { + buttonStyle: 'primary', + isSubmit: true, + label: 'Save Badge', + size: 'lg', + type: 'submit', + }, + ], + secondaryGroup: [ + { + buttonStyle: 'icon-bordered', + size: 'lg', + icon: IconOutline.ChevronLeftIcon, + route: `/gamification-admin` + + }, + ] + }, + groups: [ + { + inputs: [ + { + label: 'Badge Name', + name: CreateBadgeFormField.badgeName, + type: 'text', + validators: [ + { + validator: validatorRequired, + }, + ], + }, + { + label: 'Badge Description', + name: CreateBadgeFormField.badgeDesc, + type: 'textarea', + validators: [ + { + validator: validatorRequired, + }, + ], + }, + { + label: 'Activate', + name: CreateBadgeFormField.badgeActive, + type: 'checkbox', + validators: [ + { + validator: validatorRequired, + }, + ], + } + ], + }, + ], +} diff --git a/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-functions/create-badge-store/create-badge-request.model.ts b/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-functions/create-badge-store/create-badge-request.model.ts new file mode 100644 index 000000000..40e648857 --- /dev/null +++ b/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-functions/create-badge-store/create-badge-request.model.ts @@ -0,0 +1,5 @@ +export interface CreateBadgeRequest { + badgeActive: string + badgeName: string + badgeDesc: string +} diff --git a/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-functions/create-badge-store/create-badge.store.ts b/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-functions/create-badge-store/create-badge.store.ts new file mode 100644 index 000000000..0dea4b3ff --- /dev/null +++ b/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-functions/create-badge-store/create-badge.store.ts @@ -0,0 +1,9 @@ +import { EnvironmentConfig } from '../../../../../../../config' +import { xhrPostAsync } from '../../../../../../../lib' + +import { CreateBadgeRequest } from './create-badge-request.model' + +export async function submitRequestAsync(request: CreateBadgeRequest): Promise { + const url: string = `${EnvironmentConfig.API.V5}/gamification/badges` + await xhrPostAsync(url, request) +} diff --git a/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-functions/create-badge-store/index.ts b/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-functions/create-badge-store/index.ts new file mode 100644 index 000000000..5f0511634 --- /dev/null +++ b/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-functions/create-badge-store/index.ts @@ -0,0 +1,2 @@ +export * from './create-badge-request.model' +export { submitRequestAsync as createBadgeSubmitRequestAsync } from './create-badge.store' diff --git a/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-functions/create-badge.functions.ts b/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-functions/create-badge.functions.ts new file mode 100644 index 000000000..ddce9a486 --- /dev/null +++ b/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-functions/create-badge.functions.ts @@ -0,0 +1,5 @@ +import { CreateBadgeRequest, createBadgeSubmitRequestAsync } from './create-badge-store' + +export async function submitRequestAsync(request: CreateBadgeRequest): Promise { + return createBadgeSubmitRequestAsync(request) +} diff --git a/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-functions/index.ts b/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-functions/index.ts new file mode 100644 index 000000000..82368d47a --- /dev/null +++ b/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-functions/index.ts @@ -0,0 +1,2 @@ +export { type CreateBadgeRequest } from './create-badge-store' +export { submitRequestAsync as contactSupportSubmitRequestAsync } from './create-badge.functions' diff --git a/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/index.ts b/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/index.ts new file mode 100644 index 000000000..a323b7903 --- /dev/null +++ b/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/index.ts @@ -0,0 +1,2 @@ +export { default as CreateBadgeForm } from './CreateBadgeForm' +export { createBadgeFormDef } from './create-badge-form.config' From fd20e71f62acd4233940d8e66b447603e35b4b30 Mon Sep 17 00:00:00 2001 From: Kiril Kartunov Date: Tue, 20 Sep 2022 12:44:01 +0300 Subject: [PATCH 2/9] init create badge form --- .../badge-created-modal/BadgeCreatedModal.tsx | 28 +++++++++++++++++++ .../modals/badge-created-modal/index.ts | 1 + .../pages/create-badge/CreateBadgePage.tsx | 23 +++++++++++---- .../create-badge-form/CreateBadgeForm.tsx | 19 ++++++++----- .../create-badge-form.config.tsx | 22 ++++++--------- .../create-badge-store/create-badge.store.ts | 1 + 6 files changed, 69 insertions(+), 25 deletions(-) create mode 100644 src-ts/tools/gamification-admin/game-lib/modals/badge-created-modal/BadgeCreatedModal.tsx create mode 100644 src-ts/tools/gamification-admin/game-lib/modals/badge-created-modal/index.ts diff --git a/src-ts/tools/gamification-admin/game-lib/modals/badge-created-modal/BadgeCreatedModal.tsx b/src-ts/tools/gamification-admin/game-lib/modals/badge-created-modal/BadgeCreatedModal.tsx new file mode 100644 index 000000000..eca42d824 --- /dev/null +++ b/src-ts/tools/gamification-admin/game-lib/modals/badge-created-modal/BadgeCreatedModal.tsx @@ -0,0 +1,28 @@ +import { FC } from 'react' + +import { BaseModal } from '../../../../../lib' + +export interface BadgeCreatedModalProps { + isOpen: boolean + onClose: () => void +} + +const BadgeCreatedModal: FC = (props: BadgeCreatedModalProps) => { + + function onClose(): void { + props.onClose() + } + + return ( + + + + ) +} + +export default BadgeCreatedModal diff --git a/src-ts/tools/gamification-admin/game-lib/modals/badge-created-modal/index.ts b/src-ts/tools/gamification-admin/game-lib/modals/badge-created-modal/index.ts new file mode 100644 index 000000000..2e8ce2cda --- /dev/null +++ b/src-ts/tools/gamification-admin/game-lib/modals/badge-created-modal/index.ts @@ -0,0 +1 @@ +export { default as BadgeCreatedModal } from './BadgeCreatedModal' diff --git a/src-ts/tools/gamification-admin/pages/create-badge/CreateBadgePage.tsx b/src-ts/tools/gamification-admin/pages/create-badge/CreateBadgePage.tsx index 22d7e59d6..b6795c2a6 100644 --- a/src-ts/tools/gamification-admin/pages/create-badge/CreateBadgePage.tsx +++ b/src-ts/tools/gamification-admin/pages/create-badge/CreateBadgePage.tsx @@ -1,7 +1,9 @@ -import { FC } from 'react' +import { Dispatch, FC, SetStateAction, useState } from 'react' import { Breadcrumb, BreadcrumbItemModel, ContentLayout } from '../../../../lib' import { useGamificationBreadcrumb } from '../../game-lib' +import { CreateBadgeForm, createBadgeFormDef } from './create-badge-form' +import { BadgeCreatedModal } from '../../game-lib/modals/badge-created-modal' import styles from './CreateBadgePage.module.scss' @@ -14,17 +16,28 @@ const CreateBadgePage: FC = () => { }, ]) + const [showBadgeCreatedModal, setShowBadgeCreatedModal]: [boolean, Dispatch>] + = useState(false) + + function onSave() { + setShowBadgeCreatedModal(true) + } + return (
- +
+ setShowBadgeCreatedModal(false)} + />
) } diff --git a/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/CreateBadgeForm.tsx b/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/CreateBadgeForm.tsx index 5baf46a94..de83013c5 100644 --- a/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/CreateBadgeForm.tsx +++ b/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/CreateBadgeForm.tsx @@ -1,10 +1,12 @@ -import { FC, useContext } from 'react' +import { Dispatch, FC, SetStateAction, useState } from 'react' -import { Form, FormDefinition, formGetInputModel, FormInputModel, profileContext, ProfileContextData } from '../../../../../lib' +import { Form, FormDefinition, formGetInputModel, FormInputModel } from '../../../../../lib' import { CreateBadgeFormField } from './create-badge-form.config' import { CreateBadgeRequest } from './create-badge-functions' import { createBadgeSubmitRequestAsync } from './create-badge-functions/create-badge-store' +import { createBadgeFormDef } from './create-badge-form.config' + import styles from './CreateBadgeForm.module.scss' export interface CreateBadgeFormProps { @@ -13,21 +15,25 @@ export interface CreateBadgeFormProps { } const CreateBadgeForm: FC = (props: CreateBadgeFormProps) => { + + // createBadgeFormDef.buttons.primaryGroup[0].onClick = (e) => { console.log('save btn click', e); e.preventDefault() } - const { profile }: ProfileContextData = useContext(profileContext) function generateRequest(inputs: ReadonlyArray): CreateBadgeRequest { const badgeName: string = formGetInputModel(inputs, CreateBadgeFormField.badgeName).value as string const badgeDesc: string = formGetInputModel(inputs, CreateBadgeFormField.badgeDesc).value as string const badgeActive: string = formGetInputModel(inputs, CreateBadgeFormField.badgeActive).value as string + return { - badgeActive, - badgeName, - badgeDesc, + badgeActive, + badgeName, + badgeDesc, } } async function saveAsync(request: CreateBadgeRequest): Promise { + console.log('saveAsync', request) + return createBadgeSubmitRequestAsync(request) .then(() => { props.onSave() @@ -38,7 +44,6 @@ const CreateBadgeForm: FC = (props: CreateBadgeFormProps) <> diff --git a/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-form.config.tsx b/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-form.config.tsx index df79932da..85dc54eb6 100644 --- a/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-form.config.tsx +++ b/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-form.config.tsx @@ -1,4 +1,4 @@ -import { FormDefinition, validatorRequired, IconOutline, RadioButton } from '../../../../../lib' +import { FormDefinition, validatorRequired, IconOutline } from '../../../../../lib' export enum CreateBadgeFormField { badgeActive = 'badgeActive', @@ -13,6 +13,7 @@ export const createBadgeFormDef: FormDefinition = { buttonStyle: 'primary', isSubmit: true, label: 'Save Badge', + onClick: (e) => { }, size: 'lg', type: 'submit', }, @@ -22,14 +23,19 @@ export const createBadgeFormDef: FormDefinition = { buttonStyle: 'icon-bordered', size: 'lg', icon: IconOutline.ChevronLeftIcon, - route: `/gamification-admin` - + route: '/gamification-admin' + }, ] }, groups: [ { inputs: [ + // { + // label: 'Activate', + // name: CreateBadgeFormField.badgeActive, + // type: 'checkbox', + // }, { label: 'Badge Name', name: CreateBadgeFormField.badgeName, @@ -49,16 +55,6 @@ export const createBadgeFormDef: FormDefinition = { validator: validatorRequired, }, ], - }, - { - label: 'Activate', - name: CreateBadgeFormField.badgeActive, - type: 'checkbox', - validators: [ - { - validator: validatorRequired, - }, - ], } ], }, diff --git a/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-functions/create-badge-store/create-badge.store.ts b/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-functions/create-badge-store/create-badge.store.ts index 0dea4b3ff..0a83957da 100644 --- a/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-functions/create-badge-store/create-badge.store.ts +++ b/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-functions/create-badge-store/create-badge.store.ts @@ -4,6 +4,7 @@ import { xhrPostAsync } from '../../../../../../../lib' import { CreateBadgeRequest } from './create-badge-request.model' export async function submitRequestAsync(request: CreateBadgeRequest): Promise { + console.log('submitRequestAsync', request) const url: string = `${EnvironmentConfig.API.V5}/gamification/badges` await xhrPostAsync(url, request) } From 03cd149d8e006314cfc5bab1e7ab4e463e490701 Mon Sep 17 00:00:00 2001 From: Kiril Kartunov Date: Tue, 20 Sep 2022 18:46:15 +0300 Subject: [PATCH 3/9] dedicated checkbox input init --- .../ContactSupportForm.tsx | 3 ++ .../contact-support-form.config.ts | 6 +++ src-ts/lib/form/form-groups/FormGroups.tsx | 12 ++++- .../lib/form/form-groups/form-input/index.ts | 1 + .../input-checkbox/InputCheckbox.module.scss | 32 +++++++++++ .../input-checkbox/InputCheckbox.tsx | 53 +++++++++++++++++++ .../form-input/input-checkbox/index.ts | 1 + src-ts/lib/form/form-input.model.ts | 1 + 8 files changed, 107 insertions(+), 2 deletions(-) create mode 100644 src-ts/lib/form/form-groups/form-input/input-checkbox/InputCheckbox.module.scss create mode 100644 src-ts/lib/form/form-groups/form-input/input-checkbox/InputCheckbox.tsx create mode 100644 src-ts/lib/form/form-groups/form-input/input-checkbox/index.ts diff --git a/src-ts/lib/contact-support-form/ContactSupportForm.tsx b/src-ts/lib/contact-support-form/ContactSupportForm.tsx index 0580bf918..2f6383ce2 100644 --- a/src-ts/lib/contact-support-form/ContactSupportForm.tsx +++ b/src-ts/lib/contact-support-form/ContactSupportForm.tsx @@ -19,6 +19,9 @@ const ContactSupportForm: FC = (props: ContactSupportFo const { profile }: ProfileContextData = useContext(profileContext) function generateRequest(inputs: ReadonlyArray): ContactSupportRequest { + const activeEl: any = formGetInputModel(inputs, 'active') + console.log('generateRequest', activeEl) + const firstName: string = formGetInputModel(inputs, ContactSupportFormField.first).value as string const lastName: string = formGetInputModel(inputs, ContactSupportFormField.last).value as string const email: string = formGetInputModel(inputs, ContactSupportFormField.email).value as string diff --git a/src-ts/lib/contact-support-form/contact-support-form.config.ts b/src-ts/lib/contact-support-form/contact-support-form.config.ts index c41d6dff9..6d76f4ccf 100644 --- a/src-ts/lib/contact-support-form/contact-support-form.config.ts +++ b/src-ts/lib/contact-support-form/contact-support-form.config.ts @@ -22,6 +22,12 @@ export const contactSupportFormDef: FormDefinition = { groups: [ { inputs: [ + { + checked: true, + label: 'Activate', + name: 'active', + type: 'checkbox', + }, { label: 'First Name', name: ContactSupportFormField.first, diff --git a/src-ts/lib/form/form-groups/FormGroups.tsx b/src-ts/lib/form/form-groups/FormGroups.tsx index 2562d0146..e3ef42a0f 100644 --- a/src-ts/lib/form/form-groups/FormGroups.tsx +++ b/src-ts/lib/form/form-groups/FormGroups.tsx @@ -6,7 +6,7 @@ import { FormInputModel } from '../form-input.model' import { FormCardSet } from './form-card-set' import FormGroupItem from './form-group-item/FormGroupItem' -import { InputRating, InputText, InputTextarea } from './form-input' +import { InputCheckbox, InputRating, InputText, InputTextarea } from './form-input' import { FormInputRow } from './form-input-row' import { InputTextTypes } from './form-input/input-text/InputText' import FormRadio from './form-radio' @@ -44,7 +44,6 @@ const FormGroups: (props: FormGroupsProps) => JSX.Element = (props: FormGroupsPr /> ) break - case 'textarea': inputElement = ( JSX.Element = (props: FormGroupsPr ) break case 'checkbox': + inputElement = ( + + ) + break case 'radio': inputElement = ( ) => void + readonly tabIndex: number + readonly type: InputCheckboxTypes +} + +const InputCheckbox: FC = (props: InputCheckboxProps) => { + + const [checked, setChecked]: [boolean, Dispatch>] = useState(props.checked || false) + + return ( + + { + setChecked(!checked) + props.onChange(event) + }} + name={props.name} + tabIndex={props.tabIndex} + type={'checkbox'} + checked={checked} + /> + + ) +} + +export default InputCheckbox diff --git a/src-ts/lib/form/form-groups/form-input/input-checkbox/index.ts b/src-ts/lib/form/form-groups/form-input/input-checkbox/index.ts new file mode 100644 index 000000000..fcf5b192b --- /dev/null +++ b/src-ts/lib/form/form-groups/form-input/input-checkbox/index.ts @@ -0,0 +1 @@ +export { default as InputCheckbox } from './InputCheckbox' diff --git a/src-ts/lib/form/form-input.model.ts b/src-ts/lib/form/form-input.model.ts index a27489902..29503e2aa 100644 --- a/src-ts/lib/form/form-input.model.ts +++ b/src-ts/lib/form/form-input.model.ts @@ -20,6 +20,7 @@ export interface FormCard { export interface FormInputModel { readonly autocomplete?: FormInputAutocompleteOption readonly cards?: ReadonlyArray + checked?: boolean readonly className?: string readonly dependentFields?: Array dirty?: boolean From 460cd292ec75947eaa3b0efdc9bfc3de3a6e6e7e Mon Sep 17 00:00:00 2001 From: Brooke Date: Tue, 20 Sep 2022 10:30:11 -0700 Subject: [PATCH 4/9] GAME-108 #comment This commit adapts the InputText component to support checkboxes #time 1h --- .../lib/form/form-functions/form.functions.ts | 8 ++- src-ts/lib/form/form-groups/FormGroups.tsx | 34 ++++++++---- .../lib/form/form-groups/form-input/index.ts | 1 - .../input-checkbox/InputCheckbox.module.scss | 32 ----------- .../input-checkbox/InputCheckbox.tsx | 53 ------------------- .../form-input/input-checkbox/index.ts | 1 - .../input-text/InputText.module.scss | 29 ++++++++-- .../form-input/input-text/InputText.tsx | 9 +++- src-ts/lib/form/form-input.model.ts | 2 +- 9 files changed, 64 insertions(+), 105 deletions(-) delete mode 100644 src-ts/lib/form/form-groups/form-input/input-checkbox/InputCheckbox.module.scss delete mode 100644 src-ts/lib/form/form-groups/form-input/input-checkbox/InputCheckbox.tsx delete mode 100644 src-ts/lib/form/form-groups/form-input/input-checkbox/index.ts diff --git a/src-ts/lib/form/form-functions/form.functions.ts b/src-ts/lib/form/form-functions/form.functions.ts index 5f5e72246..f6ca85586 100644 --- a/src-ts/lib/form/form-functions/form.functions.ts +++ b/src-ts/lib/form/form-functions/form.functions.ts @@ -121,7 +121,13 @@ function handleFieldEvent(input: HTMLInputElement | HTMLTextAreaElement, inpu inputDef.touched = true // set the def value - inputDef.value = input.value + if (input.type === 'checkbox') { + const checkbox: HTMLInputElement = input as HTMLInputElement + inputDef.value = checkbox.checked + inputDef.checked = checkbox.checked + } else { + inputDef.value = input.value + } // now let's validate the field const formElements: HTMLFormControlsCollection = (input.form as HTMLFormElement).elements diff --git a/src-ts/lib/form/form-groups/FormGroups.tsx b/src-ts/lib/form/form-groups/FormGroups.tsx index e3ef42a0f..141bcb4f5 100644 --- a/src-ts/lib/form/form-groups/FormGroups.tsx +++ b/src-ts/lib/form/form-groups/FormGroups.tsx @@ -6,7 +6,7 @@ import { FormInputModel } from '../form-input.model' import { FormCardSet } from './form-card-set' import FormGroupItem from './form-group-item/FormGroupItem' -import { InputCheckbox, InputRating, InputText, InputTextarea } from './form-input' +import { InputRating, InputText, InputTextarea } from './form-input' import { FormInputRow } from './form-input-row' import { InputTextTypes } from './form-input/input-text/InputText' import FormRadio from './form-radio' @@ -23,12 +23,13 @@ const FormGroups: (props: FormGroupsProps) => JSX.Element = (props: FormGroupsPr const { formDef, onBlur, onChange }: FormGroupsProps = props - const getTabIndex: (input: FormInputModel, index: number) => number = (input, index) => { + function getTabIndex(input: FormInputModel, index: number): number { const tabIndex: number = input.notTabbable ? -1 : index + 1 + (formDef.tabIndexStart || 0) return tabIndex } - const renderInputField: (input: FormInputModel, index: number) => JSX.Element | undefined = (input, index) => { + function renderInputField(input: FormInputModel, index: number): JSX.Element | undefined { + const tabIndex: number = getTabIndex(input, index) let inputElement: JSX.Element @@ -40,7 +41,7 @@ const FormGroups: (props: FormGroupsProps) => JSX.Element = (props: FormGroupsPr {...input} onChange={onChange} tabIndex={tabIndex} - value={input.value} + value={input.value as number | undefined} /> ) break @@ -51,17 +52,19 @@ const FormGroups: (props: FormGroupsProps) => JSX.Element = (props: FormGroupsPr onBlur={onBlur} onChange={onChange} tabIndex={tabIndex} - value={input.value} + value={input.value as string | undefined} /> ) break case 'checkbox': inputElement = ( - ) break @@ -91,7 +94,7 @@ const FormGroups: (props: FormGroupsProps) => JSX.Element = (props: FormGroupsPr onChange={onChange} tabIndex={tabIndex} type={input.type as InputTextTypes || 'text'} - value={input.value} + value={input.value as string | undefined} /> ) break @@ -108,9 +111,18 @@ const FormGroups: (props: FormGroupsProps) => JSX.Element = (props: FormGroupsPr ) } - const formGroups: Array = formDef?.groups?.map((element: FormGroup, index: number) => { - return - }) || [] + const formGroups: Array = formDef?.groups + ?.map((element: FormGroup, index: number) => { + return ( + + ) + }) + || [] return (
diff --git a/src-ts/lib/form/form-groups/form-input/index.ts b/src-ts/lib/form/form-groups/form-input/index.ts index 17c210364..3f5dfa642 100644 --- a/src-ts/lib/form/form-groups/form-input/index.ts +++ b/src-ts/lib/form/form-groups/form-input/index.ts @@ -1,4 +1,3 @@ -export * from './input-checkbox' export * from './form-input-autcomplete-option.enum' export * from './input-rating' export * from './input-text' diff --git a/src-ts/lib/form/form-groups/form-input/input-checkbox/InputCheckbox.module.scss b/src-ts/lib/form/form-groups/form-input/input-checkbox/InputCheckbox.module.scss deleted file mode 100644 index 001cb128b..000000000 --- a/src-ts/lib/form/form-groups/form-input/input-checkbox/InputCheckbox.module.scss +++ /dev/null @@ -1,32 +0,0 @@ -@use '../../../../styles/typography'; -@import '../../../../styles/includes'; - -.form-input-checkbox { - @extend .body-small; - color: $black-60; - box-sizing: border-box; - border: 0; - width: 100%; - padding: 0; - margin: 0; - height: auto; - border-radius: 0; - - &:focus { - box-shadow: none; - border: none; - outline: none; - color: $black-100; - } - - &:disabled { - background-color: $black-10; - } - - &.checkbox { - & { - width: 20px; - height: 20px; - } - } -} diff --git a/src-ts/lib/form/form-groups/form-input/input-checkbox/InputCheckbox.tsx b/src-ts/lib/form/form-groups/form-input/input-checkbox/InputCheckbox.tsx deleted file mode 100644 index a296a0d07..000000000 --- a/src-ts/lib/form/form-groups/form-input/input-checkbox/InputCheckbox.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import cn from 'classnames' -import { ChangeEvent, Dispatch, FC, FocusEvent, SetStateAction, useState } from 'react' - -import { InputWrapper } from '../input-wrapper' - -import styles from './InputCheckbox.module.scss' - -export type InputCheckboxTypes = 'checkbox' | 'password' | 'text' - -interface InputCheckboxProps { - readonly checked?: boolean - readonly className?: string - readonly dirty?: boolean - readonly disabled?: boolean - readonly error?: string - readonly hideInlineErrors?: boolean - readonly hint?: string - readonly label?: string | JSX.Element - readonly name: string - readonly onChange: (event: ChangeEvent) => void - readonly tabIndex: number - readonly type: InputCheckboxTypes -} - -const InputCheckbox: FC = (props: InputCheckboxProps) => { - - const [checked, setChecked]: [boolean, Dispatch>] = useState(props.checked || false) - - return ( - - { - setChecked(!checked) - props.onChange(event) - }} - name={props.name} - tabIndex={props.tabIndex} - type={'checkbox'} - checked={checked} - /> - - ) -} - -export default InputCheckbox diff --git a/src-ts/lib/form/form-groups/form-input/input-checkbox/index.ts b/src-ts/lib/form/form-groups/form-input/input-checkbox/index.ts deleted file mode 100644 index fcf5b192b..000000000 --- a/src-ts/lib/form/form-groups/form-input/input-checkbox/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as InputCheckbox } from './InputCheckbox' diff --git a/src-ts/lib/form/form-groups/form-input/input-text/InputText.module.scss b/src-ts/lib/form/form-groups/form-input/input-text/InputText.module.scss index 7b47d447d..ac7f33476 100644 --- a/src-ts/lib/form/form-groups/form-input/input-text/InputText.module.scss +++ b/src-ts/lib/form/form-groups/form-input/input-text/InputText.module.scss @@ -30,9 +30,32 @@ } &.checkbox { - & { - width: 20px; - height: 20px; + @extend .body-small; + color: $black-60; + box-sizing: border-box; + border: 0; + width: 100%; + padding: 0; + margin: 0; + height: auto; + border-radius: 0; + + &:focus { + box-shadow: none; + border: none; + outline: none; + color: $black-100; + } + + &:disabled { + background-color: $black-10; + } + + &.checkbox { + & { + width: 20px; + height: 20px; + } } } } diff --git a/src-ts/lib/form/form-groups/form-input/input-text/InputText.tsx b/src-ts/lib/form/form-groups/form-input/input-text/InputText.tsx index 21e94e307..34bbea969 100644 --- a/src-ts/lib/form/form-groups/form-input/input-text/InputText.tsx +++ b/src-ts/lib/form/form-groups/form-input/input-text/InputText.tsx @@ -10,6 +10,7 @@ export type InputTextTypes = 'checkbox' | 'password' | 'text' interface InputTextProps { readonly autocomplete?: FormInputAutocompleteOption + readonly checked?: boolean readonly className?: string readonly dirty?: boolean readonly disabled?: boolean @@ -24,11 +25,15 @@ interface InputTextProps { readonly spellCheck?: boolean readonly tabIndex: number readonly type: InputTextTypes - readonly value?: string | number + readonly value?: string | number | boolean } const InputText: FC = (props: InputTextProps) => { + const defaultValue: string | number | undefined = props.type === 'checkbox' && !!props.checked + ? 'on' + : props.value as string | number | undefined + return ( = (props: InputTextProps) => { - value?: string + value?: string | boolean } From e8b5069174820e7c0b8fff75563a8ceceefc7cd1 Mon Sep 17 00:00:00 2001 From: Kiril Kartunov Date: Wed, 21 Sep 2022 10:06:47 +0300 Subject: [PATCH 5/9] fix checkbox init and clean up --- src-ts/lib/contact-support-form/ContactSupportForm.tsx | 3 --- .../contact-support-form.config.ts | 6 ------ src-ts/lib/form/form-functions/form.functions.ts | 10 +++++++--- .../form-groups/form-input/input-text/InputText.tsx | 1 + 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src-ts/lib/contact-support-form/ContactSupportForm.tsx b/src-ts/lib/contact-support-form/ContactSupportForm.tsx index 2f6383ce2..0580bf918 100644 --- a/src-ts/lib/contact-support-form/ContactSupportForm.tsx +++ b/src-ts/lib/contact-support-form/ContactSupportForm.tsx @@ -19,9 +19,6 @@ const ContactSupportForm: FC = (props: ContactSupportFo const { profile }: ProfileContextData = useContext(profileContext) function generateRequest(inputs: ReadonlyArray): ContactSupportRequest { - const activeEl: any = formGetInputModel(inputs, 'active') - console.log('generateRequest', activeEl) - const firstName: string = formGetInputModel(inputs, ContactSupportFormField.first).value as string const lastName: string = formGetInputModel(inputs, ContactSupportFormField.last).value as string const email: string = formGetInputModel(inputs, ContactSupportFormField.email).value as string diff --git a/src-ts/lib/contact-support-form/contact-support-form.config.ts b/src-ts/lib/contact-support-form/contact-support-form.config.ts index 6d76f4ccf..c41d6dff9 100644 --- a/src-ts/lib/contact-support-form/contact-support-form.config.ts +++ b/src-ts/lib/contact-support-form/contact-support-form.config.ts @@ -22,12 +22,6 @@ export const contactSupportFormDef: FormDefinition = { groups: [ { inputs: [ - { - checked: true, - label: 'Activate', - name: 'active', - type: 'checkbox', - }, { label: 'First Name', name: ContactSupportFormField.first, diff --git a/src-ts/lib/form/form-functions/form.functions.ts b/src-ts/lib/form/form-functions/form.functions.ts index f6ca85586..b2ec0c417 100644 --- a/src-ts/lib/form/form-functions/form.functions.ts +++ b/src-ts/lib/form/form-functions/form.functions.ts @@ -33,9 +33,13 @@ export function initializeValues(inputs: Array, formValues?: inputs .filter(input => !input.dirty && !input.touched) .forEach(input => { - input.value = !!(formValues as any)?.hasOwnProperty(input.name) - ? (formValues as any)[input.name] - : undefined + if (input.type === 'checkbox') { + input.value = input.checked || false + } else { + input.value = !!(formValues as any)?.hasOwnProperty(input.name) + ? (formValues as any)[input.name] + : undefined + } }) } diff --git a/src-ts/lib/form/form-groups/form-input/input-text/InputText.tsx b/src-ts/lib/form/form-groups/form-input/input-text/InputText.tsx index 34bbea969..de7cd32e6 100644 --- a/src-ts/lib/form/form-groups/form-input/input-text/InputText.tsx +++ b/src-ts/lib/form/form-groups/form-input/input-text/InputText.tsx @@ -44,6 +44,7 @@ const InputText: FC = (props: InputTextProps) => { > Date: Wed, 21 Sep 2022 17:15:11 +0300 Subject: [PATCH 6/9] image-picker input type --- src-ts/lib/form/form-groups/FormGroups.tsx | 9 ++- .../lib/form/form-groups/form-input/index.ts | 1 + .../InputImagePicker.module.scss | 39 +++++++++++ .../input-image-picker/InputImagePicker.tsx | 65 +++++++++++++++++++ .../form-input/input-image-picker/index.ts | 1 + .../form-input/input-text/InputText.tsx | 2 +- src-ts/lib/form/form-input.model.ts | 5 +- .../game-config/gamification-config.model.ts | 2 + .../gamification.default.config.ts | 2 + .../create-badge/CreateBadgePage.module.scss | 3 + .../pages/create-badge/CreateBadgePage.tsx | 2 +- .../CreateBadgeForm.module.scss | 2 - .../create-badge-form/CreateBadgeForm.tsx | 37 ++++++----- .../create-badge-form.config.tsx | 35 ++++++---- .../create-badge-request.model.ts | 5 +- 15 files changed, 173 insertions(+), 37 deletions(-) create mode 100644 src-ts/lib/form/form-groups/form-input/input-image-picker/InputImagePicker.module.scss create mode 100644 src-ts/lib/form/form-groups/form-input/input-image-picker/InputImagePicker.tsx create mode 100644 src-ts/lib/form/form-groups/form-input/input-image-picker/index.ts delete mode 100644 src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/CreateBadgeForm.module.scss diff --git a/src-ts/lib/form/form-groups/FormGroups.tsx b/src-ts/lib/form/form-groups/FormGroups.tsx index 141bcb4f5..1a25f9a3e 100644 --- a/src-ts/lib/form/form-groups/FormGroups.tsx +++ b/src-ts/lib/form/form-groups/FormGroups.tsx @@ -6,7 +6,7 @@ import { FormInputModel } from '../form-input.model' import { FormCardSet } from './form-card-set' import FormGroupItem from './form-group-item/FormGroupItem' -import { InputRating, InputText, InputTextarea } from './form-input' +import { InputImagePicker, InputRating, InputText, InputTextarea } from './form-input' import { FormInputRow } from './form-input-row' import { InputTextTypes } from './form-input/input-text/InputText' import FormRadio from './form-radio' @@ -86,6 +86,13 @@ const FormGroups: (props: FormGroupsProps) => JSX.Element = (props: FormGroupsPr /> ) break + case 'image-picker': + inputElement = ( + + ) + break default: inputElement = ( = (props: InputImagePickerProps) => { + + const fileInputRef: RefObject = createRef() + + // tslint:disable-next-line:no-null-keyword + const [files, setFiles]: [FileList | null, Dispatch>] = useState(null) + const [fileDataURL, setFileDataURL]: [string | undefined, Dispatch>] = useState() + + useEffect(() => { + if (files && files.length) { + const fileReader: FileReader = new FileReader() + fileReader.onload = e => { + const { result }: any = e.target + if (result) { + setFileDataURL(result) + } + } + fileReader.readAsDataURL(files[0]) + } else if (fileDataURL) { + setFileDataURL(undefined) + } + }, [ + files, + ]) + + return ( +
+
+ ) +} + +export default InputImagePicker diff --git a/src-ts/lib/form/form-groups/form-input/input-image-picker/index.ts b/src-ts/lib/form/form-groups/form-input/input-image-picker/index.ts new file mode 100644 index 000000000..ceeb9aa9d --- /dev/null +++ b/src-ts/lib/form/form-groups/form-input/input-image-picker/index.ts @@ -0,0 +1 @@ +export { default as InputImagePicker } from './InputImagePicker' diff --git a/src-ts/lib/form/form-groups/form-input/input-text/InputText.tsx b/src-ts/lib/form/form-groups/form-input/input-text/InputText.tsx index de7cd32e6..ecbb51d82 100644 --- a/src-ts/lib/form/form-groups/form-input/input-text/InputText.tsx +++ b/src-ts/lib/form/form-groups/form-input/input-text/InputText.tsx @@ -8,7 +8,7 @@ import styles from './InputText.module.scss' export type InputTextTypes = 'checkbox' | 'password' | 'text' -interface InputTextProps { +export interface InputTextProps { readonly autocomplete?: FormInputAutocompleteOption readonly checked?: boolean readonly className?: string diff --git a/src-ts/lib/form/form-input.model.ts b/src-ts/lib/form/form-input.model.ts index 1cb8277f4..991ef7b04 100644 --- a/src-ts/lib/form/form-input.model.ts +++ b/src-ts/lib/form/form-input.model.ts @@ -18,6 +18,7 @@ export interface FormCard { } export interface FormInputModel { + readonly accept?: string readonly autocomplete?: FormInputAutocompleteOption readonly cards?: ReadonlyArray checked?: boolean @@ -27,6 +28,7 @@ export interface FormInputModel { disabled?: boolean error?: string readonly events?: ReadonlyArray + readonly files?: FileList readonly hideInlineErrors?: boolean readonly hint?: string readonly id?: string @@ -36,10 +38,11 @@ export interface FormInputModel { readonly notTabbable?: boolean options?: ReadonlyArray readonly placeholder?: string + readonly size?: number readonly spellCheck?: boolean readonly title?: string touched?: boolean - readonly type: 'card-set' | 'checkbox' | 'password' | 'radio' | 'rating' | 'text' | 'textarea' + readonly type: 'card-set' | 'checkbox' | 'password' | 'radio' | 'rating' | 'text' | 'textarea' | 'image-picker' readonly validators?: ReadonlyArray value?: string | boolean } diff --git a/src-ts/tools/gamification-admin/game-config/gamification-config.model.ts b/src-ts/tools/gamification-admin/game-config/gamification-config.model.ts index c1b0124fe..c397769c1 100644 --- a/src-ts/tools/gamification-admin/game-config/gamification-config.model.ts +++ b/src-ts/tools/gamification-admin/game-config/gamification-config.model.ts @@ -1,4 +1,6 @@ export interface GamificationConfigModel { + ACCEPTED_BADGE_MIME_TYPES: string + MAX_BADGE_IMAGE_FILE_SIZE: number ORG_ID: string PAGE_SIZE: number } diff --git a/src-ts/tools/gamification-admin/game-config/gamification.default.config.ts b/src-ts/tools/gamification-admin/game-config/gamification.default.config.ts index b2c40e4d8..b35ada502 100644 --- a/src-ts/tools/gamification-admin/game-config/gamification.default.config.ts +++ b/src-ts/tools/gamification-admin/game-config/gamification.default.config.ts @@ -1,6 +1,8 @@ import { GamificationConfigModel } from './gamification-config.model' export const GamificationConfigDefault: GamificationConfigModel = { + ACCEPTED_BADGE_MIME_TYPES: 'image/svg+xml,image/svg', + MAX_BADGE_IMAGE_FILE_SIZE: 5000000, // 5mb in bytes ORG_ID: '6052dd9b-ea80-494b-b258-edd1331e27a3', PAGE_SIZE: 12, } diff --git a/src-ts/tools/gamification-admin/pages/create-badge/CreateBadgePage.module.scss b/src-ts/tools/gamification-admin/pages/create-badge/CreateBadgePage.module.scss index abde53a3a..fac1cbcbc 100644 --- a/src-ts/tools/gamification-admin/pages/create-badge/CreateBadgePage.module.scss +++ b/src-ts/tools/gamification-admin/pages/create-badge/CreateBadgePage.module.scss @@ -1,3 +1,6 @@ +@import "../../../../lib/styles/variables"; + .container { display: flex; + padding-top: $space-xxxxl; } diff --git a/src-ts/tools/gamification-admin/pages/create-badge/CreateBadgePage.tsx b/src-ts/tools/gamification-admin/pages/create-badge/CreateBadgePage.tsx index b6795c2a6..28a8e2f8b 100644 --- a/src-ts/tools/gamification-admin/pages/create-badge/CreateBadgePage.tsx +++ b/src-ts/tools/gamification-admin/pages/create-badge/CreateBadgePage.tsx @@ -2,9 +2,9 @@ import { Dispatch, FC, SetStateAction, useState } from 'react' import { Breadcrumb, BreadcrumbItemModel, ContentLayout } from '../../../../lib' import { useGamificationBreadcrumb } from '../../game-lib' -import { CreateBadgeForm, createBadgeFormDef } from './create-badge-form' import { BadgeCreatedModal } from '../../game-lib/modals/badge-created-modal' +import { CreateBadgeForm, createBadgeFormDef } from './create-badge-form' import styles from './CreateBadgePage.module.scss' const CreateBadgePage: FC = () => { diff --git a/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/CreateBadgeForm.module.scss b/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/CreateBadgeForm.module.scss deleted file mode 100644 index ca132e9b9..000000000 --- a/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/CreateBadgeForm.module.scss +++ /dev/null @@ -1,2 +0,0 @@ -@import '../../../../../lib/styles/includes'; - diff --git a/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/CreateBadgeForm.tsx b/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/CreateBadgeForm.tsx index de83013c5..b881b8b06 100644 --- a/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/CreateBadgeForm.tsx +++ b/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/CreateBadgeForm.tsx @@ -1,13 +1,10 @@ -import { Dispatch, FC, SetStateAction, useState } from 'react' +import { createRef, Dispatch, FC, RefObject, SetStateAction, useEffect, useState } from 'react' -import { Form, FormDefinition, formGetInputModel, FormInputModel } from '../../../../../lib' +import { Button, Form, FormDefinition, formGetInputModel, FormInputModel, IconOutline } from '../../../../../lib' import { CreateBadgeFormField } from './create-badge-form.config' import { CreateBadgeRequest } from './create-badge-functions' import { createBadgeSubmitRequestAsync } from './create-badge-functions/create-badge-store' -import { createBadgeFormDef } from './create-badge-form.config' - -import styles from './CreateBadgeForm.module.scss' export interface CreateBadgeFormProps { formDef: FormDefinition @@ -15,19 +12,25 @@ export interface CreateBadgeFormProps { } const CreateBadgeForm: FC = (props: CreateBadgeFormProps) => { - - // createBadgeFormDef.buttons.primaryGroup[0].onClick = (e) => { console.log('save btn click', e); e.preventDefault() } - function generateRequest(inputs: ReadonlyArray): CreateBadgeRequest { const badgeName: string = formGetInputModel(inputs, CreateBadgeFormField.badgeName).value as string const badgeDesc: string = formGetInputModel(inputs, CreateBadgeFormField.badgeDesc).value as string - const badgeActive: string = formGetInputModel(inputs, CreateBadgeFormField.badgeActive).value as string - + const badgeActive: boolean = formGetInputModel(inputs, CreateBadgeFormField.badgeActive).value as boolean + const files: FileList = formGetInputModel(inputs, CreateBadgeFormField.badgeActive).files as FileList + + console.log('generateRequest', files) + + if (!files) { + // if we don't have image file we have a problem + throw new Error(`There is no image file selected for the badge`) + } + return { badgeActive, - badgeName, badgeDesc, + badgeName, + file: files[0], } } @@ -41,13 +44,11 @@ const CreateBadgeForm: FC = (props: CreateBadgeFormProps) } return ( - <> - - + ) } diff --git a/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-form.config.tsx b/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-form.config.tsx index 85dc54eb6..3883ab4e8 100644 --- a/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-form.config.tsx +++ b/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-form.config.tsx @@ -1,9 +1,11 @@ -import { FormDefinition, validatorRequired, IconOutline } from '../../../../../lib' +import { FormDefinition, IconOutline, validatorRequired } from '../../../../../lib' +import { GamificationConfig } from '../../../game-config' export enum CreateBadgeFormField { badgeActive = 'badgeActive', badgeName = 'badgeName', badgeDesc = 'badgeDesc', + file = 'file', } export const createBadgeFormDef: FormDefinition = { @@ -21,21 +23,32 @@ export const createBadgeFormDef: FormDefinition = { secondaryGroup: [ { buttonStyle: 'icon-bordered', - size: 'lg', icon: IconOutline.ChevronLeftIcon, - route: '/gamification-admin' - + route: '/gamification-admin', + size: 'lg', }, - ] + ], }, groups: [ { inputs: [ - // { - // label: 'Activate', - // name: CreateBadgeFormField.badgeActive, - // type: 'checkbox', - // }, + { + accept: GamificationConfig.ACCEPTED_BADGE_MIME_TYPES, + name: CreateBadgeFormField.file, + size: GamificationConfig.MAX_BADGE_IMAGE_FILE_SIZE, + type: 'image-picker', + // validators: [ + // { + // validator: validatorRequired, + // }, + // ], + }, + { + checked: true, + label: 'Activate Badge', + name: CreateBadgeFormField.badgeActive, + type: 'checkbox', + }, { label: 'Badge Name', name: CreateBadgeFormField.badgeName, @@ -55,7 +68,7 @@ export const createBadgeFormDef: FormDefinition = { validator: validatorRequired, }, ], - } + }, ], }, ], diff --git a/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-functions/create-badge-store/create-badge-request.model.ts b/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-functions/create-badge-store/create-badge-request.model.ts index 40e648857..26bc449f5 100644 --- a/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-functions/create-badge-store/create-badge-request.model.ts +++ b/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-functions/create-badge-store/create-badge-request.model.ts @@ -1,5 +1,6 @@ export interface CreateBadgeRequest { - badgeActive: string - badgeName: string + badgeActive: boolean badgeDesc: string + badgeName: string + file: File } From ce758f8f5cbe027500d1157718f0e119e6a0365f Mon Sep 17 00:00:00 2001 From: Kiril Kartunov Date: Thu, 22 Sep 2022 12:13:10 +0300 Subject: [PATCH 7/9] image picker to use value & groups config API --- src-ts/lib/form/form-definition.model.ts | 3 +- .../lib/form/form-functions/form.functions.ts | 16 +++++++--- src-ts/lib/form/form-group.model.ts | 7 ++++ .../form/form-groups/FormGroups.module.scss | 6 ++++ src-ts/lib/form/form-groups/FormGroups.tsx | 17 ++++++++-- .../form-group-item/FormGroupItem.tsx | 12 ++++--- .../InputImagePicker.module.scss | 1 + .../input-image-picker/InputImagePicker.tsx | 10 ++++-- .../form-input/input-text/InputText.tsx | 3 +- src-ts/lib/form/form-input.model.ts | 4 ++- .../validator.functions.ts | 21 ++++++------ .../create-badge-form/CreateBadgeForm.tsx | 13 ++------ .../create-badge-form.config.tsx | 32 ++++++++++++------- 13 files changed, 97 insertions(+), 48 deletions(-) diff --git a/src-ts/lib/form/form-definition.model.ts b/src-ts/lib/form/form-definition.model.ts index fef7d8c58..efd292f8f 100644 --- a/src-ts/lib/form/form-definition.model.ts +++ b/src-ts/lib/form/form-definition.model.ts @@ -1,4 +1,4 @@ -import { FormButton, FormGroup } from '.' +import { FormButton, FormGroup, FormGroupOptions } from '.' export type FormAction = 'save' | 'submit' | undefined @@ -10,6 +10,7 @@ export interface FormButtons { export interface FormDefinition { readonly buttons: FormButtons readonly groups?: Array + readonly groupsOptions?: FormGroupOptions readonly shortName?: string readonly subtitle?: string readonly successMessage?: string diff --git a/src-ts/lib/form/form-functions/form.functions.ts b/src-ts/lib/form/form-functions/form.functions.ts index b2ec0c417..1c4eaf1a3 100644 --- a/src-ts/lib/form/form-functions/form.functions.ts +++ b/src-ts/lib/form/form-functions/form.functions.ts @@ -68,6 +68,7 @@ export async function onSubmitAsync( formValue: T, save: (value: T) => Promise, onSuccess?: () => void, + customValidateForm?: (formElements: HTMLFormControlsCollection, event: ValidationEvent, inputs: ReadonlyArray) => boolean ): Promise { event.preventDefault() @@ -84,7 +85,7 @@ export async function onSubmitAsync( // want to have it look like the submit succeeded const formValues: HTMLFormControlsCollection = (event.target as HTMLFormElement).elements if (action === 'submit') { - const isValid: boolean = validateForm(formValues, action, inputs) + const isValid: boolean = (customValidateForm || validateForm)(formValues, action, inputs) if (!isValid) { return Promise.reject() } @@ -119,6 +120,8 @@ function handleFieldEvent(input: HTMLInputElement | HTMLTextAreaElement, inpu const inputDef: FormInputModel = getInputModel(inputs, input.name) + const inputEl: HTMLInputElement = input as HTMLInputElement + if (event === 'change') { inputDef.dirty = input.value !== originalValue } @@ -126,9 +129,10 @@ function handleFieldEvent(input: HTMLInputElement | HTMLTextAreaElement, inpu // set the def value if (input.type === 'checkbox') { - const checkbox: HTMLInputElement = input as HTMLInputElement - inputDef.value = checkbox.checked - inputDef.checked = checkbox.checked + inputDef.value = inputEl.checked + inputDef.checked = inputEl.checked + } else if (input.type === 'file') { + inputDef.value = inputEl.files || undefined } else { inputDef.value = input.value } @@ -181,7 +185,9 @@ function validateField(formInputDef: FormInputModel, formElements: HTMLFormContr }) } -export function validateForm(formElements: HTMLFormControlsCollection, event: 'blur' | 'change' | 'submit' | 'initial', inputs: ReadonlyArray): boolean { +export type ValidationEvent = 'blur' | 'change' | 'submit' | 'initial' + +export function validateForm(formElements: HTMLFormControlsCollection, event: ValidationEvent, inputs: ReadonlyArray): boolean { const errors: ReadonlyArray = inputs?.filter(formInputDef => { formInputDef.dirty = formInputDef.dirty || event === 'submit' validateField(formInputDef, formElements, event) diff --git a/src-ts/lib/form/form-group.model.ts b/src-ts/lib/form/form-group.model.ts index 7fe97df9f..7f90a5418 100644 --- a/src-ts/lib/form/form-group.model.ts +++ b/src-ts/lib/form/form-group.model.ts @@ -1,3 +1,5 @@ +import { CSSProperties } from 'react' + import { FormInputModel } from './form-input.model' export interface FormGroup { @@ -6,3 +8,8 @@ export interface FormGroup { readonly instructions?: string readonly title?: string } + +export interface FormGroupOptions { + groupWrapStyles?: CSSProperties + renderGroupDividers?: boolean +} diff --git a/src-ts/lib/form/form-groups/FormGroups.module.scss b/src-ts/lib/form/form-groups/FormGroups.module.scss index 6c3e7a4b0..3bf262416 100644 --- a/src-ts/lib/form/form-groups/FormGroups.module.scss +++ b/src-ts/lib/form/form-groups/FormGroups.module.scss @@ -1,5 +1,11 @@ +@import "../../styles/includes"; + .form-groups { display: grid; grid-template-columns: 1fr; justify-content: center; + + @include ltemd { + grid-template-columns: 1fr !important; + } } diff --git a/src-ts/lib/form/form-groups/FormGroups.tsx b/src-ts/lib/form/form-groups/FormGroups.tsx index 1a25f9a3e..868c24af3 100644 --- a/src-ts/lib/form/form-groups/FormGroups.tsx +++ b/src-ts/lib/form/form-groups/FormGroups.tsx @@ -1,5 +1,6 @@ import { ChangeEvent, FocusEvent } from 'react' +import { PageDivider } from '../../page-divider' import { FormDefinition } from '../form-definition.model' import { FormGroup } from '../form-group.model' import { FormInputModel } from '../form-input.model' @@ -33,6 +34,8 @@ const FormGroups: (props: FormGroupsProps) => JSX.Element = (props: FormGroupsPr const tabIndex: number = getTabIndex(input, index) let inputElement: JSX.Element + + /* tslint:disable:cyclomatic-complexity */ switch (input.type) { case 'rating': @@ -90,6 +93,8 @@ const FormGroups: (props: FormGroupsProps) => JSX.Element = (props: FormGroupsPr inputElement = ( ) break @@ -126,15 +131,21 @@ const FormGroups: (props: FormGroupsProps) => JSX.Element = (props: FormGroupsPr group={element} renderFormInput={renderInputField} totalGroupCount={formDef.groups?.length || 0} + renderDividers={props.formDef.groupsOptions?.renderGroupDividers} /> ) }) || [] return ( -
- {formGroups} -
+ <> +
+ {formGroups} +
+ { + props.formDef.groupsOptions?.renderGroupDividers === false && + } + ) } diff --git a/src-ts/lib/form/form-groups/form-group-item/FormGroupItem.tsx b/src-ts/lib/form/form-groups/form-group-item/FormGroupItem.tsx index 41a568ec9..3b8840156 100644 --- a/src-ts/lib/form/form-groups/form-group-item/FormGroupItem.tsx +++ b/src-ts/lib/form/form-groups/form-group-item/FormGroupItem.tsx @@ -9,6 +9,7 @@ import styles from './FormGroupItem.module.scss' interface FormGroupItemProps { group: FormGroup + renderDividers?: boolean renderFormInput: (input: FormInputModel, index: number) => JSX.Element | undefined totalGroupCount: number } @@ -19,10 +20,11 @@ interface ItemRowProps { hasMultipleGroups: boolean, instructions?: string | undefined, isMultiFieldGroup: boolean, + renderDividers?: boolean title?: string, } -const TwoColumnItem: React.FC = ({ element, formInputs, hasMultipleGroups, instructions, isMultiFieldGroup, title }: ItemRowProps) => { +const TwoColumnItem: React.FC = ({ element, formInputs, hasMultipleGroups, instructions, isMultiFieldGroup, title, renderDividers }: ItemRowProps) => { return ( <>
@@ -41,7 +43,9 @@ const TwoColumnItem: React.FC = ({ element, formInputs, hasMultipl {formInputs}
- + { + renderDividers !== false && + } ) } @@ -67,7 +71,7 @@ const SingleColumnItem: React.FC = ({ formInputs, hasMultipleGroup ) } -const FormGroupItem: React.FC = ({ group, renderFormInput, totalGroupCount }: FormGroupItemProps) => { +const FormGroupItem: React.FC = ({ group, renderDividers, renderFormInput, totalGroupCount }: FormGroupItemProps) => { const { instructions, title, inputs, element }: FormGroup = group const formInputs: Array = inputs?.map((field: FormInputModel, index: number) => renderFormInput(field as FormInputModel, index)) || [] @@ -77,7 +81,7 @@ const FormGroupItem: React.FC = ({ group, renderFormInput, t return isCardSet ? : - + } export default FormGroupItem diff --git a/src-ts/lib/form/form-groups/form-input/input-image-picker/InputImagePicker.module.scss b/src-ts/lib/form/form-groups/form-input/input-image-picker/InputImagePicker.module.scss index 0265dd561..5641206e7 100644 --- a/src-ts/lib/form/form-groups/form-input/input-image-picker/InputImagePicker.module.scss +++ b/src-ts/lib/form/form-groups/form-input/input-image-picker/InputImagePicker.module.scss @@ -10,6 +10,7 @@ width: 132px; height: 132px; position: relative; + margin-bottom: $space-xl; @include ltemd { width: 100%; diff --git a/src-ts/lib/form/form-groups/form-input/input-image-picker/InputImagePicker.tsx b/src-ts/lib/form/form-groups/form-input/input-image-picker/InputImagePicker.tsx index 95987f136..a75eb442d 100644 --- a/src-ts/lib/form/form-groups/form-input/input-image-picker/InputImagePicker.tsx +++ b/src-ts/lib/form/form-groups/form-input/input-image-picker/InputImagePicker.tsx @@ -1,13 +1,16 @@ -import { createRef, Dispatch, FC, RefObject, SetStateAction, useEffect, useState } from 'react' +import { ChangeEvent, createRef, Dispatch, FC, RefObject, SetStateAction, useEffect, useState } from 'react' import { Button, IconOutline } from '../../../../../lib' +import { InputValue } from '../../../form-input.model' import styles from './InputImagePicker.module.scss' interface InputImagePickerProps { readonly accept?: string readonly name: string + readonly onChange: (event: ChangeEvent) => void readonly size?: number + readonly value?: InputValue } const InputImagePicker: FC = (props: InputImagePickerProps) => { @@ -48,7 +51,10 @@ const InputImagePicker: FC = (props: InputImagePickerProp accept={props.accept || '*'} className={styles.filePickerInput} ref={fileInputRef} - onChange={event => setFiles(event.target.files)} + onChange={event => { + setFiles(event.target.files) + props.onChange(event) + }} size={props.size || Infinity} /> { diff --git a/src-ts/lib/form/form-groups/form-input/input-text/InputText.tsx b/src-ts/lib/form/form-groups/form-input/input-text/InputText.tsx index ecbb51d82..a88c96fb5 100644 --- a/src-ts/lib/form/form-groups/form-input/input-text/InputText.tsx +++ b/src-ts/lib/form/form-groups/form-input/input-text/InputText.tsx @@ -1,6 +1,7 @@ import cn from 'classnames' import { FC, FocusEvent } from 'react' +import { InputValue } from '../../../form-input.model' import { FormInputAutocompleteOption } from '../form-input-autcomplete-option.enum' import { InputWrapper } from '../input-wrapper' @@ -25,7 +26,7 @@ export interface InputTextProps { readonly spellCheck?: boolean readonly tabIndex: number readonly type: InputTextTypes - readonly value?: string | number | boolean + readonly value?: InputValue } const InputText: FC = (props: InputTextProps) => { diff --git a/src-ts/lib/form/form-input.model.ts b/src-ts/lib/form/form-input.model.ts index 991ef7b04..d42f4b784 100644 --- a/src-ts/lib/form/form-input.model.ts +++ b/src-ts/lib/form/form-input.model.ts @@ -17,6 +17,8 @@ export interface FormCard { title: string } +export type InputValue = string | boolean | FileList | undefined + export interface FormInputModel { readonly accept?: string readonly autocomplete?: FormInputAutocompleteOption @@ -44,5 +46,5 @@ export interface FormInputModel { touched?: boolean readonly type: 'card-set' | 'checkbox' | 'password' | 'radio' | 'rating' | 'text' | 'textarea' | 'image-picker' readonly validators?: ReadonlyArray - value?: string | boolean + value?: InputValue } diff --git a/src-ts/lib/form/validator-functions/validator.functions.ts b/src-ts/lib/form/validator-functions/validator.functions.ts index af1c9ad7a..9dc9663f9 100644 --- a/src-ts/lib/form/validator-functions/validator.functions.ts +++ b/src-ts/lib/form/validator-functions/validator.functions.ts @@ -1,12 +1,13 @@ import { formGetInput } from '../form-functions' +import { InputValue } from '../form-input.model' -function checkForBooleanValueAndThrowError(value: string | boolean | undefined): void { +function checkForBooleanValueAndThrowError(value: InputValue): void { if (typeof value === 'boolean') { throw new Error(`The value for the email validator cannot be a boolean`) } } -export function doesNotMatchOther(value: string | boolean | undefined, formElements?: HTMLFormControlsCollection, otherFieldName?: string): string | undefined { +export function doesNotMatchOther(value: InputValue, formElements?: HTMLFormControlsCollection, otherFieldName?: string): string | undefined { checkForBooleanValueAndThrowError(value) @@ -27,7 +28,7 @@ export function doesNotMatchOther(value: string | boolean | undefined, formEleme return `Cannot match the ${getOtherFieldLabel(otherField, otherFieldName)} value` } -export function email(value: string | boolean | undefined): string | undefined { +export function email(value: InputValue): string | undefined { checkForBooleanValueAndThrowError(value) @@ -42,10 +43,10 @@ export function email(value: string | boolean | undefined): string | undefined { const emailRegex: RegExp = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ - return !emailRegex.test(value) ? 'Invalid email' : undefined + return !emailRegex.test(value as string) ? 'Invalid email' : undefined } -export function password(value: string | boolean | undefined): string | undefined { +export function password(value: InputValue): string | undefined { checkForBooleanValueAndThrowError(value) @@ -64,10 +65,10 @@ export function password(value: string | boolean | undefined): string | undefine // - at least 1 symbol or number const passwordRegex: RegExp = /^(?=.*[a-zA-Z])(?=.*[#$^+=!*()@%&\d]).{8,}$/g - return !passwordRegex.test(value) ? 'Password rules: 8+ characters, 1+ letter, and 1+ number or symbol' : undefined + return !passwordRegex.test(value as string) ? 'Password rules: 8+ characters, 1+ letter, and 1+ number or symbol' : undefined } -export function matchOther(value: string | boolean | undefined, formElements?: HTMLFormControlsCollection, otherFieldName?: string): string | undefined { +export function matchOther(value: InputValue, formElements?: HTMLFormControlsCollection, otherFieldName?: string): string | undefined { checkForBooleanValueAndThrowError(value) @@ -88,11 +89,11 @@ export function matchOther(value: string | boolean | undefined, formElements?: H return `Does not match the ${getOtherFieldLabel(otherField, otherFieldName)}` } -export function required(value: string | boolean | undefined): string | undefined { +export function required(value: InputValue): string | undefined { return (value === undefined || value === '') ? 'Required' : undefined } -export function requiredIfOther(value: string | boolean | undefined, formElements?: HTMLFormControlsCollection, otherFieldName?: string): string | undefined { +export function requiredIfOther(value: InputValue, formElements?: HTMLFormControlsCollection, otherFieldName?: string): string | undefined { // if there is a value, there's no need to check the other input if (typeof value === 'string' && !!value) { @@ -129,7 +130,7 @@ export function sslUrl(value: string | undefined): string | undefined { export interface ValidatorFn { dependentField?: string, - validator: (value: string | boolean | undefined, formValues?: HTMLFormControlsCollection, otherField?: string) => string | undefined + validator: (value: InputValue, formValues?: HTMLFormControlsCollection, otherField?: string) => string | undefined } function getOtherField(formElements?: HTMLFormControlsCollection, otherFieldName?: string): HTMLInputElement { diff --git a/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/CreateBadgeForm.tsx b/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/CreateBadgeForm.tsx index b881b8b06..5735a3748 100644 --- a/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/CreateBadgeForm.tsx +++ b/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/CreateBadgeForm.tsx @@ -1,6 +1,6 @@ -import { createRef, Dispatch, FC, RefObject, SetStateAction, useEffect, useState } from 'react' +import { FC } from 'react' -import { Button, Form, FormDefinition, formGetInputModel, FormInputModel, IconOutline } from '../../../../../lib' +import { Form, FormDefinition, formGetInputModel, FormInputModel } from '../../../../../lib' import { CreateBadgeFormField } from './create-badge-form.config' import { CreateBadgeRequest } from './create-badge-functions' @@ -17,14 +17,7 @@ const CreateBadgeForm: FC = (props: CreateBadgeFormProps) const badgeName: string = formGetInputModel(inputs, CreateBadgeFormField.badgeName).value as string const badgeDesc: string = formGetInputModel(inputs, CreateBadgeFormField.badgeDesc).value as string const badgeActive: boolean = formGetInputModel(inputs, CreateBadgeFormField.badgeActive).value as boolean - const files: FileList = formGetInputModel(inputs, CreateBadgeFormField.badgeActive).files as FileList - - console.log('generateRequest', files) - - if (!files) { - // if we don't have image file we have a problem - throw new Error(`There is no image file selected for the badge`) - } + const files: FileList = formGetInputModel(inputs, CreateBadgeFormField.file).value as FileList return { badgeActive, diff --git a/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-form.config.tsx b/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-form.config.tsx index 3883ab4e8..5ca3e0723 100644 --- a/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-form.config.tsx +++ b/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-form.config.tsx @@ -37,18 +37,16 @@ export const createBadgeFormDef: FormDefinition = { name: CreateBadgeFormField.file, size: GamificationConfig.MAX_BADGE_IMAGE_FILE_SIZE, type: 'image-picker', - // validators: [ - // { - // validator: validatorRequired, - // }, - // ], - }, - { - checked: true, - label: 'Activate Badge', - name: CreateBadgeFormField.badgeActive, - type: 'checkbox', + validators: [ + { + validator: validatorRequired, + }, + ], }, + ], + }, + { + inputs: [ { label: 'Badge Name', name: CreateBadgeFormField.badgeName, @@ -69,7 +67,19 @@ export const createBadgeFormDef: FormDefinition = { }, ], }, + { + checked: true, + label: 'Activate Badge', + name: CreateBadgeFormField.badgeActive, + type: 'checkbox', + }, ], }, ], + groupsOptions: { + groupWrapStyles: { + gridTemplateColumns: '160px 1fr', + }, + renderGroupDividers: false, + }, } From 1a923969881489f5b4ce606fc2b4c2b06a827b75 Mon Sep 17 00:00:00 2001 From: Kiril Kartunov Date: Fri, 23 Sep 2022 14:02:11 +0300 Subject: [PATCH 8/9] Connect create badge to API and clean up --- .../input-image-picker/InputImagePicker.tsx | 3 +- src-ts/lib/table/Table.tsx | 1 + .../BadgeCreatedModal.module.scss | 39 +++++++++++++++++++ .../badge-created-modal/BadgeCreatedModal.tsx | 29 +++++++++++++- .../gamification-admin.routes.tsx | 4 +- .../pages/create-badge/CreateBadgePage.tsx | 19 ++++++--- .../create-badge-form/CreateBadgeForm.tsx | 14 ++++--- .../create-badge-request.model.ts | 4 +- .../create-badge-store/create-badge.store.ts | 17 ++++++-- 9 files changed, 109 insertions(+), 21 deletions(-) create mode 100644 src-ts/tools/gamification-admin/game-lib/modals/badge-created-modal/BadgeCreatedModal.module.scss diff --git a/src-ts/lib/form/form-groups/form-input/input-image-picker/InputImagePicker.tsx b/src-ts/lib/form/form-groups/form-input/input-image-picker/InputImagePicker.tsx index a75eb442d..aa125432d 100644 --- a/src-ts/lib/form/form-groups/form-input/input-image-picker/InputImagePicker.tsx +++ b/src-ts/lib/form/form-groups/form-input/input-image-picker/InputImagePicker.tsx @@ -36,6 +36,7 @@ const InputImagePicker: FC = (props: InputImagePickerProp } }, [ files, + fileDataURL, ]) return ( @@ -59,7 +60,7 @@ const InputImagePicker: FC = (props: InputImagePickerProp /> { fileDataURL ? ( - + {'Badge ) : (
UPLOAD
IMAGE
) diff --git a/src-ts/lib/table/Table.tsx b/src-ts/lib/table/Table.tsx index ad3443d25..c9fd0a2f7 100644 --- a/src-ts/lib/table/Table.tsx +++ b/src-ts/lib/table/Table.tsx @@ -61,6 +61,7 @@ const Table: (props: TableProps) = columns, data, defaultSortDirectionMap, + props.onToggleSort, sort, ]) diff --git a/src-ts/tools/gamification-admin/game-lib/modals/badge-created-modal/BadgeCreatedModal.module.scss b/src-ts/tools/gamification-admin/game-lib/modals/badge-created-modal/BadgeCreatedModal.module.scss new file mode 100644 index 000000000..adadea7c1 --- /dev/null +++ b/src-ts/tools/gamification-admin/game-lib/modals/badge-created-modal/BadgeCreatedModal.module.scss @@ -0,0 +1,39 @@ +@import "../../../../../lib/styles/variables"; + +.wrapper { + display: flex; + flex-direction: column; + + .badge { + display: flex; + align-items: center; + margin-bottom: $space-xxl; + + .badge-image { + width: 43px; + height: 43px; + margin-right: $space-xl; + } + + .badge-image-disabled { + width: 43px; + height: 43px; + margin-right: $space-xl; + opacity: 0.5; + filter: grayscale(1); + } + + .badge-name { + font-size: 16px; + } + } + + .actions { + display: flex; + align-items: center; + + a { + margin-right: $space-md; + } + } +} \ No newline at end of file diff --git a/src-ts/tools/gamification-admin/game-lib/modals/badge-created-modal/BadgeCreatedModal.tsx b/src-ts/tools/gamification-admin/game-lib/modals/badge-created-modal/BadgeCreatedModal.tsx index eca42d824..e6a27a8c4 100644 --- a/src-ts/tools/gamification-admin/game-lib/modals/badge-created-modal/BadgeCreatedModal.tsx +++ b/src-ts/tools/gamification-admin/game-lib/modals/badge-created-modal/BadgeCreatedModal.tsx @@ -1,8 +1,12 @@ import { FC } from 'react' -import { BaseModal } from '../../../../../lib' +import { BaseModal, Button } from '../../../../../lib' +import { badgeDetailPath } from '../../../gamification-admin.routes' +import { GameBadge } from '../../game-badge.model' +import styles from './BadgeCreatedModal.module.scss' export interface BadgeCreatedModalProps { + badge: GameBadge isOpen: boolean onClose: () => void } @@ -20,7 +24,28 @@ const BadgeCreatedModal: FC = (props: BadgeCreatedModalP size='md' title={`Badge created`} > - +
+
+ {props.badge.badge_name} +

{props.badge.badge_name} badge has been sucessfully created.

+
+
+
+
) } diff --git a/src-ts/tools/gamification-admin/gamification-admin.routes.tsx b/src-ts/tools/gamification-admin/gamification-admin.routes.tsx index 108d2cf53..ee01f39d4 100644 --- a/src-ts/tools/gamification-admin/gamification-admin.routes.tsx +++ b/src-ts/tools/gamification-admin/gamification-admin.routes.tsx @@ -5,8 +5,8 @@ import BadgeDetailPage from './pages/badge-detail/BadgeDetailPage' import BadgeListingPage from './pages/badge-listing/BadgeListingPage' import CreateBadgePage from './pages/create-badge/CreateBadgePage' -const baseDetailPath: string = '/badge-detail' -const createBadgePath: string = '/create-badge' +export const baseDetailPath: string = '/badge-detail' +export const createBadgePath: string = '/create-badge' export const basePath: string = '/gamification-admin' diff --git a/src-ts/tools/gamification-admin/pages/create-badge/CreateBadgePage.tsx b/src-ts/tools/gamification-admin/pages/create-badge/CreateBadgePage.tsx index 28a8e2f8b..9d34a510c 100644 --- a/src-ts/tools/gamification-admin/pages/create-badge/CreateBadgePage.tsx +++ b/src-ts/tools/gamification-admin/pages/create-badge/CreateBadgePage.tsx @@ -1,7 +1,7 @@ import { Dispatch, FC, SetStateAction, useState } from 'react' import { Breadcrumb, BreadcrumbItemModel, ContentLayout } from '../../../../lib' -import { useGamificationBreadcrumb } from '../../game-lib' +import { GameBadge, useGamificationBreadcrumb } from '../../game-lib' import { BadgeCreatedModal } from '../../game-lib/modals/badge-created-modal' import { CreateBadgeForm, createBadgeFormDef } from './create-badge-form' @@ -19,7 +19,11 @@ const CreateBadgePage: FC = () => { const [showBadgeCreatedModal, setShowBadgeCreatedModal]: [boolean, Dispatch>] = useState(false) - function onSave() { + const [createdBadge, setCreatedBadge]: [GameBadge | undefined, Dispatch>] + = useState() + + function onSave(newBadge: GameBadge): void { + setCreatedBadge(newBadge) setShowBadgeCreatedModal(true) } @@ -34,10 +38,13 @@ const CreateBadgePage: FC = () => { onSave={onSave} /> - setShowBadgeCreatedModal(false)} - /> + { + createdBadge && setShowBadgeCreatedModal(false)} + /> + } ) } diff --git a/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/CreateBadgeForm.tsx b/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/CreateBadgeForm.tsx index 5735a3748..558132e61 100644 --- a/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/CreateBadgeForm.tsx +++ b/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/CreateBadgeForm.tsx @@ -1,6 +1,8 @@ import { FC } from 'react' import { Form, FormDefinition, formGetInputModel, FormInputModel } from '../../../../../lib' +import { GamificationConfig } from '../../../game-config' +import { GameBadge } from '../../../game-lib' import { CreateBadgeFormField } from './create-badge-form.config' import { CreateBadgeRequest } from './create-badge-functions' @@ -8,7 +10,7 @@ import { createBadgeSubmitRequestAsync } from './create-badge-functions/create-b export interface CreateBadgeFormProps { formDef: FormDefinition - onSave: () => void + onSave: (createdBadge: GameBadge) => void } const CreateBadgeForm: FC = (props: CreateBadgeFormProps) => { @@ -23,16 +25,16 @@ const CreateBadgeForm: FC = (props: CreateBadgeFormProps) badgeActive, badgeDesc, badgeName, - file: files[0], + badgeStatus: 'Active', // not used currently thus fixed field + files, + orgID: GamificationConfig.ORG_ID, } } async function saveAsync(request: CreateBadgeRequest): Promise { - console.log('saveAsync', request) - return createBadgeSubmitRequestAsync(request) - .then(() => { - props.onSave() + .then((createdBadge: GameBadge) => { + props.onSave(createdBadge) }) } diff --git a/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-functions/create-badge-store/create-badge-request.model.ts b/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-functions/create-badge-store/create-badge-request.model.ts index 26bc449f5..a0ed3c677 100644 --- a/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-functions/create-badge-store/create-badge-request.model.ts +++ b/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-functions/create-badge-store/create-badge-request.model.ts @@ -2,5 +2,7 @@ export interface CreateBadgeRequest { badgeActive: boolean badgeDesc: string badgeName: string - file: File + badgeStatus: string + files: FileList + orgID: string } diff --git a/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-functions/create-badge-store/create-badge.store.ts b/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-functions/create-badge-store/create-badge.store.ts index 0a83957da..9a2f83fba 100644 --- a/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-functions/create-badge-store/create-badge.store.ts +++ b/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-functions/create-badge-store/create-badge.store.ts @@ -1,10 +1,21 @@ import { EnvironmentConfig } from '../../../../../../../config' import { xhrPostAsync } from '../../../../../../../lib' +import { GameBadge } from '../../../../../game-lib' import { CreateBadgeRequest } from './create-badge-request.model' -export async function submitRequestAsync(request: CreateBadgeRequest): Promise { - console.log('submitRequestAsync', request) +export async function submitRequestAsync(request: CreateBadgeRequest): Promise { const url: string = `${EnvironmentConfig.API.V5}/gamification/badges` - await xhrPostAsync(url, request) + + const form: any = new FormData() + + // fill the form + form.append('file', request.files[0]) + form.append('organization_id', request.orgID) + form.append('badge_status', request.badgeStatus) + form.append('badge_name', request.badgeName) + form.append('badge_description', request.badgeDesc) + form.append('active', request.badgeActive ? 'true' : 'false') + + return xhrPostAsync(url, form) } From aa0b97fc2182816088cb40350ce49eda05d3d3f0 Mon Sep 17 00:00:00 2001 From: Kiril Kartunov Date: Mon, 26 Sep 2022 18:51:24 +0300 Subject: [PATCH 9/9] better file input configs --- .../form-input/input-image-picker/InputImagePicker.tsx | 10 ++++++---- src-ts/lib/form/form-input.model.ts | 6 ++++-- .../create-badge-form/create-badge-form.config.tsx | 6 ++++-- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src-ts/lib/form/form-groups/form-input/input-image-picker/InputImagePicker.tsx b/src-ts/lib/form/form-groups/form-input/input-image-picker/InputImagePicker.tsx index aa125432d..43882c44c 100644 --- a/src-ts/lib/form/form-groups/form-input/input-image-picker/InputImagePicker.tsx +++ b/src-ts/lib/form/form-groups/form-input/input-image-picker/InputImagePicker.tsx @@ -6,10 +6,12 @@ import { InputValue } from '../../../form-input.model' import styles from './InputImagePicker.module.scss' interface InputImagePickerProps { - readonly accept?: string + readonly fileConfig?: { + readonly acceptFileType?: string + readonly maxFileSize?: number + } readonly name: string readonly onChange: (event: ChangeEvent) => void - readonly size?: number readonly value?: InputValue } @@ -49,14 +51,14 @@ const InputImagePicker: FC = (props: InputImagePickerProp { setFiles(event.target.files) props.onChange(event) }} - size={props.size || Infinity} + size={props.fileConfig?.maxFileSize || Infinity} /> { fileDataURL ? ( diff --git a/src-ts/lib/form/form-input.model.ts b/src-ts/lib/form/form-input.model.ts index d42f4b784..419d63cb7 100644 --- a/src-ts/lib/form/form-input.model.ts +++ b/src-ts/lib/form/form-input.model.ts @@ -20,7 +20,6 @@ export interface FormCard { export type InputValue = string | boolean | FileList | undefined export interface FormInputModel { - readonly accept?: string readonly autocomplete?: FormInputAutocompleteOption readonly cards?: ReadonlyArray checked?: boolean @@ -30,6 +29,10 @@ export interface FormInputModel { disabled?: boolean error?: string readonly events?: ReadonlyArray + readonly fileConfig?: { + readonly acceptFileType?: string + readonly maxFileSize?: number + } readonly files?: FileList readonly hideInlineErrors?: boolean readonly hint?: string @@ -40,7 +43,6 @@ export interface FormInputModel { readonly notTabbable?: boolean options?: ReadonlyArray readonly placeholder?: string - readonly size?: number readonly spellCheck?: boolean readonly title?: string touched?: boolean diff --git a/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-form.config.tsx b/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-form.config.tsx index 5ca3e0723..74a0414b9 100644 --- a/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-form.config.tsx +++ b/src-ts/tools/gamification-admin/pages/create-badge/create-badge-form/create-badge-form.config.tsx @@ -33,9 +33,11 @@ export const createBadgeFormDef: FormDefinition = { { inputs: [ { - accept: GamificationConfig.ACCEPTED_BADGE_MIME_TYPES, + fileConfig: { + acceptFileType: GamificationConfig.ACCEPTED_BADGE_MIME_TYPES, + maxFileSize: GamificationConfig.MAX_BADGE_IMAGE_FILE_SIZE, + }, name: CreateBadgeFormField.file, - size: GamificationConfig.MAX_BADGE_IMAGE_FILE_SIZE, type: 'image-picker', validators: [ {