Skip to content

Commit

Permalink
circle create page with validators and select features
Browse files Browse the repository at this point in the history
  • Loading branch information
zavarin-michael committed Sep 5, 2023
1 parent 389e8c8 commit e89add4
Show file tree
Hide file tree
Showing 27 changed files with 526 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { useGetAllCirclesQuery } from '@domains/organization/redux/organizationA
import EmptyWrapper from '@domains/common/components/containers/EmptyWrapper'
import { mapReturnedData } from '@domains/common/redux/utils'
import { HighlightText } from '@domains/common/components/table/forming'
import { getVarsForAddressColumn } from '@domains/student/components/studentList/utils'
import { getVarsForAddressColumn } from '@domains/common/utils/geo'

export function CircleList() {
const [searchRequestText, setSearchRequestText] = useState('')
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const CIRCLE_NAME = 'circle_name'
export const CIRCLE_ADDRESS = 'circle_address'
export const ADDRESS_ROOM = 'address_room'
30 changes: 30 additions & 0 deletions apps/schools/domains/circle/components/createCircleForm/hooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { useMemo } from 'react'
import { PleaseInputAddressMsg, PleaseInputCircleNameMsg } from '@domains/user/components/auth/constants/message'
import { ValidatorsMap } from '@domains/common/redux/interfaces'
import { getGreaterValidator } from '@domains/common/utils/validators'

export const useCreateCircleFormValidators = () => {
return useMemo<ValidatorsMap>(() => {
return {
name: [
{
required: true,
message: PleaseInputCircleNameMsg,
whitespace: true,
type: 'string',
},
getGreaterValidator(200),
],
address: [
{
required: true,
message: PleaseInputAddressMsg,
whitespace: true,
type: 'string',
},
getGreaterValidator(200),
],
room: [getGreaterValidator(55)],
}
}, [this])
}
145 changes: 145 additions & 0 deletions apps/schools/domains/circle/components/createCircleForm/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import { Form, Typography, Input as AntdInput, Row, Spin } from 'antd'
import React, { useState } from 'react'
import { Input } from '@domains/common/components/input'
import styles from './styles/styles.module.scss'
import { Button } from '@domains/common/components/button'
import { useCreateCircleFormValidators } from './hooks'
import { useGetAllCirclesQuery } from '@domains/organization/redux/organizationApi'
import { useOrganization } from '@domains/organization/providers/organizationProvider'
import { WithTooltip } from '@domains/common/components/tooltip/withTooltip'
import { TOOLTIP_MARGIN } from './styles/styles'
import { isValidFormCheck } from '@domains/common/utils/form'
import { CIRCLE_NAME, CIRCLE_ADDRESS, ADDRESS_ROOM } from './constants'
import classnames from 'classnames'
import { AimOutlined } from '@ant-design/icons'
import { Select } from '@domains/common/components/select'
import { handleSubmitForm } from '../../handlers/circle'
import { useCreateCircleMutation } from '../../redux/circleApi'
import { getVarsForAddressColumn } from '@domains/common/utils/geo'

export const CreateCircleForm = () => {
const validators = useCreateCircleFormValidators()
const { organization, organizationId } = useOrganization()
const [form] = Form.useForm()
const [isFormValid, setIsFormValid] = useState(false)
const [mutation] = useCreateCircleMutation()
const circlesData = useGetAllCirclesQuery({
organization_id: organization.id,
})
const circlesAddresses = Array.from(
new Set(circlesData?.data?.results.map((x) => getVarsForAddressColumn(x.address)[0])),
)

const validationCheck = () => {
setIsFormValid(isValidFormCheck(form, [CIRCLE_NAME]))
}

return (
<Row className={styles.mainRow}>
<div className={styles.formContainer}>
<Form
form={form}
className={styles.table}
colon={false}
requiredMark={false}
onValuesChange={validationCheck}
onFinish={() => {
handleSubmitForm(organizationId, form, mutation).then((isSucceed) => {
if (isSucceed) window.location.href = '/circle'
})
}}
layout='vertical'
>
<Typography.Title level={1}>Добавление кружка</Typography.Title>
<WithTooltip tooltipText={'Здесь будет текст тултипа'} margin={TOOLTIP_MARGIN}>
<Form.Item
required={true}
label={
<span>
<span className={styles.requiredMark}>*</span> Название
</span>
}
name={CIRCLE_NAME}
className={styles.label}
rules={validators.name}
>
<Input required={true} placeholder='Введите название кружка' />
</Form.Item>
</WithTooltip>

<Row className={styles.complexInputContainer}>
{!circlesData.isLoading ? (
<>
<AntdInput.Group compact className={styles.complexInput}>
<Form.Item
required={true}
label={
<span>
<span className={styles.requiredMark}>*</span> Адрес
</span>
}
name={CIRCLE_ADDRESS}
initialValue={circlesAddresses[0]}
className={classnames(styles.label, styles.address)}
rules={validators.address}
>
<Select
placeholder='Выберите адрес кружка'
customType={'selectInput'}
className={styles.select}
loading={circlesData.isLoading}
options={circlesAddresses?.map((x: string | undefined) => {
return {
value: x,
label: x,
}
})}
/>
</Form.Item>

<Form.Item
label={'Помещение'}
name={ADDRESS_ROOM}
className={classnames(styles.label, styles.room)}
initialValue={''}
rules={validators.room}
>
<Input className={styles.input} placeholder='Помещение и номер' />
</Form.Item>
</AntdInput.Group>

<Button className={styles.mapButton} antdType={'text'} icon={<AimOutlined />}>
Выбрать на карте
</Button>
</>
) : (
<Spin></Spin>
)}
</Row>

<Form.Item name='button'>
<Button
disabled={!isFormValid}
key='submit'
type='schoolDefault'
htmlType='submit'
block
data-cy='resetcomplete-button'
style={{ width: '30%' }}
className={styles.button}
>
Добавить кружок
</Button>
</Form.Item>
</Form>
</div>
<div className={styles.mobileApp}>
<Typography.Title level={3}>Приложение родителей</Typography.Title>
<Typography.Title level={3} className={styles.text}>
Родители смогут увидеть ваш кружок с помощью карты и узнать информацию о нём!
</Typography.Title>
{/*<Image src={mobileApp} alt={"Мобильное приложения на странице создания кружка"}/>*/}
</div>
</Row>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
@import '../../../../common/components/styles/abstracts/colors';

.mainRow {
height: 100px;
justify-content: space-between;

.formContainer {
width: 700px;

.table {
max-width: 550px;

.requiredMark {
color: $color-required-mark;
}

.label {
font-family: 'Roboto', sans-serif;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 22px;
}

.complexInputContainer {
width: 125%;

.complexInput {
width: 76%;

.address {
width: 65%;
border-radius: 0;

.input {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
}

.room {
width: 35%;

.input {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
}
}

.mapButton {
width: 24%;
margin-top: 38px;
color: #2698FF;
padding-left: 1%;
}
}


.button {
text-align: center;
font-family: 'Roboto', sans-serif;
font-size: 16px;
font-style: normal;
font-weight: 400;
line-height: 24px;
}
}
}

.mobileApp {
padding-left: 60px;
padding-right: 60px;
padding-top: 30px;
margin-top: 20px;
height: 814px;
width: 505px;
border-radius: 12px;
background: linear-gradient(180deg, rgba(24, 144, 255, 0.3) 0%, rgba(245, 245, 245, 0.00) 100%);

.text{
font-size: 24px;
font-style: normal;
font-weight: 600;
line-height: 130%;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const TOOLTIP_MARGIN = '47px'
29 changes: 29 additions & 0 deletions apps/schools/domains/circle/handlers/circle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { FormInstance, message } from 'antd'
import { SuccessCreateCircleMsg } from '@domains/user/components/auth/constants/message'
import { removeEmpty } from '@domains/common/utils/form'
import { CIRCLE_NAME, CIRCLE_ADDRESS, ADDRESS_ROOM } from '../components/createCircleForm/constants'
import { withLoadingMessage } from '@domains/common/utils/loading'
import { ADDRESS_SEPARATOR } from '@domains/common/utils/geo'

export async function handleSubmitForm(organizationId: string, formComponent: FormInstance, mutation: any) {
console.log(formComponent.getFieldValue(CIRCLE_ADDRESS))

const response = await withLoadingMessage(
'Выполняется запрос...',
mutation,
removeEmpty({
organization: organizationId,
name: formComponent.getFieldValue(CIRCLE_NAME),
address: `${formComponent.getFieldValue(CIRCLE_ADDRESS)}${ADDRESS_SEPARATOR}${formComponent.getFieldValue(
ADDRESS_ROOM,
)}`,
}),
)

if ('data' in response) {
message.success(SuccessCreateCircleMsg)
return true
}

return false
}
85 changes: 85 additions & 0 deletions apps/schools/domains/common/components/select/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { Select as BaseSelect } from 'antd'
import React, { useState } from 'react'
import defaultStyles from './styles/default.module.scss'
import { CustomInputProps, selectStyleDictionary } from './interfaces'
import classNames from 'classnames'
import { typeSelect } from '@domains/common/constants/Select'

export const Select: React.FC<CustomInputProps> = (props) => {
const {
disabled = false,
customType = 'selectDefault',
placeholder,
label,
className,
children,
options,
...restProps
} = props

if (!typeSelect.includes(customType)) {
return (
<div className={defaultStyles.selectContainer}>
<label>{label}</label>
<BaseSelect
className={classNames(defaultStyles.select, className)}
placeholder={placeholder}
data-testid='select'
{...restProps}
>
{children}
</BaseSelect>
</div>
)
} else if (customType === 'selectInput') {
const [addressText, setAddressText] = useState('')

const handleSearch = (value: string) => {
setAddressText(value)
}

let additionalOption =
addressText != ''
? [
{
value: addressText,
label: addressText,
},
]
: []

if (options && options.filter((x) => x.value === addressText).length > 0) additionalOption = []

return (
<div className={selectStyleDictionary['selectDefault']?.selectContainer}>
<label>{label}</label>
<BaseSelect
className={classNames(selectStyleDictionary['selectDefault']?.select, className)}
{...restProps}
showSearch={true}
onSearch={handleSearch}
onSelect={() => setAddressText('')}
placeholder={placeholder}
options={options?.concat(additionalOption)}
data-testid='select'
>
{children}
</BaseSelect>
</div>
)
} else {
return (
<div className={selectStyleDictionary[customType]?.selectContainer}>
<label>{label}</label>
<BaseSelect
className={classNames(selectStyleDictionary[customType]?.select, className)}
{...restProps}
placeholder={placeholder}
data-testid='select'
>
{children}
</BaseSelect>
</div>
)
}
}
Loading

0 comments on commit e89add4

Please sign in to comment.