mirror of
https://github.com/IT4Change/ohmyform-ui.git
synced 2025-12-12 17:25:51 +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,
|
||||
project: ['./tsconfig.json'],
|
||||
},
|
||||
plugins: [
|
||||
'@typescript-eslint/eslint-plugin',
|
||||
'@typescript-eslint',
|
||||
'unused-imports'
|
||||
],
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/eslint-recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:@typescript-eslint/recommended-requiring-type-checking',
|
||||
'plugin:react/recommended',
|
||||
'plugin:jsx-a11y/recommended',
|
||||
'prettier/@typescript-eslint',
|
||||
'plugin:prettier/recommended',
|
||||
'plugin:@typescript-eslint/recommended-requiring-type-checking',
|
||||
'prettier',
|
||||
],
|
||||
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',
|
||||
'@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: {
|
||||
react: {
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -30,3 +30,4 @@ yarn-error.log*
|
||||
|
||||
# development environments
|
||||
/.idea
|
||||
schema.graphql
|
||||
|
||||
@ -16,12 +16,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
- new slider field type
|
||||
- new card layout for forms
|
||||
- field logic
|
||||
- add enviroment config
|
||||
|
||||
### Changed
|
||||
|
||||
- combined notificationts to become more versatile
|
||||
- use exported hooks for graphql
|
||||
- disable swipe gesture
|
||||
- upgrade to nextjs 12
|
||||
|
||||
### Fixed
|
||||
|
||||
|
||||
@ -30,7 +30,8 @@ WORKDIR /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
|
||||
USER ohmyform
|
||||
|
||||
@ -31,66 +31,66 @@ const AuthFooterInner: React.FC<Props> = (props) => {
|
||||
<footer className={scss.footer}>
|
||||
{props.me
|
||||
? [
|
||||
<span style={{ color: '#FFF' }} key={'user'}>
|
||||
<span style={{ color: '#FFF' }} key={'user'}>
|
||||
Hi, {props.me.username}
|
||||
</span>,
|
||||
props.me.roles.includes('admin') && (
|
||||
<Link key={'admin'} href={'/admin'}>
|
||||
<Button
|
||||
type={'link'}
|
||||
style={{
|
||||
color: '#FFF',
|
||||
}}
|
||||
>
|
||||
{t('admin')}
|
||||
</Button>
|
||||
</Link>
|
||||
),
|
||||
<Link key={'profile'} href={'/admin/profile'}>
|
||||
</span>,
|
||||
props.me.roles.includes('admin') && (
|
||||
<Link key={'admin'} href={'/admin'}>
|
||||
<Button
|
||||
type={'link'}
|
||||
style={{
|
||||
color: '#FFF',
|
||||
}}
|
||||
>
|
||||
{t('profile')}
|
||||
{t('admin')}
|
||||
</Button>
|
||||
</Link>,
|
||||
</Link>
|
||||
),
|
||||
<Link key={'profile'} href={'/admin/profile'}>
|
||||
<Button
|
||||
key={'logout'}
|
||||
type={'link'}
|
||||
onClick={logout}
|
||||
style={{
|
||||
color: '#FFF',
|
||||
}}
|
||||
>
|
||||
{t('logout')}
|
||||
</Button>,
|
||||
]
|
||||
{t('profile')}
|
||||
</Button>
|
||||
</Link>,
|
||||
<Button
|
||||
key={'logout'}
|
||||
type={'link'}
|
||||
onClick={logout}
|
||||
style={{
|
||||
color: '#FFF',
|
||||
}}
|
||||
>
|
||||
{t('logout')}
|
||||
</Button>,
|
||||
]
|
||||
: [
|
||||
<Link href={'/login'} key={'login'}>
|
||||
<Link href={'/login'} key={'login'}>
|
||||
<Button
|
||||
type={'link'}
|
||||
style={{
|
||||
color: '#FFF',
|
||||
}}
|
||||
>
|
||||
{t('login')}
|
||||
</Button>
|
||||
</Link>,
|
||||
!loading && !data?.disabledSignUp.value && (
|
||||
<Link href={'/register'} key={'register'}>
|
||||
<Button
|
||||
type={'link'}
|
||||
style={{
|
||||
color: '#FFF',
|
||||
}}
|
||||
>
|
||||
{t('login')}
|
||||
{t('register')}
|
||||
</Button>
|
||||
</Link>,
|
||||
!loading && !data?.disabledSignUp.value && (
|
||||
<Link href={'/register'} key={'register'}>
|
||||
<Button
|
||||
type={'link'}
|
||||
style={{
|
||||
color: '#FFF',
|
||||
}}
|
||||
>
|
||||
{t('register')}
|
||||
</Button>
|
||||
</Link>
|
||||
),
|
||||
]}
|
||||
</Link>
|
||||
),
|
||||
]}
|
||||
<div style={{ flex: 1 }} />
|
||||
<Select
|
||||
bordered={false}
|
||||
|
||||
@ -9,10 +9,14 @@ export const DesignTab: React.FC<TabPaneProps> = (props) => {
|
||||
|
||||
return (
|
||||
<Tabs.TabPane {...props}>
|
||||
<Form.Item label={t('form:design.font')} name={['form', 'design', 'font']}>
|
||||
<Form.Item label={t('form:design.font')} name={[
|
||||
'form', 'design', 'font',
|
||||
]}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={t('form:design.layouts')} name={['form', 'design', 'layout']}>
|
||||
<Form.Item label={t('form:design.layouts')} name={[
|
||||
'form', 'design', 'layout',
|
||||
]}>
|
||||
<Select
|
||||
options={[
|
||||
{
|
||||
@ -27,11 +31,15 @@ export const DesignTab: React.FC<TabPaneProps> = (props) => {
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
{['background', 'question', 'answer', 'button', 'buttonActive', 'buttonText'].map((name) => (
|
||||
{[
|
||||
'background', 'question', 'answer', 'button', 'buttonActive', 'buttonText',
|
||||
].map((name) => (
|
||||
<Form.Item
|
||||
key={name}
|
||||
label={t(`form:design.color.${name}`)}
|
||||
name={['form', 'design', 'colors', name]}
|
||||
name={[
|
||||
'form', 'design', 'colors', name,
|
||||
]}
|
||||
>
|
||||
<InputColor />
|
||||
</Form.Item>
|
||||
|
||||
@ -12,19 +12,25 @@ export const EndPageTab: React.FC<TabPaneProps> = (props) => {
|
||||
<Tabs.TabPane {...props}>
|
||||
<Form.Item
|
||||
label={t('form:endPage.show')}
|
||||
name={['form', 'endPage', 'show']}
|
||||
name={[
|
||||
'form', 'endPage', 'show',
|
||||
]}
|
||||
valuePropName={'checked'}
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label={t('form:endPage.title')} name={['form', 'endPage', 'title']}>
|
||||
<Form.Item label={t('form:endPage.title')} name={[
|
||||
'form', 'endPage', 'title',
|
||||
]}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label={t('form:endPage.paragraph')}
|
||||
name={['form', 'endPage', 'paragraph']}
|
||||
name={[
|
||||
'form', 'endPage', 'paragraph',
|
||||
]}
|
||||
extra={t('type:descriptionInfo')}
|
||||
>
|
||||
<Input.TextArea autoSize />
|
||||
@ -32,12 +38,16 @@ export const EndPageTab: React.FC<TabPaneProps> = (props) => {
|
||||
|
||||
<Form.Item
|
||||
label={t('form:endPage.continueButtonText')}
|
||||
name={['form', 'endPage', 'buttonText']}
|
||||
name={[
|
||||
'form', 'endPage', 'buttonText',
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
|
||||
<Form.List name={['form', 'endPage', 'buttons']}>
|
||||
<Form.List name={[
|
||||
'form', 'endPage', 'buttons',
|
||||
]}>
|
||||
{(fields, { add, remove }) => {
|
||||
return (
|
||||
<div>
|
||||
|
||||
@ -108,7 +108,9 @@ export const ExportSubmissionAction: React.FC<Props> = (props) => {
|
||||
})
|
||||
}
|
||||
setLoading(false)
|
||||
}, [form, getSubmissions, props.form, setLoading, loading])
|
||||
}, [
|
||||
form, getSubmissions, props.form, setLoading, 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 {
|
||||
Button,
|
||||
Card,
|
||||
Checkbox,
|
||||
Form,
|
||||
Input,
|
||||
Popconfirm,
|
||||
Popover,
|
||||
Space,
|
||||
Tag,
|
||||
Tooltip,
|
||||
} from 'antd'
|
||||
import { Button, Card, Checkbox, Form, Input, Popconfirm, Popover, Space, Tag, Tooltip } from 'antd'
|
||||
import { FormInstance } from 'antd/lib/form'
|
||||
import { FieldData } from 'rc-field-form/lib/interface'
|
||||
import React, { useCallback, useEffect, useState } from 'react'
|
||||
@ -26,22 +16,40 @@ interface Props {
|
||||
onChangeFields: (fields: FormFieldFragment[]) => void
|
||||
field: FieldData
|
||||
remove: (index: number) => void
|
||||
move: (from: number, to: number) => void
|
||||
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 { 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 [shouldUpdate, setShouldUpdate] = useState(false)
|
||||
const [nextTitle, setNextTitle] = useState<string>(
|
||||
form.getFieldValue(['form', 'fields', field.name as string, 'title'])
|
||||
form.getFieldValue([
|
||||
'form', 'fields', field.name as string, 'title',
|
||||
])
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (!shouldUpdate) {
|
||||
return
|
||||
}
|
||||
|
||||
const id = setTimeout(() => {
|
||||
setShouldUpdate(false)
|
||||
onChangeFields(
|
||||
fields.map((field, i) => {
|
||||
if (i === index) {
|
||||
@ -57,7 +65,9 @@ export const FieldCard: React.FC<Props> = (props) => {
|
||||
}, 500)
|
||||
|
||||
return () => clearTimeout(id)
|
||||
}, [nextTitle])
|
||||
}, [
|
||||
nextTitle, shouldUpdate, fields,
|
||||
])
|
||||
|
||||
const addLogic = useCallback((add: (defaults: unknown) => void, index: number) => {
|
||||
return (
|
||||
@ -94,14 +104,32 @@ export const FieldCard: React.FC<Props> = (props) => {
|
||||
|
||||
return (
|
||||
<Card
|
||||
title={<Tooltip title={`@${field.name as string}`}>nextTitle</Tooltip>}
|
||||
title={nextTitle}
|
||||
type={'inner'}
|
||||
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>
|
||||
{() => {
|
||||
// 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) {
|
||||
return null
|
||||
@ -145,7 +173,7 @@ export const FieldCard: React.FC<Props> = (props) => {
|
||||
<DeleteOutlined />
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
</div>
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
<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' }]}
|
||||
labelCol={{ span: 6 }}
|
||||
>
|
||||
<Input onChange={(e) => setNextTitle(e.target.value)} />
|
||||
<Input
|
||||
onChange={(e) => {
|
||||
setNextTitle(e.target.value)
|
||||
setShouldUpdate(true)
|
||||
}}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t('type:description')}
|
||||
@ -181,7 +214,7 @@ export const FieldCard: React.FC<Props> = (props) => {
|
||||
|
||||
<Form.List name={[field.name as string, 'logic']}>
|
||||
{(logic, { add, remove, move }) => {
|
||||
const addAndMove = (index) => (defaults) => {
|
||||
const addAndMove = (index: number) => (defaults) => {
|
||||
add(defaults)
|
||||
move(fields.length, index)
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ import { PlusOutlined } from '@ant-design/icons/lib'
|
||||
import { Button, Form, Select, Space, Tabs } from 'antd'
|
||||
import { FormInstance } from 'antd/lib/form'
|
||||
import { TabPaneProps } from 'antd/lib/tabs'
|
||||
import debug from 'debug'
|
||||
import { FieldData } from 'rc-field-form/lib/interface'
|
||||
import React, { useCallback, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
@ -9,6 +10,8 @@ import { FormFieldFragment } from '../../../graphql/fragment/form.fragment'
|
||||
import { FieldCard } from './field.card'
|
||||
import { adminTypes } from './types'
|
||||
|
||||
const logger = debug('FieldsTab')
|
||||
|
||||
interface Props extends TabPaneProps {
|
||||
form: FormInstance
|
||||
fields: FormFieldFragment[]
|
||||
@ -20,13 +23,25 @@ export const FieldsTab: React.FC<Props> = (props) => {
|
||||
const [nextType, setNextType] = useState('textfield')
|
||||
|
||||
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 (
|
||||
<FieldCard
|
||||
form={props.form}
|
||||
field={field}
|
||||
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}
|
||||
onChangeFields={props.onChangeFields}
|
||||
/>
|
||||
@ -85,7 +100,7 @@ export const FieldsTab: React.FC<Props> = (props) => {
|
||||
<Tabs.TabPane {...props}>
|
||||
<Form.List name={['form', 'fields']}>
|
||||
{(fields, { add, remove, move }) => {
|
||||
const addAndMove = (index) => (defaults) => {
|
||||
const addAndMove = (index: number) => (defaults) => {
|
||||
add(defaults)
|
||||
move(fields.length, index)
|
||||
}
|
||||
@ -96,7 +111,7 @@ export const FieldsTab: React.FC<Props> = (props) => {
|
||||
{fields.map((field, index) => (
|
||||
<div key={field.key}>
|
||||
<Form.Item wrapperCol={{ span: 24 }}>
|
||||
{renderType(field, index, remove)}
|
||||
{renderType(field, index, remove, move)}
|
||||
</Form.Item>
|
||||
{addField(addAndMove(index + 1), index + 1)}
|
||||
</div>
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { DeleteOutlined } from '@ant-design/icons'
|
||||
import { Alert, Button, Checkbox, Form, Mentions, Popconfirm, Select } from 'antd'
|
||||
import { FormInstance } from 'antd/lib/form'
|
||||
import FormItemContext from 'rc-field-form/lib/FieldContext'
|
||||
import { FieldData } from 'rc-field-form/lib/interface'
|
||||
import React from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
@ -16,9 +15,14 @@ interface Props {
|
||||
index: number
|
||||
}
|
||||
|
||||
export const LogicBlock: React.FC<Props> = (props) => {
|
||||
export const LogicBlock: React.FC<Props> = ({
|
||||
form,
|
||||
field,
|
||||
fields,
|
||||
remove,
|
||||
index,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const { form, field, fields, remove, index } = props
|
||||
const evaluator = useMath()
|
||||
|
||||
return (
|
||||
@ -45,9 +49,7 @@ export const LogicBlock: React.FC<Props> = (props) => {
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item noStyle shouldUpdate>
|
||||
{(form) => {
|
||||
const context = React.useContext(FormItemContext)
|
||||
|
||||
{(form: FormInstance & { prefixName: string[] }) => {
|
||||
try {
|
||||
const defaults = {}
|
||||
|
||||
@ -60,7 +62,11 @@ export const LogicBlock: React.FC<Props> = (props) => {
|
||||
})
|
||||
|
||||
const result = evaluator(
|
||||
form.getFieldValue([...context.prefixName, field.name as string, 'formula']),
|
||||
form.getFieldValue([
|
||||
...form.prefixName,
|
||||
field.name as string,
|
||||
'formula',
|
||||
]),
|
||||
defaults
|
||||
)
|
||||
|
||||
@ -109,13 +115,13 @@ export const LogicBlock: React.FC<Props> = (props) => {
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item noStyle shouldUpdate>
|
||||
{(form) => {
|
||||
const context = React.useContext(FormItemContext)
|
||||
|
||||
{(form: FormInstance & { prefixName: string[] }) => {
|
||||
return (
|
||||
<Form.Item
|
||||
hidden={
|
||||
form.getFieldValue([...context.prefixName, field.name as string, 'action']) !==
|
||||
form.getFieldValue([
|
||||
...form.prefixName, field.name as string, 'action',
|
||||
]) !==
|
||||
'jumpTo'
|
||||
}
|
||||
labelCol={{ span: 6 }}
|
||||
@ -137,13 +143,13 @@ export const LogicBlock: React.FC<Props> = (props) => {
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item noStyle shouldUpdate>
|
||||
{(form) => {
|
||||
const context = React.useContext(FormItemContext)
|
||||
|
||||
{(form: FormInstance & { prefixName: string[] }) => {
|
||||
return (
|
||||
<Form.Item
|
||||
hidden={
|
||||
form.getFieldValue([...context.prefixName, field.name as string, 'action']) !==
|
||||
form.getFieldValue([
|
||||
...form.prefixName, field.name as string, 'action',
|
||||
]) !==
|
||||
'visible'
|
||||
}
|
||||
initialValue={true}
|
||||
@ -160,13 +166,13 @@ export const LogicBlock: React.FC<Props> = (props) => {
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item noStyle shouldUpdate>
|
||||
{(form) => {
|
||||
const context = React.useContext(FormItemContext)
|
||||
|
||||
{(form: FormInstance & { prefixName: string[] }) => {
|
||||
return (
|
||||
<Form.Item
|
||||
hidden={
|
||||
form.getFieldValue([...context.prefixName, field.name as string, 'action']) !==
|
||||
form.getFieldValue([
|
||||
...form.prefixName, field.name as string, 'action',
|
||||
]) !==
|
||||
'disable'
|
||||
}
|
||||
initialValue={false}
|
||||
@ -183,13 +189,13 @@ export const LogicBlock: React.FC<Props> = (props) => {
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item noStyle shouldUpdate>
|
||||
{(form) => {
|
||||
const context = React.useContext(FormItemContext)
|
||||
|
||||
{(form: FormInstance & { prefixName: string[] }) => {
|
||||
return (
|
||||
<Form.Item
|
||||
hidden={
|
||||
form.getFieldValue([...context.prefixName, field.name as string, 'action']) !==
|
||||
form.getFieldValue([
|
||||
...form.prefixName, field.name as string, 'action',
|
||||
]) !==
|
||||
'require'
|
||||
}
|
||||
initialValue={true}
|
||||
|
||||
@ -62,7 +62,9 @@ export const NotificationCard: React.FC<Props> = (props) => {
|
||||
rules={[
|
||||
{
|
||||
required: Boolean(
|
||||
form.getFieldValue(['form', 'notifications', field.name as string, 'enabled'])
|
||||
form.getFieldValue([
|
||||
'form', 'notifications', field.name as string, 'enabled',
|
||||
])
|
||||
),
|
||||
message: t('validation:subjectRequired'),
|
||||
},
|
||||
@ -82,7 +84,9 @@ export const NotificationCard: React.FC<Props> = (props) => {
|
||||
rules={[
|
||||
{
|
||||
required: Boolean(
|
||||
form.getFieldValue(['form', 'notifications', field.name as string, 'enabled'])
|
||||
form.getFieldValue([
|
||||
'form', 'notifications', field.name as string, 'enabled',
|
||||
])
|
||||
),
|
||||
message: t('validation:templateRequired'),
|
||||
},
|
||||
@ -119,7 +123,9 @@ export const NotificationCard: React.FC<Props> = (props) => {
|
||||
rules={[
|
||||
{
|
||||
required: Boolean(
|
||||
form.getFieldValue(['form', 'notifications', field.name as string, 'enabled']) &&
|
||||
form.getFieldValue([
|
||||
'form', 'notifications', field.name as string, 'enabled',
|
||||
]) &&
|
||||
!form.getFieldValue([
|
||||
'form',
|
||||
'notifications',
|
||||
@ -156,7 +162,9 @@ export const NotificationCard: React.FC<Props> = (props) => {
|
||||
rules={[
|
||||
{
|
||||
required: Boolean(
|
||||
form.getFieldValue(['form', 'notifications', field.name as string, 'enabled']) &&
|
||||
form.getFieldValue([
|
||||
'form', 'notifications', field.name as string, 'enabled',
|
||||
]) &&
|
||||
!form.getFieldValue([
|
||||
'form',
|
||||
'notifications',
|
||||
@ -182,8 +190,12 @@ export const NotificationCard: React.FC<Props> = (props) => {
|
||||
rules={[
|
||||
{
|
||||
required: Boolean(
|
||||
form.getFieldValue(['form', 'notifications', field.name as string, 'enabled']) &&
|
||||
!form.getFieldValue(['form', 'notifications', field.name as string, 'toEmail'])
|
||||
form.getFieldValue([
|
||||
'form', 'notifications', field.name as string, 'enabled',
|
||||
]) &&
|
||||
!form.getFieldValue([
|
||||
'form', 'notifications', field.name as string, 'toEmail',
|
||||
])
|
||||
),
|
||||
message: t('validation:emailFieldRequired'),
|
||||
},
|
||||
@ -215,8 +227,12 @@ export const NotificationCard: React.FC<Props> = (props) => {
|
||||
rules={[
|
||||
{
|
||||
required: Boolean(
|
||||
form.getFieldValue(['form', 'notifications', field.name as string, 'enabled']) &&
|
||||
!form.getFieldValue(['form', 'notifications', field.name as string, 'toField'])
|
||||
form.getFieldValue([
|
||||
'form', 'notifications', field.name as string, 'enabled',
|
||||
]) &&
|
||||
!form.getFieldValue([
|
||||
'form', 'notifications', field.name as string, 'toField',
|
||||
])
|
||||
),
|
||||
message: t('validation:emailFieldRequired'),
|
||||
},
|
||||
|
||||
@ -62,7 +62,7 @@ export const NotificationsTab: React.FC<Props> = (props) => {
|
||||
<Tabs.TabPane {...props}>
|
||||
<Form.List name={['form', 'notifications']}>
|
||||
{(fields, { add, remove, move }) => {
|
||||
const addAndMove = (index) => (defaults) => {
|
||||
const addAndMove = (index: number) => (defaults) => {
|
||||
add(defaults)
|
||||
move(fields.length, index)
|
||||
}
|
||||
|
||||
@ -12,19 +12,25 @@ export const StartPageTab: React.FC<TabPaneProps> = (props) => {
|
||||
<Tabs.TabPane {...props}>
|
||||
<Form.Item
|
||||
label={t('form:startPage.show')}
|
||||
name={['form', 'startPage', 'show']}
|
||||
name={[
|
||||
'form', 'startPage', 'show',
|
||||
]}
|
||||
valuePropName={'checked'}
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label={t('form:startPage.title')} name={['form', 'startPage', 'title']}>
|
||||
<Form.Item label={t('form:startPage.title')} name={[
|
||||
'form', 'startPage', 'title',
|
||||
]}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label={t('form:startPage.paragraph')}
|
||||
name={['form', 'startPage', 'paragraph']}
|
||||
name={[
|
||||
'form', 'startPage', 'paragraph',
|
||||
]}
|
||||
extra={t('form:startPage.paragraphInfo')}
|
||||
>
|
||||
<Input.TextArea autoSize />
|
||||
@ -32,12 +38,16 @@ export const StartPageTab: React.FC<TabPaneProps> = (props) => {
|
||||
|
||||
<Form.Item
|
||||
label={t('form:startPage.continueButtonText')}
|
||||
name={['form', 'startPage', 'buttonText']}
|
||||
name={[
|
||||
'form', 'startPage', 'buttonText',
|
||||
]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
|
||||
<Form.List name={['form', 'startPage', 'buttons']}>
|
||||
<Form.List name={[
|
||||
'form', 'startPage', 'buttons',
|
||||
]}>
|
||||
{(fields, { add, remove }) => {
|
||||
return (
|
||||
<div>
|
||||
|
||||
@ -33,8 +33,6 @@ export const SubmissionValues: React.FC<Props> = (props) => {
|
||||
try {
|
||||
const data = JSON.parse(row.value) as { value: string }
|
||||
|
||||
console.log('DATA', data)
|
||||
|
||||
return data.value
|
||||
} catch (e) {
|
||||
return row.value
|
||||
|
||||
@ -20,7 +20,9 @@ export const DateType: React.FC<AdminFieldTypeProps> = ({ field }) => {
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
label={t('type:date.min')}
|
||||
name={[field.name as string, 'optionKeys', 'min']}
|
||||
name={[
|
||||
field.name as string, 'optionKeys', 'min',
|
||||
]}
|
||||
labelCol={{ span: 6 }}
|
||||
getValueFromEvent={(e: Moment) => e.format('YYYY-MM-DD')}
|
||||
getValueProps={(e: string) => ({ value: e ? moment(e) : undefined })}
|
||||
@ -30,7 +32,9 @@ export const DateType: React.FC<AdminFieldTypeProps> = ({ field }) => {
|
||||
|
||||
<Form.Item
|
||||
label={t('type:date.max')}
|
||||
name={[field.name as string, 'optionKeys', 'max']}
|
||||
name={[
|
||||
field.name as string, 'optionKeys', 'max',
|
||||
]}
|
||||
labelCol={{ span: 6 }}
|
||||
getValueFromEvent={(e: Moment) => e.format('YYYY-MM-DD')}
|
||||
getValueProps={(e: string) => ({ value: e ? moment(e) : undefined })}
|
||||
|
||||
@ -50,7 +50,9 @@ export const SliderType: React.FC<AdminFieldTypeProps> = (props) => {
|
||||
|
||||
<Form.Item
|
||||
label={t('type:slider.min')}
|
||||
name={[props.field.name as string, 'optionKeys', 'min']}
|
||||
name={[
|
||||
props.field.name as string, 'optionKeys', 'min',
|
||||
]}
|
||||
labelCol={{ span: 6 }}
|
||||
initialValue={0}
|
||||
getValueFromEvent={(value: number) =>
|
||||
@ -63,7 +65,9 @@ export const SliderType: React.FC<AdminFieldTypeProps> = (props) => {
|
||||
|
||||
<Form.Item
|
||||
label={t('type:slider.max')}
|
||||
name={[props.field.name as string, 'optionKeys', 'max']}
|
||||
name={[
|
||||
props.field.name as string, 'optionKeys', 'max',
|
||||
]}
|
||||
labelCol={{ span: 6 }}
|
||||
initialValue={100}
|
||||
getValueFromEvent={(value: number) =>
|
||||
@ -76,7 +80,9 @@ export const SliderType: React.FC<AdminFieldTypeProps> = (props) => {
|
||||
|
||||
<Form.Item
|
||||
label={t('type:slider.step')}
|
||||
name={[props.field.name as string, 'optionKeys', 'step']}
|
||||
name={[
|
||||
props.field.name as string, 'optionKeys', 'step',
|
||||
]}
|
||||
labelCol={{ span: 6 }}
|
||||
initialValue={1}
|
||||
getValueFromEvent={(value: number) =>
|
||||
|
||||
@ -54,7 +54,7 @@ export const Field: React.FC<Props> = ({ field, design, focus, ...props }) => {
|
||||
{field.title}
|
||||
</StyledH1>
|
||||
{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} />
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
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 styled from 'styled-components'
|
||||
import { FormPublicFieldFragment } from '../../../../graphql/fragment/form.public.fragment'
|
||||
import { Omf } from '../../../omf'
|
||||
import { StyledButton } from '../../../styled/button'
|
||||
import { darken, lighten } from '../../../styled/color.change'
|
||||
import { useMath } from '../../../use.math'
|
||||
import { LayoutProps } from '../layout.props'
|
||||
import { Field } from './field'
|
||||
import { Page } from './page'
|
||||
@ -12,7 +14,7 @@ import { Page } from './page'
|
||||
type Step = 'start' | 'form' | 'end'
|
||||
|
||||
const MyCard = styled.div<{ background: string }>`
|
||||
background: ${(props) => darken(props.background, 10)};
|
||||
background: ${(props) => darken(0.1, props.background)};
|
||||
height: 100%;
|
||||
min-height: 100vh;
|
||||
|
||||
@ -20,7 +22,7 @@ const MyCard = styled.div<{ background: string }>`
|
||||
|
||||
.ant-card {
|
||||
background: ${(props) => props.background};
|
||||
border-color: ${(props) => lighten(props.background, 40)};
|
||||
border-color: ${(props) => lighten(0.4, props.background)};
|
||||
width: 800px;
|
||||
margin: auto;
|
||||
max-width: 90%;
|
||||
@ -32,6 +34,8 @@ export const CardLayout: React.FC<LayoutProps> = (props) => {
|
||||
const [form] = Form.useForm()
|
||||
const [loading, setLoading] = useState(false)
|
||||
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 { setField } = props.submission
|
||||
@ -67,6 +71,31 @@ export const CardLayout: React.FC<LayoutProps> = (props) => {
|
||||
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 = () => {
|
||||
switch (step) {
|
||||
case 'start':
|
||||
@ -75,12 +104,32 @@ export const CardLayout: React.FC<LayoutProps> = (props) => {
|
||||
case 'form':
|
||||
return (
|
||||
<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) => {
|
||||
if (field.type === 'hidden') {
|
||||
return null
|
||||
}
|
||||
|
||||
if (!isVisible(field)) {
|
||||
return null
|
||||
}
|
||||
|
||||
return <Field key={field.id} field={field} design={design} focus={i === 0} />
|
||||
})}
|
||||
<div
|
||||
|
||||
@ -26,7 +26,7 @@ export const Page: React.FC<Props> = ({ design, page, next, prev }) => {
|
||||
<StyledH1 design={design} type={'question'}>
|
||||
{page.title}
|
||||
</StyledH1>
|
||||
<StyledMarkdown design={design} type={'question'} source={page.paragraph} />
|
||||
<StyledMarkdown design={design} type={'question'}>{page.paragraph}</StyledMarkdown>
|
||||
|
||||
<div
|
||||
style={{
|
||||
|
||||
@ -62,6 +62,7 @@ export const Field: React.FC<Props> = ({ field, save, design, next, prev, ...pro
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
height: '100%',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
@ -77,7 +78,7 @@ export const Field: React.FC<Props> = ({ field, save, design, next, prev, ...pro
|
||||
{field.title}
|
||||
</StyledH1>
|
||||
{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()} />
|
||||
|
||||
@ -1,31 +1,42 @@
|
||||
import { Modal } from 'antd'
|
||||
import debug from 'debug'
|
||||
import React, { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Swiper from 'react-id-swiper'
|
||||
import { ReactIdSwiperProps } from 'react-id-swiper/lib/types'
|
||||
import * as OriginalSwiper from 'swiper'
|
||||
import SwiperClass from 'swiper'
|
||||
import { Swiper, SwiperProps, SwiperSlide } from 'swiper/react'
|
||||
import { Omf } from '../../../omf'
|
||||
import { useWindowSize } from '../../../use.window.size'
|
||||
import { LayoutProps } from '../layout.props'
|
||||
import { Field } from './field'
|
||||
import { FormPage } from './page'
|
||||
|
||||
const logger = debug('layout/slider')
|
||||
|
||||
export const SliderLayout: React.FC<LayoutProps> = (props) => {
|
||||
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 { finish, setField } = props.submission
|
||||
|
||||
const goNext = () => {
|
||||
if (!swiper) return
|
||||
|
||||
logger('goNext')
|
||||
swiper.allowSlideNext = true
|
||||
swiper.slideNext()
|
||||
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',
|
||||
allowSlideNext: false,
|
||||
allowSlidePrev: true,
|
||||
@ -37,14 +48,25 @@ export const SliderLayout: React.FC<LayoutProps> = (props) => {
|
||||
<div
|
||||
style={{
|
||||
background: design.colors.background,
|
||||
overflow: 'hidden',
|
||||
height: '100vh',
|
||||
}}
|
||||
>
|
||||
<Omf />
|
||||
{/* 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 ? (
|
||||
<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,
|
||||
...fields
|
||||
.map((field, i) => {
|
||||
@ -53,42 +75,45 @@ export const SliderLayout: React.FC<LayoutProps> = (props) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<Field
|
||||
key={field.id}
|
||||
field={field}
|
||||
design={design}
|
||||
save={async (values: { [key: string]: unknown }) => {
|
||||
await setField(field.id, values[field.id])
|
||||
<SwiperSlide key={field.id}>
|
||||
<Field
|
||||
field={field}
|
||||
design={design}
|
||||
save={async (values: { [key: string]: unknown }) => {
|
||||
await setField(field.id, values[field.id])
|
||||
|
||||
if (fields.length === i + 1) {
|
||||
finish()
|
||||
}
|
||||
}}
|
||||
next={() => {
|
||||
if (fields.length === i + 1) {
|
||||
// prevent going back!
|
||||
swiper.allowSlidePrev = true
|
||||
|
||||
if (!endPage.show) {
|
||||
Modal.success({
|
||||
content: t('form:submitted'),
|
||||
okText: t('from:restart'),
|
||||
onOk: () => {
|
||||
window.location.reload()
|
||||
},
|
||||
})
|
||||
if (fields.length === i + 1) {
|
||||
await finish()
|
||||
}
|
||||
}
|
||||
}}
|
||||
next={() => {
|
||||
if (fields.length === i + 1) {
|
||||
// prevent going back!
|
||||
swiper.allowSlidePrev = true
|
||||
|
||||
goNext()
|
||||
}}
|
||||
prev={goPrev}
|
||||
/>
|
||||
if (!endPage.show) {
|
||||
Modal.success({
|
||||
content: t('form:submitted'),
|
||||
okText: t('from:restart'),
|
||||
onOk: () => {
|
||||
window.location.reload()
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
goNext()
|
||||
}}
|
||||
prev={goPrev}
|
||||
/>
|
||||
</SwiperSlide>
|
||||
)
|
||||
})
|
||||
.filter((e) => e !== null),
|
||||
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,
|
||||
].filter((e) => !!e)}
|
||||
</Swiper>
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
.main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
|
||||
.content {
|
||||
flex: 1;
|
||||
|
||||
@ -32,7 +32,7 @@ export const FormPage: React.FC<Props> = ({ page, design, next, prev, className,
|
||||
<StyledH1 design={design} type={'question'}>
|
||||
{page.title}
|
||||
</StyledH1>
|
||||
<StyledMarkdown design={design} type={'question'} source={page.paragraph} />
|
||||
<StyledMarkdown design={design} type={'question'}>{page.paragraph}</StyledMarkdown>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
import { CaretDownOutlined, UserOutlined } from '@ant-design/icons'
|
||||
import { MenuFoldOutlined, MenuUnfoldOutlined } from '@ant-design/icons/lib'
|
||||
import { Alert, Dropdown, Layout, Menu, PageHeader, Select, Space, Spin, Tag } from 'antd'
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import React, { CSSProperties, FunctionComponent } from 'react'
|
||||
import GitHubButton from 'react-github-button'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import LogoWhitePng from '../assets/images/logo_white.png'
|
||||
import { useMeQuery } from '../graphql/query/me.query'
|
||||
import { languages } from '../i18n'
|
||||
import { sideMenu, SideMenuElement } from './sidemenu'
|
||||
@ -86,7 +88,7 @@ export const Structure: FunctionComponent<Props> = (props) => {
|
||||
<ItemGroup
|
||||
key={element.key}
|
||||
title={
|
||||
<div
|
||||
<Space
|
||||
style={{
|
||||
textTransform: 'uppercase',
|
||||
paddingTop: 16,
|
||||
@ -95,8 +97,10 @@ export const Structure: FunctionComponent<Props> = (props) => {
|
||||
}}
|
||||
>
|
||||
{element.icon}
|
||||
{t(element.name)}
|
||||
</div>
|
||||
<div>
|
||||
{t(element.name)}
|
||||
</div>
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
{buildMenu(element.items)}
|
||||
@ -108,10 +112,12 @@ export const Structure: FunctionComponent<Props> = (props) => {
|
||||
<SubMenu
|
||||
key={element.key}
|
||||
title={
|
||||
<span>
|
||||
<Space>
|
||||
{element.icon}
|
||||
{t(element.name)}
|
||||
</span>
|
||||
<div>
|
||||
{t(element.name)}
|
||||
</div>
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
{buildMenu(element.items)}
|
||||
@ -128,8 +134,12 @@ export const Structure: FunctionComponent<Props> = (props) => {
|
||||
}}
|
||||
key={element.key}
|
||||
>
|
||||
{element.icon}
|
||||
{t(element.name)}
|
||||
<Space>
|
||||
{element.icon}
|
||||
<div>
|
||||
{t(element.name)}
|
||||
</div>
|
||||
</Space>
|
||||
</Menu.Item>
|
||||
)
|
||||
}
|
||||
@ -148,7 +158,7 @@ export const Structure: FunctionComponent<Props> = (props) => {
|
||||
paddingLeft: 0,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
<Space
|
||||
style={{
|
||||
float: 'left',
|
||||
color: '#FFF',
|
||||
@ -162,13 +172,18 @@ export const Structure: FunctionComponent<Props> = (props) => {
|
||||
onClick: () => setSidebar(!sidebar),
|
||||
})}
|
||||
|
||||
<img
|
||||
src={require('assets/images/logo_white_small.png') as string}
|
||||
height={30}
|
||||
style={{ marginRight: 16 }}
|
||||
alt={'OhMyForm'}
|
||||
/>
|
||||
</div>
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
}}>
|
||||
<Image
|
||||
src={LogoWhitePng.src}
|
||||
width={1608 / 12}
|
||||
height={530 / 12}
|
||||
alt={'OhMyForm'}
|
||||
/>
|
||||
</div>
|
||||
</Space>
|
||||
<div style={{ float: 'right', display: 'flex', height: '100%' }}>
|
||||
<Dropdown
|
||||
overlay={
|
||||
@ -218,9 +233,9 @@ export const Structure: FunctionComponent<Props> = (props) => {
|
||||
style={{ flex: 1 }}
|
||||
defaultSelectedKeys={['1']}
|
||||
selectedKeys={selected}
|
||||
onSelect={(s): void => setSelected(s.keyPath as string[])}
|
||||
onSelect={(s): void => setSelected(s.keyPath )}
|
||||
openKeys={open}
|
||||
onOpenChange={(open): void => setOpen(open as string[])}
|
||||
onOpenChange={(open): void => setOpen(open )}
|
||||
>
|
||||
{buildMenu(sideMenu)}
|
||||
</Menu>
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { Button } from 'antd'
|
||||
import { ButtonProps } from 'antd/lib/button/button'
|
||||
import { darken, lighten } from 'polished'
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import { darken, lighten } from './color.change'
|
||||
|
||||
interface Props extends ButtonProps {
|
||||
background: string
|
||||
@ -14,12 +14,12 @@ interface Props extends ButtonProps {
|
||||
const Styled = styled(Button)`
|
||||
background: ${(props: Props) => props.background};
|
||||
color: ${(props: Props) => props.color};
|
||||
border-color: ${(props: Props) => darken(props.background, 10)};
|
||||
border-color: ${(props: Props) => darken(0.1, props.background)};
|
||||
|
||||
:hover {
|
||||
color: ${(props: Props) => props.highlight};
|
||||
background-color: ${(props: Props) => lighten(props.background, 10)};
|
||||
border-color: ${(props: Props) => darken(props.highlight, 10)};
|
||||
background-color: ${(props: Props) => lighten(0.1, props.background)};
|
||||
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 { PickerProps } from 'antd/lib/date-picker/generatePicker'
|
||||
import { Moment } from 'moment'
|
||||
import { transparentize } from 'polished'
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import { FormPublicDesignFragment } from '../../graphql/fragment/form.public.fragment'
|
||||
import { transparentize } from './color.change'
|
||||
|
||||
type Props = { design: FormPublicDesignFragment } & PickerProps<Moment>
|
||||
|
||||
@ -36,7 +36,7 @@ const Field = styled(DatePicker)`
|
||||
color: ${(props: Props) => props.design.colors.answer};
|
||||
|
||||
::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 { InputProps } from 'antd/lib/input/Input'
|
||||
import { transparentize } from 'polished'
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import { FormPublicDesignFragment } from '../../graphql/fragment/form.public.fragment'
|
||||
import { transparentize } from './color.change'
|
||||
|
||||
interface Props extends InputProps {
|
||||
design: FormPublicDesignFragment
|
||||
@ -37,7 +37,7 @@ const Field = styled(Input)`
|
||||
color: ${(props: Props) => props.design.colors.answer};
|
||||
|
||||
::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 ReactMarkdown, { ReactMarkdownProps } from 'react-markdown'
|
||||
import ReactMarkdown from 'react-markdown'
|
||||
import { ReactMarkdownOptions } from 'react-markdown/lib/react-markdown'
|
||||
import styled from 'styled-components'
|
||||
import { FormPublicDesignFragment } from '../../graphql/fragment/form.public.fragment'
|
||||
import { lighten } from './color.change'
|
||||
|
||||
interface Props extends ReactMarkdownProps {
|
||||
interface Props extends ReactMarkdownOptions {
|
||||
type: 'question' | 'answer'
|
||||
design: FormPublicDesignFragment
|
||||
}
|
||||
@ -29,7 +30,7 @@ const Markdown = styled(ReactMarkdown)`
|
||||
}
|
||||
|
||||
blockquote {
|
||||
color: ${(props: Props) => lighten(getColor(props), 50)};
|
||||
color: ${(props: Props) => lighten(0.5, getColor(props))};
|
||||
padding-left: 20px;
|
||||
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 }) => {
|
||||
return (
|
||||
<Markdown escapeHtml={false} {...props}>
|
||||
<Markdown {...props}>
|
||||
{children}
|
||||
</Markdown>
|
||||
)
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import { InputNumber } from 'antd'
|
||||
import { InputNumberProps } from 'antd/lib/input-number'
|
||||
import { transparentize } from 'polished'
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import { FormPublicDesignFragment } from '../../graphql/fragment/form.public.fragment'
|
||||
import { transparentize } from './color.change'
|
||||
|
||||
interface Props extends InputNumberProps {
|
||||
design: FormPublicDesignFragment
|
||||
@ -38,7 +38,7 @@ const Field = styled(InputNumber)`
|
||||
color: ${(props: Props) => props.design.colors.answer};
|
||||
|
||||
::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 { SelectProps } from 'antd/lib/select'
|
||||
import { transparentize } from 'polished'
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import { FormPublicDesignFragment } from '../../graphql/fragment/form.public.fragment'
|
||||
import { transparentize } from './color.change'
|
||||
|
||||
interface Props extends SelectProps<string> {
|
||||
design: FormPublicDesignFragment
|
||||
@ -36,7 +36,7 @@ const Field = styled(Select)`
|
||||
color: ${(props: Props) => props.design.colors.answer};
|
||||
|
||||
::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 { TextAreaProps } from 'antd/lib/input/TextArea'
|
||||
import { transparentize } from 'polished'
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import { FormPublicDesignFragment } from '../../graphql/fragment/form.public.fragment'
|
||||
import { transparentize } from './color.change'
|
||||
|
||||
interface Props extends TextAreaProps {
|
||||
design: FormPublicDesignFragment
|
||||
@ -35,7 +35,7 @@ const Field = styled(Input.TextArea)`
|
||||
color: ${(props: Props) => props.design.colors.answer};
|
||||
|
||||
::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) => {
|
||||
const { refetch } = useQuery(query, { skip: true })
|
||||
|
||||
const cb = useCallback(
|
||||
return useCallback(
|
||||
(variables) => {
|
||||
return refetch(variables)
|
||||
},
|
||||
[refetch]
|
||||
)
|
||||
|
||||
return cb
|
||||
}
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
import debug from 'debug'
|
||||
import { all, create } from 'mathjs'
|
||||
import { useState } from 'react'
|
||||
|
||||
const logger = debug('useMath')
|
||||
|
||||
export const useMath = (): ((
|
||||
expression: string,
|
||||
values?: { [id: string]: string | number }
|
||||
@ -14,15 +17,24 @@ export const useMath = (): ((
|
||||
Object.keys(values).forEach((key) => {
|
||||
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]))
|
||||
}
|
||||
})
|
||||
|
||||
const result = math.evaluate(processed)
|
||||
|
||||
return Boolean(result)
|
||||
return Boolean(math.evaluate(processed))
|
||||
} catch (e) {
|
||||
logger(
|
||||
'failed to calculate %O: %s',
|
||||
{
|
||||
expression,
|
||||
values,
|
||||
},
|
||||
e.message
|
||||
)
|
||||
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import { NextRouter, useRouter as useNextRouter } from 'next/router'
|
||||
|
||||
const parseQuery = (path) => {
|
||||
type parseQueryResponse = { [key: string]: string }
|
||||
|
||||
const parseQuery = (path: string): parseQueryResponse => {
|
||||
const query = {}
|
||||
const regex = /[?&]([^&$=]+)(=([^&$]+))?/g
|
||||
let param: RegExpExecArray
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { useMutation } from '@apollo/client'
|
||||
import debug from 'debug'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import {
|
||||
SUBMISSION_SET_FIELD_MUTATION,
|
||||
@ -11,6 +12,8 @@ import {
|
||||
SubmissionStartMutationVariables,
|
||||
} from '../graphql/mutation/submission.start.mutation'
|
||||
|
||||
const logger = debug('useSubmission')
|
||||
|
||||
export interface Submission {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
setField: (fieldId: string, data: unknown) => Promise<void>
|
||||
@ -44,18 +47,19 @@ export const useSubmission = (formId: string): Submission => {
|
||||
},
|
||||
})
|
||||
.then(({ data }) => {
|
||||
logger('submission id = %O', data.submission.id)
|
||||
setSubmission({
|
||||
id: data.submission.id,
|
||||
token,
|
||||
})
|
||||
})
|
||||
.catch((e: Error) => console.error('failed to start submission', e))
|
||||
.catch((e: Error) => logger('failed to start submission %J', e))
|
||||
}, [formId])
|
||||
|
||||
const setField = useCallback(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
async (fieldId: string, data: any) => {
|
||||
console.log('just save', fieldId, data)
|
||||
logger('save field id=%O %O', fieldId, data)
|
||||
await save({
|
||||
variables: {
|
||||
submission: submission.id,
|
||||
@ -71,7 +75,7 @@ export const useSubmission = (formId: string): Submission => {
|
||||
)
|
||||
|
||||
const finish = useCallback(async () => {
|
||||
console.log('finish submission!!', formId)
|
||||
logger('finish submission!!', formId)
|
||||
|
||||
await Promise.resolve()
|
||||
}, [submission])
|
||||
|
||||
@ -48,7 +48,9 @@ export const BaseDataTab: React.FC<TabPaneProps> = (props) => {
|
||||
getValueFromEvent={(e) => {
|
||||
switch (e) {
|
||||
case 'superuser':
|
||||
return ['user', 'admin', 'superuser']
|
||||
return [
|
||||
'user', 'admin', 'superuser',
|
||||
]
|
||||
case 'admin':
|
||||
return ['user', 'admin']
|
||||
default:
|
||||
@ -70,7 +72,9 @@ export const BaseDataTab: React.FC<TabPaneProps> = (props) => {
|
||||
}}
|
||||
>
|
||||
<Select>
|
||||
{['user', 'admin', 'superuser'].map((role) => (
|
||||
{[
|
||||
'user', 'admin', 'superuser',
|
||||
].map((role) => (
|
||||
<Select.Option value={role} key={role}>
|
||||
{role.toUpperCase()}
|
||||
</Select.Option>
|
||||
|
||||
@ -40,6 +40,7 @@ export interface FormFieldLogicFragment {
|
||||
|
||||
export interface FormFieldFragment {
|
||||
id: string
|
||||
idx?: number
|
||||
title: string
|
||||
slug?: string
|
||||
type: string
|
||||
@ -126,6 +127,7 @@ export const FORM_FRAGMENT = gql`
|
||||
|
||||
fields {
|
||||
id
|
||||
idx
|
||||
title
|
||||
slug
|
||||
type
|
||||
|
||||
@ -16,7 +16,7 @@ interface Variables {
|
||||
}
|
||||
|
||||
const MUTATION = gql`
|
||||
mutation update($form: FormCreateInput!) {
|
||||
mutation createForm($form: FormCreateInput!) {
|
||||
form: createForm(form: $form) {
|
||||
...Form
|
||||
}
|
||||
|
||||
@ -12,7 +12,7 @@ interface Variables {
|
||||
}
|
||||
|
||||
const MUTATION = gql`
|
||||
mutation delete($id: ID!) {
|
||||
mutation deleteForm($id: ID!) {
|
||||
form: deleteForm(id: $id) {
|
||||
id
|
||||
}
|
||||
|
||||
@ -11,7 +11,7 @@ interface Variables {
|
||||
}
|
||||
|
||||
const MUTATION = gql`
|
||||
mutation update($form: FormUpdateInput!) {
|
||||
mutation updateForm($form: FormUpdateInput!) {
|
||||
form: updateForm(form: $form) {
|
||||
...Form
|
||||
}
|
||||
|
||||
@ -13,7 +13,7 @@ export interface LoginMutationVariables {
|
||||
}
|
||||
|
||||
export const LOGIN_MUTATION = gql`
|
||||
mutation login($username: String!, $password: String!) {
|
||||
mutation authLogin($username: String!, $password: String!) {
|
||||
tokens: authLogin(username: $username, password: $password) {
|
||||
access: accessToken
|
||||
refresh: refreshToken
|
||||
|
||||
@ -20,7 +20,7 @@ export interface Variables {
|
||||
}
|
||||
|
||||
export const MUTATION = gql`
|
||||
mutation update($user: ProfileUpdateInput!) {
|
||||
mutation updateProfile($user: ProfileUpdateInput!) {
|
||||
form: updateProfile(user: $user) {
|
||||
...AdminProfile
|
||||
}
|
||||
|
||||
@ -19,7 +19,7 @@ interface Variables {
|
||||
}
|
||||
|
||||
const MUTATION = gql`
|
||||
mutation register($user: UserCreateInput!) {
|
||||
mutation authRegister($user: UserCreateInput!) {
|
||||
tokens: authRegister(user: $user) {
|
||||
access: accessToken
|
||||
refresh: refreshToken
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { gql } from '@apollo/client/core'
|
||||
|
||||
export const SUBMISSION_FINISH_MUTATION = gql`
|
||||
mutation start($submission: ID!, $field: SubmissionSetFieldInput!) {
|
||||
mutation submissionSetField($submission: ID!, $field: SubmissionSetFieldInput!) {
|
||||
submission: submissionSetField(submission: $submission, field: $field) {
|
||||
id
|
||||
percentageComplete
|
||||
|
||||
@ -12,12 +12,11 @@ export interface SubmissionSetFieldMutationVariables {
|
||||
field: {
|
||||
token: string
|
||||
field: string
|
||||
data: string
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
id
|
||||
percentageComplete
|
||||
|
||||
@ -19,7 +19,7 @@ export interface SubmissionStartMutationVariables {
|
||||
}
|
||||
|
||||
export const SUBMISSION_START_MUTATION = gql`
|
||||
mutation start($form: ID!, $submission: SubmissionStartInput!) {
|
||||
mutation submissionStart($form: ID!, $submission: SubmissionStartInput!) {
|
||||
submission: submissionStart(form: $form, submission: $submission) {
|
||||
id
|
||||
percentageComplete
|
||||
|
||||
@ -12,7 +12,7 @@ interface Variables {
|
||||
}
|
||||
|
||||
const MUTATION = gql`
|
||||
mutation delete($id: ID!) {
|
||||
mutation deleteUser($id: ID!) {
|
||||
form: deleteUser(id: $id) {
|
||||
id
|
||||
}
|
||||
|
||||
@ -11,7 +11,7 @@ interface Variables {
|
||||
}
|
||||
|
||||
const MUTATION = gql`
|
||||
mutation update($user: UserUpdateInput!) {
|
||||
mutation updateUser($user: UserUpdateInput!) {
|
||||
form: updateUser(user: $user) {
|
||||
...AdminUser
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ interface Data {
|
||||
}
|
||||
|
||||
export const QUERY = gql`
|
||||
query profile {
|
||||
query adminMe {
|
||||
user: me {
|
||||
...AdminProfile
|
||||
}
|
||||
|
||||
@ -15,7 +15,7 @@ export interface AdminStatisticQueryData {
|
||||
export interface AdminStatisticQueryVariables {}
|
||||
|
||||
export const ADMIN_STATISTIC_QUERY = gql`
|
||||
query {
|
||||
query statistics {
|
||||
forms: getFormStatistic {
|
||||
total
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@ export interface AdminUserQueryVariables {
|
||||
}
|
||||
|
||||
export const ADMIN_USER_QUERY = gql`
|
||||
query user($id: ID!) {
|
||||
query getUserById($id: ID!) {
|
||||
user: getUserById(id: $id) {
|
||||
...AdminUser
|
||||
}
|
||||
|
||||
@ -18,7 +18,7 @@ interface Variables {
|
||||
}
|
||||
|
||||
const QUERY = gql`
|
||||
query pager($start: Int, $limit: Int) {
|
||||
query listForms($start: Int, $limit: Int) {
|
||||
pager: listForms(start: $start, limit: $limit) {
|
||||
entries {
|
||||
...Form
|
||||
|
||||
@ -11,7 +11,7 @@ interface Variables {
|
||||
}
|
||||
|
||||
const QUERY = gql`
|
||||
query form($id: ID!) {
|
||||
query getFormById($id: ID!) {
|
||||
form: getFormById(id: $id) {
|
||||
...Form
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@ interface Variables {
|
||||
}
|
||||
|
||||
const QUERY = gql`
|
||||
query pager($form: ID!, $start: Int, $limit: Int) {
|
||||
query listSubmissions($form: ID!, $start: Int, $limit: Int) {
|
||||
form: getFormById(id: $form) {
|
||||
...Form
|
||||
}
|
||||
|
||||
@ -18,7 +18,7 @@ interface Variables {
|
||||
}
|
||||
|
||||
const QUERY = gql`
|
||||
query pager($start: Int, $limit: Int) {
|
||||
query listUsers($start: Int, $limit: Int) {
|
||||
pager: listUsers(start: $start, limit: $limit) {
|
||||
entries {
|
||||
...User
|
||||
|
||||
5
next-env.d.ts
vendored
5
next-env.d.ts
vendored
@ -1,2 +1,5 @@
|
||||
/// <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 environment = process.env.NODE_ENV ? process.env.NODE_ENV : 'dev';
|
||||
const version = p.version;
|
||||
|
||||
module.exports = withImages({
|
||||
module.exports = {
|
||||
poweredByHeader: true,
|
||||
future: {
|
||||
webpack5: true,
|
||||
},
|
||||
productionBrowserSourceMaps: true,
|
||||
publicRuntimeConfig: {
|
||||
environment,
|
||||
endpoint: process.env.ENDPOINT || '/graphql',
|
||||
spa: !!process.env.SPA || false,
|
||||
mainBackground: process.env.MAIN_BACKGROUND || '#8FA2A6'
|
||||
@ -19,4 +18,4 @@ module.exports = withImages({
|
||||
env: {
|
||||
version,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
export interface NextConfigType {
|
||||
publicRuntimeConfig: {
|
||||
environment: string,
|
||||
endpoint: string
|
||||
spa?: boolean
|
||||
mainBackground?: string
|
||||
|
||||
86
package.json
86
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ohmyform-react",
|
||||
"version": "1.0.0-alpha",
|
||||
"version": "1.0.0",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"scripts": {
|
||||
"start:dev": "next dev -p 4000",
|
||||
@ -13,60 +13,60 @@
|
||||
"translation:missing": "cross-env TS_NODE_TRANSPILE_ONLY=true ts-node locales/missing.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ant-design/icons": "^4.6.2",
|
||||
"@apollo/client": "^3.3.15",
|
||||
"@lifeomic/axios-fetch": "^2.0.0",
|
||||
"antd": "^4.15.3",
|
||||
"axios": "^0.21.1",
|
||||
"@ant-design/icons": "^4.7.0",
|
||||
"@apollo/client": "^3.5.6",
|
||||
"antd": "^4.18.2",
|
||||
"axios": "^0.24.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"dayjs": "^1.10.4",
|
||||
"exceljs": "^4.2.1",
|
||||
"graphql": "^15.5.0",
|
||||
"i18next": "^19.9.2",
|
||||
"i18next-browser-languagedetector": "^6.1.0",
|
||||
"dayjs": "^1.10.7",
|
||||
"debug": "^4.3.3",
|
||||
"exceljs": "^4.3.0",
|
||||
"graphql": "^15.8.0",
|
||||
"i18next": "^21.6.4",
|
||||
"i18next-browser-languagedetector": "^6.1.2",
|
||||
"isomorphic-fetch": "^3.0.0",
|
||||
"mathjs": "^9.3.2",
|
||||
"next": "^10.2.0",
|
||||
"next-images": "^1.7.0",
|
||||
"next-redux-wrapper": "^6.0.2",
|
||||
"mathjs": "^10.0.2",
|
||||
"next": "^12.0.7",
|
||||
"next-redux-wrapper": "^7.0.5",
|
||||
"polished": "^4.1.3",
|
||||
"react": "^17.0.2",
|
||||
"react-color": "^2.19.3",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-github-button": "^0.1.11",
|
||||
"react-i18next": "^11.8.15",
|
||||
"react-icons": "^3.11.0",
|
||||
"react-i18next": "^11.15.3",
|
||||
"react-icons": "^4.3.1",
|
||||
"react-id-swiper": "^4.0.0",
|
||||
"react-markdown": "^4.3.1",
|
||||
"react-redux": "^7.2.4",
|
||||
"redux": "^4.1.0",
|
||||
"react-markdown": "^7.1.2",
|
||||
"react-redux": "^7.2.6",
|
||||
"redux": "^4.1.2",
|
||||
"redux-devtools-extension": "^2.13.9",
|
||||
"redux-thunk": "^2.3.0",
|
||||
"sass": "^1.32.12",
|
||||
"styled-components": "^5.2.3",
|
||||
"swiper": "^6.5.8"
|
||||
"redux-thunk": "^2.4.1",
|
||||
"sass": "^1.45.2",
|
||||
"styled-components": "^5.3.3",
|
||||
"swiper": "^7.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/axios": "^0.14.0",
|
||||
"@types/lifeomic__axios-fetch": "^1.5.0",
|
||||
"@types/mathjs": "^6.0.12",
|
||||
"@types/node": "^14.14.43",
|
||||
"@types/node-fetch": "^2.5.10",
|
||||
"@types/mathjs": "^9.4.2",
|
||||
"@types/node": "^16.11.17",
|
||||
"@types/node-fetch": "^3.0.3",
|
||||
"@types/react": "^17.0.4",
|
||||
"@types/styled-components": "^5.1.9",
|
||||
"@types/swiper": "^5.4.2",
|
||||
"@typescript-eslint/eslint-plugin": "^4.22.0",
|
||||
"@typescript-eslint/parser": "^4.22.0",
|
||||
"commander": "^6.2.1",
|
||||
"eslint": "^7.25.0",
|
||||
"eslint-config-prettier": "^6.15.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.4.1",
|
||||
"eslint-plugin-prettier": "^3.4.0",
|
||||
"eslint-plugin-react": "^7.23.2",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"glob": "^7.1.6",
|
||||
"@types/styled-components": "^5.1.19",
|
||||
"@types/swiper": "^6.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.8.1",
|
||||
"@typescript-eslint/parser": "^5.8.1",
|
||||
"commander": "^8.3.0",
|
||||
"eslint": "^8.6.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.5.1",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"eslint-plugin-react": "^7.28.0",
|
||||
"eslint-plugin-react-hooks": "^4.3.0",
|
||||
"eslint-plugin-unused-imports": "^2.0.0",
|
||||
"glob": "^7.2.0",
|
||||
"lodash.merge": "^4.6.2",
|
||||
"prettier": "^2.1.1",
|
||||
"ts-node": "^9.1.1",
|
||||
"typescript": "^4.2.4"
|
||||
"prettier": "^2.5.1",
|
||||
"ts-node": "^10.4.0",
|
||||
"typescript": "^4.5.4"
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,15 +2,26 @@ import { ApolloProvider } from '@apollo/client'
|
||||
import 'antd/dist/antd.css'
|
||||
import 'assets/global.scss'
|
||||
import 'assets/variables.scss'
|
||||
import debug from 'debug'
|
||||
import 'i18n'
|
||||
import App, { AppInitialProps } from 'next/app'
|
||||
import { AppType } from 'next/dist/next-server/lib/utils'
|
||||
import getConfig from 'next/config'
|
||||
import { AppInitialProps, AppType } from 'next/dist/shared/lib/utils'
|
||||
import Head from 'next/head'
|
||||
import React from 'react'
|
||||
import React, { useEffect } from 'react'
|
||||
import { wrapper } from 'store'
|
||||
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 (
|
||||
<ApolloProvider client={getClient()}>
|
||||
<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,21 +34,23 @@ const Index: NextPage = () => {
|
||||
return {
|
||||
form: {
|
||||
...next.form,
|
||||
fields: next.form.fields.map((field) => {
|
||||
const keys: FormFieldOptionKeysFragment = {}
|
||||
fields: next.form.fields
|
||||
.map((field) => {
|
||||
const keys: FormFieldOptionKeysFragment = {}
|
||||
|
||||
field.options.forEach((option) => {
|
||||
if (option.key) {
|
||||
keys[option.key] = option.value
|
||||
field.options.forEach((option) => {
|
||||
if (option.key) {
|
||||
keys[option.key] = option.value
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
...field,
|
||||
options: field.options.filter((option) => !option.key),
|
||||
optionKeys: keys,
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
...field,
|
||||
options: field.options.filter((option) => !option.key),
|
||||
optionKeys: keys,
|
||||
}
|
||||
}),
|
||||
.sort((a, b) => a.idx - b.idx),
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -69,7 +71,7 @@ const Index: NextPage = () => {
|
||||
|
||||
formData.form.fields = formData.form.fields
|
||||
.filter((e) => e && e.type)
|
||||
.map(({ optionKeys, ...field }) => {
|
||||
.map(({ optionKeys, ...field }, index) => {
|
||||
const options = field.options
|
||||
|
||||
if (optionKeys) {
|
||||
@ -89,6 +91,7 @@ const Index: NextPage = () => {
|
||||
return {
|
||||
...field,
|
||||
options,
|
||||
idx: index,
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@ -2,9 +2,11 @@ import { Alert, Layout } from 'antd'
|
||||
import { AuthFooter } from 'components/auth/footer'
|
||||
import { GetStaticProps, NextPage } from 'next'
|
||||
import getConfig from 'next/config'
|
||||
import Image from 'next/image'
|
||||
import { useRouter } from 'next/router'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import LogoWhitePng from '../assets/images/logo_white.png'
|
||||
import { LoadingPage } from '../components/loading.page'
|
||||
import { Omf } from '../components/omf'
|
||||
import { useStatusQuery } from '../graphql/query/status.query'
|
||||
@ -24,7 +26,9 @@ const Index: NextPage = () => {
|
||||
if (router.pathname !== window.location.pathname) {
|
||||
let href = 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) => {
|
||||
if (r.test(as)) {
|
||||
@ -58,16 +62,22 @@ const Index: NextPage = () => {
|
||||
}}
|
||||
>
|
||||
<Omf />
|
||||
<img
|
||||
alt={'OhMyForm'}
|
||||
<div
|
||||
style={{
|
||||
margin: 'auto',
|
||||
maxWidth: '90%',
|
||||
width: 500,
|
||||
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 && (
|
||||
<Alert
|
||||
|
||||
@ -10,11 +10,13 @@ import {
|
||||
LoginMutationVariables,
|
||||
} from 'graphql/mutation/login.mutation'
|
||||
import { NextPage } from 'next'
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import React, { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import ReactMarkdown from 'react-markdown'
|
||||
import LogoWhitePng from '../../assets/images/logo_white.png'
|
||||
import { Omf } from '../../components/omf'
|
||||
import { useSettingsQuery } from '../../graphql/query/settings.query'
|
||||
import scss from './index.module.scss'
|
||||
@ -64,9 +66,7 @@ const Index: NextPage = () => {
|
||||
width: 400,
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src={require('../../assets/images/logo_white_small.png') as string}
|
||||
alt={'OhMyForm'}
|
||||
<div
|
||||
style={{
|
||||
display: 'block',
|
||||
width: '70%',
|
||||
@ -74,14 +74,21 @@ const Index: NextPage = () => {
|
||||
marginRight: 'auto',
|
||||
marginBottom: 16,
|
||||
}}
|
||||
/>
|
||||
>
|
||||
<Image
|
||||
src={LogoWhitePng.src}
|
||||
alt={'OhMyForm'}
|
||||
width={1608 / 4}
|
||||
height={530 / 4}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{data && data.loginNote.value && (
|
||||
<Alert
|
||||
type="warning"
|
||||
showIcon
|
||||
message={t('login:note')}
|
||||
description={<ReactMarkdown escapeHtml={false} source={data.loginNote.value} />}
|
||||
description={<ReactMarkdown>{data.loginNote.value}</ReactMarkdown>}
|
||||
style={{
|
||||
marginBottom: 24,
|
||||
}}
|
||||
|
||||
@ -5,10 +5,12 @@ import { AuthLayout } from 'components/auth/layout'
|
||||
import { setAuth } from 'components/with.auth'
|
||||
import { RegisterUserData, useRegisterMutation } from 'graphql/mutation/register.mutation'
|
||||
import { NextPage } from 'next'
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import React, { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import LogoWhitePng from '../assets/images/logo_white.png'
|
||||
import { ErrorPage } from '../components/error.page'
|
||||
import { Omf } from '../components/omf'
|
||||
import { useSettingsQuery } from '../graphql/query/settings.query'
|
||||
@ -66,9 +68,7 @@ const Register: NextPage = () => {
|
||||
width: 400,
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src={require('../assets/images/logo_white_small.png') as string}
|
||||
alt={'OhMyForm'}
|
||||
<div
|
||||
style={{
|
||||
display: 'block',
|
||||
width: '70%',
|
||||
@ -76,7 +76,14 @@ const Register: NextPage = () => {
|
||||
marginRight: 'auto',
|
||||
marginBottom: 16,
|
||||
}}
|
||||
/>
|
||||
>
|
||||
<Image
|
||||
src={LogoWhitePng.src}
|
||||
alt={'OhMyForm'}
|
||||
width={1608 / 4}
|
||||
height={530 / 4}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Form.Item
|
||||
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 { AnyAction, applyMiddleware, combineReducers, createStore } from 'redux'
|
||||
import { createWrapper, HYDRATE } from 'next-redux-wrapper'
|
||||
import { AnyAction, applyMiddleware, combineReducers, createStore, Store } from 'redux'
|
||||
import { composeWithDevTools } from 'redux-devtools-extension'
|
||||
import thunkMiddleware from 'redux-thunk'
|
||||
import { auth, AuthState } from './auth'
|
||||
@ -21,8 +21,8 @@ const root = (state: State, action: AnyAction): State => {
|
||||
return combined(state, action)
|
||||
}
|
||||
|
||||
const makeStore: MakeStore<State> = () => {
|
||||
const makeStore = () => {
|
||||
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",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve"
|
||||
"jsx": "preserve",
|
||||
"incremental": true
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user