mirror of
https://github.com/IT4Change/ohmyform-ui.git
synced 2025-12-13 09:45:50 +00:00
add slug and fix missing admin error
https://github.com/ohmyform/ohmyform/issues/100
This commit is contained in:
parent
3f5104bee5
commit
97a9e1dc60
@ -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
|
||||
|
||||
@ -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')}
|
||||
|
||||
@ -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={{
|
||||
|
||||
@ -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'}
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -3,4 +3,5 @@ import { FormDesignFragment, FormFieldFragment } from '../../../graphql/fragment
|
||||
export interface FieldTypeProps {
|
||||
field: FormFieldFragment
|
||||
design: FormDesignFragment
|
||||
urlValue?: string
|
||||
}
|
||||
|
||||
@ -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 })}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user