add logic for field types

This commit is contained in:
Michael Schramm 2020-05-30 12:59:31 +02:00
parent 34d154b4dd
commit 37f9d0b62d
22 changed files with 307 additions and 73 deletions

View File

@ -1,21 +1,26 @@
import {DatePicker, Form} from 'antd' import {Form} from 'antd'
import moment from 'moment'
import React from 'react' import React from 'react'
import {StyledDateInput} from '../../styled/date.input'
import {FieldTypeProps} from './type.props' import {FieldTypeProps} from './type.props'
export const DateType: React.FC<FieldTypeProps> = ({field}) => { export const DateType: React.FC<FieldTypeProps> = ({field, design}) => {
// TODO check min and max // TODO check min and max
return ( return (
<div> <div>
<Form.Item <Form.Item
label={'Default Date'}
name={[field.id, 'value']} name={[field.id, 'value']}
rules={[ rules={[
{ required: field.required, message: 'Please provide Information' }, { required: field.required, message: 'Please provide Information' },
]} ]}
labelCol={{ span: 6 }}
> >
<DatePicker autoFocus /> <StyledDateInput
size={'large'}
defaultValue={field.value ? moment(field.value) : undefined}
design={design}
autoFocus
/>
</Form.Item> </Form.Item>
</div> </div>
) )

View File

@ -1,20 +1,24 @@
import {Form, Input} from 'antd' import {Form} from 'antd'
import React from 'react' import React from 'react'
import {StyledInput} from '../../styled/input'
import {FieldTypeProps} from './type.props' import {FieldTypeProps} from './type.props'
export const EmailType: React.FC<FieldTypeProps> = ({field}) => { export const EmailType: React.FC<FieldTypeProps> = ({field, design}) => {
return ( return (
<div> <div>
<Form.Item <Form.Item
label={'Default Email'}
name={[field.id, 'value']} name={[field.id, 'value']}
rules={[ rules={[
{ required: field.required, message: 'Please provide Information' }, { required: field.required, message: 'Please provide Information' },
{ type: 'email', message: 'Must be a valid email' } { type: 'email', message: 'Must be a valid email' }
]} ]}
labelCol={{ span: 6 }}
> >
<Input type={'email'} /> <StyledInput
design={design}
allowClear
size={'large'}
defaultValue={field.value}
/>
</Form.Item> </Form.Item>
</div> </div>
) )

View File

@ -1,20 +1,24 @@
import {Form, Input} from 'antd' import {Form} from 'antd'
import React from 'react' import React from 'react'
import {StyledInput} from '../../styled/input'
import {FieldTypeProps} from './type.props' import {FieldTypeProps} from './type.props'
export const LinkType: React.FC<FieldTypeProps> = ({field}) => { export const LinkType: React.FC<FieldTypeProps> = ({field, design}) => {
return ( return (
<div> <div>
<Form.Item <Form.Item
label={'Default Link'}
name={[field.id, 'value']} name={[field.id, 'value']}
rules={[ rules={[
{ required: field.required, message: 'Please provide Information' }, { required: field.required, message: 'Please provide Information' },
{ type: 'url', message: 'Must be a valid URL' } { type: 'url', message: 'Must be a valid URL' }
]} ]}
labelCol={{ span: 6 }}
> >
<Input type={'url'} /> <StyledInput
design={design}
allowClear
size={'large'}
defaultValue={field.value}
/>
</Form.Item> </Form.Item>
</div> </div>
) )

View File

@ -31,5 +31,30 @@ function LightenDarkenColor(col, amt) {
return (usePound?"#":"") + (g | (b << 8) | (r << 16)).toString(16); return (usePound?"#":"") + (g | (b << 8) | (r << 16)).toString(16);
} }
export const transparentize = (col: string, amt: number) => {
if (col[0] == "#") {
col = col.slice(1);
}
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) => LightenDarkenColor(color, amount) export const lighten = (color: string, amount: number) => LightenDarkenColor(color, amount)
export const darken = (color: string, amount: number) => LightenDarkenColor(color, -amount) export const darken = (color: string, amount: number) => LightenDarkenColor(color, -amount)

View File

@ -0,0 +1,61 @@
import {DatePicker} from 'antd'
import {PickerProps} from 'antd/lib/date-picker/generatePicker'
import {Moment} from 'moment'
import React, {useEffect, useState} from 'react'
import styled from 'styled-components'
import {FormDesignFragment} from '../../graphql/fragment/form.fragment'
import {transparentize} from './color.change'
type Props = { design: FormDesignFragment } & PickerProps<Moment>
export const StyledDateInput: React.FC<Props> = ({design, children, ...props}) => {
const [Field, setField] = useState()
useEffect(() => {
setField(
styled(DatePicker)`
color: ${design.colors.answerColor};
border-color: ${design.colors.answerColor};
background: none !important;
border-right: none;
border-top: none;
border-left: none;
border-radius: 0;
width: 100%;
:hover,
:active {
border-color: ${design.colors.answerColor};
}
&.ant-picker {
box-shadow: none
}
.ant-picker-clear {
background: none;
}
input {
color: ${design.colors.answerColor};
::placeholder {
color: ${transparentize(design.colors.answerColor, 60)}
}
}
.anticon {
color: ${design.colors.answerColor};
}
`
)
}, [design])
if (!Field) {
return null
}
return (
<Field {...props}>{children}</Field>
)
}

View File

@ -3,6 +3,7 @@ import {InputProps} from 'antd/lib/input/Input'
import React, {useEffect, useState} from 'react' import React, {useEffect, useState} from 'react'
import styled from 'styled-components' import styled from 'styled-components'
import {FormDesignFragment} from '../../graphql/fragment/form.fragment' import {FormDesignFragment} from '../../graphql/fragment/form.fragment'
import {transparentize} from './color.change'
interface Props extends InputProps{ interface Props extends InputProps{
design: FormDesignFragment design: FormDesignFragment
@ -38,6 +39,10 @@ export const StyledInput: React.FC<Props> = ({design, children, ...props}) => {
input { input {
background: none !important; background: none !important;
color: ${design.colors.answerColor}; color: ${design.colors.answerColor};
::placeholder {
color: ${transparentize(design.colors.answerColor, 60)}
}
} }
.anticon { .anticon {

View File

@ -104,6 +104,7 @@ export const ADMIN_FORM_FRAGMENT = gql`
questionColor questionColor
answerColor answerColor
buttonColor buttonColor
buttonActiveColor
buttonTextColor buttonTextColor
} }
font font
@ -118,6 +119,7 @@ export const ADMIN_FORM_FRAGMENT = gql`
action action
text text
bgColor bgColor
activeColor
color color
} }
} }
@ -131,6 +133,7 @@ export const ADMIN_FORM_FRAGMENT = gql`
action action
text text
bgColor bgColor
activeColor
color color
} }
} }

View File

@ -10,7 +10,7 @@ export interface AdminFormCreateMutationVariables {
} }
export const ADMIN_FORM_CREATE_MUTATION = gql` export const ADMIN_FORM_CREATE_MUTATION = gql`
mutation update($$form: FormCreateInput!) { mutation update($form: FormCreateInput!) {
form: createForm(form: $form) { form: createForm(form: $form) {
...AdminForm ...AdminForm
} }

View File

@ -5,12 +5,12 @@ import ApolloClient from 'apollo-boost'
import 'assets/global.scss' import 'assets/global.scss'
import 'assets/variables.scss' import 'assets/variables.scss'
import axios from 'axios' import axios from 'axios'
import {authConfig} from 'components/with.auth'
import {AppProps} from 'next/app' import {AppProps} from 'next/app'
import getConfig from 'next/config' import getConfig from 'next/config'
import Head from 'next/head' import Head from 'next/head'
import React from 'react' import React from 'react'
import {wrapper} from 'store' import {wrapper} from 'store'
import {authConfig} from '../components/with.auth'
const { publicRuntimeConfig } = getConfig() const { publicRuntimeConfig } = getConfig()

View File

@ -1,26 +1,26 @@
import {useMutation, useQuery} from '@apollo/react-hooks' import {useMutation, useQuery} from '@apollo/react-hooks'
import {Button, Form, Input, message, Tabs} from 'antd' import {Button, Form, Input, message, Tabs} from 'antd'
import {useForm} from 'antd/lib/form/Form' import {useForm} from 'antd/lib/form/Form'
import {NextPage} from 'next' import {cleanInput} from 'components/clean.input'
import {useRouter} from 'next/router' import {BaseDataTab} from 'components/form/admin/base.data.tab'
import React, {useState} from 'react' import {DesignTab} from 'components/form/admin/design.tab'
import {cleanInput} from '../../../../components/clean.input' import {EndPageTab} from 'components/form/admin/end.page.tab'
import {BaseDataTab} from '../../../../components/form/admin/base.data.tab' import {FieldsTab} from 'components/form/admin/fields.tab'
import {DesignTab} from '../../../../components/form/admin/design.tab' import {RespondentNotificationsTab} from 'components/form/admin/respondent.notifications.tab'
import {EndPageTab} from '../../../../components/form/admin/end.page.tab' import {SelfNotificationsTab} from 'components/form/admin/self.notifications.tab'
import {FieldsTab} from '../../../../components/form/admin/fields.tab' import {StartPageTab} from 'components/form/admin/start.page.tab'
import {RespondentNotificationsTab} from '../../../../components/form/admin/respondent.notifications.tab' import Structure from 'components/structure'
import {SelfNotificationsTab} from '../../../../components/form/admin/self.notifications.tab' import {withAuth} from 'components/with.auth'
import {StartPageTab} from '../../../../components/form/admin/start.page.tab' import {AdminFormFieldFragment} from 'graphql/fragment/admin.form.fragment'
import Structure from '../../../../components/structure'
import {withAuth} from '../../../../components/with.auth'
import {AdminFormFieldFragment} from '../../../../graphql/fragment/admin.form.fragment'
import { import {
ADMIN_FORM_UPDATE_MUTATION, ADMIN_FORM_UPDATE_MUTATION,
AdminFormUpdateMutationData, AdminFormUpdateMutationData,
AdminFormUpdateMutationVariables AdminFormUpdateMutationVariables
} from '../../../../graphql/mutation/admin.form.update.mutation' } from 'graphql/mutation/admin.form.update.mutation'
import {ADMIN_FORM_QUERY, AdminFormQueryData, AdminFormQueryVariables} from '../../../../graphql/query/admin.form.query' import {ADMIN_FORM_QUERY, AdminFormQueryData, AdminFormQueryVariables} from 'graphql/query/admin.form.query'
import {NextPage} from 'next'
import {useRouter} from 'next/router'
import React, {useState} from 'react'
const Index: NextPage = () => { const Index: NextPage = () => {
const router = useRouter() const router = useRouter()

View File

@ -0,0 +1,115 @@
import {useMutation} from '@apollo/react-hooks'
import {Button, Form, Input, message, Tabs} from 'antd'
import {useForm} from 'antd/lib/form/Form'
import {cleanInput} from 'components/clean.input'
import {BaseDataTab} from 'components/form/admin/base.data.tab'
import Structure from 'components/structure'
import {withAuth} from 'components/with.auth'
import {AdminFormQueryData} from 'graphql/query/admin.form.query'
import {NextPage} from 'next'
import {useRouter} from 'next/router'
import React, {useState} from 'react'
import {
ADMIN_FORM_CREATE_MUTATION,
AdminFormCreateMutationData,
AdminFormCreateMutationVariables
} from '../../../graphql/mutation/admin.form.create.mutation'
const Create: NextPage = () => {
const router = useRouter()
const [form] = useForm()
const [saving, setSaving] = useState(false)
const [create] = useMutation<AdminFormCreateMutationData, AdminFormCreateMutationVariables>(ADMIN_FORM_CREATE_MUTATION)
const save = async (formData: AdminFormQueryData) => {
setSaving(true)
console.log('try to save form!', formData)
try {
const next = (await create({
variables: cleanInput(formData),
})).data
message.success('Form Created')
router.replace('/admin/forms/[id]', `/admin/forms/${next.form.id}`)
} catch (e) {
console.error('failed to save', e)
message.error('Could not save Form')
}
setSaving(false)
}
return (
<Structure
loading={saving}
title={'Create New Form'}
selected={'forms'}
breadcrumbs={[
{ href: '/admin', name: 'Home' },
{ href: '/admin/forms', name: 'Form' },
]}
extra={[
<Button
key={'create'}
onClick={form.submit}
type={'primary'}
>
Save
</Button>,
]}
style={{paddingTop: 0}}
>
<Form
form={form}
onFinish={save}
onFinishFailed={errors => {
message.error('Required fields are missing')
}}
labelCol={{
xs: { span: 24 },
sm: { span: 6 },
}}
wrapperCol={{
xs: { span: 24 },
sm: { span: 18 },
}}
>
<Form.Item noStyle name={['form', 'id']}><Input type={'hidden'} /></Form.Item>
<Tabs>
{/*
<FieldsTab
key={'fields'}
tab={'Fields'}
fields={fields}
onChangeFields={setFields}
form={form}
/>
*/}
<BaseDataTab key={'base_data'} tab={'Base Data'} />
{/*
<DesignTab key={'design'} tab={'Design'} />
<SelfNotificationsTab
key={'self_notifications'}
tab={'Self Notifications'}
fields={fields}
form={form}
/>
<RespondentNotificationsTab
key={'respondent_notifications'}
tab={'Respondent Notifications'}
fields={fields}
form={form}
/>
<StartPageTab key={'start_page'} tab={'Start Page'} />
<EndPageTab key={'end_page'} tab={'End Page'} />
*/}
</Tabs>
</Form>
</Structure>
)
}
export default withAuth(Create, ['admin'])

View File

@ -2,20 +2,20 @@ import {DeleteOutlined, EditOutlined, GlobalOutlined} from '@ant-design/icons/li
import {useQuery} from '@apollo/react-hooks' import {useQuery} from '@apollo/react-hooks'
import {Button, Popconfirm, Space, Table, Tooltip} from 'antd' import {Button, Popconfirm, Space, Table, Tooltip} from 'antd'
import {PaginationProps} from 'antd/es/pagination' import {PaginationProps} from 'antd/es/pagination'
import {NextPage} from 'next' import {DateTime} from 'components/date.time'
import Link from 'next/link' import {FormIsLive} from 'components/form/admin/is.live'
import React, {useState} from 'react' import Structure from 'components/structure'
import {DateTime} from '../../../components/date.time' import {TimeAgo} from 'components/time.ago'
import {FormIsLive} from '../../../components/form/admin/is.live' import {withAuth} from 'components/with.auth'
import Structure from '../../../components/structure'
import {TimeAgo} from '../../../components/time.ago'
import {withAuth} from '../../../components/with.auth'
import { import {
PAGER_FORM_QUERY, PAGER_FORM_QUERY,
PagerFormEntryQueryData, PagerFormEntryQueryData,
PagerFormQueryData, PagerFormQueryData,
PagerFormQueryVariables PagerFormQueryVariables
} from '../../../graphql/query/pager.form.query' } from 'graphql/query/pager.form.query'
import {NextPage} from 'next'
import Link from 'next/link'
import React, {useState} from 'react'
const Index: NextPage = () => { const Index: NextPage = () => {
const [pagination, setPagination] = useState<PaginationProps>({ const [pagination, setPagination] = useState<PaginationProps>({
@ -118,6 +118,18 @@ const Index: NextPage = () => {
{ href: '/admin', name: 'Home' }, { href: '/admin', name: 'Home' },
]} ]}
padded={false} padded={false}
extra={[
<Link
key={'create'}
href={'/admin/forms/create'}
>
<Button
type={'primary'}
>
New Form
</Button>
</Link>,
]}
> >
<Table <Table
columns={columns} columns={columns}

View File

@ -1,7 +1,7 @@
import Structure from 'components/structure'
import {withAuth} from 'components/with.auth'
import {NextPage} from 'next' import {NextPage} from 'next'
import React from 'react' import React from 'react'
import Structure from '../../components/structure'
import {withAuth} from '../../components/with.auth'
const Index: NextPage = () => { const Index: NextPage = () => {
return ( return (

View File

@ -1,7 +1,7 @@
import Structure from 'components/structure'
import {withAuth} from 'components/with.auth'
import {NextPage} from 'next' import {NextPage} from 'next'
import React from 'react' import React from 'react'
import Structure from '../../../../components/structure'
import {withAuth} from '../../../../components/with.auth'
const Index: NextPage = () => { const Index: NextPage = () => {
return ( return (

View File

@ -1,7 +1,7 @@
import Structure from 'components/structure'
import {withAuth} from 'components/with.auth'
import {NextPage} from 'next' import {NextPage} from 'next'
import React from 'react' import React from 'react'
import Structure from '../../../components/structure'
import {withAuth} from '../../../components/with.auth'
const Index: NextPage = () => { const Index: NextPage = () => {
return ( return (

View File

@ -1,15 +1,15 @@
import {useQuery} from '@apollo/react-hooks' import {useQuery} from '@apollo/react-hooks'
import {ErrorPage} from 'components/error.page'
import {Field} from 'components/form/field'
import {FormPage} from 'components/form/page'
import {LoadingPage} from 'components/loading.page'
import {useWindowSize} from 'components/use.window.size'
import {FORM_QUERY, FormQueryData, FormQueryVariables} from 'graphql/query/form.query'
import {NextPage} from 'next' import {NextPage} from 'next'
import React, {useState} from 'react' import React, {useState} from 'react'
import Swiper from 'react-id-swiper' import Swiper from 'react-id-swiper'
import {ReactIdSwiperProps} from 'react-id-swiper/lib/types' import {ReactIdSwiperProps} from 'react-id-swiper/lib/types'
import * as OriginalSwiper from 'swiper' import * as OriginalSwiper from 'swiper'
import {ErrorPage} from '../../../components/error.page'
import {Field} from '../../../components/form/field'
import {FormPage} from '../../../components/form/page'
import {LoadingPage} from '../../../components/loading.page'
import {useWindowSize} from '../../../components/use.window.size'
import {FORM_QUERY, FormQueryData, FormQueryVariables} from '../../../graphql/query/form.query'
interface Props { interface Props {
id: string id: string
@ -62,14 +62,14 @@ const Index: NextPage<Props> = ({id}) => {
}}> }}>
<Swiper {...swiperConfig}> <Swiper {...swiperConfig}>
{[ {[
<FormPage data.form.startPage.show ? <FormPage
key={'start'} key={'start'}
type={'start'} type={'start'}
page={data.form.startPage} page={data.form.startPage}
design={design} design={design}
next={goNext} next={goNext}
prev={goPrev} prev={goPrev}
/>, /> : undefined,
...data.form.fields.map(field => ( ...data.form.fields.map(field => (
<Field <Field
key={field.id} key={field.id}
@ -79,15 +79,15 @@ const Index: NextPage<Props> = ({id}) => {
prev={goPrev} prev={goPrev}
/> />
)), )),
<FormPage data.form.endPage.show ? <FormPage
key={'end'} key={'end'}
type={'end'} type={'end'}
page={data.form.endPage} page={data.form.endPage}
design={design} design={design}
next={goNext} next={goNext}
prev={goPrev} prev={goPrev}
/> /> : undefined
]} ].filter(e => !!e)}
</Swiper> </Swiper>
</div> </div>
) )

View File

@ -1,6 +1,6 @@
import {ErrorPage} from 'components/error.page'
import {NextPage} from 'next' import {NextPage} from 'next'
import React from 'react' import React from 'react'
import {ErrorPage} from '../../components/error.page'
const Index: NextPage = () => { const Index: NextPage = () => {
return ( return (

View File

@ -1,7 +1,7 @@
import {Layout} from 'antd' import {Layout} from 'antd'
import {AuthFooter} from 'components/auth/footer'
import {NextPage} from 'next' import {NextPage} from 'next'
import React from 'react' import React from 'react'
import {AuthFooter} from '../components/auth/footer'
const Index: NextPage = () => { const Index: NextPage = () => {
return ( return (

View File

@ -1,8 +1,8 @@
import {Alert} from 'antd' import {Alert} from 'antd'
import {AuthFooter} from 'components/auth/footer'
import {AuthLayout} from 'components/auth/layout'
import {NextPage} from 'next' import {NextPage} from 'next'
import React from 'react' import React from 'react'
import {AuthFooter} from '../../../components/auth/footer'
import {AuthLayout} from '../../../components/auth/layout'
const Index: NextPage = () => { const Index: NextPage = () => {
return ( return (

View File

@ -1,14 +1,14 @@
import {useMutation} from '@apollo/react-hooks' import {useMutation} from '@apollo/react-hooks'
import {Button, Form, Input, message} from 'antd' import {Button, Form, Input, message} from 'antd'
import {useForm} from 'antd/lib/form/Form' import {useForm} from 'antd/lib/form/Form'
import {AuthFooter} from 'components/auth/footer'
import {AuthLayout} from 'components/auth/layout'
import {setAuth} from 'components/with.auth'
import {LOGIN_MUTATION} from 'graphql/mutation/login.mutation'
import {NextPage} from 'next' import {NextPage} from 'next'
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 {AuthFooter} from '../../components/auth/footer'
import {AuthLayout} from '../../components/auth/layout'
import {setAuth} from '../../components/with.auth'
import {LOGIN_MUTATION} from '../../graphql/mutation/login.mutation'
const Index: NextPage = () => { const Index: NextPage = () => {
const [form] = useForm() const [form] = useForm()

View File

@ -1,8 +1,8 @@
import {Alert} from 'antd' import {Alert} from 'antd'
import {AuthFooter} from 'components/auth/footer'
import {AuthLayout} from 'components/auth/layout'
import {NextPage} from 'next' import {NextPage} from 'next'
import React from 'react' import React from 'react'
import {AuthFooter} from '../../components/auth/footer'
import {AuthLayout} from '../../components/auth/layout'
const Recover: NextPage = () => { const Recover: NextPage = () => {
return ( return (

View File

@ -1,14 +1,14 @@
import {useMutation} from '@apollo/react-hooks' import {useMutation} from '@apollo/react-hooks'
import {Button, Form, Input, message} from 'antd' import {Button, Form, Input, message} from 'antd'
import {useForm} from 'antd/lib/form/Form' import {useForm} from 'antd/lib/form/Form'
import {AuthFooter} from 'components/auth/footer'
import {AuthLayout} from 'components/auth/layout'
import {setAuth} from 'components/with.auth'
import {REGISTER_MUTATION} from 'graphql/mutation/register.mutation'
import {NextPage} from 'next' import {NextPage} from 'next'
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 {AuthFooter} from '../components/auth/footer'
import {AuthLayout} from '../components/auth/layout'
import {setAuth} from '../components/with.auth'
import {REGISTER_MUTATION} from '../graphql/mutation/register.mutation'
const Register: NextPage = () => { const Register: NextPage = () => {
const [form] = useForm() const [form] = useForm()