mirror of
https://github.com/IT4Change/ohmyform-ui.git
synced 2025-12-13 09:45:50 +00:00
upgrade to nextjs 12, add visible logic check
This commit is contained in:
parent
26c2f9e095
commit
e54da2b111
52
.eslintrc.js
52
.eslintrc.js
@ -6,22 +6,64 @@ module.exports = {
|
|||||||
tsconfigRootDir: __dirname,
|
tsconfigRootDir: __dirname,
|
||||||
project: ['./tsconfig.json'],
|
project: ['./tsconfig.json'],
|
||||||
},
|
},
|
||||||
|
plugins: [
|
||||||
|
'@typescript-eslint/eslint-plugin',
|
||||||
|
'@typescript-eslint',
|
||||||
|
'unused-imports'
|
||||||
|
],
|
||||||
extends: [
|
extends: [
|
||||||
'eslint:recommended',
|
'eslint:recommended',
|
||||||
'plugin:@typescript-eslint/eslint-recommended',
|
'plugin:@typescript-eslint/eslint-recommended',
|
||||||
'plugin:@typescript-eslint/recommended',
|
'plugin:@typescript-eslint/recommended',
|
||||||
|
'plugin:@typescript-eslint/recommended-requiring-type-checking',
|
||||||
'plugin:react/recommended',
|
'plugin:react/recommended',
|
||||||
'plugin:jsx-a11y/recommended',
|
'plugin:jsx-a11y/recommended',
|
||||||
'prettier/@typescript-eslint',
|
|
||||||
'plugin:prettier/recommended',
|
|
||||||
'plugin:@typescript-eslint/recommended-requiring-type-checking',
|
'plugin:@typescript-eslint/recommended-requiring-type-checking',
|
||||||
|
'prettier',
|
||||||
],
|
],
|
||||||
rules: {
|
rules: {
|
||||||
'prettier/prettier': ['error', {}, { usePrettierrc: true }],
|
'@typescript-eslint/no-unsafe-assignment': 'off',
|
||||||
|
'@typescript-eslint/no-unsafe-argument': 'off',
|
||||||
|
'@typescript-eslint/no-unsafe-member-access': 'off',
|
||||||
'react/prop-types': 'off',
|
'react/prop-types': 'off',
|
||||||
'@typescript-eslint/no-empty-interface': 'off',
|
'@typescript-eslint/no-empty-interface': 'off',
|
||||||
'@typescript-eslint/no-var-requires': 'off',
|
'jsx-a11y/no-autofocus': 'off',
|
||||||
'jsx-a11y/no-autofocus': 'off'
|
'array-element-newline': ['error', {
|
||||||
|
'ArrayExpression': 'consistent',
|
||||||
|
'ArrayPattern': {
|
||||||
|
'minItems': 3,
|
||||||
|
'multiline': true,
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
'array-bracket-newline': ['error', {
|
||||||
|
'minItems': 3,
|
||||||
|
'multiline': true,
|
||||||
|
}],
|
||||||
|
'indent': [
|
||||||
|
'error',
|
||||||
|
2,
|
||||||
|
{
|
||||||
|
'SwitchCase': 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'no-tabs': ['error'],
|
||||||
|
'max-len': ['error', {
|
||||||
|
'code': 100,
|
||||||
|
'ignoreComments': true,
|
||||||
|
'ignoreUrls': true,
|
||||||
|
'ignoreTemplateLiterals': true,
|
||||||
|
'ignoreTrailingComments': true,
|
||||||
|
'ignoreStrings': true,
|
||||||
|
}],
|
||||||
|
'quotes': ['error', 'single', { 'avoidEscape': true }],
|
||||||
|
'comma-dangle': ['error', 'always-multiline'],
|
||||||
|
'linebreak-style': [
|
||||||
|
'error',
|
||||||
|
'unix'
|
||||||
|
],
|
||||||
|
'no-trailing-spaces': 'error',
|
||||||
|
'eol-last': 'error',
|
||||||
|
'unused-imports/no-unused-imports': 'error',
|
||||||
},
|
},
|
||||||
settings: {
|
settings: {
|
||||||
react: {
|
react: {
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -30,3 +30,4 @@ yarn-error.log*
|
|||||||
|
|
||||||
# development environments
|
# development environments
|
||||||
/.idea
|
/.idea
|
||||||
|
schema.graphql
|
||||||
|
|||||||
@ -16,12 +16,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
- new slider field type
|
- new slider field type
|
||||||
- new card layout for forms
|
- new card layout for forms
|
||||||
- field logic
|
- field logic
|
||||||
|
- add enviroment config
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- combined notificationts to become more versatile
|
- combined notificationts to become more versatile
|
||||||
- use exported hooks for graphql
|
- use exported hooks for graphql
|
||||||
- disable swipe gesture
|
- disable swipe gesture
|
||||||
|
- upgrade to nextjs 12
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
|||||||
@ -30,7 +30,8 @@ WORKDIR /usr/src/app
|
|||||||
|
|
||||||
COPY --from=builder /usr/src/app /usr/src/app
|
COPY --from=builder /usr/src/app /usr/src/app
|
||||||
|
|
||||||
ENV PORT=4000
|
ENV PORT=4000 \
|
||||||
|
NODE_ENV=production
|
||||||
|
|
||||||
# Change to non-root privilege
|
# Change to non-root privilege
|
||||||
USER ohmyform
|
USER ohmyform
|
||||||
|
|||||||
@ -9,10 +9,14 @@ export const DesignTab: React.FC<TabPaneProps> = (props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Tabs.TabPane {...props}>
|
<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 />
|
<Input />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label={t('form:design.layouts')} name={['form', 'design', 'layout']}>
|
<Form.Item label={t('form:design.layouts')} name={[
|
||||||
|
'form', 'design', 'layout',
|
||||||
|
]}>
|
||||||
<Select
|
<Select
|
||||||
options={[
|
options={[
|
||||||
{
|
{
|
||||||
@ -27,11 +31,15 @@ export const DesignTab: React.FC<TabPaneProps> = (props) => {
|
|||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
{['background', 'question', 'answer', 'button', 'buttonActive', 'buttonText'].map((name) => (
|
{[
|
||||||
|
'background', 'question', 'answer', 'button', 'buttonActive', 'buttonText',
|
||||||
|
].map((name) => (
|
||||||
<Form.Item
|
<Form.Item
|
||||||
key={name}
|
key={name}
|
||||||
label={t(`form:design.color.${name}`)}
|
label={t(`form:design.color.${name}`)}
|
||||||
name={['form', 'design', 'colors', name]}
|
name={[
|
||||||
|
'form', 'design', 'colors', name,
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
<InputColor />
|
<InputColor />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|||||||
@ -12,19 +12,25 @@ export const EndPageTab: React.FC<TabPaneProps> = (props) => {
|
|||||||
<Tabs.TabPane {...props}>
|
<Tabs.TabPane {...props}>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t('form:endPage.show')}
|
label={t('form:endPage.show')}
|
||||||
name={['form', 'endPage', 'show']}
|
name={[
|
||||||
|
'form', 'endPage', 'show',
|
||||||
|
]}
|
||||||
valuePropName={'checked'}
|
valuePropName={'checked'}
|
||||||
>
|
>
|
||||||
<Switch />
|
<Switch />
|
||||||
</Form.Item>
|
</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 />
|
<Input />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t('form:endPage.paragraph')}
|
label={t('form:endPage.paragraph')}
|
||||||
name={['form', 'endPage', 'paragraph']}
|
name={[
|
||||||
|
'form', 'endPage', 'paragraph',
|
||||||
|
]}
|
||||||
extra={t('type:descriptionInfo')}
|
extra={t('type:descriptionInfo')}
|
||||||
>
|
>
|
||||||
<Input.TextArea autoSize />
|
<Input.TextArea autoSize />
|
||||||
@ -32,12 +38,16 @@ export const EndPageTab: React.FC<TabPaneProps> = (props) => {
|
|||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t('form:endPage.continueButtonText')}
|
label={t('form:endPage.continueButtonText')}
|
||||||
name={['form', 'endPage', 'buttonText']}
|
name={[
|
||||||
|
'form', 'endPage', 'buttonText',
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
<Input />
|
<Input />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.List name={['form', 'endPage', 'buttons']}>
|
<Form.List name={[
|
||||||
|
'form', 'endPage', 'buttons',
|
||||||
|
]}>
|
||||||
{(fields, { add, remove }) => {
|
{(fields, { add, remove }) => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@ -108,7 +108,9 @@ export const ExportSubmissionAction: React.FC<Props> = (props) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
}, [form, getSubmissions, props.form, setLoading, loading])
|
}, [
|
||||||
|
form, getSubmissions, props.form, setLoading, loading,
|
||||||
|
])
|
||||||
|
|
||||||
return props.trigger(() => exportSubmissions(), loading)
|
return props.trigger(() => exportSubmissions(), loading)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,16 +1,6 @@
|
|||||||
|
import { VerticalAlignBottomOutlined, VerticalAlignTopOutlined } from '@ant-design/icons'
|
||||||
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons/lib'
|
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons/lib'
|
||||||
import {
|
import { Button, Card, Checkbox, Form, Input, Popconfirm, Popover, Space, Tag, Tooltip } from 'antd'
|
||||||
Button,
|
|
||||||
Card,
|
|
||||||
Checkbox,
|
|
||||||
Form,
|
|
||||||
Input,
|
|
||||||
Popconfirm,
|
|
||||||
Popover,
|
|
||||||
Space,
|
|
||||||
Tag,
|
|
||||||
Tooltip,
|
|
||||||
} from 'antd'
|
|
||||||
import { FormInstance } from 'antd/lib/form'
|
import { FormInstance } from 'antd/lib/form'
|
||||||
import { FieldData } from 'rc-field-form/lib/interface'
|
import { FieldData } from 'rc-field-form/lib/interface'
|
||||||
import React, { useCallback, useEffect, useState } from 'react'
|
import React, { useCallback, useEffect, useState } from 'react'
|
||||||
@ -26,22 +16,40 @@ interface Props {
|
|||||||
onChangeFields: (fields: FormFieldFragment[]) => void
|
onChangeFields: (fields: FormFieldFragment[]) => void
|
||||||
field: FieldData
|
field: FieldData
|
||||||
remove: (index: number) => void
|
remove: (index: number) => void
|
||||||
|
move: (from: number, to: number) => void
|
||||||
index: number
|
index: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FieldCard: React.FC<Props> = (props) => {
|
export const FieldCard: React.FC<Props> = ({
|
||||||
|
form,
|
||||||
|
field,
|
||||||
|
fields,
|
||||||
|
onChangeFields,
|
||||||
|
remove,
|
||||||
|
move,
|
||||||
|
index,
|
||||||
|
}) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { form, field, fields, onChangeFields, remove, index } = props
|
|
||||||
|
|
||||||
const type = form.getFieldValue(['form', 'fields', field.name as string, 'type']) as string
|
const type = form.getFieldValue([
|
||||||
|
'form', 'fields', field.name as string, 'type',
|
||||||
|
]) as string
|
||||||
const TypeComponent = adminTypes[type] || TextType
|
const TypeComponent = adminTypes[type] || TextType
|
||||||
|
|
||||||
|
const [shouldUpdate, setShouldUpdate] = useState(false)
|
||||||
const [nextTitle, setNextTitle] = useState<string>(
|
const [nextTitle, setNextTitle] = useState<string>(
|
||||||
form.getFieldValue(['form', 'fields', field.name as string, 'title'])
|
form.getFieldValue([
|
||||||
|
'form', 'fields', field.name as string, 'title',
|
||||||
|
])
|
||||||
)
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (!shouldUpdate) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const id = setTimeout(() => {
|
const id = setTimeout(() => {
|
||||||
|
setShouldUpdate(false)
|
||||||
onChangeFields(
|
onChangeFields(
|
||||||
fields.map((field, i) => {
|
fields.map((field, i) => {
|
||||||
if (i === index) {
|
if (i === index) {
|
||||||
@ -57,7 +65,9 @@ export const FieldCard: React.FC<Props> = (props) => {
|
|||||||
}, 500)
|
}, 500)
|
||||||
|
|
||||||
return () => clearTimeout(id)
|
return () => clearTimeout(id)
|
||||||
}, [nextTitle])
|
}, [
|
||||||
|
nextTitle, shouldUpdate, fields,
|
||||||
|
])
|
||||||
|
|
||||||
const addLogic = useCallback((add: (defaults: unknown) => void, index: number) => {
|
const addLogic = useCallback((add: (defaults: unknown) => void, index: number) => {
|
||||||
return (
|
return (
|
||||||
@ -94,14 +104,32 @@ export const FieldCard: React.FC<Props> = (props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
title={<Tooltip title={`@${field.name as string}`}>nextTitle</Tooltip>}
|
title={nextTitle}
|
||||||
type={'inner'}
|
type={'inner'}
|
||||||
extra={
|
extra={
|
||||||
<div>
|
<Space>
|
||||||
|
<Tooltip title={t('form:field.move.up')}>
|
||||||
|
<Button
|
||||||
|
type={'text'}
|
||||||
|
disabled={index === 0}
|
||||||
|
onClick={() => move(index, index - 1)}
|
||||||
|
icon={<VerticalAlignTopOutlined />}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip title={t('form:field.move.down')}>
|
||||||
|
<Button
|
||||||
|
type={'text'}
|
||||||
|
disabled={index + 1 >= form.getFieldValue(['form', 'fields']).length}
|
||||||
|
onClick={() => move(index, index + 1)}
|
||||||
|
icon={<VerticalAlignBottomOutlined />}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
<Form.Item noStyle shouldUpdate>
|
<Form.Item noStyle shouldUpdate>
|
||||||
{() => {
|
{() => {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||||
const slug = form.getFieldValue(['form', 'fields', field.name as string, 'slug'])
|
const slug = form.getFieldValue([
|
||||||
|
'form', 'fields', field.name as string, 'slug',
|
||||||
|
])
|
||||||
|
|
||||||
if (!slug) {
|
if (!slug) {
|
||||||
return null
|
return null
|
||||||
@ -145,7 +173,7 @@ export const FieldCard: React.FC<Props> = (props) => {
|
|||||||
<DeleteOutlined />
|
<DeleteOutlined />
|
||||||
</Button>
|
</Button>
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
</div>
|
</Space>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Form.Item name={[field.name as string, 'type']} noStyle>
|
<Form.Item name={[field.name as string, 'type']} noStyle>
|
||||||
@ -157,7 +185,12 @@ export const FieldCard: React.FC<Props> = (props) => {
|
|||||||
rules={[{ required: true, message: 'Title is required' }]}
|
rules={[{ required: true, message: 'Title is required' }]}
|
||||||
labelCol={{ span: 6 }}
|
labelCol={{ span: 6 }}
|
||||||
>
|
>
|
||||||
<Input onChange={(e) => setNextTitle(e.target.value)} />
|
<Input
|
||||||
|
onChange={(e) => {
|
||||||
|
setNextTitle(e.target.value)
|
||||||
|
setShouldUpdate(true)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t('type:description')}
|
label={t('type:description')}
|
||||||
@ -181,7 +214,7 @@ export const FieldCard: React.FC<Props> = (props) => {
|
|||||||
|
|
||||||
<Form.List name={[field.name as string, 'logic']}>
|
<Form.List name={[field.name as string, 'logic']}>
|
||||||
{(logic, { add, remove, move }) => {
|
{(logic, { add, remove, move }) => {
|
||||||
const addAndMove = (index) => (defaults) => {
|
const addAndMove = (index: number) => (defaults) => {
|
||||||
add(defaults)
|
add(defaults)
|
||||||
move(fields.length, index)
|
move(fields.length, index)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { PlusOutlined } from '@ant-design/icons/lib'
|
|||||||
import { Button, Form, Select, Space, Tabs } from 'antd'
|
import { Button, Form, Select, Space, Tabs } from 'antd'
|
||||||
import { FormInstance } from 'antd/lib/form'
|
import { FormInstance } from 'antd/lib/form'
|
||||||
import { TabPaneProps } from 'antd/lib/tabs'
|
import { TabPaneProps } from 'antd/lib/tabs'
|
||||||
|
import debug from 'debug'
|
||||||
import { FieldData } from 'rc-field-form/lib/interface'
|
import { FieldData } from 'rc-field-form/lib/interface'
|
||||||
import React, { useCallback, useState } from 'react'
|
import React, { useCallback, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
@ -9,6 +10,8 @@ import { FormFieldFragment } from '../../../graphql/fragment/form.fragment'
|
|||||||
import { FieldCard } from './field.card'
|
import { FieldCard } from './field.card'
|
||||||
import { adminTypes } from './types'
|
import { adminTypes } from './types'
|
||||||
|
|
||||||
|
const logger = debug('FieldsTab')
|
||||||
|
|
||||||
interface Props extends TabPaneProps {
|
interface Props extends TabPaneProps {
|
||||||
form: FormInstance
|
form: FormInstance
|
||||||
fields: FormFieldFragment[]
|
fields: FormFieldFragment[]
|
||||||
@ -20,13 +23,25 @@ export const FieldsTab: React.FC<Props> = (props) => {
|
|||||||
const [nextType, setNextType] = useState('textfield')
|
const [nextType, setNextType] = useState('textfield')
|
||||||
|
|
||||||
const renderType = useCallback(
|
const renderType = useCallback(
|
||||||
(field: FieldData, index: number, remove: (index: number) => void) => {
|
(
|
||||||
|
field: FieldData,
|
||||||
|
index: number,
|
||||||
|
remove: (index: number) => void,
|
||||||
|
move: (from: number, to: number) => void
|
||||||
|
) => {
|
||||||
return (
|
return (
|
||||||
<FieldCard
|
<FieldCard
|
||||||
form={props.form}
|
form={props.form}
|
||||||
field={field}
|
field={field}
|
||||||
index={index}
|
index={index}
|
||||||
remove={remove}
|
remove={(index: number) => {
|
||||||
|
logger('remove %d', index)
|
||||||
|
remove(index)
|
||||||
|
}}
|
||||||
|
move={(from: number, to: number) => {
|
||||||
|
logger('move %d TO %d', from, to)
|
||||||
|
move(from, to)
|
||||||
|
}}
|
||||||
fields={props.fields}
|
fields={props.fields}
|
||||||
onChangeFields={props.onChangeFields}
|
onChangeFields={props.onChangeFields}
|
||||||
/>
|
/>
|
||||||
@ -85,7 +100,7 @@ export const FieldsTab: React.FC<Props> = (props) => {
|
|||||||
<Tabs.TabPane {...props}>
|
<Tabs.TabPane {...props}>
|
||||||
<Form.List name={['form', 'fields']}>
|
<Form.List name={['form', 'fields']}>
|
||||||
{(fields, { add, remove, move }) => {
|
{(fields, { add, remove, move }) => {
|
||||||
const addAndMove = (index) => (defaults) => {
|
const addAndMove = (index: number) => (defaults) => {
|
||||||
add(defaults)
|
add(defaults)
|
||||||
move(fields.length, index)
|
move(fields.length, index)
|
||||||
}
|
}
|
||||||
@ -96,7 +111,7 @@ export const FieldsTab: React.FC<Props> = (props) => {
|
|||||||
{fields.map((field, index) => (
|
{fields.map((field, index) => (
|
||||||
<div key={field.key}>
|
<div key={field.key}>
|
||||||
<Form.Item wrapperCol={{ span: 24 }}>
|
<Form.Item wrapperCol={{ span: 24 }}>
|
||||||
{renderType(field, index, remove)}
|
{renderType(field, index, remove, move)}
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
{addField(addAndMove(index + 1), index + 1)}
|
{addField(addAndMove(index + 1), index + 1)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import { DeleteOutlined } from '@ant-design/icons'
|
import { DeleteOutlined } from '@ant-design/icons'
|
||||||
import { Alert, Button, Checkbox, Form, Mentions, Popconfirm, Select } from 'antd'
|
import { Alert, Button, Checkbox, Form, Mentions, Popconfirm, Select } from 'antd'
|
||||||
import { FormInstance } from 'antd/lib/form'
|
import { FormInstance } from 'antd/lib/form'
|
||||||
import FormItemContext from 'rc-field-form/lib/FieldContext'
|
|
||||||
import { FieldData } from 'rc-field-form/lib/interface'
|
import { FieldData } from 'rc-field-form/lib/interface'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
@ -16,9 +15,14 @@ interface Props {
|
|||||||
index: number
|
index: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export const LogicBlock: React.FC<Props> = (props) => {
|
export const LogicBlock: React.FC<Props> = ({
|
||||||
|
form,
|
||||||
|
field,
|
||||||
|
fields,
|
||||||
|
remove,
|
||||||
|
index,
|
||||||
|
}) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { form, field, fields, remove, index } = props
|
|
||||||
const evaluator = useMath()
|
const evaluator = useMath()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -45,9 +49,7 @@ export const LogicBlock: React.FC<Props> = (props) => {
|
|||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item noStyle shouldUpdate>
|
<Form.Item noStyle shouldUpdate>
|
||||||
{(form) => {
|
{(form: FormInstance & { prefixName: string[] }) => {
|
||||||
const context = React.useContext(FormItemContext)
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const defaults = {}
|
const defaults = {}
|
||||||
|
|
||||||
@ -60,7 +62,11 @@ export const LogicBlock: React.FC<Props> = (props) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const result = evaluator(
|
const result = evaluator(
|
||||||
form.getFieldValue([...context.prefixName, field.name as string, 'formula']),
|
form.getFieldValue([
|
||||||
|
...form.prefixName,
|
||||||
|
field.name as string,
|
||||||
|
'formula',
|
||||||
|
]),
|
||||||
defaults
|
defaults
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -109,13 +115,13 @@ export const LogicBlock: React.FC<Props> = (props) => {
|
|||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item noStyle shouldUpdate>
|
<Form.Item noStyle shouldUpdate>
|
||||||
{(form) => {
|
{(form: FormInstance & { prefixName: string[] }) => {
|
||||||
const context = React.useContext(FormItemContext)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form.Item
|
<Form.Item
|
||||||
hidden={
|
hidden={
|
||||||
form.getFieldValue([...context.prefixName, field.name as string, 'action']) !==
|
form.getFieldValue([
|
||||||
|
...form.prefixName, field.name as string, 'action',
|
||||||
|
]) !==
|
||||||
'jumpTo'
|
'jumpTo'
|
||||||
}
|
}
|
||||||
labelCol={{ span: 6 }}
|
labelCol={{ span: 6 }}
|
||||||
@ -137,13 +143,13 @@ export const LogicBlock: React.FC<Props> = (props) => {
|
|||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item noStyle shouldUpdate>
|
<Form.Item noStyle shouldUpdate>
|
||||||
{(form) => {
|
{(form: FormInstance & { prefixName: string[] }) => {
|
||||||
const context = React.useContext(FormItemContext)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form.Item
|
<Form.Item
|
||||||
hidden={
|
hidden={
|
||||||
form.getFieldValue([...context.prefixName, field.name as string, 'action']) !==
|
form.getFieldValue([
|
||||||
|
...form.prefixName, field.name as string, 'action',
|
||||||
|
]) !==
|
||||||
'visible'
|
'visible'
|
||||||
}
|
}
|
||||||
initialValue={true}
|
initialValue={true}
|
||||||
@ -160,13 +166,13 @@ export const LogicBlock: React.FC<Props> = (props) => {
|
|||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item noStyle shouldUpdate>
|
<Form.Item noStyle shouldUpdate>
|
||||||
{(form) => {
|
{(form: FormInstance & { prefixName: string[] }) => {
|
||||||
const context = React.useContext(FormItemContext)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form.Item
|
<Form.Item
|
||||||
hidden={
|
hidden={
|
||||||
form.getFieldValue([...context.prefixName, field.name as string, 'action']) !==
|
form.getFieldValue([
|
||||||
|
...form.prefixName, field.name as string, 'action',
|
||||||
|
]) !==
|
||||||
'disable'
|
'disable'
|
||||||
}
|
}
|
||||||
initialValue={false}
|
initialValue={false}
|
||||||
@ -183,13 +189,13 @@ export const LogicBlock: React.FC<Props> = (props) => {
|
|||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item noStyle shouldUpdate>
|
<Form.Item noStyle shouldUpdate>
|
||||||
{(form) => {
|
{(form: FormInstance & { prefixName: string[] }) => {
|
||||||
const context = React.useContext(FormItemContext)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form.Item
|
<Form.Item
|
||||||
hidden={
|
hidden={
|
||||||
form.getFieldValue([...context.prefixName, field.name as string, 'action']) !==
|
form.getFieldValue([
|
||||||
|
...form.prefixName, field.name as string, 'action',
|
||||||
|
]) !==
|
||||||
'require'
|
'require'
|
||||||
}
|
}
|
||||||
initialValue={true}
|
initialValue={true}
|
||||||
|
|||||||
@ -62,7 +62,9 @@ export const NotificationCard: React.FC<Props> = (props) => {
|
|||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: Boolean(
|
required: Boolean(
|
||||||
form.getFieldValue(['form', 'notifications', field.name as string, 'enabled'])
|
form.getFieldValue([
|
||||||
|
'form', 'notifications', field.name as string, 'enabled',
|
||||||
|
])
|
||||||
),
|
),
|
||||||
message: t('validation:subjectRequired'),
|
message: t('validation:subjectRequired'),
|
||||||
},
|
},
|
||||||
@ -82,7 +84,9 @@ export const NotificationCard: React.FC<Props> = (props) => {
|
|||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: Boolean(
|
required: Boolean(
|
||||||
form.getFieldValue(['form', 'notifications', field.name as string, 'enabled'])
|
form.getFieldValue([
|
||||||
|
'form', 'notifications', field.name as string, 'enabled',
|
||||||
|
])
|
||||||
),
|
),
|
||||||
message: t('validation:templateRequired'),
|
message: t('validation:templateRequired'),
|
||||||
},
|
},
|
||||||
@ -119,7 +123,9 @@ export const NotificationCard: React.FC<Props> = (props) => {
|
|||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: Boolean(
|
required: Boolean(
|
||||||
form.getFieldValue(['form', 'notifications', field.name as string, 'enabled']) &&
|
form.getFieldValue([
|
||||||
|
'form', 'notifications', field.name as string, 'enabled',
|
||||||
|
]) &&
|
||||||
!form.getFieldValue([
|
!form.getFieldValue([
|
||||||
'form',
|
'form',
|
||||||
'notifications',
|
'notifications',
|
||||||
@ -156,7 +162,9 @@ export const NotificationCard: React.FC<Props> = (props) => {
|
|||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: Boolean(
|
required: Boolean(
|
||||||
form.getFieldValue(['form', 'notifications', field.name as string, 'enabled']) &&
|
form.getFieldValue([
|
||||||
|
'form', 'notifications', field.name as string, 'enabled',
|
||||||
|
]) &&
|
||||||
!form.getFieldValue([
|
!form.getFieldValue([
|
||||||
'form',
|
'form',
|
||||||
'notifications',
|
'notifications',
|
||||||
@ -182,8 +190,12 @@ export const NotificationCard: React.FC<Props> = (props) => {
|
|||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: Boolean(
|
required: Boolean(
|
||||||
form.getFieldValue(['form', 'notifications', field.name as string, 'enabled']) &&
|
form.getFieldValue([
|
||||||
!form.getFieldValue(['form', 'notifications', field.name as string, 'toEmail'])
|
'form', 'notifications', field.name as string, 'enabled',
|
||||||
|
]) &&
|
||||||
|
!form.getFieldValue([
|
||||||
|
'form', 'notifications', field.name as string, 'toEmail',
|
||||||
|
])
|
||||||
),
|
),
|
||||||
message: t('validation:emailFieldRequired'),
|
message: t('validation:emailFieldRequired'),
|
||||||
},
|
},
|
||||||
@ -215,8 +227,12 @@ export const NotificationCard: React.FC<Props> = (props) => {
|
|||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: Boolean(
|
required: Boolean(
|
||||||
form.getFieldValue(['form', 'notifications', field.name as string, 'enabled']) &&
|
form.getFieldValue([
|
||||||
!form.getFieldValue(['form', 'notifications', field.name as string, 'toField'])
|
'form', 'notifications', field.name as string, 'enabled',
|
||||||
|
]) &&
|
||||||
|
!form.getFieldValue([
|
||||||
|
'form', 'notifications', field.name as string, 'toField',
|
||||||
|
])
|
||||||
),
|
),
|
||||||
message: t('validation:emailFieldRequired'),
|
message: t('validation:emailFieldRequired'),
|
||||||
},
|
},
|
||||||
|
|||||||
@ -62,7 +62,7 @@ export const NotificationsTab: React.FC<Props> = (props) => {
|
|||||||
<Tabs.TabPane {...props}>
|
<Tabs.TabPane {...props}>
|
||||||
<Form.List name={['form', 'notifications']}>
|
<Form.List name={['form', 'notifications']}>
|
||||||
{(fields, { add, remove, move }) => {
|
{(fields, { add, remove, move }) => {
|
||||||
const addAndMove = (index) => (defaults) => {
|
const addAndMove = (index: number) => (defaults) => {
|
||||||
add(defaults)
|
add(defaults)
|
||||||
move(fields.length, index)
|
move(fields.length, index)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,19 +12,25 @@ export const StartPageTab: React.FC<TabPaneProps> = (props) => {
|
|||||||
<Tabs.TabPane {...props}>
|
<Tabs.TabPane {...props}>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t('form:startPage.show')}
|
label={t('form:startPage.show')}
|
||||||
name={['form', 'startPage', 'show']}
|
name={[
|
||||||
|
'form', 'startPage', 'show',
|
||||||
|
]}
|
||||||
valuePropName={'checked'}
|
valuePropName={'checked'}
|
||||||
>
|
>
|
||||||
<Switch />
|
<Switch />
|
||||||
</Form.Item>
|
</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 />
|
<Input />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t('form:startPage.paragraph')}
|
label={t('form:startPage.paragraph')}
|
||||||
name={['form', 'startPage', 'paragraph']}
|
name={[
|
||||||
|
'form', 'startPage', 'paragraph',
|
||||||
|
]}
|
||||||
extra={t('form:startPage.paragraphInfo')}
|
extra={t('form:startPage.paragraphInfo')}
|
||||||
>
|
>
|
||||||
<Input.TextArea autoSize />
|
<Input.TextArea autoSize />
|
||||||
@ -32,12 +38,16 @@ export const StartPageTab: React.FC<TabPaneProps> = (props) => {
|
|||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t('form:startPage.continueButtonText')}
|
label={t('form:startPage.continueButtonText')}
|
||||||
name={['form', 'startPage', 'buttonText']}
|
name={[
|
||||||
|
'form', 'startPage', 'buttonText',
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
<Input />
|
<Input />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
|
||||||
<Form.List name={['form', 'startPage', 'buttons']}>
|
<Form.List name={[
|
||||||
|
'form', 'startPage', 'buttons',
|
||||||
|
]}>
|
||||||
{(fields, { add, remove }) => {
|
{(fields, { add, remove }) => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@ -33,8 +33,6 @@ export const SubmissionValues: React.FC<Props> = (props) => {
|
|||||||
try {
|
try {
|
||||||
const data = JSON.parse(row.value) as { value: string }
|
const data = JSON.parse(row.value) as { value: string }
|
||||||
|
|
||||||
console.log('DATA', data)
|
|
||||||
|
|
||||||
return data.value
|
return data.value
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return row.value
|
return row.value
|
||||||
|
|||||||
@ -20,7 +20,9 @@ export const DateType: React.FC<AdminFieldTypeProps> = ({ field }) => {
|
|||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t('type:date.min')}
|
label={t('type:date.min')}
|
||||||
name={[field.name as string, 'optionKeys', 'min']}
|
name={[
|
||||||
|
field.name as string, 'optionKeys', 'min',
|
||||||
|
]}
|
||||||
labelCol={{ span: 6 }}
|
labelCol={{ span: 6 }}
|
||||||
getValueFromEvent={(e: Moment) => e.format('YYYY-MM-DD')}
|
getValueFromEvent={(e: Moment) => e.format('YYYY-MM-DD')}
|
||||||
getValueProps={(e: string) => ({ value: e ? moment(e) : undefined })}
|
getValueProps={(e: string) => ({ value: e ? moment(e) : undefined })}
|
||||||
@ -30,7 +32,9 @@ export const DateType: React.FC<AdminFieldTypeProps> = ({ field }) => {
|
|||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t('type:date.max')}
|
label={t('type:date.max')}
|
||||||
name={[field.name as string, 'optionKeys', 'max']}
|
name={[
|
||||||
|
field.name as string, 'optionKeys', 'max',
|
||||||
|
]}
|
||||||
labelCol={{ span: 6 }}
|
labelCol={{ span: 6 }}
|
||||||
getValueFromEvent={(e: Moment) => e.format('YYYY-MM-DD')}
|
getValueFromEvent={(e: Moment) => e.format('YYYY-MM-DD')}
|
||||||
getValueProps={(e: string) => ({ value: e ? moment(e) : undefined })}
|
getValueProps={(e: string) => ({ value: e ? moment(e) : undefined })}
|
||||||
|
|||||||
@ -50,7 +50,9 @@ export const SliderType: React.FC<AdminFieldTypeProps> = (props) => {
|
|||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t('type:slider.min')}
|
label={t('type:slider.min')}
|
||||||
name={[props.field.name as string, 'optionKeys', 'min']}
|
name={[
|
||||||
|
props.field.name as string, 'optionKeys', 'min',
|
||||||
|
]}
|
||||||
labelCol={{ span: 6 }}
|
labelCol={{ span: 6 }}
|
||||||
initialValue={0}
|
initialValue={0}
|
||||||
getValueFromEvent={(value: number) =>
|
getValueFromEvent={(value: number) =>
|
||||||
@ -63,7 +65,9 @@ export const SliderType: React.FC<AdminFieldTypeProps> = (props) => {
|
|||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t('type:slider.max')}
|
label={t('type:slider.max')}
|
||||||
name={[props.field.name as string, 'optionKeys', 'max']}
|
name={[
|
||||||
|
props.field.name as string, 'optionKeys', 'max',
|
||||||
|
]}
|
||||||
labelCol={{ span: 6 }}
|
labelCol={{ span: 6 }}
|
||||||
initialValue={100}
|
initialValue={100}
|
||||||
getValueFromEvent={(value: number) =>
|
getValueFromEvent={(value: number) =>
|
||||||
@ -76,7 +80,9 @@ export const SliderType: React.FC<AdminFieldTypeProps> = (props) => {
|
|||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
label={t('type:slider.step')}
|
label={t('type:slider.step')}
|
||||||
name={[props.field.name as string, 'optionKeys', 'step']}
|
name={[
|
||||||
|
props.field.name as string, 'optionKeys', 'step',
|
||||||
|
]}
|
||||||
labelCol={{ span: 6 }}
|
labelCol={{ span: 6 }}
|
||||||
initialValue={1}
|
initialValue={1}
|
||||||
getValueFromEvent={(value: number) =>
|
getValueFromEvent={(value: number) =>
|
||||||
|
|||||||
@ -54,7 +54,7 @@ export const Field: React.FC<Props> = ({ field, design, focus, ...props }) => {
|
|||||||
{field.title}
|
{field.title}
|
||||||
</StyledH1>
|
</StyledH1>
|
||||||
{field.description && (
|
{field.description && (
|
||||||
<StyledMarkdown design={design} type={'question'} source={field.description} />
|
<StyledMarkdown design={design} type={'question'} >{field.description}</StyledMarkdown>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<FieldInput design={design} field={field} urlValue={getUrlDefault()} focus={focus} />
|
<FieldInput design={design} field={field} urlValue={getUrlDefault()} focus={focus} />
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
import { Card, Form, message, Modal, Spin } from 'antd'
|
import { Card, Form, message, Modal, Spin } from 'antd'
|
||||||
import React, { useState } from 'react'
|
import { darken, lighten } from 'polished'
|
||||||
|
import React, { useCallback, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
import { FormPublicFieldFragment } from '../../../../graphql/fragment/form.public.fragment'
|
||||||
import { Omf } from '../../../omf'
|
import { Omf } from '../../../omf'
|
||||||
import { StyledButton } from '../../../styled/button'
|
import { StyledButton } from '../../../styled/button'
|
||||||
import { darken, lighten } from '../../../styled/color.change'
|
import { useMath } from '../../../use.math'
|
||||||
import { LayoutProps } from '../layout.props'
|
import { LayoutProps } from '../layout.props'
|
||||||
import { Field } from './field'
|
import { Field } from './field'
|
||||||
import { Page } from './page'
|
import { Page } from './page'
|
||||||
@ -12,7 +14,7 @@ import { Page } from './page'
|
|||||||
type Step = 'start' | 'form' | 'end'
|
type Step = 'start' | 'form' | 'end'
|
||||||
|
|
||||||
const MyCard = styled.div<{ background: string }>`
|
const MyCard = styled.div<{ background: string }>`
|
||||||
background: ${(props) => darken(props.background, 10)};
|
background: ${(props) => darken(0.1, props.background)};
|
||||||
height: 100%;
|
height: 100%;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
|
|
||||||
@ -20,7 +22,7 @@ const MyCard = styled.div<{ background: string }>`
|
|||||||
|
|
||||||
.ant-card {
|
.ant-card {
|
||||||
background: ${(props) => props.background};
|
background: ${(props) => props.background};
|
||||||
border-color: ${(props) => lighten(props.background, 40)};
|
border-color: ${(props) => lighten(0.4, props.background)};
|
||||||
width: 800px;
|
width: 800px;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
max-width: 90%;
|
max-width: 90%;
|
||||||
@ -32,6 +34,8 @@ export const CardLayout: React.FC<LayoutProps> = (props) => {
|
|||||||
const [form] = Form.useForm()
|
const [form] = Form.useForm()
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
const [step, setStep] = useState<Step>(props.form.startPage.show ? 'start' : 'form')
|
const [step, setStep] = useState<Step>(props.form.startPage.show ? 'start' : 'form')
|
||||||
|
const evaluator = useMath()
|
||||||
|
const [values, setValues] = useState({})
|
||||||
|
|
||||||
const { design, startPage, endPage, fields } = props.form
|
const { design, startPage, endPage, fields } = props.form
|
||||||
const { setField } = props.submission
|
const { setField } = props.submission
|
||||||
@ -67,6 +71,31 @@ export const CardLayout: React.FC<LayoutProps> = (props) => {
|
|||||||
setLoading(false)
|
setLoading(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isVisible = useCallback((field: FormPublicFieldFragment): boolean => {
|
||||||
|
if (!field.logic) return true
|
||||||
|
|
||||||
|
console.log('DEFAULTS', values)
|
||||||
|
|
||||||
|
return field.logic
|
||||||
|
.filter(logic => logic.action === 'visible')
|
||||||
|
.map(logic => {
|
||||||
|
try {
|
||||||
|
const r = evaluator(
|
||||||
|
logic.formula,
|
||||||
|
values
|
||||||
|
)
|
||||||
|
|
||||||
|
console.log('result', r)
|
||||||
|
return Boolean(r)
|
||||||
|
} catch {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.reduce<boolean>((previous, current) => previous && current, true)
|
||||||
|
}, [
|
||||||
|
fields, form, values,
|
||||||
|
])
|
||||||
|
|
||||||
const render = () => {
|
const render = () => {
|
||||||
switch (step) {
|
switch (step) {
|
||||||
case 'start':
|
case 'start':
|
||||||
@ -75,12 +104,32 @@ export const CardLayout: React.FC<LayoutProps> = (props) => {
|
|||||||
case 'form':
|
case 'form':
|
||||||
return (
|
return (
|
||||||
<Card>
|
<Card>
|
||||||
<Form form={form} onFinish={finish}>
|
<Form
|
||||||
|
form={form}
|
||||||
|
onFinish={finish}
|
||||||
|
onValuesChange={() => {
|
||||||
|
const defaults = {}
|
||||||
|
|
||||||
|
fields.forEach(field => {
|
||||||
|
defaults[`@${field.id}`] = form.getFieldValue([field.id, 'value'])
|
||||||
|
|
||||||
|
if (field.slug) {
|
||||||
|
defaults[`$${field.slug}`] = form.getFieldValue([field.id, 'value'])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
setValues(defaults)
|
||||||
|
}}
|
||||||
|
>
|
||||||
{fields.map((field, i) => {
|
{fields.map((field, i) => {
|
||||||
if (field.type === 'hidden') {
|
if (field.type === 'hidden') {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isVisible(field)) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
return <Field key={field.id} field={field} design={design} focus={i === 0} />
|
return <Field key={field.id} field={field} design={design} focus={i === 0} />
|
||||||
})}
|
})}
|
||||||
<div
|
<div
|
||||||
|
|||||||
@ -26,7 +26,7 @@ export const Page: React.FC<Props> = ({ design, page, next, prev }) => {
|
|||||||
<StyledH1 design={design} type={'question'}>
|
<StyledH1 design={design} type={'question'}>
|
||||||
{page.title}
|
{page.title}
|
||||||
</StyledH1>
|
</StyledH1>
|
||||||
<StyledMarkdown design={design} type={'question'} source={page.paragraph} />
|
<StyledMarkdown design={design} type={'question'}>{page.paragraph}</StyledMarkdown>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
|
|||||||
@ -62,6 +62,7 @@ export const Field: React.FC<Props> = ({ field, save, design, next, prev, ...pro
|
|||||||
style={{
|
style={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
|
height: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
@ -77,7 +78,7 @@ export const Field: React.FC<Props> = ({ field, save, design, next, prev, ...pro
|
|||||||
{field.title}
|
{field.title}
|
||||||
</StyledH1>
|
</StyledH1>
|
||||||
{field.description && (
|
{field.description && (
|
||||||
<StyledMarkdown design={design} type={'question'} source={field.description} />
|
<StyledMarkdown design={design} type={'question'}>{field.description}</StyledMarkdown>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<FieldInput design={design} field={field} urlValue={getUrlDefault()} />
|
<FieldInput design={design} field={field} urlValue={getUrlDefault()} />
|
||||||
|
|||||||
@ -1,31 +1,42 @@
|
|||||||
import { Modal } from 'antd'
|
import { Modal } from 'antd'
|
||||||
|
import debug from 'debug'
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import Swiper from 'react-id-swiper'
|
import SwiperClass from 'swiper'
|
||||||
import { ReactIdSwiperProps } from 'react-id-swiper/lib/types'
|
import { Swiper, SwiperProps, SwiperSlide } from 'swiper/react'
|
||||||
import * as OriginalSwiper from 'swiper'
|
|
||||||
import { Omf } from '../../../omf'
|
import { Omf } from '../../../omf'
|
||||||
|
import { useWindowSize } from '../../../use.window.size'
|
||||||
import { LayoutProps } from '../layout.props'
|
import { LayoutProps } from '../layout.props'
|
||||||
import { Field } from './field'
|
import { Field } from './field'
|
||||||
import { FormPage } from './page'
|
import { FormPage } from './page'
|
||||||
|
|
||||||
|
const logger = debug('layout/slider')
|
||||||
|
|
||||||
export const SliderLayout: React.FC<LayoutProps> = (props) => {
|
export const SliderLayout: React.FC<LayoutProps> = (props) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const [swiper, setSwiper] = useState<OriginalSwiper.default>(null)
|
const [swiper, setSwiper] = useState<SwiperClass>(null)
|
||||||
|
const { height } = useWindowSize()
|
||||||
const { design, startPage, endPage, fields } = props.form
|
const { design, startPage, endPage, fields } = props.form
|
||||||
const { finish, setField } = props.submission
|
const { finish, setField } = props.submission
|
||||||
|
|
||||||
const goNext = () => {
|
const goNext = () => {
|
||||||
if (!swiper) return
|
if (!swiper) return
|
||||||
|
|
||||||
|
logger('goNext')
|
||||||
swiper.allowSlideNext = true
|
swiper.allowSlideNext = true
|
||||||
swiper.slideNext()
|
swiper.slideNext()
|
||||||
swiper.allowSlideNext = false
|
swiper.allowSlideNext = false
|
||||||
}
|
}
|
||||||
const goPrev = () => swiper && swiper.slidePrev()
|
const goPrev = () => {
|
||||||
|
if (!swiper) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const swiperConfig: ReactIdSwiperProps = {
|
logger('goPrevious')
|
||||||
|
swiper.slidePrev()
|
||||||
|
}
|
||||||
|
|
||||||
|
const swiperConfig: SwiperProps = {
|
||||||
direction: 'vertical',
|
direction: 'vertical',
|
||||||
allowSlideNext: false,
|
allowSlideNext: false,
|
||||||
allowSlidePrev: true,
|
allowSlidePrev: true,
|
||||||
@ -37,14 +48,25 @@ export const SliderLayout: React.FC<LayoutProps> = (props) => {
|
|||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
background: design.colors.background,
|
background: design.colors.background,
|
||||||
|
overflow: 'hidden',
|
||||||
|
height: '100vh',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Omf />
|
<Omf />
|
||||||
{/* eslint-disable-next-line @typescript-eslint/no-unsafe-member-access */}
|
{/* eslint-disable-next-line @typescript-eslint/no-unsafe-member-access */}
|
||||||
<Swiper {...swiperConfig} ref={(element) => element && setSwiper((element as any).swiper)}>
|
<Swiper
|
||||||
|
height={height}
|
||||||
|
{...swiperConfig}
|
||||||
|
onSwiper={next => {
|
||||||
|
logger('setSwiper')
|
||||||
|
setSwiper(next)
|
||||||
|
}}
|
||||||
|
>
|
||||||
{[
|
{[
|
||||||
startPage.show ? (
|
startPage.show ? (
|
||||||
<FormPage key={'start'} page={startPage} design={design} next={goNext} prev={goPrev} />
|
<SwiperSlide key={'start'}>
|
||||||
|
<FormPage page={startPage} design={design} next={goNext} prev={goPrev} />
|
||||||
|
</SwiperSlide>
|
||||||
) : undefined,
|
) : undefined,
|
||||||
...fields
|
...fields
|
||||||
.map((field, i) => {
|
.map((field, i) => {
|
||||||
@ -53,15 +75,15 @@ export const SliderLayout: React.FC<LayoutProps> = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<SwiperSlide key={field.id}>
|
||||||
<Field
|
<Field
|
||||||
key={field.id}
|
|
||||||
field={field}
|
field={field}
|
||||||
design={design}
|
design={design}
|
||||||
save={async (values: { [key: string]: unknown }) => {
|
save={async (values: { [key: string]: unknown }) => {
|
||||||
await setField(field.id, values[field.id])
|
await setField(field.id, values[field.id])
|
||||||
|
|
||||||
if (fields.length === i + 1) {
|
if (fields.length === i + 1) {
|
||||||
finish()
|
await finish()
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
next={() => {
|
next={() => {
|
||||||
@ -84,11 +106,14 @@ export const SliderLayout: React.FC<LayoutProps> = (props) => {
|
|||||||
}}
|
}}
|
||||||
prev={goPrev}
|
prev={goPrev}
|
||||||
/>
|
/>
|
||||||
|
</SwiperSlide>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.filter((e) => e !== null),
|
.filter((e) => e !== null),
|
||||||
endPage.show ? (
|
endPage.show ? (
|
||||||
<FormPage key={'end'} page={endPage} design={design} next={finish} prev={goPrev} />
|
<SwiperSlide key={'end'}>
|
||||||
|
<FormPage page={endPage} design={design} next={finish} prev={goPrev} />
|
||||||
|
</SwiperSlide>
|
||||||
) : undefined,
|
) : undefined,
|
||||||
].filter((e) => !!e)}
|
].filter((e) => !!e)}
|
||||||
</Swiper>
|
</Swiper>
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
.main {
|
.main {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
|||||||
@ -32,7 +32,7 @@ export const FormPage: React.FC<Props> = ({ page, design, next, prev, className,
|
|||||||
<StyledH1 design={design} type={'question'}>
|
<StyledH1 design={design} type={'question'}>
|
||||||
{page.title}
|
{page.title}
|
||||||
</StyledH1>
|
</StyledH1>
|
||||||
<StyledMarkdown design={design} type={'question'} source={page.paragraph} />
|
<StyledMarkdown design={design} type={'question'}>{page.paragraph}</StyledMarkdown>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
|
|||||||
@ -1,11 +1,13 @@
|
|||||||
import { CaretDownOutlined, UserOutlined } from '@ant-design/icons'
|
import { CaretDownOutlined, UserOutlined } from '@ant-design/icons'
|
||||||
import { MenuFoldOutlined, MenuUnfoldOutlined } from '@ant-design/icons/lib'
|
import { MenuFoldOutlined, MenuUnfoldOutlined } from '@ant-design/icons/lib'
|
||||||
import { Alert, Dropdown, Layout, Menu, PageHeader, Select, Space, Spin, Tag } from 'antd'
|
import { Alert, Dropdown, Layout, Menu, PageHeader, Select, Space, Spin, Tag } from 'antd'
|
||||||
|
import Image from 'next/image'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import React, { CSSProperties, FunctionComponent } from 'react'
|
import React, { CSSProperties, FunctionComponent } from 'react'
|
||||||
import GitHubButton from 'react-github-button'
|
import GitHubButton from 'react-github-button'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import LogoWhitePng from '../assets/images/logo_white.png'
|
||||||
import { useMeQuery } from '../graphql/query/me.query'
|
import { useMeQuery } from '../graphql/query/me.query'
|
||||||
import { languages } from '../i18n'
|
import { languages } from '../i18n'
|
||||||
import { sideMenu, SideMenuElement } from './sidemenu'
|
import { sideMenu, SideMenuElement } from './sidemenu'
|
||||||
@ -86,7 +88,7 @@ export const Structure: FunctionComponent<Props> = (props) => {
|
|||||||
<ItemGroup
|
<ItemGroup
|
||||||
key={element.key}
|
key={element.key}
|
||||||
title={
|
title={
|
||||||
<div
|
<Space
|
||||||
style={{
|
style={{
|
||||||
textTransform: 'uppercase',
|
textTransform: 'uppercase',
|
||||||
paddingTop: 16,
|
paddingTop: 16,
|
||||||
@ -95,8 +97,10 @@ export const Structure: FunctionComponent<Props> = (props) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{element.icon}
|
{element.icon}
|
||||||
|
<div>
|
||||||
{t(element.name)}
|
{t(element.name)}
|
||||||
</div>
|
</div>
|
||||||
|
</Space>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{buildMenu(element.items)}
|
{buildMenu(element.items)}
|
||||||
@ -108,10 +112,12 @@ export const Structure: FunctionComponent<Props> = (props) => {
|
|||||||
<SubMenu
|
<SubMenu
|
||||||
key={element.key}
|
key={element.key}
|
||||||
title={
|
title={
|
||||||
<span>
|
<Space>
|
||||||
{element.icon}
|
{element.icon}
|
||||||
|
<div>
|
||||||
{t(element.name)}
|
{t(element.name)}
|
||||||
</span>
|
</div>
|
||||||
|
</Space>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{buildMenu(element.items)}
|
{buildMenu(element.items)}
|
||||||
@ -128,8 +134,12 @@ export const Structure: FunctionComponent<Props> = (props) => {
|
|||||||
}}
|
}}
|
||||||
key={element.key}
|
key={element.key}
|
||||||
>
|
>
|
||||||
|
<Space>
|
||||||
{element.icon}
|
{element.icon}
|
||||||
|
<div>
|
||||||
{t(element.name)}
|
{t(element.name)}
|
||||||
|
</div>
|
||||||
|
</Space>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -148,7 +158,7 @@ export const Structure: FunctionComponent<Props> = (props) => {
|
|||||||
paddingLeft: 0,
|
paddingLeft: 0,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<Space
|
||||||
style={{
|
style={{
|
||||||
float: 'left',
|
float: 'left',
|
||||||
color: '#FFF',
|
color: '#FFF',
|
||||||
@ -162,13 +172,18 @@ export const Structure: FunctionComponent<Props> = (props) => {
|
|||||||
onClick: () => setSidebar(!sidebar),
|
onClick: () => setSidebar(!sidebar),
|
||||||
})}
|
})}
|
||||||
|
|
||||||
<img
|
<div style={{
|
||||||
src={require('assets/images/logo_white_small.png') as string}
|
display: 'flex',
|
||||||
height={30}
|
alignItems: 'center',
|
||||||
style={{ marginRight: 16 }}
|
}}>
|
||||||
|
<Image
|
||||||
|
src={LogoWhitePng.src}
|
||||||
|
width={1608 / 12}
|
||||||
|
height={530 / 12}
|
||||||
alt={'OhMyForm'}
|
alt={'OhMyForm'}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</Space>
|
||||||
<div style={{ float: 'right', display: 'flex', height: '100%' }}>
|
<div style={{ float: 'right', display: 'flex', height: '100%' }}>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
overlay={
|
overlay={
|
||||||
@ -218,9 +233,9 @@ export const Structure: FunctionComponent<Props> = (props) => {
|
|||||||
style={{ flex: 1 }}
|
style={{ flex: 1 }}
|
||||||
defaultSelectedKeys={['1']}
|
defaultSelectedKeys={['1']}
|
||||||
selectedKeys={selected}
|
selectedKeys={selected}
|
||||||
onSelect={(s): void => setSelected(s.keyPath as string[])}
|
onSelect={(s): void => setSelected(s.keyPath )}
|
||||||
openKeys={open}
|
openKeys={open}
|
||||||
onOpenChange={(open): void => setOpen(open as string[])}
|
onOpenChange={(open): void => setOpen(open )}
|
||||||
>
|
>
|
||||||
{buildMenu(sideMenu)}
|
{buildMenu(sideMenu)}
|
||||||
</Menu>
|
</Menu>
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import { Button } from 'antd'
|
import { Button } from 'antd'
|
||||||
import { ButtonProps } from 'antd/lib/button/button'
|
import { ButtonProps } from 'antd/lib/button/button'
|
||||||
|
import { darken, lighten } from 'polished'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
import { darken, lighten } from './color.change'
|
|
||||||
|
|
||||||
interface Props extends ButtonProps {
|
interface Props extends ButtonProps {
|
||||||
background: string
|
background: string
|
||||||
@ -14,12 +14,12 @@ interface Props extends ButtonProps {
|
|||||||
const Styled = styled(Button)`
|
const Styled = styled(Button)`
|
||||||
background: ${(props: Props) => props.background};
|
background: ${(props: Props) => props.background};
|
||||||
color: ${(props: Props) => props.color};
|
color: ${(props: Props) => props.color};
|
||||||
border-color: ${(props: Props) => darken(props.background, 10)};
|
border-color: ${(props: Props) => darken(0.1, props.background)};
|
||||||
|
|
||||||
:hover {
|
:hover {
|
||||||
color: ${(props: Props) => props.highlight};
|
color: ${(props: Props) => props.highlight};
|
||||||
background-color: ${(props: Props) => lighten(props.background, 10)};
|
background-color: ${(props: Props) => lighten(0.1, props.background)};
|
||||||
border-color: ${(props: Props) => darken(props.highlight, 10)};
|
border-color: ${(props: Props) => darken(0.1, props.highlight)};
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|||||||
@ -1,61 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
/**
|
|
||||||
* @link https://css-tricks.com/snippets/javascript/lighten-darken-color/
|
|
||||||
*
|
|
||||||
* @author Chris Coyier
|
|
||||||
*/
|
|
||||||
function LightenDarkenColor(col, amt): string {
|
|
||||||
let usePound = false
|
|
||||||
|
|
||||||
if (col[0] == '#') {
|
|
||||||
col = col.slice(1)
|
|
||||||
usePound = true
|
|
||||||
}
|
|
||||||
|
|
||||||
const num = parseInt(col, 16)
|
|
||||||
|
|
||||||
let r = (num >> 16) + amt
|
|
||||||
|
|
||||||
if (r > 255) r = 255
|
|
||||||
else if (r < 0) r = 0
|
|
||||||
|
|
||||||
let b = ((num >> 8) & 0x00ff) + amt
|
|
||||||
|
|
||||||
if (b > 255) b = 255
|
|
||||||
else if (b < 0) b = 0
|
|
||||||
|
|
||||||
let g = (num & 0x0000ff) + amt
|
|
||||||
|
|
||||||
if (g > 255) g = 255
|
|
||||||
else if (g < 0) g = 0
|
|
||||||
|
|
||||||
return (usePound ? '#' : '') + (g | (b << 8) | (r << 16)).toString(16)
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
if (r > 255) r = 255
|
|
||||||
else if (r < 0) r = 0
|
|
||||||
|
|
||||||
let b = ((num >> 8) & 0x00ff) + amt
|
|
||||||
|
|
||||||
if (b > 255) b = 255
|
|
||||||
else if (b < 0) b = 0
|
|
||||||
|
|
||||||
let g = (num & 0x0000ff) + amt
|
|
||||||
|
|
||||||
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): string => LightenDarkenColor(color, amount)
|
|
||||||
export const darken = (color: string, amount: number): string => LightenDarkenColor(color, -amount)
|
|
||||||
@ -1,10 +1,10 @@
|
|||||||
import { DatePicker } from 'antd'
|
import { DatePicker } from 'antd'
|
||||||
import { PickerProps } from 'antd/lib/date-picker/generatePicker'
|
import { PickerProps } from 'antd/lib/date-picker/generatePicker'
|
||||||
import { Moment } from 'moment'
|
import { Moment } from 'moment'
|
||||||
|
import { transparentize } from 'polished'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
import { FormPublicDesignFragment } from '../../graphql/fragment/form.public.fragment'
|
import { FormPublicDesignFragment } from '../../graphql/fragment/form.public.fragment'
|
||||||
import { transparentize } from './color.change'
|
|
||||||
|
|
||||||
type Props = { design: FormPublicDesignFragment } & PickerProps<Moment>
|
type Props = { design: FormPublicDesignFragment } & PickerProps<Moment>
|
||||||
|
|
||||||
@ -36,7 +36,7 @@ const Field = styled(DatePicker)`
|
|||||||
color: ${(props: Props) => props.design.colors.answer};
|
color: ${(props: Props) => props.design.colors.answer};
|
||||||
|
|
||||||
::placeholder {
|
::placeholder {
|
||||||
color: ${(props: Props) => transparentize(props.design.colors.answer, 60)};
|
color: ${(props: Props) => transparentize(0.6, props.design.colors.answer)};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import { Input } from 'antd'
|
import { Input } from 'antd'
|
||||||
import { InputProps } from 'antd/lib/input/Input'
|
import { InputProps } from 'antd/lib/input/Input'
|
||||||
|
import { transparentize } from 'polished'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
import { FormPublicDesignFragment } from '../../graphql/fragment/form.public.fragment'
|
import { FormPublicDesignFragment } from '../../graphql/fragment/form.public.fragment'
|
||||||
import { transparentize } from './color.change'
|
|
||||||
|
|
||||||
interface Props extends InputProps {
|
interface Props extends InputProps {
|
||||||
design: FormPublicDesignFragment
|
design: FormPublicDesignFragment
|
||||||
@ -37,7 +37,7 @@ const Field = styled(Input)`
|
|||||||
color: ${(props: Props) => props.design.colors.answer};
|
color: ${(props: Props) => props.design.colors.answer};
|
||||||
|
|
||||||
::placeholder {
|
::placeholder {
|
||||||
color: ${(props: Props) => transparentize(props.design.colors.answer, 60)};
|
color: ${(props: Props) => transparentize(0.6, props.design.colors.answer)};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
|
import { lighten } from 'polished'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import ReactMarkdown, { ReactMarkdownProps } from 'react-markdown'
|
import ReactMarkdown from 'react-markdown'
|
||||||
|
import { ReactMarkdownOptions } from 'react-markdown/lib/react-markdown'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
import { FormPublicDesignFragment } from '../../graphql/fragment/form.public.fragment'
|
import { FormPublicDesignFragment } from '../../graphql/fragment/form.public.fragment'
|
||||||
import { lighten } from './color.change'
|
|
||||||
|
|
||||||
interface Props extends ReactMarkdownProps {
|
interface Props extends ReactMarkdownOptions {
|
||||||
type: 'question' | 'answer'
|
type: 'question' | 'answer'
|
||||||
design: FormPublicDesignFragment
|
design: FormPublicDesignFragment
|
||||||
}
|
}
|
||||||
@ -29,7 +30,7 @@ const Markdown = styled(ReactMarkdown)`
|
|||||||
}
|
}
|
||||||
|
|
||||||
blockquote {
|
blockquote {
|
||||||
color: ${(props: Props) => lighten(getColor(props), 50)};
|
color: ${(props: Props) => lighten(0.5, getColor(props))};
|
||||||
padding-left: 20px;
|
padding-left: 20px;
|
||||||
border-left: 10px rgba(0, 0, 0, 0.05) solid;
|
border-left: 10px rgba(0, 0, 0, 0.05) solid;
|
||||||
}
|
}
|
||||||
@ -56,7 +57,7 @@ const Markdown = styled(ReactMarkdown)`
|
|||||||
|
|
||||||
export const StyledMarkdown: React.FC<Props> = ({ children, ...props }) => {
|
export const StyledMarkdown: React.FC<Props> = ({ children, ...props }) => {
|
||||||
return (
|
return (
|
||||||
<Markdown escapeHtml={false} {...props}>
|
<Markdown {...props}>
|
||||||
{children}
|
{children}
|
||||||
</Markdown>
|
</Markdown>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import { InputNumber } from 'antd'
|
import { InputNumber } from 'antd'
|
||||||
import { InputNumberProps } from 'antd/lib/input-number'
|
import { InputNumberProps } from 'antd/lib/input-number'
|
||||||
|
import { transparentize } from 'polished'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
import { FormPublicDesignFragment } from '../../graphql/fragment/form.public.fragment'
|
import { FormPublicDesignFragment } from '../../graphql/fragment/form.public.fragment'
|
||||||
import { transparentize } from './color.change'
|
|
||||||
|
|
||||||
interface Props extends InputNumberProps {
|
interface Props extends InputNumberProps {
|
||||||
design: FormPublicDesignFragment
|
design: FormPublicDesignFragment
|
||||||
@ -38,7 +38,7 @@ const Field = styled(InputNumber)`
|
|||||||
color: ${(props: Props) => props.design.colors.answer};
|
color: ${(props: Props) => props.design.colors.answer};
|
||||||
|
|
||||||
::placeholder {
|
::placeholder {
|
||||||
color: ${(props: Props) => transparentize(props.design.colors.answer, 60)};
|
color: ${(props: Props) => transparentize(0.6, props.design.colors.answer)};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import { Select } from 'antd'
|
import { Select } from 'antd'
|
||||||
import { SelectProps } from 'antd/lib/select'
|
import { SelectProps } from 'antd/lib/select'
|
||||||
|
import { transparentize } from 'polished'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
import { FormPublicDesignFragment } from '../../graphql/fragment/form.public.fragment'
|
import { FormPublicDesignFragment } from '../../graphql/fragment/form.public.fragment'
|
||||||
import { transparentize } from './color.change'
|
|
||||||
|
|
||||||
interface Props extends SelectProps<string> {
|
interface Props extends SelectProps<string> {
|
||||||
design: FormPublicDesignFragment
|
design: FormPublicDesignFragment
|
||||||
@ -36,7 +36,7 @@ const Field = styled(Select)`
|
|||||||
color: ${(props: Props) => props.design.colors.answer};
|
color: ${(props: Props) => props.design.colors.answer};
|
||||||
|
|
||||||
::placeholder {
|
::placeholder {
|
||||||
color: ${(props: Props) => transparentize(props.design.colors.answer, 60)};
|
color: ${(props: Props) => transparentize(0.6, props.design.colors.answer)};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import { Input } from 'antd'
|
import { Input } from 'antd'
|
||||||
import { TextAreaProps } from 'antd/lib/input/TextArea'
|
import { TextAreaProps } from 'antd/lib/input/TextArea'
|
||||||
|
import { transparentize } from 'polished'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
import { FormPublicDesignFragment } from '../../graphql/fragment/form.public.fragment'
|
import { FormPublicDesignFragment } from '../../graphql/fragment/form.public.fragment'
|
||||||
import { transparentize } from './color.change'
|
|
||||||
|
|
||||||
interface Props extends TextAreaProps {
|
interface Props extends TextAreaProps {
|
||||||
design: FormPublicDesignFragment
|
design: FormPublicDesignFragment
|
||||||
@ -35,7 +35,7 @@ const Field = styled(Input.TextArea)`
|
|||||||
color: ${(props: Props) => props.design.colors.answer};
|
color: ${(props: Props) => props.design.colors.answer};
|
||||||
|
|
||||||
::placeholder {
|
::placeholder {
|
||||||
color: ${(props: Props) => transparentize(props.design.colors.answer, 60)};
|
color: ${(props: Props) => transparentize(0.6, props.design.colors.answer)};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7,12 +7,10 @@ export const useImperativeQuery: <TData, TVariables>(
|
|||||||
) => (variables: TVariables) => Promise<ApolloQueryResult<TData>> = (query) => {
|
) => (variables: TVariables) => Promise<ApolloQueryResult<TData>> = (query) => {
|
||||||
const { refetch } = useQuery(query, { skip: true })
|
const { refetch } = useQuery(query, { skip: true })
|
||||||
|
|
||||||
const cb = useCallback(
|
return useCallback(
|
||||||
(variables) => {
|
(variables) => {
|
||||||
return refetch(variables)
|
return refetch(variables)
|
||||||
},
|
},
|
||||||
[refetch]
|
[refetch]
|
||||||
)
|
)
|
||||||
|
|
||||||
return cb
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
|
import debug from 'debug'
|
||||||
import { all, create } from 'mathjs'
|
import { all, create } from 'mathjs'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
|
|
||||||
|
const logger = debug('useMath')
|
||||||
|
|
||||||
export const useMath = (): ((
|
export const useMath = (): ((
|
||||||
expression: string,
|
expression: string,
|
||||||
values?: { [id: string]: string | number }
|
values?: { [id: string]: string | number }
|
||||||
@ -14,15 +17,24 @@ export const useMath = (): ((
|
|||||||
Object.keys(values).forEach((key) => {
|
Object.keys(values).forEach((key) => {
|
||||||
const r = new RegExp(key.replace('$', '\\$'), 'ig')
|
const r = new RegExp(key.replace('$', '\\$'), 'ig')
|
||||||
|
|
||||||
if (r.test(processed)) {
|
const test = r.test(processed)
|
||||||
|
|
||||||
|
if (test) {
|
||||||
processed = processed.replace(r, String(values[key]))
|
processed = processed.replace(r, String(values[key]))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const result = math.evaluate(processed)
|
return Boolean(math.evaluate(processed))
|
||||||
|
|
||||||
return Boolean(result)
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
logger(
|
||||||
|
'failed to calculate %O: %s',
|
||||||
|
{
|
||||||
|
expression,
|
||||||
|
values,
|
||||||
|
},
|
||||||
|
e.message
|
||||||
|
)
|
||||||
|
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
import { NextRouter, useRouter as useNextRouter } from 'next/router'
|
import { NextRouter, useRouter as useNextRouter } from 'next/router'
|
||||||
|
|
||||||
const parseQuery = (path) => {
|
type parseQueryResponse = { [key: string]: string }
|
||||||
|
|
||||||
|
const parseQuery = (path: string): parseQueryResponse => {
|
||||||
const query = {}
|
const query = {}
|
||||||
const regex = /[?&]([^&$=]+)(=([^&$]+))?/g
|
const regex = /[?&]([^&$=]+)(=([^&$]+))?/g
|
||||||
let param: RegExpExecArray
|
let param: RegExpExecArray
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { useMutation } from '@apollo/client'
|
import { useMutation } from '@apollo/client'
|
||||||
|
import debug from 'debug'
|
||||||
import { useCallback, useEffect, useState } from 'react'
|
import { useCallback, useEffect, useState } from 'react'
|
||||||
import {
|
import {
|
||||||
SUBMISSION_SET_FIELD_MUTATION,
|
SUBMISSION_SET_FIELD_MUTATION,
|
||||||
@ -11,6 +12,8 @@ import {
|
|||||||
SubmissionStartMutationVariables,
|
SubmissionStartMutationVariables,
|
||||||
} from '../graphql/mutation/submission.start.mutation'
|
} from '../graphql/mutation/submission.start.mutation'
|
||||||
|
|
||||||
|
const logger = debug('useSubmission')
|
||||||
|
|
||||||
export interface Submission {
|
export interface Submission {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
setField: (fieldId: string, data: unknown) => Promise<void>
|
setField: (fieldId: string, data: unknown) => Promise<void>
|
||||||
@ -44,18 +47,19 @@ export const useSubmission = (formId: string): Submission => {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.then(({ data }) => {
|
.then(({ data }) => {
|
||||||
|
logger('submission id = %O', data.submission.id)
|
||||||
setSubmission({
|
setSubmission({
|
||||||
id: data.submission.id,
|
id: data.submission.id,
|
||||||
token,
|
token,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.catch((e: Error) => console.error('failed to start submission', e))
|
.catch((e: Error) => logger('failed to start submission %J', e))
|
||||||
}, [formId])
|
}, [formId])
|
||||||
|
|
||||||
const setField = useCallback(
|
const setField = useCallback(
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
async (fieldId: string, data: any) => {
|
async (fieldId: string, data: any) => {
|
||||||
console.log('just save', fieldId, data)
|
logger('save field id=%O %O', fieldId, data)
|
||||||
await save({
|
await save({
|
||||||
variables: {
|
variables: {
|
||||||
submission: submission.id,
|
submission: submission.id,
|
||||||
@ -71,7 +75,7 @@ export const useSubmission = (formId: string): Submission => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
const finish = useCallback(async () => {
|
const finish = useCallback(async () => {
|
||||||
console.log('finish submission!!', formId)
|
logger('finish submission!!', formId)
|
||||||
|
|
||||||
await Promise.resolve()
|
await Promise.resolve()
|
||||||
}, [submission])
|
}, [submission])
|
||||||
|
|||||||
@ -48,7 +48,9 @@ export const BaseDataTab: React.FC<TabPaneProps> = (props) => {
|
|||||||
getValueFromEvent={(e) => {
|
getValueFromEvent={(e) => {
|
||||||
switch (e) {
|
switch (e) {
|
||||||
case 'superuser':
|
case 'superuser':
|
||||||
return ['user', 'admin', 'superuser']
|
return [
|
||||||
|
'user', 'admin', 'superuser',
|
||||||
|
]
|
||||||
case 'admin':
|
case 'admin':
|
||||||
return ['user', 'admin']
|
return ['user', 'admin']
|
||||||
default:
|
default:
|
||||||
@ -70,7 +72,9 @@ export const BaseDataTab: React.FC<TabPaneProps> = (props) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Select>
|
<Select>
|
||||||
{['user', 'admin', 'superuser'].map((role) => (
|
{[
|
||||||
|
'user', 'admin', 'superuser',
|
||||||
|
].map((role) => (
|
||||||
<Select.Option value={role} key={role}>
|
<Select.Option value={role} key={role}>
|
||||||
{role.toUpperCase()}
|
{role.toUpperCase()}
|
||||||
</Select.Option>
|
</Select.Option>
|
||||||
|
|||||||
@ -40,6 +40,7 @@ export interface FormFieldLogicFragment {
|
|||||||
|
|
||||||
export interface FormFieldFragment {
|
export interface FormFieldFragment {
|
||||||
id: string
|
id: string
|
||||||
|
idx?: number
|
||||||
title: string
|
title: string
|
||||||
slug?: string
|
slug?: string
|
||||||
type: string
|
type: string
|
||||||
@ -126,6 +127,7 @@ export const FORM_FRAGMENT = gql`
|
|||||||
|
|
||||||
fields {
|
fields {
|
||||||
id
|
id
|
||||||
|
idx
|
||||||
title
|
title
|
||||||
slug
|
slug
|
||||||
type
|
type
|
||||||
|
|||||||
@ -16,7 +16,7 @@ interface Variables {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const MUTATION = gql`
|
const MUTATION = gql`
|
||||||
mutation update($form: FormCreateInput!) {
|
mutation createForm($form: FormCreateInput!) {
|
||||||
form: createForm(form: $form) {
|
form: createForm(form: $form) {
|
||||||
...Form
|
...Form
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,7 +12,7 @@ interface Variables {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const MUTATION = gql`
|
const MUTATION = gql`
|
||||||
mutation delete($id: ID!) {
|
mutation deleteForm($id: ID!) {
|
||||||
form: deleteForm(id: $id) {
|
form: deleteForm(id: $id) {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,7 +11,7 @@ interface Variables {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const MUTATION = gql`
|
const MUTATION = gql`
|
||||||
mutation update($form: FormUpdateInput!) {
|
mutation updateForm($form: FormUpdateInput!) {
|
||||||
form: updateForm(form: $form) {
|
form: updateForm(form: $form) {
|
||||||
...Form
|
...Form
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,7 +13,7 @@ export interface LoginMutationVariables {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const LOGIN_MUTATION = gql`
|
export const LOGIN_MUTATION = gql`
|
||||||
mutation login($username: String!, $password: String!) {
|
mutation authLogin($username: String!, $password: String!) {
|
||||||
tokens: authLogin(username: $username, password: $password) {
|
tokens: authLogin(username: $username, password: $password) {
|
||||||
access: accessToken
|
access: accessToken
|
||||||
refresh: refreshToken
|
refresh: refreshToken
|
||||||
|
|||||||
@ -20,7 +20,7 @@ export interface Variables {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const MUTATION = gql`
|
export const MUTATION = gql`
|
||||||
mutation update($user: ProfileUpdateInput!) {
|
mutation updateProfile($user: ProfileUpdateInput!) {
|
||||||
form: updateProfile(user: $user) {
|
form: updateProfile(user: $user) {
|
||||||
...AdminProfile
|
...AdminProfile
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,7 +19,7 @@ interface Variables {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const MUTATION = gql`
|
const MUTATION = gql`
|
||||||
mutation register($user: UserCreateInput!) {
|
mutation authRegister($user: UserCreateInput!) {
|
||||||
tokens: authRegister(user: $user) {
|
tokens: authRegister(user: $user) {
|
||||||
access: accessToken
|
access: accessToken
|
||||||
refresh: refreshToken
|
refresh: refreshToken
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { gql } from '@apollo/client/core'
|
import { gql } from '@apollo/client/core'
|
||||||
|
|
||||||
export const SUBMISSION_FINISH_MUTATION = gql`
|
export const SUBMISSION_FINISH_MUTATION = gql`
|
||||||
mutation start($submission: ID!, $field: SubmissionSetFieldInput!) {
|
mutation submissionSetField($submission: ID!, $field: SubmissionSetFieldInput!) {
|
||||||
submission: submissionSetField(submission: $submission, field: $field) {
|
submission: submissionSetField(submission: $submission, field: $field) {
|
||||||
id
|
id
|
||||||
percentageComplete
|
percentageComplete
|
||||||
|
|||||||
@ -12,12 +12,11 @@ export interface SubmissionSetFieldMutationVariables {
|
|||||||
field: {
|
field: {
|
||||||
token: string
|
token: string
|
||||||
field: string
|
field: string
|
||||||
data: string
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SUBMISSION_SET_FIELD_MUTATION = gql`
|
export const SUBMISSION_SET_FIELD_MUTATION = gql`
|
||||||
mutation start($submission: ID!, $field: SubmissionSetFieldInput!) {
|
mutation submissionSetField($submission: ID!, $field: SubmissionSetFieldInput!) {
|
||||||
submission: submissionSetField(submission: $submission, field: $field) {
|
submission: submissionSetField(submission: $submission, field: $field) {
|
||||||
id
|
id
|
||||||
percentageComplete
|
percentageComplete
|
||||||
|
|||||||
@ -19,7 +19,7 @@ export interface SubmissionStartMutationVariables {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const SUBMISSION_START_MUTATION = gql`
|
export const SUBMISSION_START_MUTATION = gql`
|
||||||
mutation start($form: ID!, $submission: SubmissionStartInput!) {
|
mutation submissionStart($form: ID!, $submission: SubmissionStartInput!) {
|
||||||
submission: submissionStart(form: $form, submission: $submission) {
|
submission: submissionStart(form: $form, submission: $submission) {
|
||||||
id
|
id
|
||||||
percentageComplete
|
percentageComplete
|
||||||
|
|||||||
@ -12,7 +12,7 @@ interface Variables {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const MUTATION = gql`
|
const MUTATION = gql`
|
||||||
mutation delete($id: ID!) {
|
mutation deleteUser($id: ID!) {
|
||||||
form: deleteUser(id: $id) {
|
form: deleteUser(id: $id) {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,7 +11,7 @@ interface Variables {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const MUTATION = gql`
|
const MUTATION = gql`
|
||||||
mutation update($user: UserUpdateInput!) {
|
mutation updateUser($user: UserUpdateInput!) {
|
||||||
form: updateUser(user: $user) {
|
form: updateUser(user: $user) {
|
||||||
...AdminUser
|
...AdminUser
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,7 @@ interface Data {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const QUERY = gql`
|
export const QUERY = gql`
|
||||||
query profile {
|
query adminMe {
|
||||||
user: me {
|
user: me {
|
||||||
...AdminProfile
|
...AdminProfile
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,7 +15,7 @@ export interface AdminStatisticQueryData {
|
|||||||
export interface AdminStatisticQueryVariables {}
|
export interface AdminStatisticQueryVariables {}
|
||||||
|
|
||||||
export const ADMIN_STATISTIC_QUERY = gql`
|
export const ADMIN_STATISTIC_QUERY = gql`
|
||||||
query {
|
query statistics {
|
||||||
forms: getFormStatistic {
|
forms: getFormStatistic {
|
||||||
total
|
total
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,7 +10,7 @@ export interface AdminUserQueryVariables {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const ADMIN_USER_QUERY = gql`
|
export const ADMIN_USER_QUERY = gql`
|
||||||
query user($id: ID!) {
|
query getUserById($id: ID!) {
|
||||||
user: getUserById(id: $id) {
|
user: getUserById(id: $id) {
|
||||||
...AdminUser
|
...AdminUser
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,7 +18,7 @@ interface Variables {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const QUERY = gql`
|
const QUERY = gql`
|
||||||
query pager($start: Int, $limit: Int) {
|
query listForms($start: Int, $limit: Int) {
|
||||||
pager: listForms(start: $start, limit: $limit) {
|
pager: listForms(start: $start, limit: $limit) {
|
||||||
entries {
|
entries {
|
||||||
...Form
|
...Form
|
||||||
|
|||||||
@ -11,7 +11,7 @@ interface Variables {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const QUERY = gql`
|
const QUERY = gql`
|
||||||
query form($id: ID!) {
|
query getFormById($id: ID!) {
|
||||||
form: getFormById(id: $id) {
|
form: getFormById(id: $id) {
|
||||||
...Form
|
...Form
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,7 +23,7 @@ interface Variables {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const QUERY = gql`
|
const QUERY = gql`
|
||||||
query pager($form: ID!, $start: Int, $limit: Int) {
|
query listSubmissions($form: ID!, $start: Int, $limit: Int) {
|
||||||
form: getFormById(id: $form) {
|
form: getFormById(id: $form) {
|
||||||
...Form
|
...Form
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,7 +18,7 @@ interface Variables {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const QUERY = gql`
|
const QUERY = gql`
|
||||||
query pager($start: Int, $limit: Int) {
|
query listUsers($start: Int, $limit: Int) {
|
||||||
pager: listUsers(start: $start, limit: $limit) {
|
pager: listUsers(start: $start, limit: $limit) {
|
||||||
entries {
|
entries {
|
||||||
...User
|
...User
|
||||||
|
|||||||
5
next-env.d.ts
vendored
5
next-env.d.ts
vendored
@ -1,2 +1,5 @@
|
|||||||
/// <reference types="next" />
|
/// <reference types="next" />
|
||||||
/// <reference types="next/types/global" />
|
/// <reference types="next/image-types/global" />
|
||||||
|
|
||||||
|
// NOTE: This file should not be edited
|
||||||
|
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
||||||
|
|||||||
@ -1,14 +1,13 @@
|
|||||||
const withImages = require('next-images')
|
|
||||||
const p = require('./package.json')
|
const p = require('./package.json')
|
||||||
|
|
||||||
|
const environment = process.env.NODE_ENV ? process.env.NODE_ENV : 'dev';
|
||||||
const version = p.version;
|
const version = p.version;
|
||||||
|
|
||||||
module.exports = withImages({
|
module.exports = {
|
||||||
poweredByHeader: true,
|
poweredByHeader: true,
|
||||||
future: {
|
productionBrowserSourceMaps: true,
|
||||||
webpack5: true,
|
|
||||||
},
|
|
||||||
publicRuntimeConfig: {
|
publicRuntimeConfig: {
|
||||||
|
environment,
|
||||||
endpoint: process.env.ENDPOINT || '/graphql',
|
endpoint: process.env.ENDPOINT || '/graphql',
|
||||||
spa: !!process.env.SPA || false,
|
spa: !!process.env.SPA || false,
|
||||||
mainBackground: process.env.MAIN_BACKGROUND || '#8FA2A6'
|
mainBackground: process.env.MAIN_BACKGROUND || '#8FA2A6'
|
||||||
@ -19,4 +18,4 @@ module.exports = withImages({
|
|||||||
env: {
|
env: {
|
||||||
version,
|
version,
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
export interface NextConfigType {
|
export interface NextConfigType {
|
||||||
publicRuntimeConfig: {
|
publicRuntimeConfig: {
|
||||||
|
environment: string,
|
||||||
endpoint: string
|
endpoint: string
|
||||||
spa?: boolean
|
spa?: boolean
|
||||||
mainBackground?: string
|
mainBackground?: string
|
||||||
|
|||||||
86
package.json
86
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "ohmyform-react",
|
"name": "ohmyform-react",
|
||||||
"version": "1.0.0-alpha",
|
"version": "1.0.0",
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start:dev": "next dev -p 4000",
|
"start:dev": "next dev -p 4000",
|
||||||
@ -13,60 +13,60 @@
|
|||||||
"translation:missing": "cross-env TS_NODE_TRANSPILE_ONLY=true ts-node locales/missing.ts"
|
"translation:missing": "cross-env TS_NODE_TRANSPILE_ONLY=true ts-node locales/missing.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/icons": "^4.6.2",
|
"@ant-design/icons": "^4.7.0",
|
||||||
"@apollo/client": "^3.3.15",
|
"@apollo/client": "^3.5.6",
|
||||||
"@lifeomic/axios-fetch": "^2.0.0",
|
"antd": "^4.18.2",
|
||||||
"antd": "^4.15.3",
|
"axios": "^0.24.0",
|
||||||
"axios": "^0.21.1",
|
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"dayjs": "^1.10.4",
|
"dayjs": "^1.10.7",
|
||||||
"exceljs": "^4.2.1",
|
"debug": "^4.3.3",
|
||||||
"graphql": "^15.5.0",
|
"exceljs": "^4.3.0",
|
||||||
"i18next": "^19.9.2",
|
"graphql": "^15.8.0",
|
||||||
"i18next-browser-languagedetector": "^6.1.0",
|
"i18next": "^21.6.4",
|
||||||
|
"i18next-browser-languagedetector": "^6.1.2",
|
||||||
"isomorphic-fetch": "^3.0.0",
|
"isomorphic-fetch": "^3.0.0",
|
||||||
"mathjs": "^9.3.2",
|
"mathjs": "^10.0.2",
|
||||||
"next": "^10.2.0",
|
"next": "^12.0.7",
|
||||||
"next-images": "^1.7.0",
|
"next-redux-wrapper": "^7.0.5",
|
||||||
"next-redux-wrapper": "^6.0.2",
|
"polished": "^4.1.3",
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-color": "^2.19.3",
|
"react-color": "^2.19.3",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-github-button": "^0.1.11",
|
"react-github-button": "^0.1.11",
|
||||||
"react-i18next": "^11.8.15",
|
"react-i18next": "^11.15.3",
|
||||||
"react-icons": "^3.11.0",
|
"react-icons": "^4.3.1",
|
||||||
"react-id-swiper": "^4.0.0",
|
"react-id-swiper": "^4.0.0",
|
||||||
"react-markdown": "^4.3.1",
|
"react-markdown": "^7.1.2",
|
||||||
"react-redux": "^7.2.4",
|
"react-redux": "^7.2.6",
|
||||||
"redux": "^4.1.0",
|
"redux": "^4.1.2",
|
||||||
"redux-devtools-extension": "^2.13.9",
|
"redux-devtools-extension": "^2.13.9",
|
||||||
"redux-thunk": "^2.3.0",
|
"redux-thunk": "^2.4.1",
|
||||||
"sass": "^1.32.12",
|
"sass": "^1.45.2",
|
||||||
"styled-components": "^5.2.3",
|
"styled-components": "^5.3.3",
|
||||||
"swiper": "^6.5.8"
|
"swiper": "^7.4.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/axios": "^0.14.0",
|
"@types/axios": "^0.14.0",
|
||||||
"@types/lifeomic__axios-fetch": "^1.5.0",
|
"@types/mathjs": "^9.4.2",
|
||||||
"@types/mathjs": "^6.0.12",
|
"@types/node": "^16.11.17",
|
||||||
"@types/node": "^14.14.43",
|
"@types/node-fetch": "^3.0.3",
|
||||||
"@types/node-fetch": "^2.5.10",
|
|
||||||
"@types/react": "^17.0.4",
|
"@types/react": "^17.0.4",
|
||||||
"@types/styled-components": "^5.1.9",
|
"@types/styled-components": "^5.1.19",
|
||||||
"@types/swiper": "^5.4.2",
|
"@types/swiper": "^6.0.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^4.22.0",
|
"@typescript-eslint/eslint-plugin": "^5.8.1",
|
||||||
"@typescript-eslint/parser": "^4.22.0",
|
"@typescript-eslint/parser": "^5.8.1",
|
||||||
"commander": "^6.2.1",
|
"commander": "^8.3.0",
|
||||||
"eslint": "^7.25.0",
|
"eslint": "^8.6.0",
|
||||||
"eslint-config-prettier": "^6.15.0",
|
"eslint-config-prettier": "^8.3.0",
|
||||||
"eslint-plugin-jsx-a11y": "^6.4.1",
|
"eslint-plugin-jsx-a11y": "^6.5.1",
|
||||||
"eslint-plugin-prettier": "^3.4.0",
|
"eslint-plugin-prettier": "^4.0.0",
|
||||||
"eslint-plugin-react": "^7.23.2",
|
"eslint-plugin-react": "^7.28.0",
|
||||||
"eslint-plugin-react-hooks": "^4.2.0",
|
"eslint-plugin-react-hooks": "^4.3.0",
|
||||||
"glob": "^7.1.6",
|
"eslint-plugin-unused-imports": "^2.0.0",
|
||||||
|
"glob": "^7.2.0",
|
||||||
"lodash.merge": "^4.6.2",
|
"lodash.merge": "^4.6.2",
|
||||||
"prettier": "^2.1.1",
|
"prettier": "^2.5.1",
|
||||||
"ts-node": "^9.1.1",
|
"ts-node": "^10.4.0",
|
||||||
"typescript": "^4.2.4"
|
"typescript": "^4.5.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,15 +2,26 @@ import { ApolloProvider } from '@apollo/client'
|
|||||||
import 'antd/dist/antd.css'
|
import 'antd/dist/antd.css'
|
||||||
import 'assets/global.scss'
|
import 'assets/global.scss'
|
||||||
import 'assets/variables.scss'
|
import 'assets/variables.scss'
|
||||||
|
import debug from 'debug'
|
||||||
import 'i18n'
|
import 'i18n'
|
||||||
import App, { AppInitialProps } from 'next/app'
|
import getConfig from 'next/config'
|
||||||
import { AppType } from 'next/dist/next-server/lib/utils'
|
import { AppInitialProps, AppType } from 'next/dist/shared/lib/utils'
|
||||||
import Head from 'next/head'
|
import Head from 'next/head'
|
||||||
import React from 'react'
|
import React, { useEffect } from 'react'
|
||||||
import { wrapper } from 'store'
|
import { wrapper } from 'store'
|
||||||
import getClient from '../graphql/client'
|
import getClient from '../graphql/client'
|
||||||
|
import { NextConfigType } from '../next.config.type'
|
||||||
|
|
||||||
|
const { publicRuntimeConfig } = getConfig() as NextConfigType
|
||||||
|
|
||||||
|
const App: AppType = ({ Component, pageProps }) => {
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (publicRuntimeConfig.environment !== 'production') {
|
||||||
|
debug.enable('*,-micromark')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const MyApp: AppType = ({ Component, pageProps }) => {
|
|
||||||
return (
|
return (
|
||||||
<ApolloProvider client={getClient()}>
|
<ApolloProvider client={getClient()}>
|
||||||
<Head>
|
<Head>
|
||||||
@ -22,6 +33,8 @@ const MyApp: AppType = ({ Component, pageProps }) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
MyApp.getInitialProps = (context): Promise<AppInitialProps> => App.getInitialProps(context as any)
|
App.getInitialProps = (): AppInitialProps => ({
|
||||||
|
pageProps: {},
|
||||||
|
})
|
||||||
|
|
||||||
export default wrapper.withRedux(MyApp)
|
export default wrapper.withRedux(App)
|
||||||
|
|||||||
@ -34,7 +34,8 @@ const Index: NextPage = () => {
|
|||||||
return {
|
return {
|
||||||
form: {
|
form: {
|
||||||
...next.form,
|
...next.form,
|
||||||
fields: next.form.fields.map((field) => {
|
fields: next.form.fields
|
||||||
|
.map((field) => {
|
||||||
const keys: FormFieldOptionKeysFragment = {}
|
const keys: FormFieldOptionKeysFragment = {}
|
||||||
|
|
||||||
field.options.forEach((option) => {
|
field.options.forEach((option) => {
|
||||||
@ -48,7 +49,8 @@ const Index: NextPage = () => {
|
|||||||
options: field.options.filter((option) => !option.key),
|
options: field.options.filter((option) => !option.key),
|
||||||
optionKeys: keys,
|
optionKeys: keys,
|
||||||
}
|
}
|
||||||
}),
|
})
|
||||||
|
.sort((a, b) => a.idx - b.idx),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -69,7 +71,7 @@ const Index: NextPage = () => {
|
|||||||
|
|
||||||
formData.form.fields = formData.form.fields
|
formData.form.fields = formData.form.fields
|
||||||
.filter((e) => e && e.type)
|
.filter((e) => e && e.type)
|
||||||
.map(({ optionKeys, ...field }) => {
|
.map(({ optionKeys, ...field }, index) => {
|
||||||
const options = field.options
|
const options = field.options
|
||||||
|
|
||||||
if (optionKeys) {
|
if (optionKeys) {
|
||||||
@ -89,6 +91,7 @@ const Index: NextPage = () => {
|
|||||||
return {
|
return {
|
||||||
...field,
|
...field,
|
||||||
options,
|
options,
|
||||||
|
idx: index,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -2,9 +2,11 @@ import { Alert, Layout } from 'antd'
|
|||||||
import { AuthFooter } from 'components/auth/footer'
|
import { AuthFooter } from 'components/auth/footer'
|
||||||
import { GetStaticProps, NextPage } from 'next'
|
import { GetStaticProps, NextPage } from 'next'
|
||||||
import getConfig from 'next/config'
|
import getConfig from 'next/config'
|
||||||
|
import Image from 'next/image'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import React, { useEffect, useState } from 'react'
|
import React, { useEffect, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import LogoWhitePng from '../assets/images/logo_white.png'
|
||||||
import { LoadingPage } from '../components/loading.page'
|
import { LoadingPage } from '../components/loading.page'
|
||||||
import { Omf } from '../components/omf'
|
import { Omf } from '../components/omf'
|
||||||
import { useStatusQuery } from '../graphql/query/status.query'
|
import { useStatusQuery } from '../graphql/query/status.query'
|
||||||
@ -24,7 +26,9 @@ const Index: NextPage = () => {
|
|||||||
if (router.pathname !== window.location.pathname) {
|
if (router.pathname !== window.location.pathname) {
|
||||||
let href = router.asPath
|
let href = router.asPath
|
||||||
const as = router.asPath
|
const as = router.asPath
|
||||||
const possible = [/(\/form\/)[^/]+/i, /(\/admin\/forms\/)[^/]+/i, /(\/admin\/users\/)[^/]+/i]
|
const possible = [
|
||||||
|
/(\/form\/)[^/]+/i, /(\/admin\/forms\/)[^/]+/i, /(\/admin\/users\/)[^/]+/i,
|
||||||
|
]
|
||||||
|
|
||||||
possible.forEach((r) => {
|
possible.forEach((r) => {
|
||||||
if (r.test(as)) {
|
if (r.test(as)) {
|
||||||
@ -58,16 +62,22 @@ const Index: NextPage = () => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Omf />
|
<Omf />
|
||||||
<img
|
<div
|
||||||
alt={'OhMyForm'}
|
|
||||||
style={{
|
style={{
|
||||||
margin: 'auto',
|
margin: 'auto',
|
||||||
maxWidth: '90%',
|
maxWidth: '90%',
|
||||||
width: 500,
|
width: 500,
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
}}
|
}}
|
||||||
src={require('../assets/images/logo_white.png') as string}
|
>
|
||||||
|
<Image
|
||||||
|
alt={'OhMyForm'}
|
||||||
|
layout={'responsive'}
|
||||||
|
width={1608 / 4}
|
||||||
|
height={530 / 4}
|
||||||
|
src={LogoWhitePng.src}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
{status.error && (
|
{status.error && (
|
||||||
<Alert
|
<Alert
|
||||||
|
|||||||
@ -10,11 +10,13 @@ import {
|
|||||||
LoginMutationVariables,
|
LoginMutationVariables,
|
||||||
} from 'graphql/mutation/login.mutation'
|
} from 'graphql/mutation/login.mutation'
|
||||||
import { NextPage } from 'next'
|
import { NextPage } from 'next'
|
||||||
|
import Image from 'next/image'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import ReactMarkdown from 'react-markdown'
|
import ReactMarkdown from 'react-markdown'
|
||||||
|
import LogoWhitePng from '../../assets/images/logo_white.png'
|
||||||
import { Omf } from '../../components/omf'
|
import { Omf } from '../../components/omf'
|
||||||
import { useSettingsQuery } from '../../graphql/query/settings.query'
|
import { useSettingsQuery } from '../../graphql/query/settings.query'
|
||||||
import scss from './index.module.scss'
|
import scss from './index.module.scss'
|
||||||
@ -64,9 +66,7 @@ const Index: NextPage = () => {
|
|||||||
width: 400,
|
width: 400,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<img
|
<div
|
||||||
src={require('../../assets/images/logo_white_small.png') as string}
|
|
||||||
alt={'OhMyForm'}
|
|
||||||
style={{
|
style={{
|
||||||
display: 'block',
|
display: 'block',
|
||||||
width: '70%',
|
width: '70%',
|
||||||
@ -74,14 +74,21 @@ const Index: NextPage = () => {
|
|||||||
marginRight: 'auto',
|
marginRight: 'auto',
|
||||||
marginBottom: 16,
|
marginBottom: 16,
|
||||||
}}
|
}}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
src={LogoWhitePng.src}
|
||||||
|
alt={'OhMyForm'}
|
||||||
|
width={1608 / 4}
|
||||||
|
height={530 / 4}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
{data && data.loginNote.value && (
|
{data && data.loginNote.value && (
|
||||||
<Alert
|
<Alert
|
||||||
type="warning"
|
type="warning"
|
||||||
showIcon
|
showIcon
|
||||||
message={t('login:note')}
|
message={t('login:note')}
|
||||||
description={<ReactMarkdown escapeHtml={false} source={data.loginNote.value} />}
|
description={<ReactMarkdown>{data.loginNote.value}</ReactMarkdown>}
|
||||||
style={{
|
style={{
|
||||||
marginBottom: 24,
|
marginBottom: 24,
|
||||||
}}
|
}}
|
||||||
|
|||||||
@ -5,10 +5,12 @@ import { AuthLayout } from 'components/auth/layout'
|
|||||||
import { setAuth } from 'components/with.auth'
|
import { setAuth } from 'components/with.auth'
|
||||||
import { RegisterUserData, useRegisterMutation } from 'graphql/mutation/register.mutation'
|
import { RegisterUserData, useRegisterMutation } from 'graphql/mutation/register.mutation'
|
||||||
import { NextPage } from 'next'
|
import { NextPage } from 'next'
|
||||||
|
import Image from 'next/image'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import LogoWhitePng from '../assets/images/logo_white.png'
|
||||||
import { ErrorPage } from '../components/error.page'
|
import { ErrorPage } from '../components/error.page'
|
||||||
import { Omf } from '../components/omf'
|
import { Omf } from '../components/omf'
|
||||||
import { useSettingsQuery } from '../graphql/query/settings.query'
|
import { useSettingsQuery } from '../graphql/query/settings.query'
|
||||||
@ -66,9 +68,7 @@ const Register: NextPage = () => {
|
|||||||
width: 400,
|
width: 400,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<img
|
<div
|
||||||
src={require('../assets/images/logo_white_small.png') as string}
|
|
||||||
alt={'OhMyForm'}
|
|
||||||
style={{
|
style={{
|
||||||
display: 'block',
|
display: 'block',
|
||||||
width: '70%',
|
width: '70%',
|
||||||
@ -76,7 +76,14 @@ const Register: NextPage = () => {
|
|||||||
marginRight: 'auto',
|
marginRight: 'auto',
|
||||||
marginBottom: 16,
|
marginBottom: 16,
|
||||||
}}
|
}}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
src={LogoWhitePng.src}
|
||||||
|
alt={'OhMyForm'}
|
||||||
|
width={1608 / 4}
|
||||||
|
height={530 / 4}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
name="username"
|
name="username"
|
||||||
|
|||||||
425
schema.graphql
425
schema.graphql
@ -1,425 +0,0 @@
|
|||||||
# This file was generated based on ".graphqlconfig". Do not edit manually.
|
|
||||||
|
|
||||||
schema {
|
|
||||||
query: Query
|
|
||||||
mutation: Mutation
|
|
||||||
}
|
|
||||||
|
|
||||||
type AuthToken {
|
|
||||||
accessToken: String!
|
|
||||||
refreshToken: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
type Button {
|
|
||||||
action: String
|
|
||||||
activeColor: String
|
|
||||||
bgColor: String
|
|
||||||
color: String
|
|
||||||
id: ID!
|
|
||||||
text: String
|
|
||||||
url: String
|
|
||||||
}
|
|
||||||
|
|
||||||
type Colors {
|
|
||||||
answer: String!
|
|
||||||
background: String!
|
|
||||||
button: String!
|
|
||||||
buttonActive: String!
|
|
||||||
buttonText: String!
|
|
||||||
question: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
type Deleted {
|
|
||||||
id: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
type Design {
|
|
||||||
colors: Colors!
|
|
||||||
font: String
|
|
||||||
layout: String
|
|
||||||
}
|
|
||||||
|
|
||||||
type Device {
|
|
||||||
language: String
|
|
||||||
name: String!
|
|
||||||
type: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
type Form {
|
|
||||||
admin: User
|
|
||||||
created: DateTime!
|
|
||||||
design: Design!
|
|
||||||
endPage: Page!
|
|
||||||
fields: [FormField!]!
|
|
||||||
hooks: [FormHook!]!
|
|
||||||
id: ID!
|
|
||||||
isLive: Boolean!
|
|
||||||
language: String!
|
|
||||||
lastModified: DateTime
|
|
||||||
notifications: [FormNotification!]!
|
|
||||||
showFooter: Boolean!
|
|
||||||
startPage: Page!
|
|
||||||
title: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
type FormField {
|
|
||||||
description: String!
|
|
||||||
id: ID!
|
|
||||||
logic: [FormFieldLogic!]!
|
|
||||||
options: [FormFieldOption!]!
|
|
||||||
rating: FormFieldRating
|
|
||||||
required: Boolean!
|
|
||||||
slug: String
|
|
||||||
title: String!
|
|
||||||
type: String!
|
|
||||||
value: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
type FormFieldLogic {
|
|
||||||
action: String!
|
|
||||||
disable: Boolean
|
|
||||||
enabled: Boolean!
|
|
||||||
formula: String
|
|
||||||
id: ID!
|
|
||||||
jumpTo: ID
|
|
||||||
require: Boolean
|
|
||||||
visible: Boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
type FormFieldOption {
|
|
||||||
id: ID!
|
|
||||||
key: String
|
|
||||||
title: String
|
|
||||||
value: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
type FormFieldRating {
|
|
||||||
shape: String
|
|
||||||
steps: Int
|
|
||||||
}
|
|
||||||
|
|
||||||
type FormHook {
|
|
||||||
enabled: Boolean!
|
|
||||||
format: String
|
|
||||||
id: ID!
|
|
||||||
url: String
|
|
||||||
}
|
|
||||||
|
|
||||||
type FormNotification {
|
|
||||||
enabled: Boolean!
|
|
||||||
fromEmail: String
|
|
||||||
fromField: String
|
|
||||||
htmlTemplate: String
|
|
||||||
id: ID!
|
|
||||||
subject: String
|
|
||||||
toEmail: String
|
|
||||||
toField: String
|
|
||||||
}
|
|
||||||
|
|
||||||
type FormPager {
|
|
||||||
entries: [Form!]!
|
|
||||||
limit: Int!
|
|
||||||
start: Int!
|
|
||||||
total: Int!
|
|
||||||
}
|
|
||||||
|
|
||||||
type FormStatistic {
|
|
||||||
total: Int!
|
|
||||||
}
|
|
||||||
|
|
||||||
type GeoLocation {
|
|
||||||
city: String
|
|
||||||
country: String
|
|
||||||
}
|
|
||||||
|
|
||||||
type Mutation {
|
|
||||||
authLogin(password: String!, username: String!): AuthToken!
|
|
||||||
authRegister(user: UserCreateInput!): AuthToken!
|
|
||||||
createForm(form: FormCreateInput!): Form!
|
|
||||||
deleteForm(id: ID!): Deleted!
|
|
||||||
deleteUser(id: ID!): Deleted!
|
|
||||||
submissionSetField(field: SubmissionSetFieldInput!, submission: ID!): SubmissionProgress!
|
|
||||||
submissionStart(form: ID!, submission: SubmissionStartInput!): SubmissionProgress!
|
|
||||||
updateForm(form: FormUpdateInput!): Form!
|
|
||||||
updateProfile(user: ProfileUpdateInput!): Profile!
|
|
||||||
updateUser(user: UserUpdateInput!): User!
|
|
||||||
}
|
|
||||||
|
|
||||||
type Page {
|
|
||||||
buttonText: String
|
|
||||||
buttons: [Button!]!
|
|
||||||
id: ID!
|
|
||||||
paragraph: String
|
|
||||||
show: Boolean!
|
|
||||||
title: String
|
|
||||||
}
|
|
||||||
|
|
||||||
type Profile {
|
|
||||||
created: DateTime!
|
|
||||||
email: String!
|
|
||||||
firstName: String
|
|
||||||
id: ID!
|
|
||||||
language: String!
|
|
||||||
lastModified: DateTime
|
|
||||||
lastName: String
|
|
||||||
roles: [String!]!
|
|
||||||
username: String!
|
|
||||||
verifiedEmail: Boolean!
|
|
||||||
}
|
|
||||||
|
|
||||||
type Query {
|
|
||||||
getFormById(id: ID!): Form!
|
|
||||||
getFormStatistic: FormStatistic!
|
|
||||||
getSetting(key: ID!): Setting!
|
|
||||||
getSettings: SettingPager!
|
|
||||||
getSubmissionStatistic: SubmissionStatistic!
|
|
||||||
getUserById(id: ID!): User!
|
|
||||||
getUserStatistic: UserStatistic!
|
|
||||||
listForms(limit: Int = 50, start: Int = 0): FormPager!
|
|
||||||
listSubmissions(form: ID!, limit: Int = 50, start: Int = 0): SubmissionPager!
|
|
||||||
listUsers(limit: Int = 50, start: Int = 0): UserPager!
|
|
||||||
me: Profile!
|
|
||||||
status: Version!
|
|
||||||
}
|
|
||||||
|
|
||||||
type Setting {
|
|
||||||
isFalse: Boolean!
|
|
||||||
isTrue: Boolean!
|
|
||||||
key: ID!
|
|
||||||
value: String
|
|
||||||
}
|
|
||||||
|
|
||||||
type SettingPager {
|
|
||||||
entries: [Setting!]!
|
|
||||||
limit: Int!
|
|
||||||
start: Int!
|
|
||||||
total: Int!
|
|
||||||
}
|
|
||||||
|
|
||||||
type Submission {
|
|
||||||
created: DateTime!
|
|
||||||
device: Device!
|
|
||||||
fields: [SubmissionField!]!
|
|
||||||
geoLocation: GeoLocation!
|
|
||||||
id: ID!
|
|
||||||
ipAddr: String!
|
|
||||||
lastModified: DateTime
|
|
||||||
percentageComplete: Float!
|
|
||||||
timeElapsed: Float!
|
|
||||||
}
|
|
||||||
|
|
||||||
type SubmissionField {
|
|
||||||
field: FormField
|
|
||||||
id: ID!
|
|
||||||
type: String!
|
|
||||||
value: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
type SubmissionPager {
|
|
||||||
entries: [Submission!]!
|
|
||||||
limit: Int!
|
|
||||||
start: Int!
|
|
||||||
total: Int!
|
|
||||||
}
|
|
||||||
|
|
||||||
type SubmissionProgress {
|
|
||||||
created: DateTime!
|
|
||||||
id: ID!
|
|
||||||
lastModified: DateTime
|
|
||||||
percentageComplete: Float!
|
|
||||||
timeElapsed: Float!
|
|
||||||
}
|
|
||||||
|
|
||||||
type SubmissionStatistic {
|
|
||||||
total: Int!
|
|
||||||
}
|
|
||||||
|
|
||||||
type User {
|
|
||||||
created: DateTime!
|
|
||||||
email: String!
|
|
||||||
firstName: String
|
|
||||||
id: ID!
|
|
||||||
language: String!
|
|
||||||
lastModified: DateTime
|
|
||||||
lastName: String
|
|
||||||
roles: [String!]!
|
|
||||||
username: String!
|
|
||||||
verifiedEmail: Boolean!
|
|
||||||
}
|
|
||||||
|
|
||||||
type UserPager {
|
|
||||||
entries: [User!]!
|
|
||||||
limit: Int!
|
|
||||||
start: Int!
|
|
||||||
total: Int!
|
|
||||||
}
|
|
||||||
|
|
||||||
type UserStatistic {
|
|
||||||
total: Int!
|
|
||||||
}
|
|
||||||
|
|
||||||
type Version {
|
|
||||||
version: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
input ButtonInput {
|
|
||||||
action: String
|
|
||||||
activeColor: String
|
|
||||||
bgColor: String
|
|
||||||
color: String
|
|
||||||
id: ID
|
|
||||||
text: String
|
|
||||||
url: String
|
|
||||||
}
|
|
||||||
|
|
||||||
input ColorsInput {
|
|
||||||
answer: String!
|
|
||||||
background: String!
|
|
||||||
button: String!
|
|
||||||
buttonActive: String!
|
|
||||||
buttonText: String!
|
|
||||||
question: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
input DesignInput {
|
|
||||||
colors: ColorsInput!
|
|
||||||
font: String
|
|
||||||
layout: String
|
|
||||||
}
|
|
||||||
|
|
||||||
input DeviceInput {
|
|
||||||
language: String
|
|
||||||
name: String!
|
|
||||||
type: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
input FormCreateInput {
|
|
||||||
isLive: Boolean
|
|
||||||
language: String!
|
|
||||||
layout: String
|
|
||||||
showFooter: Boolean
|
|
||||||
title: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
input FormFieldInput {
|
|
||||||
description: String!
|
|
||||||
disabled: Boolean
|
|
||||||
id: ID!
|
|
||||||
logic: [FormFieldLogicInput!]
|
|
||||||
options: [FormFieldOptionInput!]
|
|
||||||
rating: FormFieldRatingInput
|
|
||||||
required: Boolean!
|
|
||||||
slug: String
|
|
||||||
title: String!
|
|
||||||
type: String!
|
|
||||||
value: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
input FormFieldLogicInput {
|
|
||||||
action: String
|
|
||||||
disable: Boolean
|
|
||||||
enabled: Boolean
|
|
||||||
formula: String
|
|
||||||
id: ID
|
|
||||||
jumpTo: ID
|
|
||||||
require: Boolean
|
|
||||||
visible: Boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
input FormFieldOptionInput {
|
|
||||||
id: ID
|
|
||||||
key: String
|
|
||||||
title: String
|
|
||||||
value: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
input FormFieldRatingInput {
|
|
||||||
shape: String
|
|
||||||
steps: Int
|
|
||||||
}
|
|
||||||
|
|
||||||
input FormHookInput {
|
|
||||||
enabled: Boolean!
|
|
||||||
format: String
|
|
||||||
id: ID!
|
|
||||||
url: String
|
|
||||||
}
|
|
||||||
|
|
||||||
input FormNotificationInput {
|
|
||||||
enabled: Boolean!
|
|
||||||
fromEmail: String
|
|
||||||
fromField: String
|
|
||||||
htmlTemplate: String
|
|
||||||
id: ID
|
|
||||||
subject: String
|
|
||||||
toEmail: String
|
|
||||||
toField: String
|
|
||||||
}
|
|
||||||
|
|
||||||
input FormUpdateInput {
|
|
||||||
design: DesignInput
|
|
||||||
endPage: PageInput
|
|
||||||
fields: [FormFieldInput!]
|
|
||||||
hooks: [FormHookInput!]
|
|
||||||
id: ID!
|
|
||||||
isLive: Boolean
|
|
||||||
language: String
|
|
||||||
notifications: [FormNotificationInput!]
|
|
||||||
showFooter: Boolean
|
|
||||||
startPage: PageInput
|
|
||||||
title: String
|
|
||||||
}
|
|
||||||
|
|
||||||
input PageInput {
|
|
||||||
buttonText: String
|
|
||||||
buttons: [ButtonInput!]!
|
|
||||||
id: ID
|
|
||||||
paragraph: String
|
|
||||||
show: Boolean!
|
|
||||||
title: String
|
|
||||||
}
|
|
||||||
|
|
||||||
input ProfileUpdateInput {
|
|
||||||
email: String
|
|
||||||
firstName: String
|
|
||||||
id: ID!
|
|
||||||
language: String
|
|
||||||
lastName: String
|
|
||||||
password: String
|
|
||||||
username: String
|
|
||||||
}
|
|
||||||
|
|
||||||
input SubmissionSetFieldInput {
|
|
||||||
data: String!
|
|
||||||
field: ID!
|
|
||||||
token: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
input SubmissionStartInput {
|
|
||||||
device: DeviceInput!
|
|
||||||
token: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
input UserCreateInput {
|
|
||||||
email: String!
|
|
||||||
firstName: String
|
|
||||||
language: String
|
|
||||||
lastName: String
|
|
||||||
password: String!
|
|
||||||
username: String!
|
|
||||||
}
|
|
||||||
|
|
||||||
input UserUpdateInput {
|
|
||||||
email: String
|
|
||||||
firstName: String
|
|
||||||
id: ID!
|
|
||||||
language: String
|
|
||||||
lastName: String
|
|
||||||
password: String
|
|
||||||
roles: [String!]
|
|
||||||
username: String
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
"A date-time string at UTC, such as 2019-12-03T09:54:33Z, compliant with the date-time format."
|
|
||||||
scalar DateTime
|
|
||||||
@ -1,5 +1,5 @@
|
|||||||
import { createWrapper, HYDRATE, MakeStore } from 'next-redux-wrapper'
|
import { createWrapper, HYDRATE } from 'next-redux-wrapper'
|
||||||
import { AnyAction, applyMiddleware, combineReducers, createStore } from 'redux'
|
import { AnyAction, applyMiddleware, combineReducers, createStore, Store } from 'redux'
|
||||||
import { composeWithDevTools } from 'redux-devtools-extension'
|
import { composeWithDevTools } from 'redux-devtools-extension'
|
||||||
import thunkMiddleware from 'redux-thunk'
|
import thunkMiddleware from 'redux-thunk'
|
||||||
import { auth, AuthState } from './auth'
|
import { auth, AuthState } from './auth'
|
||||||
@ -21,8 +21,8 @@ const root = (state: State, action: AnyAction): State => {
|
|||||||
return combined(state, action)
|
return combined(state, action)
|
||||||
}
|
}
|
||||||
|
|
||||||
const makeStore: MakeStore<State> = () => {
|
const makeStore = () => {
|
||||||
return createStore(root, undefined, composeWithDevTools(applyMiddleware(thunkMiddleware)))
|
return createStore(root, undefined, composeWithDevTools(applyMiddleware(thunkMiddleware)))
|
||||||
}
|
}
|
||||||
|
|
||||||
export const wrapper = createWrapper<State>(makeStore, { debug: false })
|
export const wrapper = createWrapper<Store<State>>(makeStore, { debug: false })
|
||||||
|
|||||||
@ -17,7 +17,8 @@
|
|||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"isolatedModules": true,
|
"isolatedModules": true,
|
||||||
"jsx": "preserve"
|
"jsx": "preserve",
|
||||||
|
"incremental": true
|
||||||
},
|
},
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"node_modules",
|
"node_modules",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user