add slug and fix missing admin error

https://github.com/ohmyform/ohmyform/issues/100
This commit is contained in:
Michael Schramm 2020-06-19 11:34:25 +02:00
parent 3f5104bee5
commit 97a9e1dc60
18 changed files with 114 additions and 23 deletions

View File

@ -8,6 +8,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## UNRELEASED
### Added
- slug for fields to be able to set value by url parameter
### Changed
- minify containers to reduce layer size

View File

@ -1,5 +1,5 @@
import { DeleteOutlined } from '@ant-design/icons/lib'
import { Button, Card, Checkbox, Form, Input, Popconfirm, Tag } from 'antd'
import { Button, Card, Checkbox, Form, Input, Popconfirm, Popover, Tag } from 'antd'
import { FormInstance } from 'antd/lib/form'
import { FieldData } from 'rc-field-form/lib/interface'
import React, { useEffect, useState } from 'react'
@ -53,7 +53,27 @@ export const FieldCard: React.FC<Props> = (props) => {
type={'inner'}
extra={
<div>
<Tag color={'blue'}>{t(`type:${type}.name`)}</Tag>
<Popover
placement={'left'}
content={
<Form.Item
name={[field.name as string, 'slug']}
label={false}
rules={[
{
pattern: /^[a-z0-9_]+$/,
message: t('validation:invalidSlug'),
},
]}
help={t('type:slugInfo')}
>
<Input />
</Form.Item>
}
title={t('type:slug')}
>
<Tag color={'blue'}>{t(`type:${type}.name`)}</Tag>
</Popover>
<Popconfirm
placement={'left'}
title={t('type:confirmDelete')}

View File

@ -1,5 +1,6 @@
import { Form, message } from 'antd'
import { useForm } from 'antd/lib/form/Form'
import { useRouter } from 'next/router'
import React from 'react'
import { FormDesignFragment, FormFieldFragment } from '../../graphql/fragment/form.fragment'
import { StyledButton } from '../styled/button'
@ -21,6 +22,7 @@ interface Props {
export const Field: React.FC<Props> = ({ field, save, design, next, prev, ...props }) => {
const [form] = useForm()
const router = useRouter()
const FieldInput: React.FC<FieldTypeProps> = fieldTypes[field.type] || TextType
@ -34,6 +36,18 @@ export const Field: React.FC<Props> = ({ field, save, design, next, prev, ...pro
await message.error('Check inputs!')
}
const getUrlDefault = (): string => {
if (router.query[field.id]) {
return router.query[field.id] as string
}
if (router.query[field.slug]) {
return router.query[field.slug] as string
}
return undefined
}
return (
<Form
form={form}
@ -61,7 +75,7 @@ export const Field: React.FC<Props> = ({ field, save, design, next, prev, ...pro
<StyledMarkdown design={design} type={'question'} source={field.description} />
)}
<FieldInput design={design} field={field} />
<FieldInput design={design} field={field} urlValue={getUrlDefault()} />
</div>
<div
style={{

View File

@ -6,7 +6,7 @@ import { useTranslation } from 'react-i18next'
import { StyledDateInput } from '../../styled/date.input'
import { FieldTypeProps } from './type.props'
export const DateType: React.FC<FieldTypeProps> = ({ field, design }) => {
export const DateType: React.FC<FieldTypeProps> = ({ field, design, urlValue }) => {
const [min, setMin] = useState<Dayjs>()
const [max, setMax] = useState<Dayjs>()
const { t } = useTranslation()
@ -22,6 +22,16 @@ export const DateType: React.FC<FieldTypeProps> = ({ field, design }) => {
})
}, [field])
let initialValue: Moment = undefined
if (field.value) {
initialValue = moment(field.value)
}
if (urlValue) {
initialValue = moment(field.value)
}
return (
<div>
<Form.Item
@ -29,7 +39,7 @@ export const DateType: React.FC<FieldTypeProps> = ({ field, design }) => {
rules={[{ required: field.required, message: t('validation:valueRequired') }]}
getValueFromEvent={(e: Moment) => e.format('YYYY-MM-DD')}
getValueProps={(e: string) => ({ value: e ? moment(e) : undefined })}
initialValue={field.value ? moment(field.value) : undefined}
initialValue={initialValue}
>
<StyledDateInput
size={'large'}

View File

@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next'
import { StyledSelect } from '../../styled/select'
import { FieldTypeProps } from './type.props'
export const DropdownType: React.FC<FieldTypeProps> = ({ field, design }) => {
export const DropdownType: React.FC<FieldTypeProps> = ({ field, design, urlValue }) => {
const [open, setOpen] = useState(false)
const { t } = useTranslation()
@ -13,7 +13,7 @@ export const DropdownType: React.FC<FieldTypeProps> = ({ field, design }) => {
<Form.Item
name={[field.id, 'value']}
rules={[{ required: field.required, message: t('validation:valueRequired') }]}
initialValue={field.value || null}
initialValue={urlValue || field.value || null}
>
<StyledSelect
design={design}

View File

@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next'
import { StyledInput } from '../../styled/input'
import { FieldTypeProps } from './type.props'
export const EmailType: React.FC<FieldTypeProps> = ({ field, design }) => {
export const EmailType: React.FC<FieldTypeProps> = ({ field, design, urlValue }) => {
const { t } = useTranslation()
return (
@ -15,7 +15,7 @@ export const EmailType: React.FC<FieldTypeProps> = ({ field, design }) => {
{ required: field.required, message: t('validation:valueRequired') },
{ type: 'email', message: t('validation:invalidEmail') },
]}
initialValue={field.value}
initialValue={urlValue || field.value}
>
<StyledInput design={design} allowClear size={'large'} />
</Form.Item>

View File

@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next'
import { StyledInput } from '../../styled/input'
import { FieldTypeProps } from './type.props'
export const LinkType: React.FC<FieldTypeProps> = ({ field, design }) => {
export const LinkType: React.FC<FieldTypeProps> = ({ field, design, urlValue }) => {
const { t } = useTranslation()
return (
@ -15,7 +15,7 @@ export const LinkType: React.FC<FieldTypeProps> = ({ field, design }) => {
{ required: field.required, message: t('validation:valueRequired') },
{ type: 'url', message: t('validation:invalidUrl') },
]}
initialValue={field.value}
initialValue={urlValue || field.value}
>
<StyledInput design={design} allowClear size={'large'} />
</Form.Item>

View File

@ -4,9 +4,19 @@ import { useTranslation } from 'react-i18next'
import { StyledNumberInput } from '../../styled/number.input'
import { FieldTypeProps } from './type.props'
export const NumberType: React.FC<FieldTypeProps> = ({ field, design }) => {
export const NumberType: React.FC<FieldTypeProps> = ({ field, design, urlValue }) => {
const { t } = useTranslation()
let initialValue: number = undefined
if (field.value) {
initialValue = parseFloat(field.value)
}
if (urlValue) {
initialValue = parseFloat(urlValue)
}
return (
<div>
<Form.Item
@ -15,7 +25,7 @@ export const NumberType: React.FC<FieldTypeProps> = ({ field, design }) => {
{ type: 'number', message: t('validation:invalidNumber') },
{ required: field.required, message: t('validation:valueRequired') },
]}
initialValue={typeof field.value === 'number' ? parseFloat(field.value) : undefined}
initialValue={initialValue}
>
<StyledNumberInput design={design} size={'large'} />
</Form.Item>

View File

@ -4,9 +4,19 @@ import { useTranslation } from 'react-i18next'
import { StyledRadio } from '../../styled/radio'
import { FieldTypeProps } from './type.props'
export const RadioType: React.FC<FieldTypeProps> = ({ field, design }) => {
export const RadioType: React.FC<FieldTypeProps> = ({ field, design, urlValue }) => {
const { t } = useTranslation()
let initialValue: string = undefined
if (field.value) {
initialValue = field.value
}
if (urlValue) {
initialValue = urlValue
}
return (
<div>
<Form.Item
@ -14,7 +24,7 @@ export const RadioType: React.FC<FieldTypeProps> = ({ field, design }) => {
rules={[{ required: field.required, message: t('validation:valueRequired') }]}
initialValue={field.options
.map((option) => option.value)
.find((value) => value === field.value)}
.find((value) => value === initialValue)}
>
<Radio.Group>
{field.options

View File

@ -3,16 +3,26 @@ import React from 'react'
import { useTranslation } from 'react-i18next'
import { FieldTypeProps } from './type.props'
export const RatingType: React.FC<FieldTypeProps> = ({ field }) => {
export const RatingType: React.FC<FieldTypeProps> = ({ field, urlValue }) => {
const { t } = useTranslation()
// TODO add ratings
let initialValue: number = undefined
if (field.value) {
initialValue = parseFloat(field.value)
}
if (urlValue) {
initialValue = parseFloat(urlValue)
}
return (
<div>
<Form.Item
name={[field.id, 'value']}
rules={[{ required: field.required, message: t('validation:valueRequired') }]}
initialValue={parseFloat(field.value)}
initialValue={initialValue}
>
<Rate allowHalf />
</Form.Item>

View File

@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next'
import { StyledInput } from '../../styled/input'
import { FieldTypeProps } from './type.props'
export const TextType: React.FC<FieldTypeProps> = ({ field, design }) => {
export const TextType: React.FC<FieldTypeProps> = ({ field, design, urlValue }) => {
const { t } = useTranslation()
// TODO focus when becomes visible
@ -13,7 +13,7 @@ export const TextType: React.FC<FieldTypeProps> = ({ field, design }) => {
<Form.Item
name={[field.id, 'value']}
rules={[{ required: field.required, message: t('validation:valueRequired') }]}
initialValue={field.value}
initialValue={urlValue || field.value}
>
<StyledInput design={design} allowClear size={'large'} />
</Form.Item>

View File

@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next'
import { StyledTextareaInput } from '../../styled/textarea.input'
import { FieldTypeProps } from './type.props'
export const TextareaType: React.FC<FieldTypeProps> = ({ field, design }) => {
export const TextareaType: React.FC<FieldTypeProps> = ({ field, design, urlValue }) => {
const { t } = useTranslation()
return (
@ -12,7 +12,7 @@ export const TextareaType: React.FC<FieldTypeProps> = ({ field, design }) => {
<Form.Item
name={[field.id, 'value']}
rules={[{ required: field.required, message: t('validation:valueRequired') }]}
initialValue={field.value}
initialValue={urlValue || field.value}
>
<StyledTextareaInput design={design} allowClear autoSize />
</Form.Item>

View File

@ -3,4 +3,5 @@ import { FormDesignFragment, FormFieldFragment } from '../../../graphql/fragment
export interface FieldTypeProps {
field: FormFieldFragment
design: FormDesignFragment
urlValue?: string
}

View File

@ -3,15 +3,21 @@ import React from 'react'
import { useTranslation } from 'react-i18next'
import { FieldTypeProps } from './type.props'
export const YesNoType: React.FC<FieldTypeProps> = ({ field }) => {
export const YesNoType: React.FC<FieldTypeProps> = ({ field, urlValue }) => {
const { t } = useTranslation()
let initialValue = !!field.value
if (urlValue !== undefined) {
initialValue = !!urlValue
}
return (
<div>
<Form.Item
name={[field.id, 'value']}
rules={[{ required: field.required, message: t('validation:valueRequired') }]}
initialValue={!!field.value}
initialValue={initialValue}
valuePropName={'checked'}
getValueFromEvent={(checked: boolean) => (checked ? '1' : '')}
getValueProps={(e: string) => ({ checked: !!e })}

View File

@ -35,6 +35,7 @@ export interface AdminFormFieldLogicJumpFragment {
export interface AdminFormFieldFragment {
id: string
title: string
slug?: string
type: string
description: string
required: boolean
@ -106,6 +107,7 @@ export const ADMIN_FORM_FRAGMENT = gql`
fields {
id
title
slug
type
description
required

View File

@ -32,6 +32,7 @@ export interface FormFieldLogicJumpFragment {
export interface FormFieldFragment {
id: string
title: string
slug?: string
type: string
description: string
required: boolean
@ -81,6 +82,7 @@ export const FORM_FRAGMENT = gql`
fields {
id
title
slug
type
description
required

View File

@ -51,6 +51,8 @@
},
"required": "Required",
"requiredInfo": "If required, default value must be set to enable users to submit form!",
"slug": "Slug",
"slugInfo": "Use slug to pass the default dynamic as url parameter ?slug=value",
"textarea": {
"default": "Default Value",
"name": "Text Area"

View File

@ -3,6 +3,7 @@
"emailRequired": "Please provide an Email",
"invalidEmail": "Must be a valid email!",
"invalidNumber": "Must be a valid number!",
"invalidSlug": "Slug can only contain lowercase a-z, 0-9 and _",
"invalidUrl": "Must be a valid URL",
"languageRequired": "Please select a Language",
"mandatoryFieldsMissing": "Mandatory fields missing",