mirror of
https://github.com/IT4Change/ohmyform-ui.git
synced 2026-01-20 19:31:17 +00:00
upgrade packages, improve field logic, fix slider, hide empty submissions, fix urls for buttons, improve data handling for fields, improve sqlite migration handling
This commit is contained in:
parent
e33b3ff392
commit
ca5edbbb3b
@ -16,7 +16,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
- new slider field type
|
- new slider field type
|
||||||
- new card layout for forms
|
- new card layout for forms
|
||||||
- field logic
|
- field logic
|
||||||
- add enviroment config
|
- add environment config
|
||||||
- anonymous form submissions (fixes https://github.com/ohmyform/ohmyform/issues/108)
|
- anonymous form submissions (fixes https://github.com/ohmyform/ohmyform/issues/108)
|
||||||
- checkbox field type (fixed https://github.com/ohmyform/ohmyform/issues/138)
|
- checkbox field type (fixed https://github.com/ohmyform/ohmyform/issues/138)
|
||||||
|
|
||||||
@ -26,6 +26,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
- use exported hooks for graphql
|
- use exported hooks for graphql
|
||||||
- disable swipe gesture
|
- disable swipe gesture
|
||||||
- upgrade to nextjs 12
|
- upgrade to nextjs 12
|
||||||
|
- change default value from value to defaultValue
|
||||||
|
- handle options and values as json correctly
|
||||||
|
- exclude empty submissions per default (https://github.com/ohmyform/ohmyform/issues/153)
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
@ -153,11 +156,13 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- `export` uses now spa mode for initial loading screen
|
- `export` uses now spa mode for initial loading screen
|
||||||
|
- change value to defaultValue for initial form
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- [OMF#93](https://github.com/ohmyform/ohmyform/issues/93) dropdown options are not saved
|
- dropdown options are not saved (https://github.com/ohmyform/ohmyform/issues/93)
|
||||||
- redirect attempts on static export
|
- redirect attempts on static export
|
||||||
|
- date can now be prefilled by url
|
||||||
|
|
||||||
## [0.9.2] - 2020-06-04
|
## [0.9.2] - 2020-06-04
|
||||||
|
|
||||||
|
|||||||
@ -78,7 +78,6 @@ export const FieldsTab: React.FC<Props> = (props) => {
|
|||||||
title: '',
|
title: '',
|
||||||
description: '',
|
description: '',
|
||||||
required: false,
|
required: false,
|
||||||
value: '',
|
|
||||||
}
|
}
|
||||||
|
|
||||||
add(defaults)
|
add(defaults)
|
||||||
|
|||||||
@ -37,7 +37,7 @@ export const LogicBlock: React.FC<Props> = ({
|
|||||||
labelCol={{ span: 6 }}
|
labelCol={{ span: 6 }}
|
||||||
label={'Formula'}
|
label={'Formula'}
|
||||||
rules={[{ required: true, message: 'combine other fields' }]}
|
rules={[{ required: true, message: 'combine other fields' }]}
|
||||||
extra={'Save form to get new IDs and slugs'}
|
extra={'Save form to get new @IDs and $slugs. (example: $slug < 21 or @id = 42)'}
|
||||||
>
|
>
|
||||||
<Mentions rows={1}>
|
<Mentions rows={1}>
|
||||||
{fields.map((field) => (
|
{fields.map((field) => (
|
||||||
@ -54,10 +54,10 @@ export const LogicBlock: React.FC<Props> = ({
|
|||||||
const defaults = {}
|
const defaults = {}
|
||||||
|
|
||||||
fields.forEach((field) => {
|
fields.forEach((field) => {
|
||||||
defaults[`@${field.id}`] = field.value
|
defaults[`@${field.id}`] = field.defaultValue
|
||||||
|
|
||||||
if (field.slug) {
|
if (field.slug) {
|
||||||
defaults[`$${field.slug}`] = field.value
|
defaults[`$${field.slug}`] = field.defaultValue
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -10,7 +10,7 @@ export const CheckboxType: React.FC<AdminFieldTypeProps> = (props) => {
|
|||||||
<div>
|
<div>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t('type:checkbox:default')}
|
label={t('type:checkbox:default')}
|
||||||
name={[props.field.name as string, 'value']}
|
name={[props.field.name as string, 'defaultValue']}
|
||||||
labelCol={{ span: 6 }}
|
labelCol={{ span: 6 }}
|
||||||
>
|
>
|
||||||
<Input />
|
<Input />
|
||||||
|
|||||||
@ -11,7 +11,7 @@ export const DateType: React.FC<AdminFieldTypeProps> = ({ field }) => {
|
|||||||
<div>
|
<div>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t('type:date.default')}
|
label={t('type:date.default')}
|
||||||
name={[field.name as string, 'value']}
|
name={[field.name as string, 'defaultValue']}
|
||||||
labelCol={{ span: 6 }}
|
labelCol={{ span: 6 }}
|
||||||
getValueFromEvent={(e: Moment) => (e ? e.format('YYYY-MM-DD') : undefined)}
|
getValueFromEvent={(e: Moment) => (e ? e.format('YYYY-MM-DD') : undefined)}
|
||||||
getValueProps={(e: string) => ({ value: e ? moment(e) : undefined })}
|
getValueProps={(e: string) => ({ value: e ? moment(e) : undefined })}
|
||||||
|
|||||||
@ -10,7 +10,7 @@ export const DropdownType: React.FC<AdminFieldTypeProps> = (props) => {
|
|||||||
<div>
|
<div>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t('type:dropdown.default')}
|
label={t('type:dropdown.default')}
|
||||||
name={[props.field.name as string, 'value']}
|
name={[props.field.name as string, 'defaultValue']}
|
||||||
labelCol={{ span: 6 }}
|
labelCol={{ span: 6 }}
|
||||||
>
|
>
|
||||||
<Input />
|
<Input />
|
||||||
|
|||||||
@ -10,7 +10,7 @@ export const EmailType: React.FC<AdminFieldTypeProps> = (props) => {
|
|||||||
<div>
|
<div>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t('type:email.default')}
|
label={t('type:email.default')}
|
||||||
name={[props.field.name as string, 'value']}
|
name={[props.field.name as string, 'defaultValue']}
|
||||||
rules={[{ type: 'email', message: t('validation:emailRequired') }]}
|
rules={[{ type: 'email', message: t('validation:emailRequired') }]}
|
||||||
labelCol={{ span: 6 }}
|
labelCol={{ span: 6 }}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -10,7 +10,7 @@ export const HiddenType: React.FC<AdminFieldTypeProps> = (props) => {
|
|||||||
<div>
|
<div>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t('type:hidden.default')}
|
label={t('type:hidden.default')}
|
||||||
name={[props.field.name as string, 'value']}
|
name={[props.field.name as string, 'defaultValue']}
|
||||||
labelCol={{ span: 6 }}
|
labelCol={{ span: 6 }}
|
||||||
>
|
>
|
||||||
<Input />
|
<Input />
|
||||||
|
|||||||
@ -10,7 +10,7 @@ export const LinkType: React.FC<AdminFieldTypeProps> = (props) => {
|
|||||||
<div>
|
<div>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t('type:link.default')}
|
label={t('type:link.default')}
|
||||||
name={[props.field.name as string, 'value']}
|
name={[props.field.name as string, 'defaultValue']}
|
||||||
rules={[{ type: 'url', message: t('validation:invalidUrl') }]}
|
rules={[{ type: 'url', message: t('validation:invalidUrl') }]}
|
||||||
labelCol={{ span: 6 }}
|
labelCol={{ span: 6 }}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -10,12 +10,8 @@ export const NumberType: React.FC<AdminFieldTypeProps> = (props) => {
|
|||||||
<div>
|
<div>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t('type:number:default')}
|
label={t('type:number:default')}
|
||||||
name={[props.field.name as string, 'value']}
|
name={[props.field.name as string, 'defaultValue']}
|
||||||
labelCol={{ span: 6 }}
|
labelCol={{ span: 6 }}
|
||||||
getValueFromEvent={(value: number) =>
|
|
||||||
typeof value === 'number' ? value.toFixed(2) : value
|
|
||||||
}
|
|
||||||
getValueProps={(value: string) => ({ value: value ? parseFloat(value) : undefined })}
|
|
||||||
>
|
>
|
||||||
<InputNumber precision={2} />
|
<InputNumber precision={2} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|||||||
@ -10,7 +10,7 @@ export const RadioType: React.FC<AdminFieldTypeProps> = (props) => {
|
|||||||
<div>
|
<div>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t('type:radio:default')}
|
label={t('type:radio:default')}
|
||||||
name={[props.field.name as string, 'value']}
|
name={[props.field.name as string, 'defaultValue']}
|
||||||
labelCol={{ span: 6 }}
|
labelCol={{ span: 6 }}
|
||||||
>
|
>
|
||||||
<Input />
|
<Input />
|
||||||
|
|||||||
@ -10,13 +10,9 @@ export const RatingType: React.FC<AdminFieldTypeProps> = (props) => {
|
|||||||
<div>
|
<div>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t('type:rating:default')}
|
label={t('type:rating:default')}
|
||||||
name={[props.field.name as string, 'value']}
|
name={[props.field.name as string, 'defaultValue']}
|
||||||
labelCol={{ span: 6 }}
|
labelCol={{ span: 6 }}
|
||||||
extra={t('type:rating.clearNote')}
|
extra={t('type:rating.clearNote')}
|
||||||
getValueFromEvent={(value: number) =>
|
|
||||||
typeof value === 'number' ? value.toFixed(2) : value
|
|
||||||
}
|
|
||||||
getValueProps={(value: string) => ({ value: value ? parseFloat(value) : undefined })}
|
|
||||||
>
|
>
|
||||||
<Rate allowHalf allowClear />
|
<Rate allowHalf allowClear />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|||||||
@ -35,11 +35,8 @@ export const SliderType: React.FC<AdminFieldTypeProps> = (props) => {
|
|||||||
return (
|
return (
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t('type:slider.default')}
|
label={t('type:slider.default')}
|
||||||
name={[props.field.name as string, 'value']}
|
name={[props.field.name as string, 'defaultValue']}
|
||||||
labelCol={{ span: 6 }}
|
labelCol={{ span: 6 }}
|
||||||
getValueFromEvent={(value: number) =>
|
|
||||||
typeof value === 'number' ? value.toFixed(2) : value
|
|
||||||
}
|
|
||||||
getValueProps={(value: string) => ({ value: value ? parseFloat(value) : undefined })}
|
getValueProps={(value: string) => ({ value: value ? parseFloat(value) : undefined })}
|
||||||
>
|
>
|
||||||
<Slider min={min} max={max} step={step} dots={(max - min) / step <= 10} />
|
<Slider min={min} max={max} step={step} dots={(max - min) / step <= 10} />
|
||||||
@ -57,10 +54,6 @@ export const SliderType: React.FC<AdminFieldTypeProps> = (props) => {
|
|||||||
]}
|
]}
|
||||||
labelCol={{ span: 6 }}
|
labelCol={{ span: 6 }}
|
||||||
initialValue={0}
|
initialValue={0}
|
||||||
getValueFromEvent={(value: number) =>
|
|
||||||
typeof value === 'number' ? value.toFixed(2) : value
|
|
||||||
}
|
|
||||||
getValueProps={(e: string) => ({ value: e ? parseFloat(e) : undefined })}
|
|
||||||
>
|
>
|
||||||
<InputNumber />
|
<InputNumber />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
@ -74,10 +67,6 @@ export const SliderType: React.FC<AdminFieldTypeProps> = (props) => {
|
|||||||
]}
|
]}
|
||||||
labelCol={{ span: 6 }}
|
labelCol={{ span: 6 }}
|
||||||
initialValue={100}
|
initialValue={100}
|
||||||
getValueFromEvent={(value: number) =>
|
|
||||||
typeof value === 'number' ? value.toFixed(2) : value
|
|
||||||
}
|
|
||||||
getValueProps={(e: string) => ({ value: e ? parseFloat(e) : undefined })}
|
|
||||||
>
|
>
|
||||||
<InputNumber />
|
<InputNumber />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
@ -91,10 +80,6 @@ export const SliderType: React.FC<AdminFieldTypeProps> = (props) => {
|
|||||||
]}
|
]}
|
||||||
labelCol={{ span: 6 }}
|
labelCol={{ span: 6 }}
|
||||||
initialValue={1}
|
initialValue={1}
|
||||||
getValueFromEvent={(value: number) =>
|
|
||||||
typeof value === 'number' ? value.toFixed(2) : value
|
|
||||||
}
|
|
||||||
getValueProps={(e: string) => ({ value: e ? parseFloat(e) : undefined })}
|
|
||||||
>
|
>
|
||||||
<InputNumber />
|
<InputNumber />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|||||||
@ -9,7 +9,7 @@ export const TextType: React.FC<AdminFieldTypeProps> = (props) => {
|
|||||||
return (
|
return (
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t('type:textfield:default')}
|
label={t('type:textfield:default')}
|
||||||
name={[props.field.name as string, 'value']}
|
name={[props.field.name as string, 'defaultValue']}
|
||||||
labelCol={{ span: 6 }}
|
labelCol={{ span: 6 }}
|
||||||
>
|
>
|
||||||
<Input />
|
<Input />
|
||||||
|
|||||||
@ -10,7 +10,7 @@ export const TextareaType: React.FC<AdminFieldTypeProps> = (props) => {
|
|||||||
<div>
|
<div>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t('type:textarea:default')}
|
label={t('type:textarea:default')}
|
||||||
name={[props.field.name as string, 'value']}
|
name={[props.field.name as string, 'defaultValue']}
|
||||||
labelCol={{ span: 6 }}
|
labelCol={{ span: 6 }}
|
||||||
>
|
>
|
||||||
<Input.TextArea autoSize />
|
<Input.TextArea autoSize />
|
||||||
|
|||||||
@ -10,11 +10,9 @@ export const YesNoType: React.FC<AdminFieldTypeProps> = (props) => {
|
|||||||
<div>
|
<div>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t('type:yes_no:default')}
|
label={t('type:yes_no:default')}
|
||||||
name={[props.field.name as string, 'value']}
|
name={[props.field.name as string, 'defaultValue']}
|
||||||
labelCol={{ span: 6 }}
|
labelCol={{ span: 6 }}
|
||||||
valuePropName={'checked'}
|
valuePropName={'checked'}
|
||||||
getValueFromEvent={(checked: boolean) => (checked ? '1' : '')}
|
|
||||||
getValueProps={(e: string) => ({ checked: !!e })}
|
|
||||||
>
|
>
|
||||||
<Switch />
|
<Switch />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { Card, Form, message, Modal, Spin } from 'antd'
|
import { Card, Form, message, Modal, Spin } from 'antd'
|
||||||
|
import debug from 'debug'
|
||||||
import { darken, lighten } from 'polished'
|
import { darken, lighten } from 'polished'
|
||||||
import React, { useCallback, useState } from 'react'
|
import React, { useCallback, useEffect, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
import { FormPublicFieldFragment } from '../../../../graphql/fragment/form.public.fragment'
|
import { FormPublicFieldFragment } from '../../../../graphql/fragment/form.public.fragment'
|
||||||
@ -13,6 +14,8 @@ import { Page } from './page'
|
|||||||
|
|
||||||
type Step = 'start' | 'form' | 'end'
|
type Step = 'start' | 'form' | 'end'
|
||||||
|
|
||||||
|
const logger = debug('layout/card')
|
||||||
|
|
||||||
const MyCard = styled.div<{ background: string }>`
|
const MyCard = styled.div<{ background: string }>`
|
||||||
background: ${(props) => darken(0.1, props.background)};
|
background: ${(props) => darken(0.1, props.background)};
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@ -41,8 +44,28 @@ export const CardLayout: React.FC<LayoutProps> = (props) => {
|
|||||||
const { design, startPage, endPage, fields } = props.form
|
const { design, startPage, endPage, fields } = props.form
|
||||||
const { setField } = props.submission
|
const { setField } = props.submission
|
||||||
|
|
||||||
|
const updateValues = useCallback(() => {
|
||||||
|
const defaults = {}
|
||||||
|
|
||||||
|
fields.forEach(field => {
|
||||||
|
const defaultValue = field.defaultValue ? JSON.parse(field.defaultValue) : null
|
||||||
|
|
||||||
|
defaults[`@${field.id}`] = form.getFieldValue([field.id, 'value']) ?? defaultValue
|
||||||
|
|
||||||
|
if (field.slug) {
|
||||||
|
defaults[`$${field.slug}`] = form.getFieldValue([field.id, 'value']) ?? defaultValue
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
setValues(defaults)
|
||||||
|
}, [fields, form])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
updateValues()
|
||||||
|
}, [updateValues])
|
||||||
|
|
||||||
const finish = async (data: { [key: number]: unknown }) => {
|
const finish = async (data: { [key: number]: unknown }) => {
|
||||||
console.log('data', data)
|
logger('finish form %O', data)
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -63,7 +86,7 @@ export const CardLayout: React.FC<LayoutProps> = (props) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
logger('failed to finish form %O', e)
|
||||||
void message.error({
|
void message.error({
|
||||||
content: 'Error saving Input',
|
content: 'Error saving Input',
|
||||||
})
|
})
|
||||||
@ -75,8 +98,6 @@ export const CardLayout: React.FC<LayoutProps> = (props) => {
|
|||||||
const isVisible = useCallback((field: FormPublicFieldFragment): boolean => {
|
const isVisible = useCallback((field: FormPublicFieldFragment): boolean => {
|
||||||
if (!field.logic) return true
|
if (!field.logic) return true
|
||||||
|
|
||||||
console.log('DEFAULTS', values)
|
|
||||||
|
|
||||||
return field.logic
|
return field.logic
|
||||||
.filter(logic => logic.action === 'visible')
|
.filter(logic => logic.action === 'visible')
|
||||||
.map(logic => {
|
.map(logic => {
|
||||||
@ -86,7 +107,6 @@ export const CardLayout: React.FC<LayoutProps> = (props) => {
|
|||||||
values
|
values
|
||||||
)
|
)
|
||||||
|
|
||||||
console.log('result', r)
|
|
||||||
return Boolean(r)
|
return Boolean(r)
|
||||||
} catch {
|
} catch {
|
||||||
return true
|
return true
|
||||||
@ -108,19 +128,7 @@ export const CardLayout: React.FC<LayoutProps> = (props) => {
|
|||||||
<Form
|
<Form
|
||||||
form={form}
|
form={form}
|
||||||
onFinish={finish}
|
onFinish={finish}
|
||||||
onValuesChange={() => {
|
onValuesChange={updateValues}
|
||||||
const defaults = {}
|
|
||||||
|
|
||||||
fields.forEach(field => {
|
|
||||||
defaults[`@${field.id}`] = form.getFieldValue([field.id, 'value'])
|
|
||||||
|
|
||||||
if (field.slug) {
|
|
||||||
defaults[`$${field.slug}`] = form.getFieldValue([field.id, 'value'])
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
setValues(defaults)
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{fields.map((field, i) => {
|
{fields.map((field, i) => {
|
||||||
if (field.type === 'hidden') {
|
if (field.type === 'hidden') {
|
||||||
|
|||||||
@ -1,16 +1,23 @@
|
|||||||
import { Checkbox, Form } from 'antd'
|
import { Checkbox, Form } from 'antd'
|
||||||
|
import debug from 'debug'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { StyledCheckbox } from '../../styled/checkbox'
|
import { StyledCheckbox } from '../../styled/checkbox'
|
||||||
import { FieldTypeProps } from './type.props'
|
import { FieldTypeProps } from './type.props'
|
||||||
|
|
||||||
|
const logger = debug('field/checkbox')
|
||||||
|
|
||||||
export const CheckboxType: React.FC<FieldTypeProps> = ({ field, design, urlValue }) => {
|
export const CheckboxType: React.FC<FieldTypeProps> = ({ field, design, urlValue }) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
let initialValue: string = undefined
|
let initialValue: string = undefined
|
||||||
|
|
||||||
if (field.value) {
|
if (field.defaultValue) {
|
||||||
initialValue = field.value
|
try {
|
||||||
|
initialValue = JSON.parse(field.defaultValue)
|
||||||
|
} catch (e) {
|
||||||
|
logger('invalid default value %O', e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (urlValue) {
|
if (urlValue) {
|
||||||
|
|||||||
@ -1,11 +1,14 @@
|
|||||||
import { Form } from 'antd'
|
import { Form } from 'antd'
|
||||||
import dayjs, { Dayjs } from 'dayjs'
|
import dayjs, { Dayjs } from 'dayjs'
|
||||||
|
import debug from 'debug'
|
||||||
import moment, { Moment } from 'moment'
|
import moment, { Moment } from 'moment'
|
||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { StyledDateInput } from '../../styled/date.input'
|
import { StyledDateInput } from '../../styled/date.input'
|
||||||
import { FieldTypeProps } from './type.props'
|
import { FieldTypeProps } from './type.props'
|
||||||
|
|
||||||
|
const logger = debug('field/date')
|
||||||
|
|
||||||
export const DateType: React.FC<FieldTypeProps> = ({ field, design, urlValue, focus }) => {
|
export const DateType: React.FC<FieldTypeProps> = ({ field, design, urlValue, focus }) => {
|
||||||
const [min, setMin] = useState<Dayjs>()
|
const [min, setMin] = useState<Dayjs>()
|
||||||
const [max, setMax] = useState<Dayjs>()
|
const [max, setMax] = useState<Dayjs>()
|
||||||
@ -24,12 +27,16 @@ export const DateType: React.FC<FieldTypeProps> = ({ field, design, urlValue, fo
|
|||||||
|
|
||||||
let initialValue: Moment = undefined
|
let initialValue: Moment = undefined
|
||||||
|
|
||||||
if (field.value) {
|
if (field.defaultValue) {
|
||||||
initialValue = moment(field.value)
|
try {
|
||||||
|
initialValue = moment(JSON.parse(field.defaultValue))
|
||||||
|
} catch (e) {
|
||||||
|
logger('invalid default value %O', e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (urlValue) {
|
if (urlValue) {
|
||||||
initialValue = moment(field.value)
|
initialValue = moment(urlValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -1,19 +1,36 @@
|
|||||||
import { Form, Select } from 'antd'
|
import { Form, Select } from 'antd'
|
||||||
|
import debug from 'debug'
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { StyledSelect } from '../../styled/select'
|
import { StyledSelect } from '../../styled/select'
|
||||||
import { FieldTypeProps } from './type.props'
|
import { FieldTypeProps } from './type.props'
|
||||||
|
|
||||||
|
const logger = debug('field/dropdown')
|
||||||
|
|
||||||
export const DropdownType: React.FC<FieldTypeProps> = ({ field, design, urlValue, focus }) => {
|
export const DropdownType: React.FC<FieldTypeProps> = ({ field, design, urlValue, focus }) => {
|
||||||
const [open, setOpen] = useState(false)
|
const [open, setOpen] = useState(false)
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
let initialValue = null
|
||||||
|
|
||||||
|
if (field.defaultValue) {
|
||||||
|
try {
|
||||||
|
initialValue = JSON.parse(field.defaultValue)
|
||||||
|
} catch (e) {
|
||||||
|
logger('invalid default value %O', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (urlValue) {
|
||||||
|
initialValue = urlValue
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name={[field.id, 'value']}
|
name={[field.id, 'value']}
|
||||||
rules={[{ required: field.required, message: t('validation:valueRequired') }]}
|
rules={[{ required: field.required, message: t('validation:valueRequired') }]}
|
||||||
initialValue={urlValue || field.value || null}
|
initialValue={initialValue}
|
||||||
>
|
>
|
||||||
<StyledSelect
|
<StyledSelect
|
||||||
autoFocus={focus}
|
autoFocus={focus}
|
||||||
|
|||||||
@ -1,12 +1,29 @@
|
|||||||
import { Form } from 'antd'
|
import { Form } from 'antd'
|
||||||
|
import debug from 'debug'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { StyledInput } from '../../styled/input'
|
import { StyledInput } from '../../styled/input'
|
||||||
import { FieldTypeProps } from './type.props'
|
import { FieldTypeProps } from './type.props'
|
||||||
|
|
||||||
|
const logger = debug('field/email')
|
||||||
|
|
||||||
export const EmailType: React.FC<FieldTypeProps> = ({ field, design, urlValue, focus }) => {
|
export const EmailType: React.FC<FieldTypeProps> = ({ field, design, urlValue, focus }) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
let initialValue = null
|
||||||
|
|
||||||
|
if (field.defaultValue) {
|
||||||
|
try {
|
||||||
|
initialValue = JSON.parse(field.defaultValue)
|
||||||
|
} catch (e) {
|
||||||
|
logger('invalid default value %O', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (urlValue) {
|
||||||
|
initialValue = urlValue
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
@ -15,7 +32,7 @@ export const EmailType: React.FC<FieldTypeProps> = ({ field, design, urlValue, f
|
|||||||
{ required: field.required, message: t('validation:valueRequired') },
|
{ required: field.required, message: t('validation:valueRequired') },
|
||||||
{ type: 'email', message: t('validation:invalidEmail') },
|
{ type: 'email', message: t('validation:invalidEmail') },
|
||||||
]}
|
]}
|
||||||
initialValue={urlValue || field.value}
|
initialValue={initialValue}
|
||||||
>
|
>
|
||||||
<StyledInput autoFocus={focus} design={design} allowClear size={'large'} />
|
<StyledInput autoFocus={focus} design={design} allowClear size={'large'} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|||||||
@ -1,12 +1,29 @@
|
|||||||
import { Form } from 'antd'
|
import { Form } from 'antd'
|
||||||
|
import debug from 'debug'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { StyledInput } from '../../styled/input'
|
import { StyledInput } from '../../styled/input'
|
||||||
import { FieldTypeProps } from './type.props'
|
import { FieldTypeProps } from './type.props'
|
||||||
|
|
||||||
|
const logger = debug('field/link')
|
||||||
|
|
||||||
export const LinkType: React.FC<FieldTypeProps> = ({ field, design, urlValue, focus }) => {
|
export const LinkType: React.FC<FieldTypeProps> = ({ field, design, urlValue, focus }) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
let initialValue = null
|
||||||
|
|
||||||
|
if (field.defaultValue) {
|
||||||
|
try {
|
||||||
|
initialValue = JSON.parse(field.defaultValue)
|
||||||
|
} catch (e) {
|
||||||
|
logger('invalid default value %O', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (urlValue) {
|
||||||
|
initialValue = urlValue
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
@ -15,7 +32,7 @@ export const LinkType: React.FC<FieldTypeProps> = ({ field, design, urlValue, fo
|
|||||||
{ required: field.required, message: t('validation:valueRequired') },
|
{ required: field.required, message: t('validation:valueRequired') },
|
||||||
{ type: 'url', message: t('validation:invalidUrl') },
|
{ type: 'url', message: t('validation:invalidUrl') },
|
||||||
]}
|
]}
|
||||||
initialValue={urlValue || field.value}
|
initialValue={initialValue}
|
||||||
>
|
>
|
||||||
<StyledInput autoFocus={focus} design={design} allowClear size={'large'} />
|
<StyledInput autoFocus={focus} design={design} allowClear size={'large'} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|||||||
@ -1,16 +1,23 @@
|
|||||||
import { Form } from 'antd'
|
import { Form } from 'antd'
|
||||||
|
import debug from 'debug'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { StyledNumberInput } from '../../styled/number.input'
|
import { StyledNumberInput } from '../../styled/number.input'
|
||||||
import { FieldTypeProps } from './type.props'
|
import { FieldTypeProps } from './type.props'
|
||||||
|
|
||||||
|
const logger = debug('field/number')
|
||||||
|
|
||||||
export const NumberType: React.FC<FieldTypeProps> = ({ field, design, urlValue, focus }) => {
|
export const NumberType: React.FC<FieldTypeProps> = ({ field, design, urlValue, focus }) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
let initialValue: number = undefined
|
let initialValue: number = undefined
|
||||||
|
|
||||||
if (field.value) {
|
if (field.defaultValue) {
|
||||||
initialValue = parseFloat(field.value)
|
try {
|
||||||
|
initialValue = JSON.parse(field.defaultValue)
|
||||||
|
} catch (e) {
|
||||||
|
logger('invalid default value %O', e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (urlValue) {
|
if (urlValue) {
|
||||||
|
|||||||
@ -1,16 +1,23 @@
|
|||||||
import { Form, Radio } from 'antd'
|
import { Form, Radio } from 'antd'
|
||||||
|
import debug from 'debug'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { StyledRadio } from '../../styled/radio'
|
import { StyledRadio } from '../../styled/radio'
|
||||||
import { FieldTypeProps } from './type.props'
|
import { FieldTypeProps } from './type.props'
|
||||||
|
|
||||||
|
const logger = debug('field/radio')
|
||||||
|
|
||||||
export const RadioType: React.FC<FieldTypeProps> = ({ field, design, urlValue }) => {
|
export const RadioType: React.FC<FieldTypeProps> = ({ field, design, urlValue }) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
let initialValue: string = undefined
|
let initialValue: string = undefined
|
||||||
|
|
||||||
if (field.value) {
|
if (field.defaultValue) {
|
||||||
initialValue = field.value
|
try {
|
||||||
|
initialValue = JSON.parse(field.defaultValue)
|
||||||
|
} catch (e) {
|
||||||
|
logger('invalid default value %O', e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (urlValue) {
|
if (urlValue) {
|
||||||
|
|||||||
@ -1,15 +1,22 @@
|
|||||||
import { Form, Rate } from 'antd'
|
import { Form, Rate } from 'antd'
|
||||||
|
import debug from 'debug'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { FieldTypeProps } from './type.props'
|
import { FieldTypeProps } from './type.props'
|
||||||
|
|
||||||
|
const logger = debug('field/rating')
|
||||||
|
|
||||||
export const RatingType: React.FC<FieldTypeProps> = ({ field, urlValue }) => {
|
export const RatingType: React.FC<FieldTypeProps> = ({ field, urlValue }) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
let initialValue: number = undefined
|
let initialValue: number = undefined
|
||||||
|
|
||||||
if (field.value) {
|
if (field.defaultValue) {
|
||||||
initialValue = parseFloat(field.value)
|
try {
|
||||||
|
initialValue = JSON.parse(field.defaultValue)
|
||||||
|
} catch (e) {
|
||||||
|
logger('invalid default value %O', e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (urlValue) {
|
if (urlValue) {
|
||||||
|
|||||||
@ -1,8 +1,11 @@
|
|||||||
import { Form, Slider, Spin } from 'antd'
|
import { Form, Slider, Spin } from 'antd'
|
||||||
|
import debug from 'debug'
|
||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { FieldTypeProps } from './type.props'
|
import { FieldTypeProps } from './type.props'
|
||||||
|
|
||||||
|
const logger = debug('field/slider')
|
||||||
|
|
||||||
export const SliderType: React.FC<FieldTypeProps> = ({ field, urlValue }) => {
|
export const SliderType: React.FC<FieldTypeProps> = ({ field, urlValue }) => {
|
||||||
const [min, setMin] = useState<number>()
|
const [min, setMin] = useState<number>()
|
||||||
const [max, setMax] = useState<number>()
|
const [max, setMax] = useState<number>()
|
||||||
@ -14,13 +17,25 @@ export const SliderType: React.FC<FieldTypeProps> = ({ field, urlValue }) => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
field.options.forEach((option) => {
|
field.options.forEach((option) => {
|
||||||
if (option.key === 'min') {
|
if (option.key === 'min') {
|
||||||
setMin(parseFloat(option.value))
|
try {
|
||||||
|
setMin(JSON.parse(option.value))
|
||||||
|
} catch (e) {
|
||||||
|
logger('invalid min value %O', e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (option.key === 'max') {
|
if (option.key === 'max') {
|
||||||
setMax(parseFloat(option.value))
|
try {
|
||||||
|
setMax(JSON.parse(option.value))
|
||||||
|
} catch (e) {
|
||||||
|
logger('invalid max value %O', e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (option.key === 'step') {
|
if (option.key === 'step') {
|
||||||
setStep(parseFloat(option.value))
|
try {
|
||||||
|
setStep(JSON.parse(option.value))
|
||||||
|
} catch (e) {
|
||||||
|
logger('invalid step value %O', e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -29,8 +44,12 @@ export const SliderType: React.FC<FieldTypeProps> = ({ field, urlValue }) => {
|
|||||||
|
|
||||||
let initialValue: number = undefined
|
let initialValue: number = undefined
|
||||||
|
|
||||||
if (field.value) {
|
if (field.defaultValue) {
|
||||||
initialValue = parseFloat(field.value)
|
try {
|
||||||
|
initialValue = JSON.parse(field.defaultValue)
|
||||||
|
} catch (e) {
|
||||||
|
logger('invalid default value %O', e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (urlValue) {
|
if (urlValue) {
|
||||||
@ -51,10 +70,6 @@ export const SliderType: React.FC<FieldTypeProps> = ({ field, urlValue }) => {
|
|||||||
name={[field.id, 'value']}
|
name={[field.id, 'value']}
|
||||||
rules={[{ required: field.required, message: t('validation:valueRequired') }]}
|
rules={[{ required: field.required, message: t('validation:valueRequired') }]}
|
||||||
initialValue={initialValue}
|
initialValue={initialValue}
|
||||||
getValueFromEvent={(value: number) =>
|
|
||||||
typeof value === 'number' ? value.toFixed(2) : value
|
|
||||||
}
|
|
||||||
getValueProps={(value: string) => ({ value: value ? parseFloat(value) : undefined })}
|
|
||||||
>
|
>
|
||||||
<Slider
|
<Slider
|
||||||
min={min}
|
min={min}
|
||||||
|
|||||||
@ -1,19 +1,36 @@
|
|||||||
import { Form } from 'antd'
|
import { Form } from 'antd'
|
||||||
|
import debug from 'debug'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { StyledInput } from '../../styled/input'
|
import { StyledInput } from '../../styled/input'
|
||||||
import { FieldTypeProps } from './type.props'
|
import { FieldTypeProps } from './type.props'
|
||||||
|
|
||||||
|
const logger = debug('field/text')
|
||||||
|
|
||||||
export const TextType: React.FC<FieldTypeProps> = ({ field, design, urlValue, focus }) => {
|
export const TextType: React.FC<FieldTypeProps> = ({ field, design, urlValue, focus }) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
// TODO focus when becomes visible
|
// TODO focus when becomes visible
|
||||||
|
|
||||||
|
let initialValue = undefined
|
||||||
|
|
||||||
|
if (field.defaultValue) {
|
||||||
|
try {
|
||||||
|
initialValue = JSON.parse(field.defaultValue)
|
||||||
|
} catch (e) {
|
||||||
|
logger('invalid default value %O', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (urlValue) {
|
||||||
|
initialValue = urlValue
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name={[field.id, 'value']}
|
name={[field.id, 'value']}
|
||||||
rules={[{ required: field.required, message: t('validation:valueRequired') }]}
|
rules={[{ required: field.required, message: t('validation:valueRequired') }]}
|
||||||
initialValue={urlValue || field.value}
|
initialValue={initialValue}
|
||||||
>
|
>
|
||||||
<StyledInput autoFocus={focus} design={design} allowClear size={'large'} />
|
<StyledInput autoFocus={focus} design={design} allowClear size={'large'} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|||||||
@ -1,18 +1,35 @@
|
|||||||
import { Form } from 'antd'
|
import { Form } from 'antd'
|
||||||
|
import debug from 'debug'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { StyledTextareaInput } from '../../styled/textarea.input'
|
import { StyledTextareaInput } from '../../styled/textarea.input'
|
||||||
import { FieldTypeProps } from './type.props'
|
import { FieldTypeProps } from './type.props'
|
||||||
|
|
||||||
|
const logger = debug('field/textarea')
|
||||||
|
|
||||||
export const TextareaType: React.FC<FieldTypeProps> = ({ field, design, urlValue, focus }) => {
|
export const TextareaType: React.FC<FieldTypeProps> = ({ field, design, urlValue, focus }) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
|
let initialValue = undefined
|
||||||
|
|
||||||
|
if (field.defaultValue) {
|
||||||
|
try {
|
||||||
|
initialValue = JSON.parse(field.defaultValue)
|
||||||
|
} catch (e) {
|
||||||
|
logger('invalid default value %O', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (urlValue) {
|
||||||
|
initialValue = urlValue
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name={[field.id, 'value']}
|
name={[field.id, 'value']}
|
||||||
rules={[{ required: field.required, message: t('validation:valueRequired') }]}
|
rules={[{ required: field.required, message: t('validation:valueRequired') }]}
|
||||||
initialValue={urlValue || field.value}
|
initialValue={initialValue}
|
||||||
>
|
>
|
||||||
<StyledTextareaInput autoFocus={focus} design={design} allowClear autoSize />
|
<StyledTextareaInput autoFocus={focus} design={design} allowClear autoSize />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|||||||
@ -1,12 +1,24 @@
|
|||||||
import { Form, Switch } from 'antd'
|
import { Form, Switch } from 'antd'
|
||||||
|
import debug from 'debug'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { FieldTypeProps } from './type.props'
|
import { FieldTypeProps } from './type.props'
|
||||||
|
|
||||||
|
const logger = debug('field/link')
|
||||||
|
|
||||||
export const YesNoType: React.FC<FieldTypeProps> = ({ field, urlValue }) => {
|
export const YesNoType: React.FC<FieldTypeProps> = ({ field, urlValue }) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
let initialValue = !!field.value
|
|
||||||
|
let initialValue: boolean = undefined
|
||||||
|
|
||||||
|
if (field.defaultValue) {
|
||||||
|
try {
|
||||||
|
initialValue = JSON.parse(field.defaultValue)
|
||||||
|
} catch (e) {
|
||||||
|
logger('invalid default value %O', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (urlValue !== undefined) {
|
if (urlValue !== undefined) {
|
||||||
initialValue = !!urlValue
|
initialValue = !!urlValue
|
||||||
@ -19,8 +31,6 @@ export const YesNoType: React.FC<FieldTypeProps> = ({ field, urlValue }) => {
|
|||||||
rules={[{ required: field.required, message: t('validation:valueRequired') }]}
|
rules={[{ required: field.required, message: t('validation:valueRequired') }]}
|
||||||
initialValue={initialValue}
|
initialValue={initialValue}
|
||||||
valuePropName={'checked'}
|
valuePropName={'checked'}
|
||||||
getValueFromEvent={(checked: boolean) => (checked ? '1' : '')}
|
|
||||||
getValueProps={(e: string) => ({ checked: !!e })}
|
|
||||||
>
|
>
|
||||||
<Switch />
|
<Switch />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|||||||
@ -185,9 +185,9 @@ export const Structure: FunctionComponent<Props> = (props) => {
|
|||||||
<Dropdown
|
<Dropdown
|
||||||
overlay={
|
overlay={
|
||||||
<Menu>
|
<Menu>
|
||||||
<Menu.Item onClick={() => router.push('/admin/profile')}>Profile</Menu.Item>
|
<Menu.Item key={'profile'} onClick={() => router.push('/admin/profile')}>Profile</Menu.Item>
|
||||||
<Menu.Divider />
|
<Menu.Divider key={'d1'} />
|
||||||
<Menu.Item onClick={signOut}>Logout</Menu.Item>
|
<Menu.Item key={'logout'} onClick={signOut}>Logout</Menu.Item>
|
||||||
</Menu>
|
</Menu>
|
||||||
}
|
}
|
||||||
onVisibleChange={setUserMenu}
|
onVisibleChange={setUserMenu}
|
||||||
@ -237,7 +237,7 @@ export const Structure: FunctionComponent<Props> = (props) => {
|
|||||||
{buildMenu(sideMenu)}
|
{buildMenu(sideMenu)}
|
||||||
</Menu>
|
</Menu>
|
||||||
<Menu mode="inline" selectable={false}>
|
<Menu mode="inline" selectable={false}>
|
||||||
<Menu.Item className={'language-selector'}>
|
<Menu.Item className={'language-selector'} key={'language-selector'}>
|
||||||
<Select
|
<Select
|
||||||
bordered={false}
|
bordered={false}
|
||||||
value={i18n.language.replace(/-.*/, '')}
|
value={i18n.language.replace(/-.*/, '')}
|
||||||
@ -253,10 +253,10 @@ export const Structure: FunctionComponent<Props> = (props) => {
|
|||||||
))}
|
))}
|
||||||
</Select>
|
</Select>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item style={{ display: 'flex', alignItems: 'center' }}>
|
<Menu.Item style={{ display: 'flex', alignItems: 'center' }} key={'github'}>
|
||||||
<GitHubButton type="stargazers" namespace="ohmyform" repo="ohmyform" />
|
<GitHubButton type="stargazers" namespace="ohmyform" repo="ohmyform" />
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
<Menu.Item>
|
<Menu.Item key={'version'}>
|
||||||
Version: <Tag color="gold">{process.env.version}</Tag>
|
Version: <Tag color="gold">{process.env.version}</Tag>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
</Menu>
|
</Menu>
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import debug from 'debug'
|
import debug from 'debug'
|
||||||
import { all, create } from 'mathjs'
|
import { formula, init } from 'expressionparser'
|
||||||
import { useState } from 'react'
|
|
||||||
|
|
||||||
const logger = debug('useMath')
|
const logger = debug('useMath')
|
||||||
|
|
||||||
@ -8,9 +7,15 @@ export const useMath = (): ((
|
|||||||
expression: string,
|
expression: string,
|
||||||
values?: { [id: string]: string | number }
|
values?: { [id: string]: string | number }
|
||||||
) => boolean) => {
|
) => boolean) => {
|
||||||
const [math] = useState(create(all, {}))
|
|
||||||
|
|
||||||
return (expression, values) => {
|
return (expression, values) => {
|
||||||
|
const parser = init(formula, (term: string) => {
|
||||||
|
if (values[term]) {
|
||||||
|
return values[term]
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`Invalid term: ${term}`);
|
||||||
|
})
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let processed = expression
|
let processed = expression
|
||||||
|
|
||||||
@ -24,7 +29,9 @@ export const useMath = (): ((
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return Boolean(math.evaluate(processed))
|
parser.expressionToValue(expression)
|
||||||
|
|
||||||
|
return Boolean(parser.expressionToValue(expression))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger(
|
logger(
|
||||||
'failed to calculate %O: %s',
|
'failed to calculate %O: %s',
|
||||||
|
|||||||
@ -46,7 +46,7 @@ export interface FormFieldFragment {
|
|||||||
type: string
|
type: string
|
||||||
description: string
|
description: string
|
||||||
required: boolean
|
required: boolean
|
||||||
value: string
|
defaultValue?: string
|
||||||
|
|
||||||
options: FormFieldOptionFragment[]
|
options: FormFieldOptionFragment[]
|
||||||
optionKeys?: FormFieldOptionKeysFragment
|
optionKeys?: FormFieldOptionKeysFragment
|
||||||
@ -135,7 +135,7 @@ export const FORM_FRAGMENT = gql`
|
|||||||
type
|
type
|
||||||
description
|
description
|
||||||
required
|
required
|
||||||
value
|
defaultValue
|
||||||
|
|
||||||
options {
|
options {
|
||||||
id
|
id
|
||||||
|
|||||||
@ -42,7 +42,7 @@ export interface FormPublicFieldFragment {
|
|||||||
type: string
|
type: string
|
||||||
description: string
|
description: string
|
||||||
required: boolean
|
required: boolean
|
||||||
value: string
|
defaultValue: string
|
||||||
|
|
||||||
options: FormPublicFieldOptionFragment[]
|
options: FormPublicFieldOptionFragment[]
|
||||||
|
|
||||||
@ -93,7 +93,7 @@ export const FORM_PUBLIC_FRAGMENT = gql`
|
|||||||
type
|
type
|
||||||
description
|
description
|
||||||
required
|
required
|
||||||
value
|
defaultValue
|
||||||
|
|
||||||
logic {
|
logic {
|
||||||
id
|
id
|
||||||
|
|||||||
@ -20,15 +20,19 @@ interface Variables {
|
|||||||
form: string
|
form: string
|
||||||
start?: number
|
start?: number
|
||||||
limit?: number
|
limit?: number
|
||||||
|
filter?: {
|
||||||
|
finished?: boolean
|
||||||
|
excludeEmpty?: boolean
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const QUERY = gql`
|
const QUERY = gql`
|
||||||
query listSubmissions($form: ID!, $start: Int, $limit: Int) {
|
query listSubmissions($form: ID!, $start: Int, $limit: Int, $filter: SubmissionPagerFilterInput) {
|
||||||
form: getFormById(id: $form) {
|
form: getFormById(id: $form) {
|
||||||
...PagerForm
|
...PagerForm
|
||||||
}
|
}
|
||||||
|
|
||||||
pager: listSubmissions(form: $form, start: $start, limit: $limit) {
|
pager: listSubmissions(form: $form, start: $start, limit: $limit, filter: $filter) {
|
||||||
entries {
|
entries {
|
||||||
...Submission
|
...Submission
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,13 +20,13 @@
|
|||||||
"dayjs": "^1.10.7",
|
"dayjs": "^1.10.7",
|
||||||
"debug": "^4.3.3",
|
"debug": "^4.3.3",
|
||||||
"exceljs": "^4.3.0",
|
"exceljs": "^4.3.0",
|
||||||
|
"expressionparser": "^1.1.5",
|
||||||
"graphql": "^16.3.0",
|
"graphql": "^16.3.0",
|
||||||
"i18next": "^21.6.12",
|
"i18next": "^21.6.12",
|
||||||
"i18next-browser-languagedetector": "^6.1.3",
|
"i18next-browser-languagedetector": "^6.1.3",
|
||||||
"imagemin-optipng": "^8.0.0",
|
"imagemin-optipng": "^8.0.0",
|
||||||
"isomorphic-fetch": "^3.0.0",
|
"isomorphic-fetch": "^3.0.0",
|
||||||
"jimp": "^0.16.1",
|
"jimp": "^0.16.1",
|
||||||
"mathjs": "^10.1.1",
|
|
||||||
"next": "^12.1.0",
|
"next": "^12.1.0",
|
||||||
"next-compose-plugins": "^2.2.1",
|
"next-compose-plugins": "^2.2.1",
|
||||||
"next-optimized-images": "^2.6.2",
|
"next-optimized-images": "^2.6.2",
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import { NotificationsTab } from 'components/form/admin/notifications.tab'
|
|||||||
import { StartPageTab } from 'components/form/admin/start.page.tab'
|
import { StartPageTab } from 'components/form/admin/start.page.tab'
|
||||||
import { Structure } from 'components/structure'
|
import { Structure } from 'components/structure'
|
||||||
import { withAuth } from 'components/with.auth'
|
import { withAuth } from 'components/with.auth'
|
||||||
|
import debug from 'debug'
|
||||||
import { useFormUpdateMutation } from 'graphql/mutation/form.update.mutation'
|
import { useFormUpdateMutation } from 'graphql/mutation/form.update.mutation'
|
||||||
import { NextPage } from 'next'
|
import { NextPage } from 'next'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
@ -22,6 +23,8 @@ import {
|
|||||||
} from '../../../../graphql/fragment/form.fragment'
|
} from '../../../../graphql/fragment/form.fragment'
|
||||||
import { Data, useFormQuery } from '../../../../graphql/query/form.query'
|
import { Data, useFormQuery } from '../../../../graphql/query/form.query'
|
||||||
|
|
||||||
|
const logger = debug('page/admin/form/[id]')
|
||||||
|
|
||||||
const Index: NextPage = () => {
|
const Index: NextPage = () => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@ -40,12 +43,17 @@ const Index: NextPage = () => {
|
|||||||
|
|
||||||
field.options.forEach((option) => {
|
field.options.forEach((option) => {
|
||||||
if (option.key) {
|
if (option.key) {
|
||||||
keys[option.key] = option.value
|
try {
|
||||||
|
keys[option.key] = JSON.parse(option.value)
|
||||||
|
} catch (e) {
|
||||||
|
logger('invalid option value %O', e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...field,
|
...field,
|
||||||
|
defaultValue: field.defaultValue ? JSON.parse(field.defaultValue) : null,
|
||||||
options: field.options.filter((option) => !option.key),
|
options: field.options.filter((option) => !option.key),
|
||||||
optionKeys: keys,
|
optionKeys: keys,
|
||||||
}
|
}
|
||||||
@ -76,13 +84,13 @@ const Index: NextPage = () => {
|
|||||||
|
|
||||||
if (optionKeys) {
|
if (optionKeys) {
|
||||||
Object.keys(optionKeys).forEach((key) => {
|
Object.keys(optionKeys).forEach((key) => {
|
||||||
if (!optionKeys[key]) {
|
if (optionKeys[key] === undefined) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
options.push({
|
options.push({
|
||||||
id: null, // TODO improve this
|
id: null, // TODO improve this
|
||||||
value: optionKeys[key],
|
value: JSON.stringify(optionKeys[key]),
|
||||||
key,
|
key,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -90,6 +98,7 @@ const Index: NextPage = () => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
...field,
|
...field,
|
||||||
|
defaultValue: field.defaultValue !== null ? JSON.stringify(field.defaultValue) : null,
|
||||||
options,
|
options,
|
||||||
idx: index,
|
idx: index,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,6 +31,9 @@ const Submissions: NextPage = () => {
|
|||||||
form: router.query.id as string,
|
form: router.query.id as string,
|
||||||
limit: pagination.pageSize,
|
limit: pagination.pageSize,
|
||||||
start: Math.max(0, pagination.current - 1) * pagination.pageSize || 0,
|
start: Math.max(0, pagination.current - 1) * pagination.pageSize || 0,
|
||||||
|
filter: {
|
||||||
|
excludeEmpty: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
onCompleted: ({ pager, form }) => {
|
onCompleted: ({ pager, form }) => {
|
||||||
setPagination({
|
setPagination({
|
||||||
|
|||||||
@ -85,11 +85,11 @@ const Index: NextPage = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link href={'/admin/users/[id]'} as={`/admin/users/${user.id}`}>
|
|
||||||
<Tooltip title={user.email}>
|
<Tooltip title={user.email}>
|
||||||
|
<Link href={`/admin/users/${user.id}`} passHref>
|
||||||
<Button type={'dashed'}>{user.username}</Button>
|
<Button type={'dashed'}>{user.username}</Button>
|
||||||
</Tooltip>
|
|
||||||
</Link>
|
</Link>
|
||||||
|
</Tooltip>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
responsive: ['lg'],
|
responsive: ['lg'],
|
||||||
@ -124,15 +124,15 @@ const Index: NextPage = () => {
|
|||||||
render(_, row) {
|
render(_, row) {
|
||||||
return (
|
return (
|
||||||
<Space direction={width < 600 ? 'vertical' : 'horizontal'}>
|
<Space direction={width < 600 ? 'vertical' : 'horizontal'}>
|
||||||
<Link href={'/admin/forms/[id]/submissions'} as={`/admin/forms/${row.id}/submissions`}>
|
|
||||||
<Tooltip title={'Show Submissions'}>
|
<Tooltip title={'Show Submissions'}>
|
||||||
|
<Link href={`/admin/forms/${row.id}/submissions`} passHref>
|
||||||
<Button>
|
<Button>
|
||||||
<UnorderedListOutlined />
|
<UnorderedListOutlined />
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
|
||||||
</Link>
|
</Link>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
<Link href={'/admin/forms/[id]'} as={`/admin/forms/${row.id}`}>
|
<Link href={`/admin/forms/${row.id}`} passHref>
|
||||||
<Button type={'primary'}>
|
<Button type={'primary'}>
|
||||||
<EditOutlined />
|
<EditOutlined />
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@ -85,7 +85,7 @@ const Index: NextPage = () => {
|
|||||||
render(_, row) {
|
render(_, row) {
|
||||||
return (
|
return (
|
||||||
<Space direction={width < 600 ? 'vertical' : 'horizontal'}>
|
<Space direction={width < 600 ? 'vertical' : 'horizontal'}>
|
||||||
<Link href={'/admin/users/[id]'} as={`/admin/users/${row.id}`}>
|
<Link href={`/admin/users/${row.id}`} passHref>
|
||||||
<Button type={'primary'}>
|
<Button type={'primary'}>
|
||||||
<EditOutlined />
|
<EditOutlined />
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
20
yarn.lock
20
yarn.lock
@ -2339,6 +2339,11 @@ expand-brackets@^2.1.4:
|
|||||||
snapdragon "^0.8.1"
|
snapdragon "^0.8.1"
|
||||||
to-regex "^3.0.1"
|
to-regex "^3.0.1"
|
||||||
|
|
||||||
|
expressionparser@^1.1.5:
|
||||||
|
version "1.1.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/expressionparser/-/expressionparser-1.1.5.tgz#b16bc45e7557be437c2259e2ab0abd8b857e979b"
|
||||||
|
integrity sha512-9GldsRvXhcJ+ZPiK7fel5KBpgU+LjjQjnyWQVugJcvRc99lXTBICwXKaQV2laZ1KUlTVUBnMmFpgtkMVwwMiHA==
|
||||||
|
|
||||||
ext-list@^2.0.0:
|
ext-list@^2.0.0:
|
||||||
version "2.2.2"
|
version "2.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/ext-list/-/ext-list-2.2.2.tgz#0b98e64ed82f5acf0f2931babf69212ef52ddd37"
|
resolved "https://registry.yarnpkg.com/ext-list/-/ext-list-2.2.2.tgz#0b98e64ed82f5acf0f2931babf69212ef52ddd37"
|
||||||
@ -3713,21 +3718,6 @@ mathjs@*:
|
|||||||
tiny-emitter "^2.1.0"
|
tiny-emitter "^2.1.0"
|
||||||
typed-function "^2.0.0"
|
typed-function "^2.0.0"
|
||||||
|
|
||||||
mathjs@^10.1.1:
|
|
||||||
version "10.1.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/mathjs/-/mathjs-10.1.1.tgz#99b647387b65c4b5c47b71e11d59481473ccfa0d"
|
|
||||||
integrity sha512-4QJP8a0Vy90ajFYESnITSluCrQBZnI+2XQhKJIRdo/6t95oupffS5qA4MTWnLGm5GsEZF179JSMjST7wCdZQkA==
|
|
||||||
dependencies:
|
|
||||||
"@babel/runtime" "^7.16.7"
|
|
||||||
complex.js "^2.0.15"
|
|
||||||
decimal.js "^10.3.1"
|
|
||||||
escape-latex "^1.2.0"
|
|
||||||
fraction.js "^4.1.2"
|
|
||||||
javascript-natural-sort "^0.7.1"
|
|
||||||
seedrandom "^3.0.5"
|
|
||||||
tiny-emitter "^2.1.0"
|
|
||||||
typed-function "^2.0.0"
|
|
||||||
|
|
||||||
mdast-util-definitions@^5.0.0:
|
mdast-util-definitions@^5.0.0:
|
||||||
version "5.1.0"
|
version "5.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/mdast-util-definitions/-/mdast-util-definitions-5.1.0.tgz#b6d10ef00a3c4cf191e8d9a5fa58d7f4a366f817"
|
resolved "https://registry.yarnpkg.com/mdast-util-definitions/-/mdast-util-definitions-5.1.0.tgz#b6d10ef00a3c4cf191e8d9a5fa58d7f4a366f817"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user