mirror of
https://github.com/IT4Change/ohmyform-ui.git
synced 2025-12-13 09:45:50 +00:00
apply eslint
This commit is contained in:
parent
c2608f047e
commit
8d81390c83
16
.eslintrc.js
16
.eslintrc.js
@ -2,21 +2,25 @@ module.exports = {
|
||||
root: true,
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
ecmaFeatures: { jsx: true }
|
||||
ecmaFeatures: { jsx: true },
|
||||
},
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/eslint-recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:react/recommended',
|
||||
"plugin:jsx-a11y/recommended",
|
||||
'plugin:jsx-a11y/recommended',
|
||||
'prettier/@typescript-eslint',
|
||||
'plugin:prettier/recommended',
|
||||
],
|
||||
rules: {
|
||||
'prettier/prettier': [
|
||||
"error", {}, { "usePrettierrc": true }
|
||||
],
|
||||
'prettier/prettier': ['error', {}, { usePrettierrc: true }],
|
||||
'react/prop-types': 'off',
|
||||
'@typescript-eslint/no-empty-interface': 'off',
|
||||
},
|
||||
};
|
||||
settings: {
|
||||
react: {
|
||||
version: 'detect',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@ -9,4 +9,3 @@ cache:
|
||||
script:
|
||||
- yarn
|
||||
- yarn lint
|
||||
- yarn build
|
||||
|
||||
@ -1,13 +1,12 @@
|
||||
import {UpOutlined} from '@ant-design/icons/lib'
|
||||
import {useQuery} from '@apollo/react-hooks'
|
||||
import {Button, Menu, Select} from 'antd'
|
||||
import { useQuery } from '@apollo/react-hooks'
|
||||
import { Button, Select } from 'antd'
|
||||
import Link from 'next/link'
|
||||
import {useRouter} from 'next/router'
|
||||
import { useRouter } from 'next/router'
|
||||
import React from 'react'
|
||||
import {useTranslation} from 'react-i18next'
|
||||
import {SETTINGS_QUERY, SettingsQueryData} from '../../graphql/query/settings.query'
|
||||
import {languages} from '../../i18n'
|
||||
import {clearAuth, withAuth} from '../with.auth'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { SETTINGS_QUERY, SettingsQueryData } from '../../graphql/query/settings.query'
|
||||
import { languages } from '../../i18n'
|
||||
import { clearAuth, withAuth } from '../with.auth'
|
||||
|
||||
interface Props {
|
||||
me?: {
|
||||
@ -16,10 +15,10 @@ interface Props {
|
||||
}
|
||||
}
|
||||
|
||||
const AuthFooterInner: React.FC<Props> = props => {
|
||||
const AuthFooterInner: React.FC<Props> = (props) => {
|
||||
const { t, i18n } = useTranslation()
|
||||
const router = useRouter()
|
||||
const {data} = useQuery<SettingsQueryData>(SETTINGS_QUERY)
|
||||
const { data } = useQuery<SettingsQueryData>(SETTINGS_QUERY)
|
||||
|
||||
const logout = () => {
|
||||
clearAuth()
|
||||
@ -38,67 +37,55 @@ const AuthFooterInner: React.FC<Props> = props => {
|
||||
}}
|
||||
>
|
||||
<Link href={'/admin'}>
|
||||
<Button
|
||||
type={'link'}
|
||||
ghost
|
||||
>
|
||||
<Button type={'link'} ghost>
|
||||
{t('admin')}
|
||||
</Button>
|
||||
</Link>
|
||||
{props.me ? (
|
||||
[
|
||||
<span style={{color: '#FFF'}} key={'user'}>
|
||||
{props.me
|
||||
? [
|
||||
<span style={{ color: '#FFF' }} key={'user'}>
|
||||
Hi, {props.me.username}
|
||||
</span>,
|
||||
<Button
|
||||
key={'logout'}
|
||||
type={'link'}
|
||||
ghost
|
||||
onClick={logout}
|
||||
>
|
||||
<Button key={'logout'} type={'link'} ghost onClick={logout}>
|
||||
{t('logout')}
|
||||
</Button>
|
||||
</Button>,
|
||||
]
|
||||
): (
|
||||
[
|
||||
: [
|
||||
<Link href={'/login'} key={'login'}>
|
||||
<Button
|
||||
type={'link'}
|
||||
ghost
|
||||
>
|
||||
<Button type={'link'} ghost>
|
||||
{t('login')}
|
||||
</Button>
|
||||
</Link>,
|
||||
<Link href={'/register'} key={'register'}>
|
||||
<Button
|
||||
type={'link'}
|
||||
ghost
|
||||
disabled={data ? data.disabledSignUp.value : false}
|
||||
>
|
||||
<Button type={'link'} ghost disabled={data ? data.disabledSignUp.value : false}>
|
||||
{t('register')}
|
||||
</Button>
|
||||
</Link>
|
||||
]
|
||||
)}
|
||||
<div style={{flex: 1}} />
|
||||
</Link>,
|
||||
]}
|
||||
<div style={{ flex: 1 }} />
|
||||
<Select
|
||||
bordered={false}
|
||||
value={i18n.language.replace(/-.*/, '')}
|
||||
onChange={next => i18n.changeLanguage(next)}
|
||||
onChange={(next) => i18n.changeLanguage(next)}
|
||||
style={{
|
||||
color: '#FFF',
|
||||
}}
|
||||
suffixIcon={false}
|
||||
>
|
||||
{languages.map(language => <Select.Option value={language} key={language}>{t(`language:${language}`)}</Select.Option> )}
|
||||
{languages.map((language) => (
|
||||
<Select.Option value={language} key={language}>
|
||||
{t(`language:${language}`)}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
<Button
|
||||
type={'link'}
|
||||
target={'_blank'}
|
||||
rel={'noreferrer'}
|
||||
ghost
|
||||
href={'https://www.ohmyform.com'}
|
||||
style={{
|
||||
color: '#FFF'
|
||||
color: '#FFF',
|
||||
}}
|
||||
>
|
||||
© OhMyForm
|
||||
|
||||
@ -1,17 +1,19 @@
|
||||
import {Layout, Spin} from 'antd'
|
||||
import { Layout, Spin } from 'antd'
|
||||
import React from 'react'
|
||||
|
||||
interface Props {
|
||||
loading?: boolean
|
||||
}
|
||||
|
||||
export const AuthLayout: React.FC<Props> = props => {
|
||||
export const AuthLayout: React.FC<Props> = (props) => {
|
||||
return (
|
||||
<Spin spinning={props.loading}>
|
||||
<Layout style={{
|
||||
<Layout
|
||||
style={{
|
||||
height: '100vh',
|
||||
background: '#437fdc'
|
||||
}}>
|
||||
background: '#437fdc',
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
</Layout>
|
||||
</Spin>
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
|
||||
const omitDeepArrayWalk = (arr, key) => {
|
||||
return arr.map((val) => {
|
||||
if (Array.isArray(val)) return omitDeepArrayWalk(val, key)
|
||||
@ -7,19 +6,24 @@ const omitDeepArrayWalk = (arr, key) => {
|
||||
})
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const omitDeep = (obj: any, key: string | number): any => {
|
||||
const keys: Array<any> = Object.keys(obj);
|
||||
const newObj: any = {};
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const keys: Array<any> = Object.keys(obj)
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const newObj: any = {}
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
keys.forEach((i: any) => {
|
||||
if (i !== key) {
|
||||
const val: any = obj[i];
|
||||
if (val instanceof Date) newObj[i] = val;
|
||||
else if (Array.isArray(val)) newObj[i] = omitDeepArrayWalk(val, key);
|
||||
else if (typeof val === 'object' && val !== null) newObj[i] = omitDeep(val, key);
|
||||
else newObj[i] = val;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const val: any = obj[i]
|
||||
if (val instanceof Date) newObj[i] = val
|
||||
else if (Array.isArray(val)) newObj[i] = omitDeepArrayWalk(val, key)
|
||||
else if (typeof val === 'object' && val !== null) newObj[i] = omitDeep(val, key)
|
||||
else newObj[i] = val
|
||||
}
|
||||
});
|
||||
return newObj;
|
||||
})
|
||||
return newObj
|
||||
}
|
||||
|
||||
export const cleanInput = <T>(obj: T): T => {
|
||||
|
||||
@ -7,13 +7,15 @@ interface Props {
|
||||
hideTime?: boolean
|
||||
}
|
||||
|
||||
export const DateTime: React.FC<Props> = props => {
|
||||
export const DateTime: React.FC<Props> = (props) => {
|
||||
const format = props.hideTime ? 'YYYY-MM-DD' : 'YYYY-MM-DD HH:mm'
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
display: 'inline-block'
|
||||
}}>
|
||||
<div
|
||||
style={{
|
||||
display: 'inline-block',
|
||||
}}
|
||||
>
|
||||
{dayjs(props.date).format(format)}
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -2,13 +2,15 @@ import React from 'react'
|
||||
|
||||
export const ErrorPage: React.FC = () => {
|
||||
return (
|
||||
<div style={{
|
||||
<div
|
||||
style={{
|
||||
height: '100vh',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<h1>ERROR</h1>
|
||||
<p>there was an error with your request</p>
|
||||
</div>
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import {Form, Input, Select, Switch, Tabs} from 'antd'
|
||||
import {TabPaneProps} from 'antd/lib/tabs'
|
||||
import { Form, Input, Select, Switch, Tabs } from 'antd'
|
||||
import { TabPaneProps } from 'antd/lib/tabs'
|
||||
import React from 'react'
|
||||
import {useTranslation} from 'react-i18next'
|
||||
import {languages} from '../../../i18n'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { languages } from '../../../i18n'
|
||||
|
||||
export const BaseDataTab: React.FC<TabPaneProps> = props => {
|
||||
export const BaseDataTab: React.FC<TabPaneProps> = (props) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
@ -41,7 +41,11 @@ export const BaseDataTab: React.FC<TabPaneProps> = props => {
|
||||
]}
|
||||
>
|
||||
<Select>
|
||||
{languages.map(language => <Select.Option value={language} key={language}>{t(`language:${language}`)}</Select.Option> )}
|
||||
{languages.map((language) => (
|
||||
<Select.Option value={language} key={language}>
|
||||
{t(`language:${language}`)}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
@ -52,7 +56,6 @@ export const BaseDataTab: React.FC<TabPaneProps> = props => {
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
|
||||
</Tabs.TabPane>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,18 +1,15 @@
|
||||
import {Form, Input, Tabs} from 'antd'
|
||||
import {TabPaneProps} from 'antd/lib/tabs'
|
||||
import { Form, Input, Tabs } from 'antd'
|
||||
import { TabPaneProps } from 'antd/lib/tabs'
|
||||
import React from 'react'
|
||||
import {useTranslation} from 'react-i18next'
|
||||
import {InputColor} from '../../input/color'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { InputColor } from '../../input/color'
|
||||
|
||||
export const DesignTab: React.FC<TabPaneProps> = props => {
|
||||
export const DesignTab: React.FC<TabPaneProps> = (props) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<Tabs.TabPane {...props}>
|
||||
<Form.Item
|
||||
label={t('form:design.font')}
|
||||
name={['form', 'design', 'font']}
|
||||
>
|
||||
<Form.Item label={t('form:design.font')} name={['form', 'design', 'font']}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
|
||||
@ -23,8 +20,12 @@ export const DesignTab: React.FC<TabPaneProps> = props => {
|
||||
'buttonColor',
|
||||
'buttonActiveColor',
|
||||
'buttonTextColor',
|
||||
].map(name => (
|
||||
<Form.Item key={name} label={t(`form:design.${name}`)} name={['form', 'design', 'colors', name]}>
|
||||
].map((name) => (
|
||||
<Form.Item
|
||||
key={name}
|
||||
label={t(`form:design.${name}`)}
|
||||
name={['form', 'design', 'colors', name]}
|
||||
>
|
||||
<InputColor />
|
||||
</Form.Item>
|
||||
))}
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import {DeleteOutlined, PlusOutlined} from '@ant-design/icons/lib'
|
||||
import {Button, Card, Form, Input, Switch, Tabs} from 'antd'
|
||||
import {TabPaneProps} from 'antd/lib/tabs'
|
||||
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons/lib'
|
||||
import { Button, Card, Form, Input, Switch, Tabs } from 'antd'
|
||||
import { TabPaneProps } from 'antd/lib/tabs'
|
||||
import React from 'react'
|
||||
import {useTranslation} from 'react-i18next'
|
||||
import {InputColor} from '../../input/color'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { InputColor } from '../../input/color'
|
||||
|
||||
export const EndPageTab: React.FC<TabPaneProps> = props => {
|
||||
export const EndPageTab: React.FC<TabPaneProps> = (props) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
@ -18,17 +18,11 @@ export const EndPageTab: React.FC<TabPaneProps> = props => {
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label={t('form:endPage.title')}
|
||||
name={['form', 'endPage', 'title']}
|
||||
>
|
||||
<Form.Item label={t('form:endPage.title')} name={['form', 'endPage', 'title']}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label={t('form:endPage.paragraph')}
|
||||
name={['form', 'endPage', 'paragraph']}
|
||||
>
|
||||
<Form.Item label={t('form:endPage.paragraph')} name={['form', 'endPage', 'paragraph']}>
|
||||
<Input.TextArea autoSize />
|
||||
</Form.Item>
|
||||
|
||||
@ -39,9 +33,7 @@ export const EndPageTab: React.FC<TabPaneProps> = props => {
|
||||
<Input />
|
||||
</Form.Item>
|
||||
|
||||
<Form.List
|
||||
name={['form', 'endPage', 'buttons']}
|
||||
>
|
||||
<Form.List name={['form', 'endPage', 'buttons']}>
|
||||
{(fields, { add, remove }) => {
|
||||
return (
|
||||
<div>
|
||||
@ -53,40 +45,53 @@ export const EndPageTab: React.FC<TabPaneProps> = props => {
|
||||
label={index === 0 ? t('form:endPage.buttons') : ''}
|
||||
key={field.key}
|
||||
>
|
||||
<Card
|
||||
actions={[
|
||||
<DeleteOutlined key={'delete'} onClick={() => remove(index)} />
|
||||
]}
|
||||
>
|
||||
<Card actions={[<DeleteOutlined key={'delete'} onClick={() => remove(index)} />]}>
|
||||
<Form.Item
|
||||
label={t('form:endPage.url')}
|
||||
name={[field.key, 'url']}
|
||||
rules={[
|
||||
{type: 'url', message: t('validation:invalidUrl')}
|
||||
]}
|
||||
rules={[{ type: 'url', message: t('validation:invalidUrl') }]}
|
||||
labelCol={{ span: 6 }}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t('form:endPage.action')} name={[field.key, 'action']} labelCol={{ span: 6 }}>
|
||||
<Form.Item
|
||||
label={t('form:endPage.action')}
|
||||
name={[field.key, 'action']}
|
||||
labelCol={{ span: 6 }}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t('form:endPage.text')} name={[field.key, 'text']} labelCol={{ span: 6 }}>
|
||||
<Form.Item
|
||||
label={t('form:endPage.text')}
|
||||
name={[field.key, 'text']}
|
||||
labelCol={{ span: 6 }}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t('form:endPage.bgColor')} name={[field.key, 'bgColor']} labelCol={{ span: 6 }}>
|
||||
<Form.Item
|
||||
label={t('form:endPage.bgColor')}
|
||||
name={[field.key, 'bgColor']}
|
||||
labelCol={{ span: 6 }}
|
||||
>
|
||||
<InputColor />
|
||||
</Form.Item>
|
||||
<Form.Item label={t('form:endPage.activeColor')} name={[field.key, 'activeColor']} labelCol={{ span: 6 }}>
|
||||
<Form.Item
|
||||
label={t('form:endPage.activeColor')}
|
||||
name={[field.key, 'activeColor']}
|
||||
labelCol={{ span: 6 }}
|
||||
>
|
||||
<InputColor />
|
||||
</Form.Item>
|
||||
<Form.Item label={t('form:endPage.color')} name={[field.key, 'color']} labelCol={{ span: 6 }}>
|
||||
<Form.Item
|
||||
label={t('form:endPage.color')}
|
||||
name={[field.key, 'color']}
|
||||
labelCol={{ span: 6 }}
|
||||
>
|
||||
<InputColor />
|
||||
</Form.Item>
|
||||
</Card>
|
||||
</Form.Item>
|
||||
)
|
||||
)}
|
||||
))}
|
||||
<Form.Item
|
||||
wrapperCol={{
|
||||
sm: { offset: 6 },
|
||||
@ -95,7 +100,7 @@ export const EndPageTab: React.FC<TabPaneProps> = props => {
|
||||
<Button
|
||||
type="dashed"
|
||||
onClick={() => {
|
||||
add();
|
||||
add()
|
||||
}}
|
||||
style={{ width: '60%' }}
|
||||
>
|
||||
|
||||
@ -1,41 +1,37 @@
|
||||
import {DeleteOutlined} from '@ant-design/icons/lib'
|
||||
import {Button, Card, Checkbox, Form, Input, Popconfirm, Tag} from 'antd'
|
||||
import {FormInstance} from 'antd/lib/form'
|
||||
import {FieldData} from 'rc-field-form/lib/interface'
|
||||
import React, {useEffect, useState} from 'react'
|
||||
import {useTranslation} from 'react-i18next'
|
||||
import {AdminFormFieldFragment} from '../../../graphql/fragment/admin.form.fragment'
|
||||
import {adminTypes} from './types'
|
||||
import {TextType} from './types/text.type'
|
||||
import { DeleteOutlined } from '@ant-design/icons/lib'
|
||||
import { Button, Card, Checkbox, Form, Input, Popconfirm, Tag } from 'antd'
|
||||
import { FormInstance } from 'antd/lib/form'
|
||||
import { FieldData } from 'rc-field-form/lib/interface'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { AdminFormFieldFragment } from '../../../graphql/fragment/admin.form.fragment'
|
||||
import { adminTypes } from './types'
|
||||
import { TextType } from './types/text.type'
|
||||
|
||||
interface Props {
|
||||
form: FormInstance
|
||||
fields: AdminFormFieldFragment[]
|
||||
onChangeFields: (fields: AdminFormFieldFragment[]) => any
|
||||
onChangeFields: (fields: AdminFormFieldFragment[]) => void
|
||||
field: FieldData
|
||||
remove: (index: number) => void
|
||||
index: number
|
||||
}
|
||||
|
||||
export const FieldCard: React.FC<Props> = props => {
|
||||
export const FieldCard: React.FC<Props> = (props) => {
|
||||
const { t } = useTranslation()
|
||||
const {
|
||||
form,
|
||||
field,
|
||||
fields,
|
||||
onChangeFields,
|
||||
remove,
|
||||
index,
|
||||
} = props
|
||||
const { form, field, fields, onChangeFields, remove, index } = props
|
||||
|
||||
const type = form.getFieldValue(['form', 'fields', field.name as string, 'type'])
|
||||
const TypeComponent = adminTypes[type] || TextType
|
||||
|
||||
const [nextTitle, setNextTitle] = useState(form.getFieldValue(['form', 'fields', field.name as string, 'title']))
|
||||
const [nextTitle, setNextTitle] = useState(
|
||||
form.getFieldValue(['form', 'fields', field.name as string, 'title'])
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
const id = setTimeout(() => {
|
||||
onChangeFields(fields.map((field, i) => {
|
||||
onChangeFields(
|
||||
fields.map((field, i) => {
|
||||
if (i === index) {
|
||||
return {
|
||||
...field,
|
||||
@ -44,7 +40,8 @@ export const FieldCard: React.FC<Props> = props => {
|
||||
} else {
|
||||
return field
|
||||
}
|
||||
}))
|
||||
})
|
||||
)
|
||||
}, 500)
|
||||
|
||||
return () => clearTimeout(id)
|
||||
@ -54,7 +51,7 @@ export const FieldCard: React.FC<Props> = props => {
|
||||
<Card
|
||||
title={nextTitle}
|
||||
type={'inner'}
|
||||
extra={(
|
||||
extra={
|
||||
<div>
|
||||
<Tag color={'blue'}>{t(`type:${type}.name`)}</Tag>
|
||||
<Popconfirm
|
||||
@ -67,24 +64,24 @@ export const FieldCard: React.FC<Props> = props => {
|
||||
onChangeFields(fields.filter((e, i) => i !== index))
|
||||
}}
|
||||
>
|
||||
<Button danger><DeleteOutlined /></Button>
|
||||
<Button danger>
|
||||
<DeleteOutlined />
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
</div>
|
||||
)}
|
||||
actions={[
|
||||
<DeleteOutlined key={'delete'} onClick={() => remove(index)} />
|
||||
]}
|
||||
}
|
||||
actions={[<DeleteOutlined key={'delete'} onClick={() => remove(index)} />]}
|
||||
>
|
||||
<Form.Item name={[field.name as string, 'type']} noStyle><Input type={'hidden'} /></Form.Item>
|
||||
<Form.Item name={[field.name as string, 'type']} noStyle>
|
||||
<Input type={'hidden'} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t('type:title')}
|
||||
name={[field.name as string, 'title']}
|
||||
rules={[
|
||||
{ required: true, message: 'Title is required' }
|
||||
]}
|
||||
rules={[{ required: true, message: 'Title is required' }]}
|
||||
labelCol={{ span: 6 }}
|
||||
>
|
||||
<Input onChange={e => setNextTitle(e.target.value)}/>
|
||||
<Input onChange={(e) => setNextTitle(e.target.value)} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t('type:description')}
|
||||
@ -103,10 +100,7 @@ export const FieldCard: React.FC<Props> = props => {
|
||||
<Checkbox />
|
||||
</Form.Item>
|
||||
|
||||
<TypeComponent
|
||||
field={field}
|
||||
form={form}
|
||||
/>
|
||||
<TypeComponent field={field} form={form} />
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,24 +1,25 @@
|
||||
import {PlusOutlined} from '@ant-design/icons/lib'
|
||||
import {Button, Form, Select, Space, Tabs} from 'antd'
|
||||
import {FormInstance} from 'antd/lib/form'
|
||||
import {TabPaneProps} from 'antd/lib/tabs'
|
||||
import React, {useCallback, useState} from 'react'
|
||||
import {useTranslation} from 'react-i18next'
|
||||
import {AdminFormFieldFragment} from '../../../graphql/fragment/admin.form.fragment'
|
||||
import {FieldCard} from './field.card'
|
||||
import {adminTypes} from './types'
|
||||
import { PlusOutlined } from '@ant-design/icons/lib'
|
||||
import { Button, Form, Select, Space, Tabs } from 'antd'
|
||||
import { FormInstance } from 'antd/lib/form'
|
||||
import { TabPaneProps } from 'antd/lib/tabs'
|
||||
import React, { useCallback, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { AdminFormFieldFragment } from '../../../graphql/fragment/admin.form.fragment'
|
||||
import { FieldCard } from './field.card'
|
||||
import { adminTypes } from './types'
|
||||
|
||||
interface Props extends TabPaneProps {
|
||||
form: FormInstance
|
||||
fields: AdminFormFieldFragment[]
|
||||
onChangeFields: (fields: AdminFormFieldFragment[]) => any
|
||||
onChangeFields: (fields: AdminFormFieldFragment[]) => void
|
||||
}
|
||||
|
||||
export const FieldsTab: React.FC<Props> = props => {
|
||||
export const FieldsTab: React.FC<Props> = (props) => {
|
||||
const { t } = useTranslation()
|
||||
const [nextType, setNextType] = useState('textfield')
|
||||
|
||||
const renderType = useCallback((field, index, remove) => {
|
||||
const renderType = useCallback(
|
||||
(field, index, remove) => {
|
||||
return (
|
||||
<FieldCard
|
||||
form={props.form}
|
||||
@ -29,21 +30,26 @@ export const FieldsTab: React.FC<Props> = props => {
|
||||
onChangeFields={props.onChangeFields}
|
||||
/>
|
||||
)
|
||||
}, [props.fields])
|
||||
},
|
||||
[props.fields]
|
||||
)
|
||||
|
||||
const addField = useCallback((add, index) => {
|
||||
const addField = useCallback(
|
||||
(add, index) => {
|
||||
return (
|
||||
<Form.Item
|
||||
wrapperCol={{span: 24}}
|
||||
>
|
||||
<Form.Item wrapperCol={{ span: 24 }}>
|
||||
<Space
|
||||
style={{
|
||||
width: '100%',
|
||||
justifyContent: 'flex-end',
|
||||
}}
|
||||
>
|
||||
<Select value={nextType} onChange={e => setNextType(e)} style={{ minWidth: 200 }}>
|
||||
{Object.keys(adminTypes).map(type => <Select.Option value={type} key={type}>{t(`type:${type}.name`)}</Select.Option> )}
|
||||
<Select value={nextType} onChange={(e) => setNextType(e)} style={{ minWidth: 200 }}>
|
||||
{Object.keys(adminTypes).map((type) => (
|
||||
<Select.Option value={type} key={type}>
|
||||
{t(`type:${type}.name`)}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
<Button
|
||||
type="dashed"
|
||||
@ -58,7 +64,7 @@ export const FieldsTab: React.FC<Props> = props => {
|
||||
title: '',
|
||||
description: '',
|
||||
required: false,
|
||||
value: ''
|
||||
value: '',
|
||||
}
|
||||
|
||||
add(defaults)
|
||||
@ -72,15 +78,13 @@ export const FieldsTab: React.FC<Props> = props => {
|
||||
</Space>
|
||||
</Form.Item>
|
||||
)
|
||||
}, [props.fields, nextType])
|
||||
|
||||
},
|
||||
[props.fields, nextType]
|
||||
)
|
||||
|
||||
return (
|
||||
<Tabs.TabPane {...props}>
|
||||
|
||||
<Form.List
|
||||
name={['form', 'fields']}
|
||||
>
|
||||
<Form.List name={['form', 'fields']}>
|
||||
{(fields, { add, remove, move }) => {
|
||||
const addAndMove = (index) => (defaults) => {
|
||||
add(defaults)
|
||||
@ -102,7 +106,6 @@ export const FieldsTab: React.FC<Props> = props => {
|
||||
)
|
||||
}}
|
||||
</Form.List>
|
||||
|
||||
</Tabs.TabPane>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,22 +1,26 @@
|
||||
import {CheckCircleOutlined, CloseCircleOutlined} from '@ant-design/icons/lib'
|
||||
import { CheckCircleOutlined, CloseCircleOutlined } from '@ant-design/icons/lib'
|
||||
import React from 'react'
|
||||
|
||||
interface Props {
|
||||
isLive: boolean
|
||||
}
|
||||
|
||||
export const FormIsLive: React.FC<Props> = props => {
|
||||
export const FormIsLive: React.FC<Props> = (props) => {
|
||||
if (props.isLive) {
|
||||
return (
|
||||
<CheckCircleOutlined style={{
|
||||
color: 'green'
|
||||
}} />
|
||||
<CheckCircleOutlined
|
||||
style={{
|
||||
color: 'green',
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<CloseCircleOutlined style={{
|
||||
color: 'red'
|
||||
}} />
|
||||
<CloseCircleOutlined
|
||||
style={{
|
||||
color: 'red',
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
import {InfoCircleOutlined} from '@ant-design/icons/lib'
|
||||
import {Form, Input, Select, Switch, Tabs} from 'antd'
|
||||
import {FormInstance} from 'antd/lib/form'
|
||||
import {TabPaneProps} from 'antd/lib/tabs'
|
||||
import React, {useEffect, useState} from 'react'
|
||||
import {Trans, useTranslation} from 'react-i18next'
|
||||
import {AdminFormFieldFragment} from '../../../graphql/fragment/admin.form.fragment'
|
||||
import { InfoCircleOutlined } from '@ant-design/icons/lib'
|
||||
import { Form, Input, Select, Switch, Tabs } from 'antd'
|
||||
import { FormInstance } from 'antd/lib/form'
|
||||
import { TabPaneProps } from 'antd/lib/tabs'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { AdminFormFieldFragment } from '../../../graphql/fragment/admin.form.fragment'
|
||||
|
||||
interface Props extends TabPaneProps {
|
||||
form: FormInstance
|
||||
fields: AdminFormFieldFragment[]
|
||||
}
|
||||
|
||||
export const RespondentNotificationsTab: React.FC<Props> = props => {
|
||||
export const RespondentNotificationsTab: React.FC<Props> = (props) => {
|
||||
const { t } = useTranslation()
|
||||
const [enabled, setEnabled] = useState<boolean>()
|
||||
|
||||
@ -33,7 +33,7 @@ export const RespondentNotificationsTab: React.FC<Props> = props => {
|
||||
|
||||
const groups = {}
|
||||
|
||||
props.fields.forEach(field => {
|
||||
props.fields.forEach((field) => {
|
||||
if (!groups[field.type]) {
|
||||
groups[field.type] = []
|
||||
}
|
||||
@ -47,7 +47,7 @@ export const RespondentNotificationsTab: React.FC<Props> = props => {
|
||||
name={['form', 'respondentNotifications', 'enabled']}
|
||||
valuePropName={'checked'}
|
||||
>
|
||||
<Switch onChange={e => setEnabled(e.valueOf())} />
|
||||
<Switch onChange={(e) => setEnabled(e.valueOf())} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
@ -72,12 +72,13 @@ export const RespondentNotificationsTab: React.FC<Props> = props => {
|
||||
message: t('validation:templateRequired'),
|
||||
},
|
||||
]}
|
||||
extra={(
|
||||
extra={
|
||||
<div>
|
||||
<Trans>form:respondentNotifications.htmlTemplateInfo</Trans>
|
||||
<a
|
||||
href={'https://mjml.io/try-it-live'}
|
||||
target={'_blank'}
|
||||
rel={'noreferrer'}
|
||||
style={{
|
||||
marginLeft: 16,
|
||||
}}
|
||||
@ -85,7 +86,7 @@ export const RespondentNotificationsTab: React.FC<Props> = props => {
|
||||
<InfoCircleOutlined />
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
}
|
||||
>
|
||||
<Input.TextArea autoSize />
|
||||
</Form.Item>
|
||||
@ -102,10 +103,12 @@ export const RespondentNotificationsTab: React.FC<Props> = props => {
|
||||
]}
|
||||
>
|
||||
<Select>
|
||||
{Object.keys(groups).map(key => (
|
||||
{Object.keys(groups).map((key) => (
|
||||
<Select.OptGroup label={key.toUpperCase()} key={key}>
|
||||
{groups[key].map(field => (
|
||||
<Select.Option value={field.id} key={field.id}>{field.title}</Select.Option>
|
||||
{groups[key].map((field) => (
|
||||
<Select.Option value={field.id} key={field.id}>
|
||||
{field.title}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select.OptGroup>
|
||||
))}
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
import {InfoCircleOutlined} from '@ant-design/icons/lib'
|
||||
import {Form, Input, Select, Switch, Tabs} from 'antd'
|
||||
import {FormInstance} from 'antd/lib/form'
|
||||
import {TabPaneProps} from 'antd/lib/tabs'
|
||||
import React, {useEffect, useState} from 'react'
|
||||
import {Trans, useTranslation} from 'react-i18next'
|
||||
import {AdminFormFieldFragment} from '../../../graphql/fragment/admin.form.fragment'
|
||||
import { InfoCircleOutlined } from '@ant-design/icons/lib'
|
||||
import { Form, Input, Select, Switch, Tabs } from 'antd'
|
||||
import { FormInstance } from 'antd/lib/form'
|
||||
import { TabPaneProps } from 'antd/lib/tabs'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { Trans, useTranslation } from 'react-i18next'
|
||||
import { AdminFormFieldFragment } from '../../../graphql/fragment/admin.form.fragment'
|
||||
|
||||
interface Props extends TabPaneProps {
|
||||
form: FormInstance
|
||||
fields: AdminFormFieldFragment[]
|
||||
}
|
||||
|
||||
export const SelfNotificationsTab: React.FC<Props> = props => {
|
||||
export const SelfNotificationsTab: React.FC<Props> = (props) => {
|
||||
const { t } = useTranslation()
|
||||
const [enabled, setEnabled] = useState<boolean>()
|
||||
|
||||
@ -31,7 +31,7 @@ export const SelfNotificationsTab: React.FC<Props> = props => {
|
||||
}, [enabled])
|
||||
|
||||
const groups = {}
|
||||
props.fields.forEach(field => {
|
||||
props.fields.forEach((field) => {
|
||||
if (!groups[field.type]) {
|
||||
groups[field.type] = []
|
||||
}
|
||||
@ -45,7 +45,7 @@ export const SelfNotificationsTab: React.FC<Props> = props => {
|
||||
name={['form', 'selfNotifications', 'enabled']}
|
||||
valuePropName={'checked'}
|
||||
>
|
||||
<Switch onChange={e => setEnabled(e.valueOf())} />
|
||||
<Switch onChange={(e) => setEnabled(e.valueOf())} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
@ -70,12 +70,13 @@ export const SelfNotificationsTab: React.FC<Props> = props => {
|
||||
message: t('validation:templateRequired'),
|
||||
},
|
||||
]}
|
||||
extra={(
|
||||
extra={
|
||||
<div>
|
||||
<Trans>form:selfNotifications.htmlTemplateInfo</Trans>
|
||||
<a
|
||||
href={'https://mjml.io/try-it-live'}
|
||||
target={'_blank'}
|
||||
rel={'noreferrer'}
|
||||
style={{
|
||||
marginLeft: 16,
|
||||
}}
|
||||
@ -83,7 +84,7 @@ export const SelfNotificationsTab: React.FC<Props> = props => {
|
||||
<InfoCircleOutlined />
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
}
|
||||
>
|
||||
<Input.TextArea autoSize />
|
||||
</Form.Item>
|
||||
@ -94,10 +95,12 @@ export const SelfNotificationsTab: React.FC<Props> = props => {
|
||||
extra={t('form:selfNotifications.fromFieldInfo')}
|
||||
>
|
||||
<Select>
|
||||
{Object.keys(groups).map(key => (
|
||||
{Object.keys(groups).map((key) => (
|
||||
<Select.OptGroup label={key.toUpperCase()} key={key}>
|
||||
{groups[key].map(field => (
|
||||
<Select.Option value={field.id} key={field.id}>{field.title}</Select.Option>
|
||||
{groups[key].map((field) => (
|
||||
<Select.Option value={field.id} key={field.id}>
|
||||
{field.title}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select.OptGroup>
|
||||
))}
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import {DeleteOutlined, PlusOutlined} from '@ant-design/icons/lib'
|
||||
import {Button, Card, Form, Input, Switch, Tabs} from 'antd'
|
||||
import {TabPaneProps} from 'antd/lib/tabs'
|
||||
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons/lib'
|
||||
import { Button, Card, Form, Input, Switch, Tabs } from 'antd'
|
||||
import { TabPaneProps } from 'antd/lib/tabs'
|
||||
import React from 'react'
|
||||
import {useTranslation} from 'react-i18next'
|
||||
import {InputColor} from '../../input/color'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { InputColor } from '../../input/color'
|
||||
|
||||
export const StartPageTab: React.FC<TabPaneProps> = props => {
|
||||
export const StartPageTab: React.FC<TabPaneProps> = (props) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
@ -18,17 +18,11 @@ export const StartPageTab: React.FC<TabPaneProps> = props => {
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label={t('form:startPage.title')}
|
||||
name={['form', 'startPage', 'title']}
|
||||
>
|
||||
<Form.Item label={t('form:startPage.title')} name={['form', 'startPage', 'title']}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label={t('form:startPage.paragraph')}
|
||||
name={['form', 'startPage', 'paragraph']}
|
||||
>
|
||||
<Form.Item label={t('form:startPage.paragraph')} name={['form', 'startPage', 'paragraph']}>
|
||||
<Input.TextArea autoSize />
|
||||
</Form.Item>
|
||||
|
||||
@ -39,9 +33,7 @@ export const StartPageTab: React.FC<TabPaneProps> = props => {
|
||||
<Input />
|
||||
</Form.Item>
|
||||
|
||||
<Form.List
|
||||
name={['form', 'startPage', 'buttons']}
|
||||
>
|
||||
<Form.List name={['form', 'startPage', 'buttons']}>
|
||||
{(fields, { add, remove }) => {
|
||||
return (
|
||||
<div>
|
||||
@ -53,40 +45,53 @@ export const StartPageTab: React.FC<TabPaneProps> = props => {
|
||||
label={index === 0 ? t('form:startPage.buttons') : ''}
|
||||
key={field.key}
|
||||
>
|
||||
<Card
|
||||
actions={[
|
||||
<DeleteOutlined key={'delete'} onClick={() => remove(index)} />
|
||||
]}
|
||||
>
|
||||
<Card actions={[<DeleteOutlined key={'delete'} onClick={() => remove(index)} />]}>
|
||||
<Form.Item
|
||||
label={t('form:startPage.url')}
|
||||
name={[field.key, 'url']}
|
||||
rules={[
|
||||
{type: 'url', message: t('validation:invalidUrl')}
|
||||
]}
|
||||
rules={[{ type: 'url', message: t('validation:invalidUrl') }]}
|
||||
labelCol={{ span: 6 }}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t('form:startPage.action')} name={[field.key, 'action']} labelCol={{ span: 6 }}>
|
||||
<Form.Item
|
||||
label={t('form:startPage.action')}
|
||||
name={[field.key, 'action']}
|
||||
labelCol={{ span: 6 }}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t('form:startPage.text')} name={[field.key, 'text']} labelCol={{ span: 6 }}>
|
||||
<Form.Item
|
||||
label={t('form:startPage.text')}
|
||||
name={[field.key, 'text']}
|
||||
labelCol={{ span: 6 }}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t('form:startPage.bgColor')} name={[field.key, 'bgColor']} labelCol={{ span: 6 }}>
|
||||
<Form.Item
|
||||
label={t('form:startPage.bgColor')}
|
||||
name={[field.key, 'bgColor']}
|
||||
labelCol={{ span: 6 }}
|
||||
>
|
||||
<InputColor />
|
||||
</Form.Item>
|
||||
<Form.Item label={t('form:startPage.activeColor')} name={[field.key, 'activeColor']} labelCol={{ span: 6 }}>
|
||||
<Form.Item
|
||||
label={t('form:startPage.activeColor')}
|
||||
name={[field.key, 'activeColor']}
|
||||
labelCol={{ span: 6 }}
|
||||
>
|
||||
<InputColor />
|
||||
</Form.Item>
|
||||
<Form.Item label={t('form:startPage.color')} name={[field.key, 'color']} labelCol={{ span: 6 }}>
|
||||
<Form.Item
|
||||
label={t('form:startPage.color')}
|
||||
name={[field.key, 'color']}
|
||||
labelCol={{ span: 6 }}
|
||||
>
|
||||
<InputColor />
|
||||
</Form.Item>
|
||||
</Card>
|
||||
</Form.Item>
|
||||
)
|
||||
)}
|
||||
))}
|
||||
<Form.Item
|
||||
wrapperCol={{
|
||||
sm: { offset: 6 },
|
||||
@ -95,7 +100,7 @@ export const StartPageTab: React.FC<TabPaneProps> = props => {
|
||||
<Button
|
||||
type="dashed"
|
||||
onClick={() => {
|
||||
add();
|
||||
add()
|
||||
}}
|
||||
style={{ width: '60%' }}
|
||||
>
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import {Descriptions, Table} from 'antd'
|
||||
import {ColumnsType} from 'antd/lib/table/interface'
|
||||
import { Descriptions, Table } from 'antd'
|
||||
import { ColumnsType } from 'antd/lib/table/interface'
|
||||
import React from 'react'
|
||||
import {useTranslation} from 'react-i18next'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
AdminPagerSubmissionEntryFieldQueryData,
|
||||
AdminPagerSubmissionEntryQueryData,
|
||||
AdminPagerSubmissionFormQueryData
|
||||
AdminPagerSubmissionFormQueryData,
|
||||
} from '../../../graphql/query/admin.pager.submission.query'
|
||||
|
||||
interface Props {
|
||||
@ -13,24 +13,23 @@ interface Props {
|
||||
submission: AdminPagerSubmissionEntryQueryData
|
||||
}
|
||||
|
||||
export const SubmissionValues: React.FC<Props> = props => {
|
||||
export const SubmissionValues: React.FC<Props> = (props) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const columns: ColumnsType<AdminPagerSubmissionEntryFieldQueryData> = [
|
||||
{
|
||||
title: t('submission:field'),
|
||||
render: (row: AdminPagerSubmissionEntryFieldQueryData) => {
|
||||
|
||||
if (row.field) {
|
||||
return `${row.field.title}${row.field.required ? '*' : ''}`
|
||||
}
|
||||
|
||||
return `${row.id}`
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t('submission:value'),
|
||||
render: row => {
|
||||
render: (row) => {
|
||||
try {
|
||||
const data = JSON.parse(row.value)
|
||||
|
||||
@ -38,24 +37,28 @@ export const SubmissionValues: React.FC<Props> = props => {
|
||||
} catch (e) {
|
||||
return row.value
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Descriptions title={t('submission:submission')}>
|
||||
<Descriptions.Item label={t('submission:country')}>{props.submission.geoLocation.country}</Descriptions.Item>
|
||||
<Descriptions.Item label={t('submission:city')}>{props.submission.geoLocation.city}</Descriptions.Item>
|
||||
<Descriptions.Item label={t('submission:device.type')}>{props.submission.device.type}</Descriptions.Item>
|
||||
<Descriptions.Item label={t('submission:device.name')}>{props.submission.device.name}</Descriptions.Item>
|
||||
<Descriptions.Item label={t('submission:country')}>
|
||||
{props.submission.geoLocation.country}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t('submission:city')}>
|
||||
{props.submission.geoLocation.city}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t('submission:device.type')}>
|
||||
{props.submission.device.type}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t('submission:device.name')}>
|
||||
{props.submission.device.name}
|
||||
</Descriptions.Item>
|
||||
</Descriptions>
|
||||
|
||||
<Table
|
||||
columns={columns}
|
||||
dataSource={props.submission.fields}
|
||||
rowKey={'id'}
|
||||
/>
|
||||
<Table columns={columns} dataSource={props.submission.fields} rowKey={'id'} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import {DatePicker, Form} from 'antd'
|
||||
import { DatePicker, Form } from 'antd'
|
||||
import moment from 'moment'
|
||||
import React from 'react'
|
||||
import {useTranslation} from 'react-i18next'
|
||||
import {AdminFieldTypeProps} from './type.props'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { AdminFieldTypeProps } from './type.props'
|
||||
|
||||
export const DateType: React.FC<AdminFieldTypeProps> = ({field, form}) => {
|
||||
export const DateType: React.FC<AdminFieldTypeProps> = ({ field }) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
@ -13,19 +13,17 @@ export const DateType: React.FC<AdminFieldTypeProps> = ({field, form}) => {
|
||||
label={t('type:date.default')}
|
||||
name={[field.name, 'value']}
|
||||
labelCol={{ span: 6 }}
|
||||
getValueFromEvent={e => e ? e.format('YYYY-MM-DD') : undefined}
|
||||
getValueProps={e => ({value: e ? moment(e) : undefined})}
|
||||
getValueFromEvent={(e) => (e ? e.format('YYYY-MM-DD') : undefined)}
|
||||
getValueProps={(e) => ({ value: e ? moment(e) : undefined })}
|
||||
>
|
||||
<DatePicker
|
||||
format={'YYYY-MM-DD'}
|
||||
/>
|
||||
<DatePicker format={'YYYY-MM-DD'} />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t('type:date.min')}
|
||||
name={[field.name, 'optionKeys', 'min']}
|
||||
labelCol={{ span: 6 }}
|
||||
getValueFromEvent={e => e.format('YYYY-MM-DD')}
|
||||
getValueProps={e => ({value: e ? moment(e) : undefined})}
|
||||
getValueFromEvent={(e) => e.format('YYYY-MM-DD')}
|
||||
getValueProps={(e) => ({ value: e ? moment(e) : undefined })}
|
||||
>
|
||||
<DatePicker />
|
||||
</Form.Item>
|
||||
@ -34,8 +32,8 @@ export const DateType: React.FC<AdminFieldTypeProps> = ({field, form}) => {
|
||||
label={t('type:date.max')}
|
||||
name={[field.name, 'optionKeys', 'max']}
|
||||
labelCol={{ span: 6 }}
|
||||
getValueFromEvent={e => e.format('YYYY-MM-DD')}
|
||||
getValueProps={e => ({value: e ? moment(e) : undefined})}
|
||||
getValueFromEvent={(e) => e.format('YYYY-MM-DD')}
|
||||
getValueProps={(e) => ({ value: e ? moment(e) : undefined })}
|
||||
>
|
||||
<DatePicker />
|
||||
</Form.Item>
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import {Button, Col, Form, Input, Row} from 'antd'
|
||||
import { Button, Col, Form, Input, Row } from 'antd'
|
||||
import React from 'react'
|
||||
import {useTranslation} from 'react-i18next'
|
||||
import {AdminFieldTypeProps} from './type.props'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { AdminFieldTypeProps } from './type.props'
|
||||
|
||||
export const DropdownType: React.FC<AdminFieldTypeProps> = props => {
|
||||
export const DropdownType: React.FC<AdminFieldTypeProps> = (props) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
@ -16,11 +16,8 @@ export const DropdownType: React.FC<AdminFieldTypeProps> = props => {
|
||||
<Input />
|
||||
</Form.Item>
|
||||
|
||||
<Form.List
|
||||
name={[props.field.name, 'options']}
|
||||
>
|
||||
<Form.List name={[props.field.name, 'options']}>
|
||||
{(fields, { add, remove }) => {
|
||||
|
||||
return (
|
||||
<div>
|
||||
{fields.map((field, index) => (
|
||||
@ -37,7 +34,7 @@ export const DropdownType: React.FC<AdminFieldTypeProps> = props => {
|
||||
<Form.Item
|
||||
wrapperCol={{ span: 24 }}
|
||||
name={[field.name, 'title']}
|
||||
style={{marginBottom: 0}}
|
||||
style={{ marginBottom: 0 }}
|
||||
>
|
||||
<Input placeholder={t('type:dropdown.titlePlaceholder')} />
|
||||
</Form.Item>
|
||||
@ -46,10 +43,8 @@ export const DropdownType: React.FC<AdminFieldTypeProps> = props => {
|
||||
<Form.Item
|
||||
wrapperCol={{ span: 24 }}
|
||||
name={[field.name, 'value']}
|
||||
style={{marginBottom: 0}}
|
||||
rules={[
|
||||
{ required: true, message: t('validation:valueRequired') }
|
||||
]}
|
||||
style={{ marginBottom: 0 }}
|
||||
rules={[{ required: true, message: t('validation:valueRequired') }]}
|
||||
>
|
||||
<Input placeholder={t('type:dropdown.valuePlaceholder')} />
|
||||
</Form.Item>
|
||||
@ -69,11 +64,9 @@ export const DropdownType: React.FC<AdminFieldTypeProps> = props => {
|
||||
}}
|
||||
labelCol={{ span: 6 }}
|
||||
>
|
||||
<Button
|
||||
type={'dashed'}
|
||||
onClick={() => add()}
|
||||
>
|
||||
{t('type:dropdown.addOption')}</Button>
|
||||
<Button type={'dashed'} onClick={() => add()}>
|
||||
{t('type:dropdown.addOption')}
|
||||
</Button>
|
||||
</Form.Item>
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import {Form, Input} from 'antd'
|
||||
import { Form, Input } from 'antd'
|
||||
import React from 'react'
|
||||
import {useTranslation} from 'react-i18next'
|
||||
import {AdminFieldTypeProps} from './type.props'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { AdminFieldTypeProps } from './type.props'
|
||||
|
||||
export const EmailType: React.FC<AdminFieldTypeProps> = props => {
|
||||
export const EmailType: React.FC<AdminFieldTypeProps> = (props) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
@ -11,9 +11,7 @@ export const EmailType: React.FC<AdminFieldTypeProps> = props => {
|
||||
<Form.Item
|
||||
label={t('type:email.default')}
|
||||
name={[props.field.name, 'value']}
|
||||
rules={[
|
||||
{ type: 'email', message: t('validation:emailRequired') }
|
||||
]}
|
||||
rules={[{ type: 'email', message: t('validation:emailRequired') }]}
|
||||
labelCol={{ span: 6 }}
|
||||
>
|
||||
<Input type={'email'} />
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import {Form, Input} from 'antd'
|
||||
import { Form, Input } from 'antd'
|
||||
import React from 'react'
|
||||
import {useTranslation} from 'react-i18next'
|
||||
import {AdminFieldTypeProps} from './type.props'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { AdminFieldTypeProps } from './type.props'
|
||||
|
||||
export const HiddenType: React.FC<AdminFieldTypeProps> = props => {
|
||||
export const HiddenType: React.FC<AdminFieldTypeProps> = (props) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
|
||||
@ -1,29 +1,29 @@
|
||||
import React from 'react'
|
||||
import {DateType} from './date.type'
|
||||
import {DropdownType} from './dropdown.type'
|
||||
import {EmailType} from './email.type'
|
||||
import {HiddenType} from './hidden.type'
|
||||
import {LinkType} from './link.type'
|
||||
import {NumberType} from './number.type'
|
||||
import {RadioType} from './radio.type'
|
||||
import {RatingType} from './rating.type'
|
||||
import {TextType} from './text.type'
|
||||
import {TextareaType} from './textarea.type'
|
||||
import {AdminFieldTypeProps} from './type.props'
|
||||
import {YesNoType} from './yes_no.type'
|
||||
import { DateType } from './date.type'
|
||||
import { DropdownType } from './dropdown.type'
|
||||
import { EmailType } from './email.type'
|
||||
import { HiddenType } from './hidden.type'
|
||||
import { LinkType } from './link.type'
|
||||
import { NumberType } from './number.type'
|
||||
import { RadioType } from './radio.type'
|
||||
import { RatingType } from './rating.type'
|
||||
import { TextType } from './text.type'
|
||||
import { TextareaType } from './textarea.type'
|
||||
import { AdminFieldTypeProps } from './type.props'
|
||||
import { YesNoType } from './yes_no.type'
|
||||
|
||||
export const adminTypes: {
|
||||
[key: string]: React.FC<AdminFieldTypeProps>
|
||||
} = {
|
||||
'textfield': TextType,
|
||||
'date': DateType,
|
||||
'email': EmailType,
|
||||
'textarea': TextareaType,
|
||||
'link': LinkType,
|
||||
'dropdown': DropdownType,
|
||||
'rating': RatingType,
|
||||
'radio': RadioType,
|
||||
'hidden': HiddenType,
|
||||
'yes_no': YesNoType,
|
||||
'number': NumberType,
|
||||
textfield: TextType,
|
||||
date: DateType,
|
||||
email: EmailType,
|
||||
textarea: TextareaType,
|
||||
link: LinkType,
|
||||
dropdown: DropdownType,
|
||||
rating: RatingType,
|
||||
radio: RadioType,
|
||||
hidden: HiddenType,
|
||||
yes_no: YesNoType,
|
||||
number: NumberType,
|
||||
}
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import {Form, Input} from 'antd'
|
||||
import { Form, Input } from 'antd'
|
||||
import React from 'react'
|
||||
import {useTranslation} from 'react-i18next'
|
||||
import {AdminFieldTypeProps} from './type.props'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { AdminFieldTypeProps } from './type.props'
|
||||
|
||||
export const LinkType: React.FC<AdminFieldTypeProps> = props => {
|
||||
export const LinkType: React.FC<AdminFieldTypeProps> = (props) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
@ -11,9 +11,7 @@ export const LinkType: React.FC<AdminFieldTypeProps> = props => {
|
||||
<Form.Item
|
||||
label={t('type:link.default')}
|
||||
name={[props.field.name, 'value']}
|
||||
rules={[
|
||||
{ type: 'url', message: t('validation:invalidUrl') }
|
||||
]}
|
||||
rules={[{ type: 'url', message: t('validation:invalidUrl') }]}
|
||||
labelCol={{ span: 6 }}
|
||||
>
|
||||
<Input type={'url'} />
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import {Form, InputNumber} from 'antd'
|
||||
import { Form, InputNumber } from 'antd'
|
||||
import React from 'react'
|
||||
import {useTranslation} from 'react-i18next'
|
||||
import {AdminFieldTypeProps} from './type.props'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { AdminFieldTypeProps } from './type.props'
|
||||
|
||||
export const NumberType: React.FC<AdminFieldTypeProps> = props => {
|
||||
export const NumberType: React.FC<AdminFieldTypeProps> = (props) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import {Button, Col, Form, Input, Row} from 'antd'
|
||||
import { Button, Col, Form, Input, Row } from 'antd'
|
||||
import React from 'react'
|
||||
import {useTranslation} from 'react-i18next'
|
||||
import {AdminFieldTypeProps} from './type.props'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { AdminFieldTypeProps } from './type.props'
|
||||
|
||||
export const RadioType: React.FC<AdminFieldTypeProps> = props => {
|
||||
export const RadioType: React.FC<AdminFieldTypeProps> = (props) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
@ -16,11 +16,8 @@ export const RadioType: React.FC<AdminFieldTypeProps> = props => {
|
||||
<Input />
|
||||
</Form.Item>
|
||||
|
||||
<Form.List
|
||||
name={[props.field.name, 'options']}
|
||||
>
|
||||
<Form.List name={[props.field.name, 'options']}>
|
||||
{(fields, { add, remove }) => {
|
||||
|
||||
return (
|
||||
<div>
|
||||
{fields.map((field, index) => (
|
||||
@ -37,7 +34,7 @@ export const RadioType: React.FC<AdminFieldTypeProps> = props => {
|
||||
<Form.Item
|
||||
wrapperCol={{ span: 24 }}
|
||||
name={[field.name, 'title']}
|
||||
style={{marginBottom: 0}}
|
||||
style={{ marginBottom: 0 }}
|
||||
>
|
||||
<Input placeholder={t('type:radio:titlePlaceholder')} />
|
||||
</Form.Item>
|
||||
@ -46,10 +43,8 @@ export const RadioType: React.FC<AdminFieldTypeProps> = props => {
|
||||
<Form.Item
|
||||
wrapperCol={{ span: 24 }}
|
||||
name={[field.name, 'value']}
|
||||
style={{marginBottom: 0}}
|
||||
rules={[
|
||||
{ required: true, message: t('validation:valueRequired') }
|
||||
]}
|
||||
style={{ marginBottom: 0 }}
|
||||
rules={[{ required: true, message: t('validation:valueRequired') }]}
|
||||
>
|
||||
<Input placeholder={t('type:radio:valuePlaceholder')} />
|
||||
</Form.Item>
|
||||
@ -69,10 +64,7 @@ export const RadioType: React.FC<AdminFieldTypeProps> = props => {
|
||||
}}
|
||||
labelCol={{ span: 6 }}
|
||||
>
|
||||
<Button
|
||||
type={'dashed'}
|
||||
onClick={() => add()}
|
||||
>
|
||||
<Button type={'dashed'} onClick={() => add()}>
|
||||
{t('type:radio:addOption')}
|
||||
</Button>
|
||||
</Form.Item>
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import {Form, Rate} from 'antd'
|
||||
import { Form, Rate } from 'antd'
|
||||
import React from 'react'
|
||||
import {useTranslation} from 'react-i18next'
|
||||
import {AdminFieldTypeProps} from './type.props'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { AdminFieldTypeProps } from './type.props'
|
||||
|
||||
export const RatingType: React.FC<AdminFieldTypeProps> = props => {
|
||||
export const RatingType: React.FC<AdminFieldTypeProps> = (props) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
// TODO add ratings
|
||||
@ -15,10 +15,7 @@ export const RatingType: React.FC<AdminFieldTypeProps> = props => {
|
||||
labelCol={{ span: 6 }}
|
||||
extra={t('type:rating.clearNote')}
|
||||
>
|
||||
<Rate
|
||||
allowHalf
|
||||
allowClear
|
||||
/>
|
||||
<Rate allowHalf allowClear />
|
||||
</Form.Item>
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import {Form, Input} from 'antd'
|
||||
import { Form, Input } from 'antd'
|
||||
import React from 'react'
|
||||
import {useTranslation} from 'react-i18next'
|
||||
import {AdminFieldTypeProps} from './type.props'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { AdminFieldTypeProps } from './type.props'
|
||||
|
||||
export const TextType: React.FC<AdminFieldTypeProps> = props => {
|
||||
export const TextType: React.FC<AdminFieldTypeProps> = (props) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import {Form, Input} from 'antd'
|
||||
import { Form, Input } from 'antd'
|
||||
import React from 'react'
|
||||
import {useTranslation} from 'react-i18next'
|
||||
import {AdminFieldTypeProps} from './type.props'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { AdminFieldTypeProps } from './type.props'
|
||||
|
||||
export const TextareaType: React.FC<AdminFieldTypeProps> = props => {
|
||||
export const TextareaType: React.FC<AdminFieldTypeProps> = (props) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import {FormInstance} from 'antd/lib/form'
|
||||
import { FormInstance } from 'antd/lib/form'
|
||||
|
||||
export interface AdminFieldTypeProps {
|
||||
form: FormInstance
|
||||
field: any
|
||||
field: { name: string }
|
||||
}
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import {Form, Input} from 'antd'
|
||||
import { Form, Input } from 'antd'
|
||||
import React from 'react'
|
||||
import {useTranslation} from 'react-i18next'
|
||||
import {AdminFieldTypeProps} from './type.props'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { AdminFieldTypeProps } from './type.props'
|
||||
|
||||
export const YesNoType: React.FC<AdminFieldTypeProps> = props => {
|
||||
export const YesNoType: React.FC<AdminFieldTypeProps> = (props) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
// TODO add switch
|
||||
|
||||
@ -1,24 +1,25 @@
|
||||
import {Form, message} from 'antd'
|
||||
import {useForm} from 'antd/lib/form/Form'
|
||||
import { Form, message } from 'antd'
|
||||
import { useForm } from 'antd/lib/form/Form'
|
||||
import React from 'react'
|
||||
import {FormDesignFragment, FormFieldFragment} from '../../graphql/fragment/form.fragment'
|
||||
import {StyledButton} from '../styled/button'
|
||||
import {StyledH1} from '../styled/h1'
|
||||
import {StyledP} from '../styled/p'
|
||||
import {fieldTypes} from './types'
|
||||
import {TextType} from './types/text.type'
|
||||
import {FieldTypeProps} from './types/type.props'
|
||||
import { FormDesignFragment, FormFieldFragment } from '../../graphql/fragment/form.fragment'
|
||||
import { StyledButton } from '../styled/button'
|
||||
import { StyledH1 } from '../styled/h1'
|
||||
import { StyledP } from '../styled/p'
|
||||
import { fieldTypes } from './types'
|
||||
import { TextType } from './types/text.type'
|
||||
import { FieldTypeProps } from './types/type.props'
|
||||
|
||||
interface Props {
|
||||
field: FormFieldFragment
|
||||
design: FormDesignFragment
|
||||
|
||||
save: (data: any) => any
|
||||
next: () => any
|
||||
prev: () => any
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
save: (data: any) => void
|
||||
next: () => void
|
||||
prev: () => void
|
||||
}
|
||||
|
||||
export const Field: React.FC<Props> = ({field, save, design, children, next, prev, ...props}) => {
|
||||
export const Field: React.FC<Props> = ({ field, save, design, next, prev, ...props }) => {
|
||||
const [form] = useForm()
|
||||
|
||||
const FieldInput: React.FC<FieldTypeProps> = fieldTypes[field.type] || TextType
|
||||
@ -44,33 +45,42 @@ export const Field: React.FC<Props> = ({field, save, design, children, next, pre
|
||||
flexDirection: 'column',
|
||||
}}
|
||||
>
|
||||
<div style={{
|
||||
<div
|
||||
style={{
|
||||
flex: 1,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
padding: 32,
|
||||
justifyContent: 'flex-end',
|
||||
}}>
|
||||
<StyledH1 design={design} type={'question'}>{field.title}</StyledH1>
|
||||
{field.description && <StyledP design={design} type={'question'}>{field.description}</StyledP>}
|
||||
}}
|
||||
>
|
||||
<StyledH1 design={design} type={'question'}>
|
||||
{field.title}
|
||||
</StyledH1>
|
||||
{field.description && (
|
||||
<StyledP design={design} type={'question'}>
|
||||
{field.description}
|
||||
</StyledP>
|
||||
)}
|
||||
|
||||
<FieldInput
|
||||
design={design}
|
||||
field={field}
|
||||
/>
|
||||
<FieldInput design={design} field={field} />
|
||||
</div>
|
||||
<div style={{
|
||||
<div
|
||||
style={{
|
||||
padding: 32,
|
||||
display: 'flex',
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<StyledButton
|
||||
background={design.colors.buttonColor}
|
||||
color={design.colors.buttonTextColor}
|
||||
highlight={design.colors.buttonActiveColor}
|
||||
onClick={prev}
|
||||
>{'Previous'}</StyledButton>
|
||||
>
|
||||
{'Previous'}
|
||||
</StyledButton>
|
||||
|
||||
<div style={{flex: 1}} />
|
||||
<div style={{ flex: 1 }} />
|
||||
|
||||
<StyledButton
|
||||
background={design.colors.buttonColor}
|
||||
@ -78,7 +88,9 @@ export const Field: React.FC<Props> = ({field, save, design, children, next, pre
|
||||
highlight={design.colors.buttonActiveColor}
|
||||
size={'large'}
|
||||
onClick={form.submit}
|
||||
>{'Next'}</StyledButton>
|
||||
>
|
||||
{'Next'}
|
||||
</StyledButton>
|
||||
</div>
|
||||
</Form>
|
||||
)
|
||||
|
||||
@ -1,43 +1,54 @@
|
||||
import {Space} from 'antd'
|
||||
import { Space } from 'antd'
|
||||
import React from 'react'
|
||||
import {FormDesignFragment, FormPageFragment} from '../../graphql/fragment/form.fragment'
|
||||
import {StyledButton} from '../styled/button'
|
||||
import {StyledH1} from '../styled/h1'
|
||||
import {StyledP} from '../styled/p'
|
||||
import { FormDesignFragment, FormPageFragment } from '../../graphql/fragment/form.fragment'
|
||||
import { StyledButton } from '../styled/button'
|
||||
import { StyledH1 } from '../styled/h1'
|
||||
import { StyledP } from '../styled/p'
|
||||
|
||||
interface Props {
|
||||
type: 'start' | 'end'
|
||||
page: FormPageFragment
|
||||
design: FormDesignFragment
|
||||
|
||||
next: () => any
|
||||
prev: () => any
|
||||
next: () => void
|
||||
prev: () => void
|
||||
}
|
||||
|
||||
export const FormPage: React.FC<Props> = ({page, design, next, prev, type, children, ...props}) => {
|
||||
export const FormPage: React.FC<Props> = ({ page, design, next, ...props }) => {
|
||||
if (!page.show) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
}} {...props}>
|
||||
<div style={{
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
flex: 1,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
flexDirection: 'column',
|
||||
}}>
|
||||
<StyledH1 design={design} type={'question'}>{page.title}</StyledH1>
|
||||
<StyledP design={design} type={'question'}>{page.paragraph}</StyledP>
|
||||
}}
|
||||
>
|
||||
<StyledH1 design={design} type={'question'}>
|
||||
{page.title}
|
||||
</StyledH1>
|
||||
<StyledP design={design} type={'question'}>
|
||||
{page.paragraph}
|
||||
</StyledP>
|
||||
</div>
|
||||
<div style={{
|
||||
<div
|
||||
style={{
|
||||
padding: 32,
|
||||
display: 'flex',
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
{page.buttons.length > 0 && (
|
||||
<Space>
|
||||
{page.buttons.map((button, key) => {
|
||||
@ -49,13 +60,16 @@ export const FormPage: React.FC<Props> = ({page, design, next, prev, type, child
|
||||
key={key}
|
||||
href={button.url}
|
||||
target={'_blank'}
|
||||
>{button.text}</StyledButton>
|
||||
rel={'noreferrer'}
|
||||
>
|
||||
{button.text}
|
||||
</StyledButton>
|
||||
)
|
||||
})}
|
||||
</Space>
|
||||
)}
|
||||
|
||||
<div style={{flex: 1}} />
|
||||
<div style={{ flex: 1 }} />
|
||||
|
||||
<StyledButton
|
||||
background={design.colors.buttonColor}
|
||||
@ -63,7 +77,9 @@ export const FormPage: React.FC<Props> = ({page, design, next, prev, type, child
|
||||
highlight={design.colors.buttonActiveColor}
|
||||
size={'large'}
|
||||
onClick={next}
|
||||
>{page.buttonText || 'Continue'}</StyledButton>
|
||||
>
|
||||
{page.buttonText || 'Continue'}
|
||||
</StyledButton>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -1,16 +1,16 @@
|
||||
import {Form} from 'antd'
|
||||
import dayjs, {Dayjs} from 'dayjs'
|
||||
import moment from 'moment'
|
||||
import React, {useEffect, useState} from 'react'
|
||||
import {StyledDateInput} from '../../styled/date.input'
|
||||
import {FieldTypeProps} from './type.props'
|
||||
import { Form } from 'antd'
|
||||
import dayjs, { Dayjs } from 'dayjs'
|
||||
import moment, { Moment } from 'moment'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
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 }) => {
|
||||
const [min, setMin] = useState<Dayjs>()
|
||||
const [max, setMax] = useState<Dayjs>()
|
||||
|
||||
useEffect(() => {
|
||||
field.options.forEach(option => {
|
||||
field.options.forEach((option) => {
|
||||
if (option.key === 'min') {
|
||||
setMin(dayjs(option.value))
|
||||
}
|
||||
@ -24,22 +24,19 @@ export const DateType: React.FC<FieldTypeProps> = ({ field, design}) => {
|
||||
<div>
|
||||
<Form.Item
|
||||
name={[field.id, 'value']}
|
||||
rules={[
|
||||
{ required: field.required, message: 'Please provide Information' },
|
||||
]}
|
||||
getValueFromEvent={e => e.format('YYYY-MM-DD')}
|
||||
getValueProps={e => ({value: e ? moment(e) : undefined})}
|
||||
rules={[{ required: field.required, message: 'Please provide Information' }]}
|
||||
getValueFromEvent={(e) => e.format('YYYY-MM-DD')}
|
||||
getValueProps={(e) => ({ value: e ? moment(e) : undefined })}
|
||||
initialValue={field.value ? moment(field.value) : undefined}
|
||||
>
|
||||
<StyledDateInput
|
||||
size={'large'}
|
||||
design={design}
|
||||
autoFocus
|
||||
disabledDate={(d: any) => {
|
||||
if (min && min.isAfter(d)) {
|
||||
disabledDate={(d: Moment) => {
|
||||
if (min && min.isAfter(d.toDate())) {
|
||||
return true
|
||||
}
|
||||
if (max && max.isBefore(d)) {
|
||||
if (max && max.isBefore(d.toDate())) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
||||
@ -1,23 +1,31 @@
|
||||
import {Form, Select} from 'antd'
|
||||
import React, {useState} from 'react'
|
||||
import {StyledSelect} from '../../styled/select'
|
||||
import {FieldTypeProps} from './type.props'
|
||||
import { Form, Select } from 'antd'
|
||||
import React, { useState } from 'react'
|
||||
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 }) => {
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Form.Item
|
||||
name={[field.id, 'value']}
|
||||
rules={[
|
||||
{ required: field.required, message: 'Please provide Information' },
|
||||
]}
|
||||
rules={[{ required: field.required, message: 'Please provide Information' }]}
|
||||
initialValue={field.value || null}
|
||||
>
|
||||
<StyledSelect design={design} open={open} onBlur={() => setOpen(false)} onFocus={() => setOpen(true)} onSelect={() => setOpen(false)}>
|
||||
{field.options.filter(option => option.key === null).map(option => (
|
||||
<Select.Option value={option.value} key={option.value}>OK{option.title || option.value}</Select.Option>
|
||||
<StyledSelect
|
||||
design={design}
|
||||
open={open}
|
||||
onBlur={() => setOpen(false)}
|
||||
onFocus={() => setOpen(true)}
|
||||
onSelect={() => setOpen(false)}
|
||||
>
|
||||
{field.options
|
||||
.filter((option) => option.key === null)
|
||||
.map((option) => (
|
||||
<Select.Option value={option.value} key={option.value}>
|
||||
OK{option.title || option.value}
|
||||
</Select.Option>
|
||||
))}
|
||||
</StyledSelect>
|
||||
</Form.Item>
|
||||
|
||||
@ -1,24 +1,20 @@
|
||||
import {Form} from 'antd'
|
||||
import { Form } from 'antd'
|
||||
import React from 'react'
|
||||
import {StyledInput} from '../../styled/input'
|
||||
import {FieldTypeProps} from './type.props'
|
||||
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 }) => {
|
||||
return (
|
||||
<div>
|
||||
<Form.Item
|
||||
name={[field.id, 'value']}
|
||||
rules={[
|
||||
{ required: field.required, message: 'Please provide Information' },
|
||||
{ type: 'email', message: 'Must be a valid email' }
|
||||
{ type: 'email', message: 'Must be a valid email' },
|
||||
]}
|
||||
initialValue={field.value}
|
||||
>
|
||||
<StyledInput
|
||||
design={design}
|
||||
allowClear
|
||||
size={'large'}
|
||||
/>
|
||||
<StyledInput design={design} allowClear size={'large'} />
|
||||
</Form.Item>
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -1,27 +1,27 @@
|
||||
import React from 'react'
|
||||
import {DateType} from './date.type'
|
||||
import {DropdownType} from './dropdown.type'
|
||||
import {EmailType} from './email.type'
|
||||
import {LinkType} from './link.type'
|
||||
import {NumberType} from './number.type'
|
||||
import {RadioType} from './radio.type'
|
||||
import {RatingType} from './rating.type'
|
||||
import {TextType} from './text.type'
|
||||
import {TextareaType} from './textarea.type'
|
||||
import {FieldTypeProps} from './type.props'
|
||||
import {YesNoType} from './yes_no.type'
|
||||
import { DateType } from './date.type'
|
||||
import { DropdownType } from './dropdown.type'
|
||||
import { EmailType } from './email.type'
|
||||
import { LinkType } from './link.type'
|
||||
import { NumberType } from './number.type'
|
||||
import { RadioType } from './radio.type'
|
||||
import { RatingType } from './rating.type'
|
||||
import { TextType } from './text.type'
|
||||
import { TextareaType } from './textarea.type'
|
||||
import { FieldTypeProps } from './type.props'
|
||||
import { YesNoType } from './yes_no.type'
|
||||
|
||||
export const fieldTypes: {
|
||||
[key: string]: React.FC<FieldTypeProps>
|
||||
} = {
|
||||
'textfield': TextType,
|
||||
'date': DateType,
|
||||
'email': EmailType,
|
||||
'textarea': TextareaType,
|
||||
'link': LinkType,
|
||||
'dropdown': DropdownType,
|
||||
'rating': RatingType,
|
||||
'radio': RadioType,
|
||||
'yes_no': YesNoType,
|
||||
'number': NumberType,
|
||||
textfield: TextType,
|
||||
date: DateType,
|
||||
email: EmailType,
|
||||
textarea: TextareaType,
|
||||
link: LinkType,
|
||||
dropdown: DropdownType,
|
||||
rating: RatingType,
|
||||
radio: RadioType,
|
||||
yes_no: YesNoType,
|
||||
number: NumberType,
|
||||
}
|
||||
|
||||
@ -1,24 +1,20 @@
|
||||
import {Form} from 'antd'
|
||||
import { Form } from 'antd'
|
||||
import React from 'react'
|
||||
import {StyledInput} from '../../styled/input'
|
||||
import {FieldTypeProps} from './type.props'
|
||||
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 }) => {
|
||||
return (
|
||||
<div>
|
||||
<Form.Item
|
||||
name={[field.id, 'value']}
|
||||
rules={[
|
||||
{ required: field.required, message: 'Please provide Information' },
|
||||
{ type: 'url', message: 'Must be a valid URL' }
|
||||
{ type: 'url', message: 'Must be a valid URL' },
|
||||
]}
|
||||
initialValue={field.value}
|
||||
>
|
||||
<StyledInput
|
||||
design={design}
|
||||
allowClear
|
||||
size={'large'}
|
||||
/>
|
||||
<StyledInput design={design} allowClear size={'large'} />
|
||||
</Form.Item>
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import {Form} from 'antd'
|
||||
import { Form } from 'antd'
|
||||
import React from 'react'
|
||||
import {StyledNumberInput} from '../../styled/number.input'
|
||||
import {FieldTypeProps} from './type.props'
|
||||
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 }) => {
|
||||
return (
|
||||
<div>
|
||||
<Form.Item
|
||||
@ -14,10 +14,7 @@ export const NumberType: React.FC<FieldTypeProps> = ({field, design}) => {
|
||||
]}
|
||||
initialValue={parseFloat(field.value)}
|
||||
>
|
||||
<StyledNumberInput
|
||||
design={design}
|
||||
size={'large'}
|
||||
/>
|
||||
<StyledNumberInput design={design} size={'large'} />
|
||||
</Form.Item>
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -1,25 +1,23 @@
|
||||
import {Form, Radio} from 'antd'
|
||||
import { Form, Radio } from 'antd'
|
||||
import React from 'react'
|
||||
import {StyledRadio} from '../../styled/radio'
|
||||
import {FieldTypeProps} from './type.props'
|
||||
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 }) => {
|
||||
return (
|
||||
<div>
|
||||
<Form.Item
|
||||
name={[field.id, 'value']}
|
||||
rules={[
|
||||
{ required: field.required, message: 'Please provide Information' },
|
||||
]}
|
||||
initialValue={field.options.map(option => option.value).find(value => value === field.value)}
|
||||
rules={[{ required: field.required, message: 'Please provide Information' }]}
|
||||
initialValue={field.options
|
||||
.map((option) => option.value)
|
||||
.find((value) => value === field.value)}
|
||||
>
|
||||
<Radio.Group>
|
||||
{field.options.filter(option => option.key === null).map(option => (
|
||||
<StyledRadio
|
||||
design={design}
|
||||
value={option.value}
|
||||
key={option.value}
|
||||
>
|
||||
{field.options
|
||||
.filter((option) => option.key === null)
|
||||
.map((option) => (
|
||||
<StyledRadio design={design} value={option.value} key={option.value}>
|
||||
{option.title || option.value}
|
||||
</StyledRadio>
|
||||
))}
|
||||
|
||||
@ -1,17 +1,15 @@
|
||||
import {Form, Rate} from 'antd'
|
||||
import { Form, Rate } from 'antd'
|
||||
import React from 'react'
|
||||
import {FieldTypeProps} from './type.props'
|
||||
import { FieldTypeProps } from './type.props'
|
||||
|
||||
export const RatingType: React.FC<FieldTypeProps> = ({field}) => {
|
||||
export const RatingType: React.FC<FieldTypeProps> = ({ field }) => {
|
||||
// TODO add ratings
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Form.Item
|
||||
name={[field.id, 'value']}
|
||||
rules={[
|
||||
{ required: field.required, message: 'Please provide Information' },
|
||||
]}
|
||||
rules={[{ required: field.required, message: 'Please provide Information' }]}
|
||||
initialValue={parseFloat(field.value)}
|
||||
>
|
||||
<Rate allowHalf />
|
||||
|
||||
@ -1,27 +1,19 @@
|
||||
import {Form} from 'antd'
|
||||
import { Form } from 'antd'
|
||||
import React from 'react'
|
||||
import {StyledInput} from '../../styled/input'
|
||||
import {FieldTypeProps} from './type.props'
|
||||
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 }) => {
|
||||
// TODO focus when becomes visible
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Form.Item
|
||||
name={[field.id, 'value']}
|
||||
rules={[
|
||||
{ required: field.required, message: 'Please provide Information' }
|
||||
]}
|
||||
rules={[{ required: field.required, message: 'Please provide Information' }]}
|
||||
initialValue={field.value}
|
||||
>
|
||||
<StyledInput
|
||||
design={design}
|
||||
allowClear
|
||||
size={'large'}
|
||||
/>
|
||||
<StyledInput design={design} allowClear size={'large'} />
|
||||
</Form.Item>
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -1,23 +1,17 @@
|
||||
import {Form} from 'antd'
|
||||
import { Form } from 'antd'
|
||||
import React from 'react'
|
||||
import {StyledTextareaInput} from '../../styled/textarea.input'
|
||||
import {FieldTypeProps} from './type.props'
|
||||
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 }) => {
|
||||
return (
|
||||
<div>
|
||||
<Form.Item
|
||||
name={[field.id, 'value']}
|
||||
rules={[
|
||||
{ required: field.required, message: 'Please provide Information' },
|
||||
]}
|
||||
rules={[{ required: field.required, message: 'Please provide Information' }]}
|
||||
initialValue={field.value}
|
||||
>
|
||||
<StyledTextareaInput
|
||||
design={design}
|
||||
allowClear
|
||||
autoSize
|
||||
/>
|
||||
<StyledTextareaInput design={design} allowClear autoSize />
|
||||
</Form.Item>
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import {FormDesignFragment, FormFieldFragment} from '../../../graphql/fragment/form.fragment'
|
||||
import { FormDesignFragment, FormFieldFragment } from '../../../graphql/fragment/form.fragment'
|
||||
|
||||
export interface FieldTypeProps {
|
||||
field: FormFieldFragment
|
||||
|
||||
@ -1,15 +1,13 @@
|
||||
import {Form, Switch} from 'antd'
|
||||
import { Form, Switch } from 'antd'
|
||||
import React from 'react'
|
||||
import {FieldTypeProps} from './type.props'
|
||||
import { FieldTypeProps } from './type.props'
|
||||
|
||||
export const YesNoType: React.FC<FieldTypeProps> = ({field}) => {
|
||||
export const YesNoType: React.FC<FieldTypeProps> = ({ field }) => {
|
||||
return (
|
||||
<div>
|
||||
<Form.Item
|
||||
name={[field.id, 'value']}
|
||||
rules={[
|
||||
{ required: field.required, message: 'Please provide Information' },
|
||||
]}
|
||||
rules={[{ required: field.required, message: 'Please provide Information' }]}
|
||||
initialValue={field.value}
|
||||
>
|
||||
<Switch />
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
import React, {useEffect} from 'react'
|
||||
import {BlockPicker} from 'react-color'
|
||||
import React, { useEffect } from 'react'
|
||||
import { BlockPicker } from 'react-color'
|
||||
|
||||
interface Props {
|
||||
value?: string
|
||||
onChange?: any
|
||||
onChange?: (value: string) => void
|
||||
}
|
||||
|
||||
export const InputColor: React.FC<Props> = props => {
|
||||
export const InputColor: React.FC<Props> = (props) => {
|
||||
useEffect(() => {
|
||||
if (!props.value) {
|
||||
props.onChange('#FFF')
|
||||
@ -18,13 +18,13 @@ export const InputColor: React.FC<Props> = props => {
|
||||
triangle={'hide'}
|
||||
width={'100%'}
|
||||
color={props.value}
|
||||
onChange={e => props.onChange(e.hex)}
|
||||
onChange={(e) => props.onChange(e.hex)}
|
||||
styles={{
|
||||
default: {
|
||||
card: {
|
||||
flexDirection: 'row',
|
||||
display: 'flex',
|
||||
boxShadow: 'none'
|
||||
boxShadow: 'none',
|
||||
},
|
||||
head: {
|
||||
flex: 1,
|
||||
|
||||
@ -1,20 +1,22 @@
|
||||
import {Spin} from 'antd'
|
||||
import { Spin } from 'antd'
|
||||
import React from 'react'
|
||||
|
||||
interface Props {
|
||||
message?: string
|
||||
}
|
||||
|
||||
export const LoadingPage: React.FC<Props> = props => {
|
||||
export const LoadingPage: React.FC<Props> = (props) => {
|
||||
return (
|
||||
<div style={{
|
||||
<div
|
||||
style={{
|
||||
height: '100vh',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
}}>
|
||||
<Spin size="large"/>
|
||||
}}
|
||||
>
|
||||
<Spin size="large" />
|
||||
{props.message}
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import {HomeOutlined, MessageOutlined, TeamOutlined} from '@ant-design/icons'
|
||||
import {UserOutlined} from '@ant-design/icons/lib'
|
||||
import { HomeOutlined, MessageOutlined, TeamOutlined } from '@ant-design/icons'
|
||||
import { UserOutlined } from '@ant-design/icons/lib'
|
||||
import React from 'react'
|
||||
|
||||
export interface SideMenuElement {
|
||||
@ -9,7 +9,7 @@ export interface SideMenuElement {
|
||||
name: string
|
||||
group?: boolean
|
||||
href?: string
|
||||
icon?: any
|
||||
icon?: JSX.Element
|
||||
}
|
||||
|
||||
export const sideMenu: SideMenuElement[] = [
|
||||
@ -36,7 +36,7 @@ export const sideMenu: SideMenuElement[] = [
|
||||
href: '/admin/forms',
|
||||
icon: <MessageOutlined />,
|
||||
},
|
||||
]
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'administration',
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
import {CaretDownOutlined, UserOutlined} from '@ant-design/icons'
|
||||
import {MenuFoldOutlined, MenuUnfoldOutlined} from '@ant-design/icons/lib'
|
||||
import {Dropdown, Layout, Menu, PageHeader, Select, Spin, Tag} from 'antd'
|
||||
import { CaretDownOutlined, UserOutlined } from '@ant-design/icons'
|
||||
import { MenuFoldOutlined, MenuUnfoldOutlined } from '@ant-design/icons/lib'
|
||||
import { Dropdown, Layout, Menu, PageHeader, Select, Spin, Tag } from 'antd'
|
||||
import Link from 'next/link'
|
||||
import {useRouter} from 'next/router'
|
||||
import React, {FunctionComponent} from 'react'
|
||||
import {useTranslation} from 'react-i18next'
|
||||
import {languages} from '../i18n'
|
||||
import {sideMenu, SideMenuElement} from './sidemenu'
|
||||
import {useWindowSize} from './use.window.size'
|
||||
import {clearAuth} from './with.auth'
|
||||
import { useRouter } from 'next/router'
|
||||
import React, { CSSProperties, FunctionComponent } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { languages } from '../i18n'
|
||||
import { sideMenu, SideMenuElement } from './sidemenu'
|
||||
import { useWindowSize } from './use.window.size'
|
||||
import { clearAuth } from './with.auth'
|
||||
|
||||
const { SubMenu, ItemGroup } = Menu
|
||||
const { Header, Content, Sider } = Layout
|
||||
@ -22,15 +22,14 @@ interface BreadcrumbEntry {
|
||||
interface Props {
|
||||
loading?: boolean
|
||||
padded?: boolean
|
||||
style?: any
|
||||
style?: CSSProperties
|
||||
|
||||
selected?: string
|
||||
|
||||
|
||||
breadcrumbs?: BreadcrumbEntry[]
|
||||
title?: string
|
||||
subTitle?: string
|
||||
extra?: any[]
|
||||
extra?: JSX.Element[]
|
||||
}
|
||||
|
||||
const Structure: FunctionComponent<Props> = (props) => {
|
||||
@ -63,23 +62,26 @@ const Structure: FunctionComponent<Props> = (props) => {
|
||||
}, [props.selected])
|
||||
|
||||
const buildMenu = (data: SideMenuElement[]): JSX.Element[] => {
|
||||
return data.map((element): JSX.Element => {
|
||||
return data.map(
|
||||
(element): JSX.Element => {
|
||||
if (element.items && element.items.length > 0) {
|
||||
if (element.group) {
|
||||
return (
|
||||
<ItemGroup
|
||||
key={element.key}
|
||||
title={(
|
||||
<div style={{
|
||||
title={
|
||||
<div
|
||||
style={{
|
||||
textTransform: 'uppercase',
|
||||
paddingTop: 16,
|
||||
fontWeight: 'bold',
|
||||
color: '#444'
|
||||
}}>
|
||||
color: '#444',
|
||||
}}
|
||||
>
|
||||
{element.icon}
|
||||
{element.name}
|
||||
</div>
|
||||
)}
|
||||
}
|
||||
>
|
||||
{buildMenu(element.items)}
|
||||
</ItemGroup>
|
||||
@ -114,7 +116,8 @@ const Structure: FunctionComponent<Props> = (props) => {
|
||||
{element.name}
|
||||
</Menu.Item>
|
||||
)
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
const signOut = async (): Promise<void> => {
|
||||
@ -129,46 +132,57 @@ const Structure: FunctionComponent<Props> = (props) => {
|
||||
paddingLeft: 0,
|
||||
}}
|
||||
>
|
||||
<div style={{
|
||||
<div
|
||||
style={{
|
||||
float: 'left',
|
||||
color: '#FFF',
|
||||
fontSize: 14,
|
||||
marginRight: 26,
|
||||
fontWeight: 'bold'
|
||||
}}>
|
||||
fontWeight: 'bold',
|
||||
}}
|
||||
>
|
||||
{React.createElement(sidebar ? MenuUnfoldOutlined : MenuFoldOutlined, {
|
||||
className: 'sidebar-toggle',
|
||||
onClick: () => setSidebar(!sidebar),
|
||||
})}
|
||||
|
||||
<img src={require('assets/images/logo_white_small.png')} height={30} style={{marginRight: 16}} alt={'OhMyForm'} />
|
||||
<img
|
||||
src={require('assets/images/logo_white_small.png')}
|
||||
height={30}
|
||||
style={{ marginRight: 16 }}
|
||||
alt={'OhMyForm'}
|
||||
/>
|
||||
</div>
|
||||
<div style={{float: 'right', display: 'flex', height: '100%'}}>
|
||||
<div style={{ float: 'right', display: 'flex', height: '100%' }}>
|
||||
<Dropdown
|
||||
overlay={(
|
||||
overlay={
|
||||
<Menu>
|
||||
<Menu.Item onClick={() => router.push('/admin/profile')}>Profile</Menu.Item>
|
||||
<Menu.Divider/>
|
||||
<Menu.Divider />
|
||||
<Menu.Item onClick={signOut}>Logout</Menu.Item>
|
||||
</Menu>
|
||||
)}
|
||||
}
|
||||
onVisibleChange={setUserMenu}
|
||||
visible={userMenu}
|
||||
>
|
||||
<a style={{
|
||||
<div
|
||||
style={{
|
||||
color: '#FFF',
|
||||
alignItems: 'center',
|
||||
display: 'inline-flex',
|
||||
}}>
|
||||
<UserOutlined style={{fontSize: 24}} />
|
||||
}}
|
||||
>
|
||||
<UserOutlined style={{ fontSize: 24 }} />
|
||||
<CaretDownOutlined />
|
||||
</a>
|
||||
</div>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</Header>
|
||||
<Layout style={{
|
||||
<Layout
|
||||
style={{
|
||||
height: '100%',
|
||||
}}>
|
||||
}}
|
||||
>
|
||||
<Sider
|
||||
collapsed={sidebar}
|
||||
trigger={null}
|
||||
@ -193,20 +207,21 @@ const Structure: FunctionComponent<Props> = (props) => {
|
||||
>
|
||||
{buildMenu(sideMenu)}
|
||||
</Menu>
|
||||
<Menu
|
||||
mode="inline"
|
||||
selectable={false}
|
||||
>
|
||||
<Menu mode="inline" selectable={false}>
|
||||
<Menu.Item className={'language-selector'}>
|
||||
<Select
|
||||
bordered={false}
|
||||
value={i18n.language.replace(/-.*/, '')}
|
||||
onChange={next => i18n.changeLanguage(next)}
|
||||
onChange={(next) => i18n.changeLanguage(next)}
|
||||
style={{
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
{languages.map(language => <Select.Option value={language} key={language}>{t(`language:${language}`)}</Select.Option> )}
|
||||
{languages.map((language) => (
|
||||
<Select.Option value={language} key={language}>
|
||||
{t(`language:${language}`)}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
@ -222,17 +237,17 @@ const Structure: FunctionComponent<Props> = (props) => {
|
||||
extra={props.extra}
|
||||
breadcrumb={{
|
||||
routes: [
|
||||
...(props.breadcrumbs || []).map(b => ({
|
||||
...(props.breadcrumbs || []).map((b) => ({
|
||||
breadcrumbName: b.name,
|
||||
path: ''
|
||||
path: '',
|
||||
})),
|
||||
{
|
||||
breadcrumbName: props.title,
|
||||
path: ''
|
||||
}
|
||||
path: '',
|
||||
},
|
||||
],
|
||||
params: props.breadcrumbs,
|
||||
itemRender: (route, params: BreadcrumbEntry[], routes, paths) => {
|
||||
itemRender(route, params: BreadcrumbEntry[], routes) {
|
||||
if (routes.indexOf(route) === routes.length - 1) {
|
||||
return <span>{route.breadcrumbName}</span>
|
||||
}
|
||||
@ -240,27 +255,22 @@ const Structure: FunctionComponent<Props> = (props) => {
|
||||
const entry = params[routes.indexOf(route)]
|
||||
|
||||
return (
|
||||
<Link
|
||||
href={entry.href}
|
||||
as={entry.as || entry.href}
|
||||
>
|
||||
<a>
|
||||
{entry.name}
|
||||
</a>
|
||||
<Link href={entry.href} as={entry.as || entry.href}>
|
||||
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
|
||||
<a>{entry.name}</a>
|
||||
</Link>
|
||||
)
|
||||
}}}
|
||||
},
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Spin
|
||||
spinning={!!props.loading}
|
||||
>
|
||||
<Spin spinning={!!props.loading}>
|
||||
<Content
|
||||
style={{
|
||||
background: props.padded ? '#fff' : null,
|
||||
padding: props.padded ? 24 : 0,
|
||||
...props.style
|
||||
...props.style,
|
||||
}}
|
||||
>
|
||||
{props.children}
|
||||
|
||||
@ -1,29 +1,27 @@
|
||||
import {Button} from 'antd'
|
||||
import {ButtonProps} from 'antd/lib/button/button'
|
||||
import { Button } from 'antd'
|
||||
import { ButtonProps } from 'antd/lib/button/button'
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import {darken, lighten} from './color.change'
|
||||
import { darken, lighten } from './color.change'
|
||||
|
||||
interface Props extends ButtonProps {
|
||||
background: any
|
||||
highlight: any
|
||||
color: any
|
||||
background: string
|
||||
highlight: string
|
||||
color: string
|
||||
}
|
||||
|
||||
const Styled = styled(Button)`
|
||||
background: ${props => props.background};
|
||||
color: ${props => props.color};
|
||||
border-color: ${props => darken(props.background, 10)};
|
||||
background: ${(props) => props.background};
|
||||
color: ${(props) => props.color};
|
||||
border-color: ${(props) => darken(props.background, 10)};
|
||||
|
||||
:hover {
|
||||
color: ${props => props.highlight};
|
||||
background-color: ${props => lighten(props.background, 10)};
|
||||
border-color: ${props => darken(props.highlight, 10)};
|
||||
color: ${(props) => props.highlight};
|
||||
background-color: ${(props) => lighten(props.background, 10)};
|
||||
border-color: ${(props) => darken(props.highlight, 10)};
|
||||
}
|
||||
`
|
||||
|
||||
export const StyledButton: React.FC<Props> = ({children, ...props}) => {
|
||||
return (
|
||||
<Styled {...props}>{children}</Styled>
|
||||
)
|
||||
export const StyledButton: React.FC<Props> = ({ children, ...props }) => {
|
||||
return <Styled {...props}>{children}</Styled>
|
||||
}
|
||||
|
||||
@ -3,58 +3,58 @@
|
||||
*
|
||||
* @author Chris Coyier
|
||||
*/
|
||||
function LightenDarkenColor(col, amt) {
|
||||
let usePound = false;
|
||||
function LightenDarkenColor(col, amt): string {
|
||||
let usePound = false
|
||||
|
||||
if (col[0] == "#") {
|
||||
col = col.slice(1);
|
||||
usePound = true;
|
||||
if (col[0] == '#') {
|
||||
col = col.slice(1)
|
||||
usePound = true
|
||||
}
|
||||
|
||||
const num = parseInt(col, 16)
|
||||
|
||||
let r = (num >> 16) + amt;
|
||||
let r = (num >> 16) + amt
|
||||
|
||||
if (r > 255) r = 255;
|
||||
else if (r < 0) r = 0;
|
||||
if (r > 255) r = 255
|
||||
else if (r < 0) r = 0
|
||||
|
||||
let b = ((num >> 8) & 0x00FF) + amt;
|
||||
let b = ((num >> 8) & 0x00ff) + amt
|
||||
|
||||
if (b > 255) b = 255;
|
||||
else if (b < 0) b = 0;
|
||||
if (b > 255) b = 255
|
||||
else if (b < 0) b = 0
|
||||
|
||||
let g = (num & 0x0000FF) + amt;
|
||||
let g = (num & 0x0000ff) + amt
|
||||
|
||||
if (g > 255) g = 255;
|
||||
else if (g < 0) g = 0;
|
||||
if (g > 255) g = 255
|
||||
else if (g < 0) g = 0
|
||||
|
||||
return (usePound?"#":"") + (g | (b << 8) | (r << 16)).toString(16);
|
||||
return (usePound ? '#' : '') + (g | (b << 8) | (r << 16)).toString(16)
|
||||
}
|
||||
|
||||
export const transparentize = (col: string, amt: number) => {
|
||||
if (col[0] == "#") {
|
||||
col = col.slice(1);
|
||||
export const transparentize = (col: string, amt: number): string => {
|
||||
if (col[0] == '#') {
|
||||
col = col.slice(1)
|
||||
}
|
||||
|
||||
const num = parseInt(col, 16)
|
||||
|
||||
let r = (num >> 16) + amt;
|
||||
let r = (num >> 16) + amt
|
||||
|
||||
if (r > 255) r = 255;
|
||||
else if (r < 0) r = 0;
|
||||
if (r > 255) r = 255
|
||||
else if (r < 0) r = 0
|
||||
|
||||
let b = ((num >> 8) & 0x00FF) + amt;
|
||||
let b = ((num >> 8) & 0x00ff) + amt
|
||||
|
||||
if (b > 255) b = 255;
|
||||
else if (b < 0) b = 0;
|
||||
if (b > 255) b = 255
|
||||
else if (b < 0) b = 0
|
||||
|
||||
let g = (num & 0x0000FF) + amt;
|
||||
let g = (num & 0x0000ff) + amt
|
||||
|
||||
if (g > 255) g = 255;
|
||||
else if (g < 0) g = 0;
|
||||
if (g > 255) g = 255
|
||||
else if (g < 0) g = 0
|
||||
|
||||
return `rgba(${r}, ${b}, ${g}, ${1 - amt / 100})`
|
||||
}
|
||||
|
||||
export const lighten = (color: string, amount: number) => LightenDarkenColor(color, amount)
|
||||
export const darken = (color: string, amount: number) => LightenDarkenColor(color, -amount)
|
||||
export const lighten = (color: string, amount: number): string => LightenDarkenColor(color, amount)
|
||||
export const darken = (color: string, amount: number): string => LightenDarkenColor(color, -amount)
|
||||
|
||||
@ -1,16 +1,16 @@
|
||||
import {DatePicker} from 'antd'
|
||||
import {PickerProps} from 'antd/lib/date-picker/generatePicker'
|
||||
import {Moment} from 'moment'
|
||||
import { DatePicker } from 'antd'
|
||||
import { PickerProps } from 'antd/lib/date-picker/generatePicker'
|
||||
import { Moment } from 'moment'
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import {FormDesignFragment} from '../../graphql/fragment/form.fragment'
|
||||
import {transparentize} from './color.change'
|
||||
import { FormDesignFragment } from '../../graphql/fragment/form.fragment'
|
||||
import { transparentize } from './color.change'
|
||||
|
||||
type Props = { design: FormDesignFragment } & PickerProps<Moment>
|
||||
|
||||
const Field = styled(DatePicker)`
|
||||
color: ${props => props.design.colors.answerColor};
|
||||
border-color: ${props => props.design.colors.answerColor};
|
||||
color: ${(props) => props.design.colors.answerColor};
|
||||
border-color: ${(props) => props.design.colors.answerColor};
|
||||
background: none !important;
|
||||
border-right: none;
|
||||
border-top: none;
|
||||
@ -20,11 +20,11 @@ const Field = styled(DatePicker)`
|
||||
|
||||
:hover,
|
||||
:active {
|
||||
border-color: ${props => props.design.colors.answerColor};
|
||||
border-color: ${(props) => props.design.colors.answerColor};
|
||||
}
|
||||
|
||||
&.ant-picker {
|
||||
box-shadow: none
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.ant-picker-clear {
|
||||
@ -32,20 +32,18 @@ const Field = styled(DatePicker)`
|
||||
}
|
||||
|
||||
input {
|
||||
color: ${props => props.design.colors.answerColor};
|
||||
color: ${(props) => props.design.colors.answerColor};
|
||||
|
||||
::placeholder {
|
||||
color: ${props => transparentize(props.design.colors.answerColor, 60)}
|
||||
color: ${(props) => transparentize(props.design.colors.answerColor, 60)};
|
||||
}
|
||||
}
|
||||
|
||||
.anticon {
|
||||
color: ${props => props.design.colors.answerColor};
|
||||
color: ${(props) => props.design.colors.answerColor};
|
||||
}
|
||||
`
|
||||
|
||||
export const StyledDateInput: React.FC<Props> = ({children, ...props}) => {
|
||||
return (
|
||||
<Field {...props}>{children}</Field>
|
||||
)
|
||||
export const StyledDateInput: React.FC<Props> = ({ children, ...props }) => {
|
||||
return <Field {...props}>{children}</Field>
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import {FormDesignFragment} from '../../graphql/fragment/form.fragment'
|
||||
import { FormDesignFragment } from '../../graphql/fragment/form.fragment'
|
||||
|
||||
interface Props {
|
||||
type: 'question' | 'answer'
|
||||
@ -8,11 +8,12 @@ interface Props {
|
||||
}
|
||||
|
||||
const Header = styled.h1`
|
||||
color: ${props => props.type === 'question' ? props.design.colors.questionColor : props.design.colors.answerColor}
|
||||
color: ${(props) =>
|
||||
props.type === 'question'
|
||||
? props.design.colors.questionColor
|
||||
: props.design.colors.answerColor};
|
||||
`
|
||||
|
||||
export const StyledH1: React.FC<Props> = ({children, ...props}) => {
|
||||
return (
|
||||
<Header {...props}>{children}</Header>
|
||||
)
|
||||
export const StyledH1: React.FC<Props> = ({ children, ...props }) => {
|
||||
return <Header {...props}>{children}</Header>
|
||||
}
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import {FormDesignFragment} from '../../graphql/fragment/form.fragment'
|
||||
import { FormDesignFragment } from '../../graphql/fragment/form.fragment'
|
||||
|
||||
interface Props {
|
||||
type: 'question' | 'answer'
|
||||
design: FormDesignFragment
|
||||
}
|
||||
const Header = styled.h2`
|
||||
color: ${props => props.type === 'question' ? props.design.colors.questionColor : props.design.colors.answerColor}
|
||||
color: ${(props) =>
|
||||
props.type === 'question'
|
||||
? props.design.colors.questionColor
|
||||
: props.design.colors.answerColor};
|
||||
`
|
||||
|
||||
export const StyledH2: React.FC<Props> = ({children, ...props}) => {
|
||||
return (
|
||||
<Header {...props}>{children}</Header>
|
||||
)
|
||||
export const StyledH2: React.FC<Props> = ({ children, ...props }) => {
|
||||
return <Header {...props}>{children}</Header>
|
||||
}
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
import {Input} from 'antd'
|
||||
import {InputProps} from 'antd/lib/input/Input'
|
||||
import { Input } from 'antd'
|
||||
import { InputProps } from 'antd/lib/input/Input'
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import {FormDesignFragment} from '../../graphql/fragment/form.fragment'
|
||||
import {transparentize} from './color.change'
|
||||
import { FormDesignFragment } from '../../graphql/fragment/form.fragment'
|
||||
import { transparentize } from './color.change'
|
||||
|
||||
interface Props extends InputProps{
|
||||
interface Props extends InputProps {
|
||||
design: FormDesignFragment
|
||||
}
|
||||
|
||||
const Field = styled(Input)`
|
||||
color: ${props => props.design.colors.answerColor};
|
||||
border-color: ${props => props.design.colors.answerColor};
|
||||
color: ${(props) => props.design.colors.answerColor};
|
||||
border-color: ${(props) => props.design.colors.answerColor};
|
||||
background: none !important;
|
||||
border-right: none;
|
||||
border-top: none;
|
||||
@ -19,34 +19,32 @@ const Field = styled(Input)`
|
||||
border-radius: 0;
|
||||
|
||||
:focus {
|
||||
outline: ${props => props.design.colors.answerColor} auto 5px
|
||||
outline: ${(props) => props.design.colors.answerColor} auto 5px;
|
||||
}
|
||||
|
||||
:hover,
|
||||
:active {
|
||||
border-color: ${props => props.design.colors.answerColor};
|
||||
border-color: ${(props) => props.design.colors.answerColor};
|
||||
}
|
||||
|
||||
&.ant-input-affix-wrapper {
|
||||
box-shadow: none
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
input {
|
||||
background: none !important;
|
||||
color: ${props => props.design.colors.answerColor};
|
||||
color: ${(props) => props.design.colors.answerColor};
|
||||
|
||||
::placeholder {
|
||||
color: ${props => transparentize(props.design.colors.answerColor, 60)}
|
||||
color: ${(props) => transparentize(props.design.colors.answerColor, 60)};
|
||||
}
|
||||
}
|
||||
|
||||
.anticon {
|
||||
color: ${props => props.design.colors.answerColor};
|
||||
color: ${(props) => props.design.colors.answerColor};
|
||||
}
|
||||
`
|
||||
|
||||
export const StyledInput: React.FC<Props> = ({children, ...props}) => {
|
||||
return (
|
||||
<Field {...props}>{children}</Field>
|
||||
)
|
||||
export const StyledInput: React.FC<Props> = ({ children, ...props }) => {
|
||||
return <Field {...props}>{children}</Field>
|
||||
}
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
import {InputNumber} from 'antd'
|
||||
import {InputNumberProps} from 'antd/lib/input-number'
|
||||
import { InputNumber } from 'antd'
|
||||
import { InputNumberProps } from 'antd/lib/input-number'
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import {FormDesignFragment} from '../../graphql/fragment/form.fragment'
|
||||
import {transparentize} from './color.change'
|
||||
import { FormDesignFragment } from '../../graphql/fragment/form.fragment'
|
||||
import { transparentize } from './color.change'
|
||||
|
||||
interface Props extends InputNumberProps {
|
||||
design: FormDesignFragment
|
||||
}
|
||||
|
||||
const Field = styled(InputNumber)`
|
||||
color: ${props => props.design.colors.answerColor};
|
||||
border-color: ${props => props.design.colors.answerColor};
|
||||
color: ${(props) => props.design.colors.answerColor};
|
||||
border-color: ${(props) => props.design.colors.answerColor};
|
||||
background: none !important;
|
||||
border-right: none;
|
||||
border-top: none;
|
||||
@ -20,34 +20,32 @@ const Field = styled(InputNumber)`
|
||||
width: 100%;
|
||||
|
||||
:focus {
|
||||
outline: ${props => props.design.colors.answerColor} auto 5px
|
||||
outline: ${(props) => props.design.colors.answerColor} auto 5px;
|
||||
}
|
||||
|
||||
:hover,
|
||||
:active {
|
||||
border-color: ${props => props.design.colors.answerColor};
|
||||
border-color: ${(props) => props.design.colors.answerColor};
|
||||
}
|
||||
|
||||
&.ant-input-number {
|
||||
box-shadow: none
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
input {
|
||||
background: none !important;
|
||||
color: ${props => props.design.colors.answerColor};
|
||||
color: ${(props) => props.design.colors.answerColor};
|
||||
|
||||
::placeholder {
|
||||
color: ${props => transparentize(props.design.colors.answerColor, 60)}
|
||||
color: ${(props) => transparentize(props.design.colors.answerColor, 60)};
|
||||
}
|
||||
}
|
||||
|
||||
.anticon {
|
||||
color: ${props => props.design.colors.answerColor};
|
||||
color: ${(props) => props.design.colors.answerColor};
|
||||
}
|
||||
`
|
||||
|
||||
export const StyledNumberInput: React.FC<Props> = ({children, ...props}) => {
|
||||
return (
|
||||
<Field {...props}>{children}</Field>
|
||||
)
|
||||
export const StyledNumberInput: React.FC<Props> = ({ children, ...props }) => {
|
||||
return <Field {...props}>{children}</Field>
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import {FormDesignFragment} from '../../graphql/fragment/form.fragment'
|
||||
import { FormDesignFragment } from '../../graphql/fragment/form.fragment'
|
||||
|
||||
interface Props {
|
||||
type: 'question' | 'answer'
|
||||
@ -8,11 +8,12 @@ interface Props {
|
||||
}
|
||||
|
||||
const Paragraph = styled.p`
|
||||
color: ${props => props.type === 'question' ? props.design.colors.questionColor : props.design.colors.answerColor}
|
||||
color: ${(props) =>
|
||||
props.type === 'question'
|
||||
? props.design.colors.questionColor
|
||||
: props.design.colors.answerColor};
|
||||
`
|
||||
|
||||
export const StyledP: React.FC<Props> = ({children, ...props}) => {
|
||||
return (
|
||||
<Paragraph {...props}>{children}</Paragraph>
|
||||
)
|
||||
export const StyledP: React.FC<Props> = ({ children, ...props }) => {
|
||||
return <Paragraph {...props}>{children}</Paragraph>
|
||||
}
|
||||
|
||||
@ -1,39 +1,37 @@
|
||||
import {Radio} from 'antd'
|
||||
import {RadioProps} from 'antd/lib/radio/interface'
|
||||
import { Radio } from 'antd'
|
||||
import { RadioProps } from 'antd/lib/radio/interface'
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import {FormDesignFragment} from '../../graphql/fragment/form.fragment'
|
||||
import { FormDesignFragment } from '../../graphql/fragment/form.fragment'
|
||||
|
||||
interface Props extends RadioProps {
|
||||
design: FormDesignFragment
|
||||
}
|
||||
|
||||
const Field = styled(Radio)`
|
||||
color: ${props => props.design.colors.answerColor};
|
||||
border-color: ${props => props.design.colors.answerColor};
|
||||
color: ${(props) => props.design.colors.answerColor};
|
||||
border-color: ${(props) => props.design.colors.answerColor};
|
||||
background: none;
|
||||
|
||||
.ant-radio {
|
||||
.ant-radio-inner {
|
||||
border-color: ${props => props.design.colors.answerColor};
|
||||
border-color: ${(props) => props.design.colors.answerColor};
|
||||
|
||||
&::after {
|
||||
background: ${props => props.design.colors.answerColor};
|
||||
background: ${(props) => props.design.colors.answerColor};
|
||||
}
|
||||
}
|
||||
|
||||
&::after {
|
||||
border-color: ${props => props.design.colors.answerColor};
|
||||
border-color: ${(props) => props.design.colors.answerColor};
|
||||
}
|
||||
}
|
||||
|
||||
.anticon {
|
||||
color: ${props => props.design.colors.answerColor};
|
||||
color: ${(props) => props.design.colors.answerColor};
|
||||
}
|
||||
`
|
||||
|
||||
export const StyledRadio: React.FC<Props> = ({children, ...props}) => {
|
||||
return (
|
||||
<Field {...props}>{children}</Field>
|
||||
)
|
||||
export const StyledRadio: React.FC<Props> = ({ children, ...props }) => {
|
||||
return <Field {...props}>{children}</Field>
|
||||
}
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import {Select} from 'antd'
|
||||
import {SelectProps} from 'antd/lib/select'
|
||||
import { Select } from 'antd'
|
||||
import { SelectProps } from 'antd/lib/select'
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import {FormDesignFragment} from '../../graphql/fragment/form.fragment'
|
||||
import {transparentize} from './color.change'
|
||||
import { FormDesignFragment } from '../../graphql/fragment/form.fragment'
|
||||
import { transparentize } from './color.change'
|
||||
|
||||
interface Props extends SelectProps<string> {
|
||||
design: FormDesignFragment
|
||||
@ -11,8 +11,8 @@ interface Props extends SelectProps<string> {
|
||||
|
||||
const Field = styled(Select)`
|
||||
.ant-select-selector {
|
||||
color: ${props => props.design.colors.answerColor};
|
||||
border-color: ${props => props.design.colors.answerColor} !important;
|
||||
color: ${(props) => props.design.colors.answerColor};
|
||||
border-color: ${(props) => props.design.colors.answerColor} !important;
|
||||
background: none !important;
|
||||
border-right: none !important;
|
||||
border-top: none !important;
|
||||
@ -22,30 +22,28 @@ const Field = styled(Select)`
|
||||
}
|
||||
|
||||
:focus {
|
||||
outline: ${props => props.design.colors.answerColor} auto 5px
|
||||
outline: ${(props) => props.design.colors.answerColor} auto 5px;
|
||||
}
|
||||
|
||||
:hover,
|
||||
:active {
|
||||
border-color: ${props => props.design.colors.answerColor};
|
||||
border-color: ${(props) => props.design.colors.answerColor};
|
||||
}
|
||||
|
||||
input {
|
||||
background: none !important;
|
||||
color: ${props => props.design.colors.answerColor};
|
||||
color: ${(props) => props.design.colors.answerColor};
|
||||
|
||||
::placeholder {
|
||||
color: ${props => transparentize(props.design.colors.answerColor, 60)}
|
||||
color: ${(props) => transparentize(props.design.colors.answerColor, 60)};
|
||||
}
|
||||
}
|
||||
|
||||
.anticon {
|
||||
color: ${props => props.design.colors.answerColor};
|
||||
color: ${(props) => props.design.colors.answerColor};
|
||||
}
|
||||
`
|
||||
|
||||
export const StyledSelect: React.FC<Props> = ({children, ...props}) => {
|
||||
return (
|
||||
<Field {...props}>{children}</Field>
|
||||
)
|
||||
export const StyledSelect: React.FC<Props> = ({ children, ...props }) => {
|
||||
return <Field {...props}>{children}</Field>
|
||||
}
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
import {Input} from 'antd'
|
||||
import {TextAreaProps} from 'antd/lib/input/TextArea'
|
||||
import { Input } from 'antd'
|
||||
import { TextAreaProps } from 'antd/lib/input/TextArea'
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import {FormDesignFragment} from '../../graphql/fragment/form.fragment'
|
||||
import {transparentize} from './color.change'
|
||||
import { FormDesignFragment } from '../../graphql/fragment/form.fragment'
|
||||
import { transparentize } from './color.change'
|
||||
|
||||
interface Props extends TextAreaProps {
|
||||
design: FormDesignFragment
|
||||
}
|
||||
|
||||
const Field = styled(Input.TextArea)`
|
||||
color: ${props => props.design.colors.answerColor};
|
||||
border-color: ${props => props.design.colors.answerColor};
|
||||
color: ${(props) => props.design.colors.answerColor};
|
||||
border-color: ${(props) => props.design.colors.answerColor};
|
||||
background: none !important;
|
||||
border-right: none;
|
||||
border-top: none;
|
||||
@ -21,30 +21,28 @@ const Field = styled(Input.TextArea)`
|
||||
:focus {
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
border-color: ${props => props.design.colors.answerColor};
|
||||
border-color: ${(props) => props.design.colors.answerColor};
|
||||
}
|
||||
|
||||
:hover,
|
||||
:active {
|
||||
border-color: ${props => props.design.colors.answerColor};
|
||||
border-color: ${(props) => props.design.colors.answerColor};
|
||||
}
|
||||
|
||||
input {
|
||||
background: none !important;
|
||||
color: ${props => props.design.colors.answerColor};
|
||||
color: ${(props) => props.design.colors.answerColor};
|
||||
|
||||
::placeholder {
|
||||
color: ${props => transparentize(props.design.colors.answerColor, 60)}
|
||||
color: ${(props) => transparentize(props.design.colors.answerColor, 60)};
|
||||
}
|
||||
}
|
||||
|
||||
.anticon {
|
||||
color: ${props => props.design.colors.answerColor};
|
||||
color: ${(props) => props.design.colors.answerColor};
|
||||
}
|
||||
`
|
||||
|
||||
export const StyledTextareaInput: React.FC<Props> = ({children, ...props}) => {
|
||||
return (
|
||||
<Field {...props}>{children}</Field>
|
||||
)
|
||||
export const StyledTextareaInput: React.FC<Props> = ({ children, ...props }) => {
|
||||
return <Field {...props}>{children}</Field>
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import {Tooltip} from 'antd'
|
||||
import { Tooltip } from 'antd'
|
||||
import dayjs from 'dayjs'
|
||||
import relativeTime from 'dayjs/plugin/relativeTime'
|
||||
import React from 'react'
|
||||
@ -9,13 +9,15 @@ interface Props {
|
||||
date: string
|
||||
}
|
||||
|
||||
export const TimeAgo: React.FC<Props> = props => {
|
||||
export const TimeAgo: React.FC<Props> = (props) => {
|
||||
const date = dayjs(props.date)
|
||||
return (
|
||||
<Tooltip title={date.format('YYYY-MM-DD HH:mm:ss')}>
|
||||
<div style={{
|
||||
display: 'inline-block'
|
||||
}}>
|
||||
<div
|
||||
style={{
|
||||
display: 'inline-block',
|
||||
}}
|
||||
>
|
||||
{date.fromNow()}
|
||||
</div>
|
||||
</Tooltip>
|
||||
|
||||
@ -1,37 +1,47 @@
|
||||
import {useMutation} from '@apollo/react-hooks'
|
||||
import {useCallback, useEffect, useState} from 'react'
|
||||
import { useMutation } from '@apollo/react-hooks'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import {
|
||||
SUBMISSION_SET_FIELD_MUTATION,
|
||||
SubmissionSetFieldMutationData,
|
||||
SubmissionSetFieldMutationVariables
|
||||
SubmissionSetFieldMutationVariables,
|
||||
} from '../graphql/mutation/submission.set.field.mutation'
|
||||
import {
|
||||
SUBMISSION_START_MUTATION,
|
||||
SubmissionStartMutationData,
|
||||
SubmissionStartMutationVariables
|
||||
SubmissionStartMutationVariables,
|
||||
} from '../graphql/mutation/submission.start.mutation'
|
||||
|
||||
export const useSubmission = (formId: string) => {
|
||||
const [submission, setSubmission] = useState<{ id: string, token: string }>()
|
||||
interface Submission {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
setField: (fieldId: string, data: any) => Promise<void>
|
||||
finish: () => void
|
||||
}
|
||||
|
||||
const [start] = useMutation<SubmissionStartMutationData, SubmissionStartMutationVariables>(SUBMISSION_START_MUTATION)
|
||||
const [save] = useMutation<SubmissionSetFieldMutationData, SubmissionSetFieldMutationVariables>(SUBMISSION_SET_FIELD_MUTATION)
|
||||
export const useSubmission = (formId: string): Submission => {
|
||||
const [submission, setSubmission] = useState<{ id: string; token: string }>()
|
||||
|
||||
const [start] = useMutation<SubmissionStartMutationData, SubmissionStartMutationVariables>(
|
||||
SUBMISSION_START_MUTATION
|
||||
)
|
||||
const [save] = useMutation<SubmissionSetFieldMutationData, SubmissionSetFieldMutationVariables>(
|
||||
SUBMISSION_SET_FIELD_MUTATION
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
;(async () => {
|
||||
const token = [...Array(40)].map(() => Math.random().toString(36)[2]).join('')
|
||||
|
||||
const {data} = await start({
|
||||
const { data } = await start({
|
||||
variables: {
|
||||
form: formId,
|
||||
submission: {
|
||||
token,
|
||||
device: {
|
||||
name: /Mobi/i.test(window.navigator.userAgent) ? 'mobile' : 'desktop',
|
||||
type: window.navigator.userAgent
|
||||
}
|
||||
}
|
||||
}
|
||||
type: window.navigator.userAgent,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
setSubmission({
|
||||
@ -41,7 +51,9 @@ export const useSubmission = (formId: string) => {
|
||||
})()
|
||||
}, [formId])
|
||||
|
||||
const setField = useCallback(async (fieldId: string, data: any) => {
|
||||
const setField = useCallback(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
async (fieldId: string, data: any) => {
|
||||
console.log('just save', fieldId, data)
|
||||
await save({
|
||||
variables: {
|
||||
@ -49,11 +61,13 @@ export const useSubmission = (formId: string) => {
|
||||
field: {
|
||||
token: submission.token,
|
||||
field: fieldId,
|
||||
data: JSON.stringify(data)
|
||||
}
|
||||
}
|
||||
data: JSON.stringify(data),
|
||||
},
|
||||
},
|
||||
})
|
||||
}, [submission])
|
||||
},
|
||||
[submission]
|
||||
)
|
||||
|
||||
const finish = useCallback(() => {
|
||||
console.log('finish submission!!', formId)
|
||||
|
||||
@ -1,29 +1,29 @@
|
||||
import {useEffect, useState} from 'react'
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
export const useWindowSize = () => {
|
||||
const isClient = typeof window === 'object';
|
||||
export const useWindowSize = (): { width?: number; height?: number } => {
|
||||
const isClient = typeof window === 'object'
|
||||
|
||||
function getSize() {
|
||||
return {
|
||||
width: isClient ? window.innerWidth : undefined,
|
||||
height: isClient ? window.innerHeight : undefined
|
||||
};
|
||||
height: isClient ? window.innerHeight : undefined,
|
||||
}
|
||||
}
|
||||
|
||||
const [windowSize, setWindowSize] = useState(getSize);
|
||||
const [windowSize, setWindowSize] = useState(getSize)
|
||||
|
||||
useEffect(() => {
|
||||
if (!isClient) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
function handleResize() {
|
||||
setWindowSize(getSize());
|
||||
setWindowSize(getSize())
|
||||
}
|
||||
|
||||
window.addEventListener('resize', handleResize);
|
||||
return () => window.removeEventListener('resize', handleResize);
|
||||
}, []); // Empty array ensures that effect is only run on mount and unmount
|
||||
window.addEventListener('resize', handleResize)
|
||||
return () => window.removeEventListener('resize', handleResize)
|
||||
}, []) // Empty array ensures that effect is only run on mount and unmount
|
||||
|
||||
return windowSize;
|
||||
return windowSize
|
||||
}
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import {Form, Input, Select, Tabs} from 'antd'
|
||||
import {TabPaneProps} from 'antd/lib/tabs'
|
||||
import { Form, Input, Select, Tabs } from 'antd'
|
||||
import { TabPaneProps } from 'antd/lib/tabs'
|
||||
import React from 'react'
|
||||
import {languages} from '../../../i18n'
|
||||
import { languages } from '../../../i18n'
|
||||
|
||||
export const BaseDataTab: React.FC<TabPaneProps> = props => {
|
||||
export const BaseDataTab: React.FC<TabPaneProps> = (props) => {
|
||||
return (
|
||||
<Tabs.TabPane {...props}>
|
||||
<Form.Item
|
||||
@ -45,7 +45,7 @@ export const BaseDataTab: React.FC<TabPaneProps> = props => {
|
||||
message: 'Please select a role',
|
||||
},
|
||||
]}
|
||||
getValueFromEvent={e => {
|
||||
getValueFromEvent={(e) => {
|
||||
switch (e) {
|
||||
case 'superuser':
|
||||
return ['user', 'admin', 'superuser']
|
||||
@ -55,7 +55,7 @@ export const BaseDataTab: React.FC<TabPaneProps> = props => {
|
||||
return ['user']
|
||||
}
|
||||
}}
|
||||
getValueProps={v => {
|
||||
getValueProps={(v) => {
|
||||
let role = 'user'
|
||||
|
||||
if (v && v.includes('superuser')) {
|
||||
@ -65,12 +65,16 @@ export const BaseDataTab: React.FC<TabPaneProps> = props => {
|
||||
}
|
||||
|
||||
return {
|
||||
value: role
|
||||
value: role,
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Select>
|
||||
{['user', 'admin', 'superuser'].map(role => <Select.Option value={role} key={role}>{role.toUpperCase()}</Select.Option> )}
|
||||
{['user', 'admin', 'superuser'].map((role) => (
|
||||
<Select.Option value={role} key={role}>
|
||||
{role.toUpperCase()}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
@ -85,24 +89,21 @@ export const BaseDataTab: React.FC<TabPaneProps> = props => {
|
||||
]}
|
||||
>
|
||||
<Select>
|
||||
{languages.map(language => <Select.Option value={language} key={language}>{language.toUpperCase()}</Select.Option> )}
|
||||
{languages.map((language) => (
|
||||
<Select.Option value={language} key={language}>
|
||||
{language.toUpperCase()}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="First Name"
|
||||
name={['user', 'firstName']}
|
||||
>
|
||||
<Form.Item label="First Name" name={['user', 'firstName']}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label="Last Name"
|
||||
name={['user', 'lastName']}
|
||||
>
|
||||
<Form.Item label="Last Name" name={['user', 'lastName']}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
|
||||
</Tabs.TabPane>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,16 +1,15 @@
|
||||
import {Tag} from "antd"
|
||||
import React, {CSSProperties} from 'react'
|
||||
import { Tag } from 'antd'
|
||||
import React, { CSSProperties } from 'react'
|
||||
|
||||
interface Props {
|
||||
roles: string[]
|
||||
}
|
||||
|
||||
export const UserRole: React.FC<Props> = props => {
|
||||
export const UserRole: React.FC<Props> = (props) => {
|
||||
let color
|
||||
let level = 'unknown'
|
||||
const css: CSSProperties = {}
|
||||
|
||||
|
||||
if (props.roles.includes('superuser')) {
|
||||
color = 'red'
|
||||
level = 'superuser'
|
||||
@ -24,10 +23,7 @@ export const UserRole: React.FC<Props> = props => {
|
||||
}
|
||||
|
||||
return (
|
||||
<Tag
|
||||
color={color}
|
||||
style={css}
|
||||
>
|
||||
<Tag color={color} style={css}>
|
||||
{level.toUpperCase()}
|
||||
</Tag>
|
||||
)
|
||||
|
||||
@ -1,19 +1,19 @@
|
||||
import {useQuery} from '@apollo/react-hooks'
|
||||
import {AxiosRequestConfig} from 'axios'
|
||||
import {useRouter} from 'next/router'
|
||||
import React, {useEffect, useState} from 'react'
|
||||
import {useTranslation} from 'react-i18next'
|
||||
import {ME_QUERY, MeQueryData} from '../graphql/query/me.query'
|
||||
import {LoadingPage} from './loading.page'
|
||||
import { useQuery } from '@apollo/react-hooks'
|
||||
import { AxiosRequestConfig } from 'axios'
|
||||
import { useRouter } from 'next/router'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { ME_QUERY, MeQueryData } from '../graphql/query/me.query'
|
||||
import { LoadingPage } from './loading.page'
|
||||
|
||||
export const clearAuth = async () => {
|
||||
export const clearAuth = async (): Promise<void> => {
|
||||
localStorage.removeItem('access')
|
||||
localStorage.removeItem('refresh')
|
||||
|
||||
// TODO logout on server!
|
||||
}
|
||||
|
||||
export const setAuth = (access, refresh) => {
|
||||
export const setAuth = (access: string, refresh: string): void => {
|
||||
localStorage.setItem('access', access)
|
||||
localStorage.setItem('refresh', refresh)
|
||||
}
|
||||
@ -37,12 +37,14 @@ export const authConfig = async (config: AxiosRequestConfig = {}): Promise<Axios
|
||||
return config
|
||||
}
|
||||
|
||||
export const withAuth = (Component, roles: string[] = []): React.FC => {
|
||||
return props => {
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types,@typescript-eslint/no-explicit-any
|
||||
export const withAuth = (Component: any, roles: string[] = []): React.FC => {
|
||||
// eslint-disable-next-line react/display-name
|
||||
return (props) => {
|
||||
const { t } = useTranslation()
|
||||
const router = useRouter()
|
||||
const [access, setAccess] = useState(false)
|
||||
const {loading, data, error} = useQuery<MeQueryData>(ME_QUERY)
|
||||
const { loading, data, error } = useQuery<MeQueryData>(ME_QUERY)
|
||||
|
||||
useEffect(() => {
|
||||
if (roles.length === 0) {
|
||||
@ -67,10 +69,7 @@ export const withAuth = (Component, roles: string[] = []): React.FC => {
|
||||
return
|
||||
}
|
||||
|
||||
const next = roles
|
||||
.map(role => data.me.roles.includes(role))
|
||||
.filter(p => p)
|
||||
.length > 0
|
||||
const next = roles.map((role) => data.me.roles.includes(role)).filter((p) => p).length > 0
|
||||
|
||||
setAccess(next)
|
||||
|
||||
@ -88,5 +87,5 @@ export const withAuth = (Component, roles: string[] = []): React.FC => {
|
||||
}
|
||||
|
||||
return <Component me={data && data.me} {...props} />
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import {gql} from 'apollo-boost'
|
||||
import { gql } from 'apollo-boost'
|
||||
|
||||
export interface AdminFormPageFragment {
|
||||
show: boolean
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import {gql} from 'apollo-boost'
|
||||
import { gql } from 'apollo-boost'
|
||||
|
||||
export interface AdminProfileFragment {
|
||||
id: string
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import {gql} from 'apollo-boost'
|
||||
import { gql } from 'apollo-boost'
|
||||
|
||||
export interface AdminUserFragment {
|
||||
id: string
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import {gql} from 'apollo-boost'
|
||||
import { gql } from 'apollo-boost'
|
||||
|
||||
export interface FormPageFragment {
|
||||
show: boolean
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import {gql} from 'apollo-boost'
|
||||
import { gql } from 'apollo-boost'
|
||||
|
||||
export interface SubmissionFragment {
|
||||
id?: string
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import {gql} from 'apollo-boost'
|
||||
import {ADMIN_FORM_FRAGMENT, AdminFormFragment} from '../fragment/admin.form.fragment'
|
||||
import { gql } from 'apollo-boost'
|
||||
import { ADMIN_FORM_FRAGMENT, AdminFormFragment } from '../fragment/admin.form.fragment'
|
||||
|
||||
export interface AdminFormCreateMutationData {
|
||||
form: AdminFormFragment
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import {gql} from 'apollo-boost'
|
||||
import { gql } from 'apollo-boost'
|
||||
|
||||
export interface AdminFormDeleteMutationData {
|
||||
form: {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import {gql} from 'apollo-boost'
|
||||
import {ADMIN_FORM_FRAGMENT, AdminFormFragment} from '../fragment/admin.form.fragment'
|
||||
import { gql } from 'apollo-boost'
|
||||
import { ADMIN_FORM_FRAGMENT, AdminFormFragment } from '../fragment/admin.form.fragment'
|
||||
|
||||
export interface AdminFormUpdateMutationData {
|
||||
form: AdminFormFragment
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import {gql} from 'apollo-boost'
|
||||
import {ADMIN_PROFILE_FRAGMENT} from '../fragment/admin.profile.fragment'
|
||||
import {AdminUserFragment} from '../fragment/admin.user.fragment'
|
||||
import { gql } from 'apollo-boost'
|
||||
import { ADMIN_PROFILE_FRAGMENT } from '../fragment/admin.profile.fragment'
|
||||
import { AdminUserFragment } from '../fragment/admin.user.fragment'
|
||||
|
||||
export interface AdminProfileUpdateMutationData {
|
||||
user: AdminUserFragment
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import {gql} from 'apollo-boost'
|
||||
import { gql } from 'apollo-boost'
|
||||
|
||||
export interface AdminUserDeleteMutationData {
|
||||
form: {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import {gql} from 'apollo-boost'
|
||||
import {ADMIN_USER_FRAGMENT, AdminUserFragment} from '../fragment/admin.user.fragment'
|
||||
import { gql } from 'apollo-boost'
|
||||
import { ADMIN_USER_FRAGMENT, AdminUserFragment } from '../fragment/admin.user.fragment'
|
||||
|
||||
export interface AdminUserUpdateMutationData {
|
||||
user: AdminUserFragment
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import {gql} from 'apollo-boost'
|
||||
import { gql } from 'apollo-boost'
|
||||
|
||||
export const LOGIN_MUTATION = gql`
|
||||
mutation login($username: String!, $password: String!) {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import {gql} from 'apollo-boost'
|
||||
import { gql } from 'apollo-boost'
|
||||
|
||||
export const REGISTER_MUTATION = gql`
|
||||
mutation register($user: UserCreateInput!) {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import {gql} from 'apollo-boost'
|
||||
import { gql } from 'apollo-boost'
|
||||
|
||||
export const SUBMISSION_FINISH_MUTATION = gql`
|
||||
mutation start($submission: ID!,$field: SubmissionSetFieldInput!) {
|
||||
mutation start($submission: ID!, $field: SubmissionSetFieldInput!) {
|
||||
submission: submissionSetField(submission: $submission, field: $field) {
|
||||
id
|
||||
percentageComplete
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import {gql} from 'apollo-boost'
|
||||
import { gql } from 'apollo-boost'
|
||||
|
||||
export interface SubmissionSetFieldMutationData {
|
||||
submission: {
|
||||
@ -17,7 +17,7 @@ export interface SubmissionSetFieldMutationVariables {
|
||||
}
|
||||
|
||||
export const SUBMISSION_SET_FIELD_MUTATION = gql`
|
||||
mutation start($submission: ID!,$field: SubmissionSetFieldInput!) {
|
||||
mutation start($submission: ID!, $field: SubmissionSetFieldInput!) {
|
||||
submission: submissionSetField(submission: $submission, field: $field) {
|
||||
id
|
||||
percentageComplete
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import {gql} from 'apollo-boost'
|
||||
import { gql } from 'apollo-boost'
|
||||
|
||||
export interface SubmissionStartMutationData {
|
||||
submission: {
|
||||
@ -19,7 +19,7 @@ export interface SubmissionStartMutationVariables {
|
||||
}
|
||||
|
||||
export const SUBMISSION_START_MUTATION = gql`
|
||||
mutation start($form: ID!,$submission: SubmissionStartInput!) {
|
||||
mutation start($form: ID!, $submission: SubmissionStartInput!) {
|
||||
submission: submissionStart(form: $form, submission: $submission) {
|
||||
id
|
||||
percentageComplete
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import {gql} from 'apollo-boost'
|
||||
import {ADMIN_FORM_FRAGMENT, AdminFormFragment} from '../fragment/admin.form.fragment'
|
||||
import { gql } from 'apollo-boost'
|
||||
import { ADMIN_FORM_FRAGMENT, AdminFormFragment } from '../fragment/admin.form.fragment'
|
||||
|
||||
export interface AdminFormQueryData {
|
||||
form: AdminFormFragment
|
||||
@ -10,8 +10,8 @@ export interface AdminFormQueryVariables {
|
||||
}
|
||||
|
||||
export const ADMIN_FORM_QUERY = gql`
|
||||
query form($id: ID!){
|
||||
form:getFormById(id: $id) {
|
||||
query form($id: ID!) {
|
||||
form: getFormById(id: $id) {
|
||||
...AdminForm
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import {gql} from 'apollo-boost'
|
||||
import { gql } from 'apollo-boost'
|
||||
|
||||
export interface AdminPagerFormEntryQueryData {
|
||||
id: string
|
||||
@ -30,7 +30,7 @@ export interface AdminPagerFormQueryVariables {
|
||||
}
|
||||
|
||||
export const ADMIN_PAGER_FORM_QUERY = gql`
|
||||
query pager($start: Int, $limit: Int){
|
||||
query pager($start: Int, $limit: Int) {
|
||||
pager: listForms(start: $start, limit: $limit) {
|
||||
entries {
|
||||
id
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import {gql} from 'apollo-boost'
|
||||
import { gql } from 'apollo-boost'
|
||||
|
||||
export interface AdminPagerSubmissionFormFieldQueryData {
|
||||
title: string
|
||||
@ -56,7 +56,7 @@ export interface AdminPagerSubmissionQueryVariables {
|
||||
}
|
||||
|
||||
export const ADMIN_PAGER_SUBMISSION_QUERY = gql`
|
||||
query pager($form: ID!, $start: Int, $limit: Int){
|
||||
query pager($form: ID!, $start: Int, $limit: Int) {
|
||||
form: getFormById(id: $form) {
|
||||
id
|
||||
title
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import {gql} from 'apollo-boost'
|
||||
import { gql } from 'apollo-boost'
|
||||
|
||||
export interface AdminPagerUserEntryQueryData {
|
||||
id: string
|
||||
@ -24,7 +24,7 @@ export interface AdminPagerUserQueryVariables {
|
||||
}
|
||||
|
||||
export const ADMIN_PAGER_USER_QUERY = gql`
|
||||
query pager($start: Int, $limit: Int){
|
||||
query pager($start: Int, $limit: Int) {
|
||||
pager: listUsers(start: $start, limit: $limit) {
|
||||
entries {
|
||||
id
|
||||
|
||||
@ -1,16 +1,15 @@
|
||||
import {gql} from 'apollo-boost'
|
||||
import {ADMIN_PROFILE_FRAGMENT, AdminProfileFragment} from '../fragment/admin.profile.fragment'
|
||||
import { gql } from 'apollo-boost'
|
||||
import { ADMIN_PROFILE_FRAGMENT, AdminProfileFragment } from '../fragment/admin.profile.fragment'
|
||||
|
||||
export interface AdminProfileQueryData {
|
||||
user: AdminProfileFragment
|
||||
}
|
||||
|
||||
export interface AdminProfileQueryVariables {
|
||||
}
|
||||
export interface AdminProfileQueryVariables {}
|
||||
|
||||
export const ADMIN_PROFILE_QUERY = gql`
|
||||
query profile {
|
||||
user:me {
|
||||
user: me {
|
||||
...AdminProfile
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import {gql} from 'apollo-boost'
|
||||
import { gql } from 'apollo-boost'
|
||||
|
||||
export interface AdminStatisticQueryData {
|
||||
forms: {
|
||||
@ -12,8 +12,7 @@ export interface AdminStatisticQueryData {
|
||||
}
|
||||
}
|
||||
|
||||
export interface AdminStatisticQueryVariables {
|
||||
}
|
||||
export interface AdminStatisticQueryVariables {}
|
||||
|
||||
export const ADMIN_STATISTIC_QUERY = gql`
|
||||
query {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import {gql} from 'apollo-boost'
|
||||
import {ADMIN_USER_FRAGMENT, AdminUserFragment} from '../fragment/admin.user.fragment'
|
||||
import { gql } from 'apollo-boost'
|
||||
import { ADMIN_USER_FRAGMENT, AdminUserFragment } from '../fragment/admin.user.fragment'
|
||||
|
||||
export interface AdminUserQueryData {
|
||||
user: AdminUserFragment
|
||||
@ -10,8 +10,8 @@ export interface AdminUserQueryVariables {
|
||||
}
|
||||
|
||||
export const ADMIN_USER_QUERY = gql`
|
||||
query user($id: ID!){
|
||||
user:getUserById(id: $id) {
|
||||
query user($id: ID!) {
|
||||
user: getUserById(id: $id) {
|
||||
...AdminUser
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import {gql} from 'apollo-boost'
|
||||
import {FORM_FRAGMENT, FormFragment} from '../fragment/form.fragment'
|
||||
import { gql } from 'apollo-boost'
|
||||
import { FORM_FRAGMENT, FormFragment } from '../fragment/form.fragment'
|
||||
|
||||
export interface FormQueryData {
|
||||
form: FormFragment
|
||||
@ -10,8 +10,8 @@ export interface FormQueryVariables {
|
||||
}
|
||||
|
||||
export const FORM_QUERY = gql`
|
||||
query form($id: ID!){
|
||||
form:getFormById(id: $id) {
|
||||
query form($id: ID!) {
|
||||
form: getFormById(id: $id) {
|
||||
...Form
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import {gql} from 'apollo-boost'
|
||||
import { gql } from 'apollo-boost'
|
||||
|
||||
export interface MeQueryData {
|
||||
me: {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import {gql} from 'apollo-boost'
|
||||
import { gql } from 'apollo-boost'
|
||||
|
||||
export interface SettingsQueryData {
|
||||
disabledSignUp: {
|
||||
@ -8,7 +8,7 @@ export interface SettingsQueryData {
|
||||
|
||||
export const SETTINGS_QUERY = gql`
|
||||
query {
|
||||
disabledSignUp: getByKey (key: "SIGNUP_DISABLED") {
|
||||
disabledSignUp: getByKey(key: "SIGNUP_DISABLED") {
|
||||
value: isTrue
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
import {ApolloProvider} from '@apollo/react-common'
|
||||
import {buildAxiosFetch} from '@lifeomic/axios-fetch'
|
||||
import { ApolloProvider } from '@apollo/react-common'
|
||||
import { buildAxiosFetch } from '@lifeomic/axios-fetch'
|
||||
import 'antd/dist/antd.css'
|
||||
import ApolloClient from 'apollo-boost'
|
||||
import 'assets/global.scss'
|
||||
import 'assets/variables.scss'
|
||||
import axios from 'axios'
|
||||
import {authConfig} from 'components/with.auth'
|
||||
import { authConfig } from 'components/with.auth'
|
||||
import 'i18n'
|
||||
import {AppProps} from 'next/app'
|
||||
import { AppProps } from 'next/app'
|
||||
import getConfig from 'next/config'
|
||||
import Head from 'next/head'
|
||||
import React from 'react'
|
||||
import {wrapper} from 'store'
|
||||
import { wrapper } from 'store'
|
||||
|
||||
const { publicRuntimeConfig } = getConfig()
|
||||
|
||||
@ -20,7 +20,7 @@ const client = new ApolloClient({
|
||||
fetch: buildAxiosFetch(axios),
|
||||
request: async (operation): Promise<void> => {
|
||||
operation.setContext(await authConfig())
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const App: React.FC<AppProps> = ({ Component, pageProps }) => {
|
||||
|
||||
@ -1,32 +1,35 @@
|
||||
import {useMutation, useQuery} from '@apollo/react-hooks'
|
||||
import {Button, Form, Input, message, Tabs} from 'antd'
|
||||
import {useForm} from 'antd/lib/form/Form'
|
||||
import {cleanInput} from 'components/clean.input'
|
||||
import {BaseDataTab} from 'components/form/admin/base.data.tab'
|
||||
import {DesignTab} from 'components/form/admin/design.tab'
|
||||
import {EndPageTab} from 'components/form/admin/end.page.tab'
|
||||
import {FieldsTab} from 'components/form/admin/fields.tab'
|
||||
import {RespondentNotificationsTab} from 'components/form/admin/respondent.notifications.tab'
|
||||
import {SelfNotificationsTab} from 'components/form/admin/self.notifications.tab'
|
||||
import {StartPageTab} from 'components/form/admin/start.page.tab'
|
||||
import { useMutation, useQuery } from '@apollo/react-hooks'
|
||||
import { Button, Form, Input, message, Tabs } from 'antd'
|
||||
import { useForm } from 'antd/lib/form/Form'
|
||||
import { cleanInput } from 'components/clean.input'
|
||||
import { BaseDataTab } from 'components/form/admin/base.data.tab'
|
||||
import { DesignTab } from 'components/form/admin/design.tab'
|
||||
import { EndPageTab } from 'components/form/admin/end.page.tab'
|
||||
import { FieldsTab } from 'components/form/admin/fields.tab'
|
||||
import { RespondentNotificationsTab } from 'components/form/admin/respondent.notifications.tab'
|
||||
import { SelfNotificationsTab } from 'components/form/admin/self.notifications.tab'
|
||||
import { StartPageTab } from 'components/form/admin/start.page.tab'
|
||||
import Structure from 'components/structure'
|
||||
import {withAuth} from 'components/with.auth'
|
||||
import { withAuth } from 'components/with.auth'
|
||||
import {
|
||||
AdminFormFieldFragment,
|
||||
AdminFormFieldOptionFragment,
|
||||
AdminFormFieldOptionKeysFragment
|
||||
AdminFormFieldOptionKeysFragment,
|
||||
} from 'graphql/fragment/admin.form.fragment'
|
||||
import {
|
||||
ADMIN_FORM_UPDATE_MUTATION,
|
||||
AdminFormUpdateMutationData,
|
||||
AdminFormUpdateMutationVariables
|
||||
AdminFormUpdateMutationVariables,
|
||||
} from 'graphql/mutation/admin.form.update.mutation'
|
||||
import {ADMIN_FORM_QUERY, AdminFormQueryData, AdminFormQueryVariables} from 'graphql/query/admin.form.query'
|
||||
import {NextPage} from 'next'
|
||||
import {
|
||||
ADMIN_FORM_QUERY,
|
||||
AdminFormQueryData,
|
||||
AdminFormQueryVariables,
|
||||
} from 'graphql/query/admin.form.query'
|
||||
import { NextPage } from 'next'
|
||||
import Link from 'next/link'
|
||||
import {useRouter} from 'next/router'
|
||||
import React, {useState} from 'react'
|
||||
import {useTranslation} from 'react-i18next'
|
||||
import { useRouter } from 'next/router'
|
||||
import React, { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
const Index: NextPage = () => {
|
||||
const { t } = useTranslation()
|
||||
@ -34,19 +37,21 @@ const Index: NextPage = () => {
|
||||
const [form] = useForm()
|
||||
const [saving, setSaving] = useState(false)
|
||||
const [fields, setFields] = useState<AdminFormFieldFragment[]>([])
|
||||
const [update] = useMutation<AdminFormUpdateMutationData, AdminFormUpdateMutationVariables>(ADMIN_FORM_UPDATE_MUTATION)
|
||||
const [update] = useMutation<AdminFormUpdateMutationData, AdminFormUpdateMutationVariables>(
|
||||
ADMIN_FORM_UPDATE_MUTATION
|
||||
)
|
||||
|
||||
const processNext = (next: AdminFormQueryData): AdminFormQueryData => {
|
||||
next.form.fields = next.form.fields.map(field => {
|
||||
next.form.fields = next.form.fields.map((field) => {
|
||||
const keys: AdminFormFieldOptionKeysFragment = {}
|
||||
|
||||
field.options.forEach(option => {
|
||||
field.options.forEach((option) => {
|
||||
if (option.key) {
|
||||
keys[option.key] = option.value
|
||||
}
|
||||
})
|
||||
|
||||
field.options = field.options.filter(option => !option.key)
|
||||
field.options = field.options.filter((option) => !option.key)
|
||||
field.optionKeys = keys
|
||||
return field
|
||||
})
|
||||
@ -54,21 +59,26 @@ const Index: NextPage = () => {
|
||||
return next
|
||||
}
|
||||
|
||||
const {data, loading, error} = useQuery<AdminFormQueryData, AdminFormQueryVariables>(ADMIN_FORM_QUERY, {
|
||||
const { data, loading } = useQuery<AdminFormQueryData, AdminFormQueryVariables>(
|
||||
ADMIN_FORM_QUERY,
|
||||
{
|
||||
variables: {
|
||||
id: router.query.id as string
|
||||
id: router.query.id as string,
|
||||
},
|
||||
onCompleted: next => {
|
||||
onCompleted: (next) => {
|
||||
next = processNext(next)
|
||||
form.setFieldsValue(next)
|
||||
setFields(next.form.fields)
|
||||
},
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
const save = async (formData: AdminFormQueryData) => {
|
||||
setSaving(true)
|
||||
|
||||
formData.form.fields = formData.form.fields.filter(e => e && e.type).map(({optionKeys, ...field}) => {
|
||||
formData.form.fields = formData.form.fields
|
||||
.filter((e) => e && e.type)
|
||||
.map(({ optionKeys, ...field }) => {
|
||||
const options = field.options
|
||||
|
||||
if (optionKeys) {
|
||||
@ -86,14 +96,18 @@ const Index: NextPage = () => {
|
||||
|
||||
return {
|
||||
...field,
|
||||
options
|
||||
options,
|
||||
}
|
||||
})
|
||||
|
||||
try {
|
||||
const next = processNext((await update({
|
||||
const next = processNext(
|
||||
(
|
||||
await update({
|
||||
variables: cleanInput(formData),
|
||||
})).data)
|
||||
})
|
||||
).data
|
||||
)
|
||||
|
||||
form.setFieldsValue(next)
|
||||
setFields(next.form.fields)
|
||||
@ -124,18 +138,16 @@ const Index: NextPage = () => {
|
||||
>
|
||||
<Button>{t('admin:submissions')}</Button>
|
||||
</Link>,
|
||||
<Button
|
||||
key={'save'}
|
||||
onClick={form.submit}
|
||||
type={'primary'}
|
||||
>{t('form:updateNow')}</Button>,
|
||||
<Button key={'save'} onClick={form.submit} type={'primary'}>
|
||||
{t('form:updateNow')}
|
||||
</Button>,
|
||||
]}
|
||||
style={{paddingTop: 0}}
|
||||
style={{ paddingTop: 0 }}
|
||||
>
|
||||
<Form
|
||||
form={form}
|
||||
onFinish={save}
|
||||
onFinishFailed={errors => {
|
||||
onFinishFailed={() => {
|
||||
// TODO process errors
|
||||
message.error(t('validation:mandatoryFieldsMissing'))
|
||||
}}
|
||||
@ -148,7 +160,9 @@ const Index: NextPage = () => {
|
||||
sm: { span: 18 },
|
||||
}}
|
||||
>
|
||||
<Form.Item noStyle name={['form', 'id']}><Input type={'hidden'} /></Form.Item>
|
||||
<Form.Item noStyle name={['form', 'id']}>
|
||||
<Input type={'hidden'} />
|
||||
</Form.Item>
|
||||
|
||||
<Tabs>
|
||||
<FieldsTab
|
||||
|
||||
@ -1,24 +1,25 @@
|
||||
import {useQuery} from '@apollo/react-hooks'
|
||||
import {Button, Progress, Table} from 'antd'
|
||||
import {PaginationProps} from 'antd/es/pagination'
|
||||
import {ColumnsType} from 'antd/lib/table/interface'
|
||||
import {DateTime} from 'components/date.time'
|
||||
import { useQuery } from '@apollo/react-hooks'
|
||||
import { Button, Progress, Table } from 'antd'
|
||||
import { PaginationProps } from 'antd/es/pagination'
|
||||
import { ProgressProps } from 'antd/lib/progress'
|
||||
import { ColumnsType } from 'antd/lib/table/interface'
|
||||
import { DateTime } from 'components/date.time'
|
||||
import Structure from 'components/structure'
|
||||
import {TimeAgo} from 'components/time.ago'
|
||||
import {withAuth} from 'components/with.auth'
|
||||
import { TimeAgo } from 'components/time.ago'
|
||||
import { withAuth } from 'components/with.auth'
|
||||
import dayjs from 'dayjs'
|
||||
import {NextPage} from 'next'
|
||||
import { NextPage } from 'next'
|
||||
import Link from 'next/link'
|
||||
import {useRouter} from 'next/router'
|
||||
import React, {useState} from 'react'
|
||||
import {useTranslation} from 'react-i18next'
|
||||
import {SubmissionValues} from '../../../../components/form/admin/submission.values'
|
||||
import { useRouter } from 'next/router'
|
||||
import React, { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { SubmissionValues } from '../../../../components/form/admin/submission.values'
|
||||
import {
|
||||
ADMIN_PAGER_SUBMISSION_QUERY,
|
||||
AdminPagerSubmissionEntryQueryData,
|
||||
AdminPagerSubmissionFormQueryData,
|
||||
AdminPagerSubmissionQueryData,
|
||||
AdminPagerSubmissionQueryVariables
|
||||
AdminPagerSubmissionQueryVariables,
|
||||
} from '../../../../graphql/query/admin.pager.submission.query'
|
||||
|
||||
const Submissions: NextPage = () => {
|
||||
@ -29,48 +30,56 @@ const Submissions: NextPage = () => {
|
||||
})
|
||||
const [form, setForm] = useState<AdminPagerSubmissionFormQueryData>()
|
||||
const [entries, setEntries] = useState<AdminPagerSubmissionEntryQueryData[]>()
|
||||
const {loading, refetch} = useQuery<AdminPagerSubmissionQueryData, AdminPagerSubmissionQueryVariables>(ADMIN_PAGER_SUBMISSION_QUERY, {
|
||||
const { loading, refetch } = useQuery<
|
||||
AdminPagerSubmissionQueryData,
|
||||
AdminPagerSubmissionQueryVariables
|
||||
>(ADMIN_PAGER_SUBMISSION_QUERY, {
|
||||
variables: {
|
||||
form: router.query.id as string,
|
||||
limit: pagination.pageSize,
|
||||
start: Math.max(0, pagination.current - 1) * pagination.pageSize || 0
|
||||
start: Math.max(0, pagination.current - 1) * pagination.pageSize || 0,
|
||||
},
|
||||
onCompleted: ({pager, form}) => {
|
||||
onCompleted: ({ pager, form }) => {
|
||||
setPagination({
|
||||
...pagination,
|
||||
total: pager.total,
|
||||
})
|
||||
setForm(form)
|
||||
setEntries(pager.entries)
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const columns:ColumnsType<AdminPagerSubmissionEntryQueryData> = [
|
||||
const columns: ColumnsType<AdminPagerSubmissionEntryQueryData> = [
|
||||
{
|
||||
title: t('submission:progress'),
|
||||
render: (row: AdminPagerSubmissionEntryQueryData) => {
|
||||
let status: any = 'active'
|
||||
render(row: AdminPagerSubmissionEntryQueryData) {
|
||||
const props: ProgressProps = {
|
||||
status: 'active',
|
||||
percent: Math.round(row.percentageComplete * 100),
|
||||
}
|
||||
|
||||
if (row.percentageComplete >= 1) {
|
||||
status = 'success'
|
||||
props.status = 'success'
|
||||
} else if (dayjs().diff(dayjs(row.lastModified), 'hour') > 4) {
|
||||
status = 'exception'
|
||||
props.status = 'exception'
|
||||
}
|
||||
|
||||
return (
|
||||
<Progress percent={Math.round(row.percentageComplete * 100)} status={status} />
|
||||
)
|
||||
}
|
||||
return <Progress {...props} />
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t('submission:created'),
|
||||
dataIndex: 'created',
|
||||
render: date => <DateTime date={date} />
|
||||
render(date) {
|
||||
return <DateTime date={date} />
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t('submission:lastModified'),
|
||||
dataIndex: 'lastModified',
|
||||
render: date => <TimeAgo date={date} />
|
||||
render(date) {
|
||||
return <TimeAgo date={date} />
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
@ -82,23 +91,20 @@ const Submissions: NextPage = () => {
|
||||
breadcrumbs={[
|
||||
{ href: '/admin', name: t('admin:home') },
|
||||
{ href: '/admin/forms', name: t('admin:forms') },
|
||||
{ href: '/admin/forms/[id]', name: loading || !form ? t('form:loading') : t('form:mange', { title: form.title }), as: `/admin/forms/${router.query.id}` },
|
||||
{
|
||||
href: '/admin/forms/[id]',
|
||||
name: loading || !form ? t('form:loading') : t('form:mange', { title: form.title }),
|
||||
as: `/admin/forms/${router.query.id}`,
|
||||
},
|
||||
]}
|
||||
padded={false}
|
||||
extra={[
|
||||
<Link
|
||||
key={'edit'}
|
||||
href={'/admin/forms/[id]'}
|
||||
as={`/admin/forms/${router.query.id}`}
|
||||
>
|
||||
<Link key={'edit'} href={'/admin/forms/[id]'} as={`/admin/forms/${router.query.id}`}>
|
||||
<Button>{t('submission:edit')}</Button>
|
||||
</Link>,
|
||||
<Button
|
||||
key={'web'}
|
||||
href={`/form/${router.query.id}`}
|
||||
target={'_blank'}
|
||||
type={'primary'}
|
||||
>{t('submission:add')}</Button>,
|
||||
<Button key={'web'} href={`/form/${router.query.id}`} target={'_blank'} type={'primary'}>
|
||||
{t('submission:add')}
|
||||
</Button>,
|
||||
]}
|
||||
>
|
||||
<Table
|
||||
@ -107,10 +113,14 @@ const Submissions: NextPage = () => {
|
||||
rowKey={'id'}
|
||||
pagination={pagination}
|
||||
expandable={{
|
||||
expandedRowRender: record => <SubmissionValues form={form} submission={record} />,
|
||||
rowExpandable: record => record.percentageComplete > 0,
|
||||
expandedRowRender(record) {
|
||||
return <SubmissionValues form={form} submission={record} />
|
||||
},
|
||||
rowExpandable(record) {
|
||||
return record.percentageComplete > 0
|
||||
},
|
||||
}}
|
||||
onChange={next => {
|
||||
onChange={(next) => {
|
||||
setPagination(next)
|
||||
refetch()
|
||||
}}
|
||||
|
||||
@ -1,19 +1,19 @@
|
||||
import {useMutation} from '@apollo/react-hooks'
|
||||
import {Button, Form, Input, message, Tabs} from 'antd'
|
||||
import {useForm} from 'antd/lib/form/Form'
|
||||
import {cleanInput} from 'components/clean.input'
|
||||
import {BaseDataTab} from 'components/form/admin/base.data.tab'
|
||||
import { useMutation } from '@apollo/react-hooks'
|
||||
import { Button, Form, Input, message, Tabs } from 'antd'
|
||||
import { useForm } from 'antd/lib/form/Form'
|
||||
import { cleanInput } from 'components/clean.input'
|
||||
import { BaseDataTab } from 'components/form/admin/base.data.tab'
|
||||
import Structure from 'components/structure'
|
||||
import {withAuth} from 'components/with.auth'
|
||||
import {AdminFormQueryData} from 'graphql/query/admin.form.query'
|
||||
import {NextPage} from 'next'
|
||||
import {useRouter} from 'next/router'
|
||||
import React, {useState} from 'react'
|
||||
import {useTranslation} from 'react-i18next'
|
||||
import { withAuth } from 'components/with.auth'
|
||||
import { AdminFormQueryData } from 'graphql/query/admin.form.query'
|
||||
import { NextPage } from 'next'
|
||||
import { useRouter } from 'next/router'
|
||||
import React, { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
ADMIN_FORM_CREATE_MUTATION,
|
||||
AdminFormCreateMutationData,
|
||||
AdminFormCreateMutationVariables
|
||||
AdminFormCreateMutationVariables,
|
||||
} from '../../../graphql/mutation/admin.form.create.mutation'
|
||||
|
||||
const Create: NextPage = () => {
|
||||
@ -21,15 +21,19 @@ const Create: NextPage = () => {
|
||||
const router = useRouter()
|
||||
const [form] = useForm()
|
||||
const [saving, setSaving] = useState(false)
|
||||
const [create] = useMutation<AdminFormCreateMutationData, AdminFormCreateMutationVariables>(ADMIN_FORM_CREATE_MUTATION)
|
||||
const [create] = useMutation<AdminFormCreateMutationData, AdminFormCreateMutationVariables>(
|
||||
ADMIN_FORM_CREATE_MUTATION
|
||||
)
|
||||
|
||||
const save = async (formData: AdminFormQueryData) => {
|
||||
setSaving(true)
|
||||
|
||||
try {
|
||||
const next = (await create({
|
||||
const next = (
|
||||
await create({
|
||||
variables: cleanInput(formData),
|
||||
})).data
|
||||
})
|
||||
).data
|
||||
|
||||
message.success(t('form:created'))
|
||||
|
||||
@ -52,18 +56,16 @@ const Create: NextPage = () => {
|
||||
{ href: '/admin/forms', name: t('admin:forms') },
|
||||
]}
|
||||
extra={[
|
||||
<Button
|
||||
key={'create'}
|
||||
onClick={form.submit}
|
||||
type={'primary'}
|
||||
>{t('form:createNow')}</Button>,
|
||||
<Button key={'create'} onClick={form.submit} type={'primary'}>
|
||||
{t('form:createNow')}
|
||||
</Button>,
|
||||
]}
|
||||
style={{paddingTop: 0}}
|
||||
style={{ paddingTop: 0 }}
|
||||
>
|
||||
<Form
|
||||
form={form}
|
||||
onFinish={save}
|
||||
onFinishFailed={errors => {
|
||||
onFinishFailed={() => {
|
||||
// TODO process errors
|
||||
message.error(t('validation:mandatoryFieldsMissing'))
|
||||
}}
|
||||
@ -76,7 +78,9 @@ const Create: NextPage = () => {
|
||||
sm: { span: 18 },
|
||||
}}
|
||||
>
|
||||
<Form.Item noStyle name={['form', 'id']}><Input type={'hidden'} /></Form.Item>
|
||||
<Form.Item noStyle name={['form', 'id']}>
|
||||
<Input type={'hidden'} />
|
||||
</Form.Item>
|
||||
|
||||
<Tabs>
|
||||
<BaseDataTab key={'base_data'} tab={t('form:baseDataTab')} />
|
||||
|
||||
@ -1,27 +1,32 @@
|
||||
import {DeleteOutlined, EditOutlined, GlobalOutlined, UnorderedListOutlined} from '@ant-design/icons/lib'
|
||||
import {useMutation, useQuery} from '@apollo/react-hooks'
|
||||
import {Button, message, Popconfirm, Space, Table, Tooltip} from 'antd'
|
||||
import {PaginationProps} from 'antd/es/pagination'
|
||||
import {ColumnsType} from 'antd/lib/table/interface'
|
||||
import {DateTime} from 'components/date.time'
|
||||
import {FormIsLive} from 'components/form/admin/is.live'
|
||||
import {
|
||||
DeleteOutlined,
|
||||
EditOutlined,
|
||||
GlobalOutlined,
|
||||
UnorderedListOutlined,
|
||||
} from '@ant-design/icons/lib'
|
||||
import { useMutation, useQuery } from '@apollo/react-hooks'
|
||||
import { Button, message, Popconfirm, Space, Table, Tooltip } from 'antd'
|
||||
import { PaginationProps } from 'antd/es/pagination'
|
||||
import { ColumnsType } from 'antd/lib/table/interface'
|
||||
import { DateTime } from 'components/date.time'
|
||||
import { FormIsLive } from 'components/form/admin/is.live'
|
||||
import Structure from 'components/structure'
|
||||
import {TimeAgo} from 'components/time.ago'
|
||||
import {withAuth} from 'components/with.auth'
|
||||
import { TimeAgo } from 'components/time.ago'
|
||||
import { withAuth } from 'components/with.auth'
|
||||
import {
|
||||
ADMIN_PAGER_FORM_QUERY,
|
||||
AdminPagerFormEntryQueryData,
|
||||
AdminPagerFormQueryData,
|
||||
AdminPagerFormQueryVariables
|
||||
AdminPagerFormQueryVariables,
|
||||
} from 'graphql/query/admin.pager.form.query'
|
||||
import {NextPage} from 'next'
|
||||
import { NextPage } from 'next'
|
||||
import Link from 'next/link'
|
||||
import React, {useState} from 'react'
|
||||
import {useTranslation} from 'react-i18next'
|
||||
import React, { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
ADMIN_FORM_DELETE_MUTATION,
|
||||
AdminFormDeleteMutationData,
|
||||
AdminFormDeleteMutationVariables
|
||||
AdminFormDeleteMutationVariables,
|
||||
} from '../../../graphql/mutation/admin.form.delete.mutation'
|
||||
|
||||
const Index: NextPage = () => {
|
||||
@ -30,29 +35,35 @@ const Index: NextPage = () => {
|
||||
pageSize: 25,
|
||||
})
|
||||
const [entries, setEntries] = useState<AdminPagerFormEntryQueryData[]>()
|
||||
const {loading, refetch} = useQuery<AdminPagerFormQueryData, AdminPagerFormQueryVariables>(ADMIN_PAGER_FORM_QUERY, {
|
||||
const { loading, refetch } = useQuery<AdminPagerFormQueryData, AdminPagerFormQueryVariables>(
|
||||
ADMIN_PAGER_FORM_QUERY,
|
||||
{
|
||||
variables: {
|
||||
limit: pagination.pageSize,
|
||||
start: Math.max(0, pagination.current - 1) * pagination.pageSize || 0
|
||||
start: Math.max(0, pagination.current - 1) * pagination.pageSize || 0,
|
||||
},
|
||||
onCompleted: ({pager}) => {
|
||||
onCompleted: ({ pager }) => {
|
||||
setPagination({
|
||||
...pagination,
|
||||
total: pager.total,
|
||||
})
|
||||
setEntries(pager.entries)
|
||||
},
|
||||
}
|
||||
})
|
||||
const [executeDelete] = useMutation<AdminFormDeleteMutationData, AdminFormDeleteMutationVariables>(ADMIN_FORM_DELETE_MUTATION)
|
||||
)
|
||||
const [executeDelete] = useMutation<
|
||||
AdminFormDeleteMutationData,
|
||||
AdminFormDeleteMutationVariables
|
||||
>(ADMIN_FORM_DELETE_MUTATION)
|
||||
|
||||
const deleteForm = async (form) => {
|
||||
try {
|
||||
await executeDelete({
|
||||
variables: {
|
||||
id: form.id
|
||||
}
|
||||
id: form.id,
|
||||
},
|
||||
})
|
||||
const next = entries.filter(entry => entry.id !== form.id)
|
||||
const next = entries.filter((entry) => entry.id !== form.id)
|
||||
if (next.length === 0) {
|
||||
setPagination({ ...pagination, current: 1 })
|
||||
} else {
|
||||
@ -69,7 +80,9 @@ const Index: NextPage = () => {
|
||||
{
|
||||
title: t('form:row.isLive'),
|
||||
dataIndex: 'isLive',
|
||||
render: live => <FormIsLive isLive={live} />
|
||||
render(live) {
|
||||
return <FormIsLive isLive={live} />
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t('form:row.title'),
|
||||
@ -78,7 +91,8 @@ const Index: NextPage = () => {
|
||||
{
|
||||
title: t('form:row.admin'),
|
||||
dataIndex: 'admin',
|
||||
render: user => (
|
||||
render(user) {
|
||||
return (
|
||||
<Link href={'/admin/users/[id]'} as={`/admin/users/${user.id}`}>
|
||||
<Tooltip title={user.email}>
|
||||
<Button type={'dashed'}>{user.username}</Button>
|
||||
@ -86,41 +100,46 @@ const Index: NextPage = () => {
|
||||
</Link>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t('form:row.language'),
|
||||
dataIndex: 'language',
|
||||
render: lang => t(`language:${lang}`)
|
||||
render(lang) {
|
||||
return t(`language:${lang}`)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t('form:row.created'),
|
||||
dataIndex: 'created',
|
||||
render: date => <DateTime date={date} />
|
||||
render(date) {
|
||||
return <DateTime date={date} />
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t('form:row.lastModified'),
|
||||
dataIndex: 'lastModified',
|
||||
render: date => <TimeAgo date={date} />
|
||||
render(date) {
|
||||
return <TimeAgo date={date} />
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t('form:row.menu'),
|
||||
align: 'right',
|
||||
render: row => {
|
||||
render(row) {
|
||||
return (
|
||||
<Space>
|
||||
<Link
|
||||
href={'/admin/forms/[id]/submissions'}
|
||||
as={`/admin/forms/${row.id}/submissions`}
|
||||
>
|
||||
<Link href={'/admin/forms/[id]/submissions'} as={`/admin/forms/${row.id}/submissions`}>
|
||||
<Tooltip title={'Show Submissions'}>
|
||||
<Button><UnorderedListOutlined /></Button>
|
||||
<Button>
|
||||
<UnorderedListOutlined />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</Link>
|
||||
|
||||
<Link
|
||||
href={'/admin/forms/[id]'}
|
||||
as={`/admin/forms/${row.id}`}
|
||||
>
|
||||
<Button type={'primary'}><EditOutlined /></Button>
|
||||
<Link href={'/admin/forms/[id]'} as={`/admin/forms/${row.id}`}>
|
||||
<Button type={'primary'}>
|
||||
<EditOutlined />
|
||||
</Button>
|
||||
</Link>
|
||||
|
||||
<Popconfirm
|
||||
@ -129,20 +148,19 @@ const Index: NextPage = () => {
|
||||
okText={t('form:deleteNow')}
|
||||
okButtonProps={{ danger: true }}
|
||||
>
|
||||
<Button danger><DeleteOutlined /></Button>
|
||||
<Button danger>
|
||||
<DeleteOutlined />
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
|
||||
<Tooltip title={row.isLive ? null : 'Not Public accessible!'}>
|
||||
<Button
|
||||
href={`/form/${row.id}`}
|
||||
target={'_blank'}
|
||||
>
|
||||
<Button href={`/form/${row.id}`} target={'_blank'}>
|
||||
<GlobalOutlined />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</Space>
|
||||
)
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
@ -151,18 +169,11 @@ const Index: NextPage = () => {
|
||||
title={t('admin:forms')}
|
||||
selected={'forms'}
|
||||
loading={loading}
|
||||
breadcrumbs={[
|
||||
{ href: '/admin', name: t('admin:home') },
|
||||
]}
|
||||
breadcrumbs={[{ href: '/admin', name: t('admin:home') }]}
|
||||
padded={false}
|
||||
extra={[
|
||||
<Link
|
||||
key={'create'}
|
||||
href={'/admin/forms/create'}
|
||||
>
|
||||
<Button
|
||||
type={'primary'}
|
||||
>{t('form:new')}</Button>
|
||||
<Link key={'create'} href={'/admin/forms/create'}>
|
||||
<Button type={'primary'}>{t('form:new')}</Button>
|
||||
</Link>,
|
||||
]}
|
||||
>
|
||||
@ -171,7 +182,7 @@ const Index: NextPage = () => {
|
||||
dataSource={entries}
|
||||
rowKey={'id'}
|
||||
pagination={pagination}
|
||||
onChange={next => {
|
||||
onChange={(next) => {
|
||||
setPagination(next)
|
||||
refetch()
|
||||
}}
|
||||
|
||||
@ -1,26 +1,24 @@
|
||||
import {useQuery} from '@apollo/react-hooks'
|
||||
import {Col, Row, Statistic} from 'antd'
|
||||
import { useQuery } from '@apollo/react-hooks'
|
||||
import { Col, Row, Statistic } from 'antd'
|
||||
import Structure from 'components/structure'
|
||||
import {withAuth} from 'components/with.auth'
|
||||
import {NextPage} from 'next'
|
||||
import { withAuth } from 'components/with.auth'
|
||||
import { NextPage } from 'next'
|
||||
import React from 'react'
|
||||
import {useTranslation} from 'react-i18next'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
ADMIN_STATISTIC_QUERY,
|
||||
AdminStatisticQueryData,
|
||||
AdminStatisticQueryVariables
|
||||
AdminStatisticQueryVariables,
|
||||
} from '../../graphql/query/admin.statistic.query'
|
||||
|
||||
const Index: NextPage = () => {
|
||||
const { t } = useTranslation()
|
||||
const {data, loading} = useQuery<AdminStatisticQueryData, AdminStatisticQueryVariables>(ADMIN_STATISTIC_QUERY)
|
||||
const { data, loading } = useQuery<AdminStatisticQueryData, AdminStatisticQueryVariables>(
|
||||
ADMIN_STATISTIC_QUERY
|
||||
)
|
||||
|
||||
return (
|
||||
<Structure
|
||||
title={t('admin:home')}
|
||||
selected={'home'}
|
||||
loading={loading}
|
||||
>
|
||||
<Structure title={t('admin:home')} selected={'home'} loading={loading}>
|
||||
<Row gutter={16}>
|
||||
<Col span={8}>
|
||||
<Statistic title={t('statistic:totalForms')} value={data && data.forms.total} />
|
||||
@ -31,7 +29,10 @@ const Index: NextPage = () => {
|
||||
</Col>
|
||||
|
||||
<Col span={8}>
|
||||
<Statistic title={t('statistic:totalSubmissions')} value={data && data.submissions.total} />
|
||||
<Statistic
|
||||
title={t('statistic:totalSubmissions')}
|
||||
value={data && data.submissions.total}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Structure>
|
||||
|
||||
@ -1,46 +1,51 @@
|
||||
import {useMutation, useQuery} from '@apollo/react-hooks'
|
||||
import {Button, Form, Input, message, Select} from 'antd'
|
||||
import {useForm} from 'antd/lib/form/Form'
|
||||
import {NextPage} from 'next'
|
||||
import {useRouter} from 'next/router'
|
||||
import React, {useState} from 'react'
|
||||
import {useTranslation} from 'react-i18next'
|
||||
import {cleanInput} from '../../components/clean.input'
|
||||
import { useMutation, useQuery } from '@apollo/react-hooks'
|
||||
import { Button, Form, Input, message, Select } from 'antd'
|
||||
import { useForm } from 'antd/lib/form/Form'
|
||||
import { NextPage } from 'next'
|
||||
import React, { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { cleanInput } from '../../components/clean.input'
|
||||
import Structure from '../../components/structure'
|
||||
import {
|
||||
ADMIN_PROFILE_UPDATE_MUTATION,
|
||||
AdminProfileUpdateMutationData,
|
||||
AdminProfileUpdateMutationVariables
|
||||
AdminProfileUpdateMutationVariables,
|
||||
} from '../../graphql/mutation/admin.profile.update.mutation'
|
||||
import {
|
||||
ADMIN_PROFILE_QUERY,
|
||||
AdminProfileQueryData,
|
||||
AdminProfileQueryVariables
|
||||
AdminProfileQueryVariables,
|
||||
} from '../../graphql/query/admin.profile.query'
|
||||
import {AdminUserQueryData} from '../../graphql/query/admin.user.query'
|
||||
import {languages} from '../../i18n'
|
||||
import { AdminUserQueryData } from '../../graphql/query/admin.user.query'
|
||||
import { languages } from '../../i18n'
|
||||
|
||||
const Profile: NextPage = () => {
|
||||
const { t } = useTranslation()
|
||||
const router = useRouter()
|
||||
const [form] = useForm()
|
||||
const [saving, setSaving] = useState(false)
|
||||
|
||||
const {data, loading, error} = useQuery<AdminProfileQueryData, AdminProfileQueryVariables>(ADMIN_PROFILE_QUERY, {
|
||||
onCompleted: next => {
|
||||
const { loading } = useQuery<AdminProfileQueryData, AdminProfileQueryVariables>(
|
||||
ADMIN_PROFILE_QUERY,
|
||||
{
|
||||
onCompleted: (next) => {
|
||||
form.setFieldsValue(next)
|
||||
},
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
const [update] = useMutation<AdminProfileUpdateMutationData, AdminProfileUpdateMutationVariables>(ADMIN_PROFILE_UPDATE_MUTATION)
|
||||
const [update] = useMutation<AdminProfileUpdateMutationData, AdminProfileUpdateMutationVariables>(
|
||||
ADMIN_PROFILE_UPDATE_MUTATION
|
||||
)
|
||||
|
||||
const save = async (formData: AdminUserQueryData) => {
|
||||
setSaving(true)
|
||||
|
||||
try {
|
||||
const next = (await update({
|
||||
const next = (
|
||||
await update({
|
||||
variables: cleanInput(formData),
|
||||
})).data
|
||||
})
|
||||
).data
|
||||
|
||||
form.setFieldsValue(next)
|
||||
|
||||
@ -53,27 +58,22 @@ const Profile: NextPage = () => {
|
||||
setSaving(false)
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<Structure
|
||||
loading={loading || saving}
|
||||
title={t('admin:profile')}
|
||||
selected={'profile'}
|
||||
breadcrumbs={[
|
||||
{ href: '/admin', name: t('admin:home') },
|
||||
]}
|
||||
breadcrumbs={[{ href: '/admin', name: t('admin:home') }]}
|
||||
extra={[
|
||||
<Button
|
||||
key={'save'}
|
||||
onClick={form.submit}
|
||||
type={'primary'}
|
||||
>{t('profile:updateNow')}</Button>,
|
||||
<Button key={'save'} onClick={form.submit} type={'primary'}>
|
||||
{t('profile:updateNow')}
|
||||
</Button>,
|
||||
]}
|
||||
>
|
||||
<Form
|
||||
form={form}
|
||||
onFinish={save}
|
||||
onFinishFailed={errors => {
|
||||
onFinishFailed={() => {
|
||||
// TODO process errors
|
||||
message.error(t('validation:mandatoryFieldsMissing'))
|
||||
}}
|
||||
@ -86,7 +86,9 @@ const Profile: NextPage = () => {
|
||||
sm: { span: 18 },
|
||||
}}
|
||||
>
|
||||
<Form.Item noStyle name={['user', 'id']}><Input type={'hidden'} /></Form.Item>
|
||||
<Form.Item noStyle name={['user', 'id']}>
|
||||
<Input type={'hidden'} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label={t('profile:username')}
|
||||
@ -129,21 +131,19 @@ const Profile: NextPage = () => {
|
||||
]}
|
||||
>
|
||||
<Select>
|
||||
{languages.map(language => <Select.Option value={language} key={language}>{t(`language:${language}`)}</Select.Option> )}
|
||||
{languages.map((language) => (
|
||||
<Select.Option value={language} key={language}>
|
||||
{t(`language:${language}`)}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label={t('profile:firstName')}
|
||||
name={['user', 'firstName']}
|
||||
>
|
||||
<Form.Item label={t('profile:firstName')} name={['user', 'firstName']}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label={t('profile:lastName')}
|
||||
name={['user', 'lastName']}
|
||||
>
|
||||
<Form.Item label={t('profile:lastName')} name={['user', 'lastName']}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
|
||||
@ -1,20 +1,24 @@
|
||||
import {useMutation, useQuery} from '@apollo/react-hooks'
|
||||
import {Button, Form, Input, message, Tabs} from 'antd'
|
||||
import {useForm} from 'antd/lib/form/Form'
|
||||
import { useMutation, useQuery } from '@apollo/react-hooks'
|
||||
import { Button, Form, Input, message, Tabs } from 'antd'
|
||||
import { useForm } from 'antd/lib/form/Form'
|
||||
import Structure from 'components/structure'
|
||||
import {withAuth} from 'components/with.auth'
|
||||
import {NextPage} from 'next'
|
||||
import {useRouter} from 'next/router'
|
||||
import React, {useState} from 'react'
|
||||
import {useTranslation} from 'react-i18next'
|
||||
import {cleanInput} from '../../../../components/clean.input'
|
||||
import {BaseDataTab} from '../../../../components/user/admin/base.data.tab'
|
||||
import { withAuth } from 'components/with.auth'
|
||||
import { NextPage } from 'next'
|
||||
import { useRouter } from 'next/router'
|
||||
import React, { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { cleanInput } from '../../../../components/clean.input'
|
||||
import { BaseDataTab } from '../../../../components/user/admin/base.data.tab'
|
||||
import {
|
||||
ADMIN_USER_UPDATE_MUTATION,
|
||||
AdminUserUpdateMutationData,
|
||||
AdminUserUpdateMutationVariables
|
||||
AdminUserUpdateMutationVariables,
|
||||
} from '../../../../graphql/mutation/admin.user.update.mutation'
|
||||
import {ADMIN_USER_QUERY, AdminUserQueryData, AdminUserQueryVariables} from '../../../../graphql/query/admin.user.query'
|
||||
import {
|
||||
ADMIN_USER_QUERY,
|
||||
AdminUserQueryData,
|
||||
AdminUserQueryVariables,
|
||||
} from '../../../../graphql/query/admin.user.query'
|
||||
|
||||
const Index: NextPage = () => {
|
||||
const { t } = useTranslation()
|
||||
@ -22,23 +26,30 @@ const Index: NextPage = () => {
|
||||
const [form] = useForm()
|
||||
const [saving, setSaving] = useState(false)
|
||||
|
||||
const {data, loading, error} = useQuery<AdminUserQueryData, AdminUserQueryVariables>(ADMIN_USER_QUERY, {
|
||||
const { data, loading } = useQuery<AdminUserQueryData, AdminUserQueryVariables>(
|
||||
ADMIN_USER_QUERY,
|
||||
{
|
||||
variables: {
|
||||
id: router.query.id as string
|
||||
id: router.query.id as string,
|
||||
},
|
||||
onCompleted: next => {
|
||||
onCompleted: (next) => {
|
||||
form.setFieldsValue(next)
|
||||
},
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
const [update] = useMutation<AdminUserUpdateMutationData, AdminUserUpdateMutationVariables>(ADMIN_USER_UPDATE_MUTATION)
|
||||
const [update] = useMutation<AdminUserUpdateMutationData, AdminUserUpdateMutationVariables>(
|
||||
ADMIN_USER_UPDATE_MUTATION
|
||||
)
|
||||
|
||||
const save = async (formData: AdminUserQueryData) => {
|
||||
setSaving(true)
|
||||
try {
|
||||
const next = (await update({
|
||||
const next = (
|
||||
await update({
|
||||
variables: cleanInput(formData),
|
||||
})).data
|
||||
})
|
||||
).data
|
||||
|
||||
form.setFieldsValue(next)
|
||||
|
||||
@ -61,18 +72,16 @@ const Index: NextPage = () => {
|
||||
{ href: '/admin/users', name: t('admin:users') },
|
||||
]}
|
||||
extra={[
|
||||
<Button
|
||||
key={'save'}
|
||||
onClick={form.submit}
|
||||
type={'primary'}
|
||||
>{t('user:updateNow')}</Button>,
|
||||
<Button key={'save'} onClick={form.submit} type={'primary'}>
|
||||
{t('user:updateNow')}
|
||||
</Button>,
|
||||
]}
|
||||
style={{paddingTop: 0}}
|
||||
style={{ paddingTop: 0 }}
|
||||
>
|
||||
<Form
|
||||
form={form}
|
||||
onFinish={save}
|
||||
onFinishFailed={errors => {
|
||||
onFinishFailed={() => {
|
||||
// TODO process errors
|
||||
message.error(t('validation:mandatoryFieldsMissing'))
|
||||
}}
|
||||
@ -85,13 +94,12 @@ const Index: NextPage = () => {
|
||||
sm: { span: 18 },
|
||||
}}
|
||||
>
|
||||
<Form.Item noStyle name={['user', 'id']}><Input type={'hidden'} /></Form.Item>
|
||||
<Form.Item noStyle name={['user', 'id']}>
|
||||
<Input type={'hidden'} />
|
||||
</Form.Item>
|
||||
|
||||
<Tabs>
|
||||
<BaseDataTab
|
||||
key={'base_data'}
|
||||
tab={t('user:baseData')}
|
||||
/>
|
||||
<BaseDataTab key={'base_data'} tab={t('user:baseData')} />
|
||||
</Tabs>
|
||||
</Form>
|
||||
</Structure>
|
||||
|
||||
@ -1,26 +1,26 @@
|
||||
import {DeleteOutlined, EditOutlined} from '@ant-design/icons/lib'
|
||||
import {useMutation, useQuery} from '@apollo/react-hooks'
|
||||
import {Button, message, Popconfirm, Space, Table, Tag} from 'antd'
|
||||
import {PaginationProps} from 'antd/es/pagination'
|
||||
import {ColumnsType} from 'antd/lib/table/interface'
|
||||
import { DeleteOutlined, EditOutlined } from '@ant-design/icons/lib'
|
||||
import { useMutation, useQuery } from '@apollo/react-hooks'
|
||||
import { Button, message, Popconfirm, Space, Table, Tag } from 'antd'
|
||||
import { PaginationProps } from 'antd/es/pagination'
|
||||
import { ColumnsType } from 'antd/lib/table/interface'
|
||||
import Structure from 'components/structure'
|
||||
import {withAuth} from 'components/with.auth'
|
||||
import {NextPage} from 'next'
|
||||
import { withAuth } from 'components/with.auth'
|
||||
import { NextPage } from 'next'
|
||||
import Link from 'next/link'
|
||||
import React, {useState} from 'react'
|
||||
import {useTranslation} from 'react-i18next'
|
||||
import {DateTime} from '../../../components/date.time'
|
||||
import {UserRole} from '../../../components/user/role'
|
||||
import React, { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { DateTime } from '../../../components/date.time'
|
||||
import { UserRole } from '../../../components/user/role'
|
||||
import {
|
||||
ADMIN_USER_DELETE_MUTATION,
|
||||
AdminUserDeleteMutationData,
|
||||
AdminUserDeleteMutationVariables
|
||||
AdminUserDeleteMutationVariables,
|
||||
} from '../../../graphql/mutation/admin.user.delete.mutation'
|
||||
import {
|
||||
ADMIN_PAGER_USER_QUERY,
|
||||
AdminPagerUserEntryQueryData,
|
||||
AdminPagerUserQueryData,
|
||||
AdminPagerUserQueryVariables
|
||||
AdminPagerUserQueryVariables,
|
||||
} from '../../../graphql/query/admin.pager.user.query'
|
||||
|
||||
const Index: NextPage = () => {
|
||||
@ -29,29 +29,35 @@ const Index: NextPage = () => {
|
||||
pageSize: 10,
|
||||
})
|
||||
const [entries, setEntries] = useState<AdminPagerUserEntryQueryData[]>()
|
||||
const {loading, refetch} = useQuery<AdminPagerUserQueryData, AdminPagerUserQueryVariables>(ADMIN_PAGER_USER_QUERY, {
|
||||
const { loading, refetch } = useQuery<AdminPagerUserQueryData, AdminPagerUserQueryVariables>(
|
||||
ADMIN_PAGER_USER_QUERY,
|
||||
{
|
||||
variables: {
|
||||
limit: pagination.pageSize,
|
||||
start: Math.max(0, pagination.current - 1) * pagination.pageSize || 0
|
||||
start: Math.max(0, pagination.current - 1) * pagination.pageSize || 0,
|
||||
},
|
||||
onCompleted: ({pager}) => {
|
||||
onCompleted: ({ pager }) => {
|
||||
setPagination({
|
||||
...pagination,
|
||||
total: pager.total,
|
||||
})
|
||||
setEntries(pager.entries)
|
||||
},
|
||||
}
|
||||
})
|
||||
const [executeDelete] = useMutation<AdminUserDeleteMutationData, AdminUserDeleteMutationVariables>(ADMIN_USER_DELETE_MUTATION)
|
||||
)
|
||||
const [executeDelete] = useMutation<
|
||||
AdminUserDeleteMutationData,
|
||||
AdminUserDeleteMutationVariables
|
||||
>(ADMIN_USER_DELETE_MUTATION)
|
||||
|
||||
const deleteUser = async (form) => {
|
||||
try {
|
||||
await executeDelete({
|
||||
variables: {
|
||||
id: form.id
|
||||
}
|
||||
id: form.id,
|
||||
},
|
||||
})
|
||||
const next = entries.filter(entry => entry.id !== form.id)
|
||||
const next = entries.filter((entry) => entry.id !== form.id)
|
||||
if (next.length === 0) {
|
||||
setPagination({ ...pagination, current: 1 })
|
||||
} else {
|
||||
@ -67,28 +73,33 @@ const Index: NextPage = () => {
|
||||
{
|
||||
title: t('user:row.roles'),
|
||||
dataIndex: 'roles',
|
||||
render: roles => <UserRole roles={roles} />
|
||||
render(roles) {
|
||||
return <UserRole roles={roles} />
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t('user:row.email'),
|
||||
render: row => <Tag color={row.verifiedEmail ? 'lime' : 'volcano' }>{row.email}</Tag>
|
||||
render(row) {
|
||||
return <Tag color={row.verifiedEmail ? 'lime' : 'volcano'}>{row.email}</Tag>
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t('user:row.created'),
|
||||
dataIndex: 'created',
|
||||
render: date => <DateTime date={date} />
|
||||
render(date) {
|
||||
return <DateTime date={date} />
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t('user:row.menu'),
|
||||
align: 'right',
|
||||
render: row => {
|
||||
render(row) {
|
||||
return (
|
||||
<Space>
|
||||
<Link
|
||||
href={'/admin/users/[id]'}
|
||||
as={`/admin/users/${row.id}`}
|
||||
>
|
||||
<Button type={'primary'}><EditOutlined /></Button>
|
||||
<Link href={'/admin/users/[id]'} as={`/admin/users/${row.id}`}>
|
||||
<Button type={'primary'}>
|
||||
<EditOutlined />
|
||||
</Button>
|
||||
</Link>
|
||||
|
||||
<Popconfirm
|
||||
@ -97,11 +108,13 @@ const Index: NextPage = () => {
|
||||
okText={t('user:deleteNow')}
|
||||
okButtonProps={{ danger: true }}
|
||||
>
|
||||
<Button danger><DeleteOutlined /></Button>
|
||||
<Button danger>
|
||||
<DeleteOutlined />
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
</Space>
|
||||
)
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
@ -109,9 +122,7 @@ const Index: NextPage = () => {
|
||||
<Structure
|
||||
title={t('admin:users')}
|
||||
loading={loading}
|
||||
breadcrumbs={[
|
||||
{ href: '/admin', name: t('admin:home') },
|
||||
]}
|
||||
breadcrumbs={[{ href: '/admin', name: t('admin:home') }]}
|
||||
padded={false}
|
||||
>
|
||||
<Table
|
||||
@ -119,7 +130,7 @@ const Index: NextPage = () => {
|
||||
dataSource={entries}
|
||||
rowKey={'id'}
|
||||
pagination={pagination}
|
||||
onChange={next => {
|
||||
onChange={(next) => {
|
||||
setPagination(next)
|
||||
refetch()
|
||||
}}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user