From 3f727215ca6adb63ce4e2fa1b0e73abf719b9430 Mon Sep 17 00:00:00 2001 From: Subina Date: Wed, 23 Aug 2023 13:59:53 +0545 Subject: [PATCH] Add edit functionality in question types - Integer question types - Text question types - Rank question types - Note question types - Date question types - Time question types - File question types - Image question types --- .../DateQuestionForm/index.tsx | 195 ++++++++++++++--- .../FileQuestionForm/index.tsx | 195 ++++++++++++++--- .../ImageQuestionForm/index.tsx | 196 ++++++++++++++--- .../IntegerQuestionForm/index.tsx | 191 ++++++++++++++--- .../NoteQuestionForm/index.tsx | 196 ++++++++++++++--- .../QuestionPreview/index.module.css | 6 + .../QuestionPreview/index.tsx | 157 ++++++++++---- .../RankQuestionForm/index.tsx | 197 +++++++++++++++--- .../TextQuestionForm/index.tsx | 192 ++++++++++++++--- .../TimeQuestionForm/index.tsx | 196 ++++++++++++++--- src/views/QuestionnaireEdit/index.tsx | 44 ++-- src/views/QuestionnaireEdit/queries.ts | 54 +++++ 12 files changed, 1569 insertions(+), 250 deletions(-) create mode 100644 src/views/QuestionnaireEdit/queries.ts diff --git a/src/views/QuestionnaireEdit/DateQuestionForm/index.tsx b/src/views/QuestionnaireEdit/DateQuestionForm/index.tsx index bcff3d0..2cca976 100644 --- a/src/views/QuestionnaireEdit/DateQuestionForm/index.tsx +++ b/src/views/QuestionnaireEdit/DateQuestionForm/index.tsx @@ -1,13 +1,18 @@ -import { useCallback } from 'react'; +import { useCallback, useMemo } from 'react'; import { - randomString, + isDefined, + isNotDefined, } from '@togglecorp/fujs'; import { Button, TextInput, useAlert, } from '@the-deep/deep-ui'; -import { gql, useMutation } from '@apollo/client'; +import { + gql, + useMutation, + useQuery, +} from '@apollo/client'; import { ObjectSchema, createSubmitHandler, @@ -20,14 +25,25 @@ import { import { CreateDateQuestionMutation, CreateDateQuestionMutationVariables, + UpdateDateQuestionMutation, + UpdateDateQuestionMutationVariables, + QuestionInfoQuery, + QuestionInfoQueryVariables, QuestionCreateInput, + QuestionUpdateInput, QuestionTypeEnum, } from '#generated/types'; import DateQuestionPreview from '#components/questionPreviews/DateQuestionPreview'; +import PillarSelectInput from '#components/PillarSelectInput'; +import { + QUESTION_FRAGMENT, + QUESTION_INFO, +} from '../queries.ts'; import styles from './index.module.css'; const CREATE_DATE_QUESTION = gql` + ${QUESTION_FRAGMENT} mutation CreateDateQuestion( $projectId: ID!, $input: QuestionCreateInput!, @@ -39,12 +55,38 @@ const CREATE_DATE_QUESTION = gql` ) { ok errors + result { + ...QuestionResponse + } } } } } `; +const UPDATE_DATE_QUESTION = gql` + ${QUESTION_FRAGMENT} + mutation UpdateDateQuestion( + $projectId: ID!, + $questionId: ID!, + $input: QuestionUpdateInput!, + ) { + private { + projectScope(pk: $projectId) { + updateQuestion ( + data: $input + id: $questionId, + ) { + ok + errors + result { + ...QuestionResponse + } + } + } + } + } +`; type FormType = PartialForm; type FormSchema = ObjectSchema; type FormSchemaFields = ReturnType; @@ -67,6 +109,10 @@ const schema: FormSchema = { required: true, requiredValidation: requiredStringCondition, }, + group: { + required: true, + requiredValidation: requiredStringCondition, + }, hint: {}, }), }; @@ -74,16 +120,67 @@ const schema: FormSchema = { interface Props { projectId: string; questionnaireId: string; + questionId?: string; } function DateQuestionForm(props: Props) { const { projectId, questionnaireId, + questionId, } = props; const alert = useAlert(); + const initialFormValue: FormType = { + type: 'DATE' as QuestionTypeEnum, + questionnaire: questionnaireId, + }; + + const { + pristine, + validate, + value: formValue, + error: formError, + setFieldValue, + setValue, + setError, + } = useForm(schema, { value: initialFormValue }); + + const fieldError = getErrorObject(formError); + + const questionInfoVariables = useMemo(() => { + if (isNotDefined(projectId) || isNotDefined(questionId)) { + return undefined; + } + return ({ + projectId, + questionId, + }); + }, [ + projectId, + questionId, + ]); + + useQuery( + QUESTION_INFO, + { + skip: isNotDefined(questionInfoVariables), + variables: questionInfoVariables, + onCompleted: (response) => { + const questionResponse = response.private.projectScope?.question; + setValue({ + name: questionResponse?.name, + type: questionResponse?.type, + questionnaire: questionResponse?.questionnaireId, + label: questionResponse?.label, + group: questionResponse?.groupId, + hint: questionResponse?.hint, + }); + }, + }, + ); + const [ triggerQuestionCreate, { loading: createQuestionPending }, @@ -115,39 +212,67 @@ function DateQuestionForm(props: Props) { }, }, ); - const initialFormValue: FormType = { - type: 'DATE' as QuestionTypeEnum, - questionnaire: questionnaireId, - name: randomString(), - }; - - const { - pristine, - validate, - value: formValue, - error: formError, - setFieldValue, - setError, - } = useForm(schema, { value: initialFormValue }); - const fieldError = getErrorObject(formError); + const [ + triggerQuestionUpdate, + { loading: updateQuestionPending }, + ] = useMutation( + UPDATE_DATE_QUESTION, + { + onCompleted: (questionResponse) => { + const response = questionResponse?.private?.projectScope?.updateQuestion; + if (!response) { + return; + } + if (response.ok) { + alert.show( + 'Question updated successfully.', + { variant: 'success' }, + ); + } else { + alert.show( + 'Failed to update question.', + { variant: 'error' }, + ); + } + }, + onError: () => { + alert.show( + 'Failed to update question.', + { variant: 'error' }, + ); + }, + }, + ); const handleQuestionSubmit = useCallback(() => { const handler = createSubmitHandler( validate, setError, (valueFromForm) => { - triggerQuestionCreate({ - variables: { - projectId, - input: valueFromForm as QuestionCreateInput, - }, - }); + if (isDefined(questionId)) { + triggerQuestionUpdate({ + variables: { + projectId, + questionId, + input: valueFromForm as QuestionUpdateInput, + }, + }); + } else { + triggerQuestionCreate({ + variables: { + projectId, + input: valueFromForm as QuestionCreateInput, + }, + }); + } }, ); handler(); }, [ triggerQuestionCreate, + triggerQuestionUpdate, + questionId, projectId, setError, validate, @@ -176,12 +301,32 @@ function DateQuestionForm(props: Props) { error={fieldError?.hint} onChange={setFieldValue} /> + + diff --git a/src/views/QuestionnaireEdit/FileQuestionForm/index.tsx b/src/views/QuestionnaireEdit/FileQuestionForm/index.tsx index 67bf2ce..b8e7884 100644 --- a/src/views/QuestionnaireEdit/FileQuestionForm/index.tsx +++ b/src/views/QuestionnaireEdit/FileQuestionForm/index.tsx @@ -1,13 +1,18 @@ -import { useCallback } from 'react'; +import { useCallback, useMemo } from 'react'; import { - randomString, + isDefined, + isNotDefined, } from '@togglecorp/fujs'; import { TextInput, Button, useAlert, } from '@the-deep/deep-ui'; -import { gql, useMutation } from '@apollo/client'; +import { + gql, + useMutation, + useQuery, +} from '@apollo/client'; import { ObjectSchema, createSubmitHandler, @@ -20,14 +25,25 @@ import { import { CreateFileQuestionMutation, CreateFileQuestionMutationVariables, + UpdateFileQuestionMutation, + UpdateFileQuestionMutationVariables, + QuestionInfoQuery, + QuestionInfoQueryVariables, QuestionCreateInput, + QuestionUpdateInput, QuestionTypeEnum, } from '#generated/types'; import FileQuestionPreview from '#components/questionPreviews/FileQuestionPreview'; +import PillarSelectInput from '#components/PillarSelectInput'; +import { + QUESTION_FRAGMENT, + QUESTION_INFO, +} from '../queries.ts'; import styles from './index.module.css'; const CREATE_FILE_QUESTION = gql` + ${QUESTION_FRAGMENT} mutation CreateFileQuestion( $projectId: ID!, $input: QuestionCreateInput!, @@ -39,12 +55,38 @@ const CREATE_FILE_QUESTION = gql` ) { ok errors + result { + ...QuestionResponse + } } } } } `; +const UPDATE_FILE_QUESTION = gql` + ${QUESTION_FRAGMENT} + mutation UpdateFileQuestion( + $projectId: ID!, + $questionId: ID!, + $input: QuestionUpdateInput!, + ) { + private { + projectScope(pk: $projectId) { + updateQuestion ( + data: $input + id: $questionId, + ) { + ok + errors + result { + ...QuestionResponse + } + } + } + } + } +`; type FormType = PartialForm; type FormSchema = ObjectSchema; type FormSchemaFields = ReturnType; @@ -67,6 +109,10 @@ const schema: FormSchema = { required: true, requiredValidation: requiredStringCondition, }, + group: { + required: true, + requiredValidation: requiredStringCondition, + }, hint: {}, }), }; @@ -74,16 +120,67 @@ const schema: FormSchema = { interface Props { projectId: string; questionnaireId: string; + questionId?: string; } function FileQuestionForm(props: Props) { const { projectId, questionnaireId, + questionId, } = props; const alert = useAlert(); + const initialFormValue: FormType = { + type: 'FILE' as QuestionTypeEnum, + questionnaire: questionnaireId, + }; + + const { + pristine, + validate, + value: formValue, + error: formError, + setFieldValue, + setValue, + setError, + } = useForm(schema, { value: initialFormValue }); + + const fieldError = getErrorObject(formError); + + const questionInfoVariables = useMemo(() => { + if (isNotDefined(projectId) || isNotDefined(questionId)) { + return undefined; + } + return ({ + projectId, + questionId, + }); + }, [ + projectId, + questionId, + ]); + + useQuery( + QUESTION_INFO, + { + skip: isNotDefined(questionInfoVariables), + variables: questionInfoVariables, + onCompleted: (response) => { + const questionResponse = response.private.projectScope?.question; + setValue({ + name: questionResponse?.name, + type: questionResponse?.type, + questionnaire: questionResponse?.questionnaireId, + label: questionResponse?.label, + group: questionResponse?.groupId, + hint: questionResponse?.hint, + }); + }, + }, + ); + const [ triggerQuestionCreate, { loading: createQuestionPending }, @@ -115,39 +212,67 @@ function FileQuestionForm(props: Props) { }, }, ); - const initialFormValue: FormType = { - type: 'FILE' as QuestionTypeEnum, - questionnaire: questionnaireId, - name: randomString(), - }; - - const { - pristine, - validate, - value: formValue, - error: formError, - setFieldValue, - setError, - } = useForm(schema, { value: initialFormValue }); - const fieldError = getErrorObject(formError); + const [ + triggerQuestionUpdate, + { loading: updateQuestionPending }, + ] = useMutation( + UPDATE_FILE_QUESTION, + { + onCompleted: (questionResponse) => { + const response = questionResponse?.private?.projectScope?.updateQuestion; + if (!response) { + return; + } + if (response.ok) { + alert.show( + 'Question updated successfully.', + { variant: 'success' }, + ); + } else { + alert.show( + 'Failed to update question.', + { variant: 'error' }, + ); + } + }, + onError: () => { + alert.show( + 'Failed to update question.', + { variant: 'error' }, + ); + }, + }, + ); const handleQuestionSubmit = useCallback(() => { const handler = createSubmitHandler( validate, setError, (valueFromForm) => { - triggerQuestionCreate({ - variables: { - projectId, - input: valueFromForm as QuestionCreateInput, - }, - }); + if (isDefined(questionId)) { + triggerQuestionUpdate({ + variables: { + projectId, + questionId, + input: valueFromForm as QuestionUpdateInput, + }, + }); + } else { + triggerQuestionCreate({ + variables: { + projectId, + input: valueFromForm as QuestionCreateInput, + }, + }); + } }, ); handler(); }, [ triggerQuestionCreate, + triggerQuestionUpdate, + questionId, projectId, setError, validate, @@ -175,12 +300,32 @@ function FileQuestionForm(props: Props) { error={fieldError?.hint} onChange={setFieldValue} /> + + diff --git a/src/views/QuestionnaireEdit/ImageQuestionForm/index.tsx b/src/views/QuestionnaireEdit/ImageQuestionForm/index.tsx index f85133c..1cf0ecc 100644 --- a/src/views/QuestionnaireEdit/ImageQuestionForm/index.tsx +++ b/src/views/QuestionnaireEdit/ImageQuestionForm/index.tsx @@ -1,13 +1,18 @@ -import { useCallback } from 'react'; +import { useCallback, useMemo } from 'react'; import { - randomString, + isDefined, + isNotDefined, } from '@togglecorp/fujs'; import { TextInput, Button, useAlert, } from '@the-deep/deep-ui'; -import { gql, useMutation } from '@apollo/client'; +import { + gql, + useMutation, + useQuery, +} from '@apollo/client'; import { ObjectSchema, createSubmitHandler, @@ -20,14 +25,25 @@ import { import { CreateImageQuestionMutation, CreateImageQuestionMutationVariables, + UpdateImageQuestionMutation, + UpdateImageQuestionMutationVariables, + QuestionInfoQuery, + QuestionInfoQueryVariables, QuestionCreateInput, + QuestionUpdateInput, QuestionTypeEnum, } from '#generated/types'; import ImageQuestionPreview from '#components/questionPreviews/ImageQuestionPreview'; +import PillarSelectInput from '#components/PillarSelectInput'; +import { + QUESTION_FRAGMENT, + QUESTION_INFO, +} from '../queries.ts'; import styles from './index.module.css'; const CREATE_IMAGE_QUESTION = gql` + ${QUESTION_FRAGMENT} mutation CreateImageQuestion( $projectId: ID!, $input: QuestionCreateInput!, @@ -39,12 +55,38 @@ const CREATE_IMAGE_QUESTION = gql` ) { ok errors + result { + ...QuestionResponse + } } } } } `; +const UPDATE_IMAGE_QUESTION = gql` + ${QUESTION_FRAGMENT} + mutation UpdateImageQuestion( + $projectId: ID!, + $questionId: ID!, + $input: QuestionUpdateInput!, + ) { + private { + projectScope(pk: $projectId) { + updateQuestion ( + data: $input + id: $questionId, + ) { + ok + errors + result { + ...QuestionResponse + } + } + } + } + } +`; type FormType = PartialForm; type FormSchema = ObjectSchema; type FormSchemaFields = ReturnType; @@ -67,6 +109,10 @@ const schema: FormSchema = { required: true, requiredValidation: requiredStringCondition, }, + group: { + required: true, + requiredValidation: requiredStringCondition, + }, hint: {}, }), }; @@ -74,16 +120,67 @@ const schema: FormSchema = { interface Props { projectId: string; questionnaireId: string; + questionId?: string; } function ImageQuestionForm(props: Props) { const { projectId, questionnaireId, + questionId, } = props; const alert = useAlert(); + const initialFormValue: FormType = { + type: 'IMAGE' as QuestionTypeEnum, + questionnaire: questionnaireId, + }; + + const { + pristine, + validate, + value: formValue, + error: formError, + setFieldValue, + setValue, + setError, + } = useForm(schema, { value: initialFormValue }); + + const fieldError = getErrorObject(formError); + + const questionInfoVariables = useMemo(() => { + if (isNotDefined(projectId) || isNotDefined(questionId)) { + return undefined; + } + return ({ + projectId, + questionId, + }); + }, [ + projectId, + questionId, + ]); + + useQuery( + QUESTION_INFO, + { + skip: isNotDefined(questionInfoVariables), + variables: questionInfoVariables, + onCompleted: (response) => { + const questionResponse = response.private.projectScope?.question; + setValue({ + name: questionResponse?.name, + type: questionResponse?.type, + questionnaire: questionResponse?.questionnaireId, + label: questionResponse?.label, + group: questionResponse?.groupId, + hint: questionResponse?.hint, + }); + }, + }, + ); + const [ triggerQuestionCreate, { loading: createQuestionPending }, @@ -115,39 +212,66 @@ function ImageQuestionForm(props: Props) { }, }, ); - const initialFormValue: FormType = { - type: 'IMAGE' as QuestionTypeEnum, - questionnaire: questionnaireId, - name: randomString(), - }; - - const { - pristine, - validate, - value: formValue, - error: formError, - setFieldValue, - setError, - } = useForm(schema, { value: initialFormValue }); - - const fieldError = getErrorObject(formError); + const [ + triggerQuestionUpdate, + { loading: updateQuestionPending }, + ] = useMutation( + UPDATE_IMAGE_QUESTION, + { + onCompleted: (questionResponse) => { + const response = questionResponse?.private?.projectScope?.updateQuestion; + if (!response) { + return; + } + if (response.ok) { + alert.show( + 'Question updated successfully.', + { variant: 'success' }, + ); + } else { + alert.show( + 'Failed to update question.', + { variant: 'error' }, + ); + } + }, + onError: () => { + alert.show( + 'Failed to update question.', + { variant: 'error' }, + ); + }, + }, + ); const handleQuestionSubmit = useCallback(() => { const handler = createSubmitHandler( validate, setError, (valueFromForm) => { - triggerQuestionCreate({ - variables: { - projectId, - input: valueFromForm as QuestionCreateInput, - }, - }); + if (isDefined(questionId)) { + triggerQuestionUpdate({ + variables: { + projectId, + questionId, + input: valueFromForm as QuestionUpdateInput, + }, + }); + } else { + triggerQuestionCreate({ + variables: { + projectId, + input: valueFromForm as QuestionCreateInput, + }, + }); + } }, ); handler(); }, [ triggerQuestionCreate, + triggerQuestionUpdate, + questionId, projectId, setError, validate, @@ -175,12 +299,32 @@ function ImageQuestionForm(props: Props) { error={fieldError?.hint} onChange={setFieldValue} /> + + diff --git a/src/views/QuestionnaireEdit/IntegerQuestionForm/index.tsx b/src/views/QuestionnaireEdit/IntegerQuestionForm/index.tsx index ae5b097..6d25e31 100644 --- a/src/views/QuestionnaireEdit/IntegerQuestionForm/index.tsx +++ b/src/views/QuestionnaireEdit/IntegerQuestionForm/index.tsx @@ -1,13 +1,14 @@ -import { useCallback } from 'react'; +import { useCallback, useMemo } from 'react'; import { - randomString, + isNotDefined, + isDefined, } from '@togglecorp/fujs'; import { Button, TextInput, useAlert, } from '@the-deep/deep-ui'; -import { gql, useMutation } from '@apollo/client'; +import { gql, useMutation, useQuery } from '@apollo/client'; import { ObjectSchema, createSubmitHandler, @@ -20,14 +21,25 @@ import { import { CreateIntegerQuestionMutation, CreateIntegerQuestionMutationVariables, + UpdateIntegerQuestionMutation, + UpdateIntegerQuestionMutationVariables, + QuestionInfoQuery, + QuestionInfoQueryVariables, QuestionCreateInput, + QuestionUpdateInput, QuestionTypeEnum, } from '#generated/types'; import IntegerQuestionPreview from '#components/questionPreviews/IntegerQuestionPreview'; +import PillarSelectInput from '#components/PillarSelectInput'; +import { + QUESTION_FRAGMENT, + QUESTION_INFO, +} from '../queries.ts'; import styles from './index.module.css'; const CREATE_INTEGER_QUESTION = gql` + ${QUESTION_FRAGMENT} mutation CreateIntegerQuestion( $projectId: ID!, $input: QuestionCreateInput!, @@ -39,12 +51,38 @@ const CREATE_INTEGER_QUESTION = gql` ) { ok errors + result { + ...QuestionResponse + } } } } } `; +const UPDATE_INTEGER_QUESTION = gql` + ${QUESTION_FRAGMENT} + mutation UpdateIntegerQuestion( + $projectId: ID!, + $questionId: ID!, + $input: QuestionUpdateInput!, + ) { + private { + projectScope(pk: $projectId) { + updateQuestion ( + data: $input + id: $questionId, + ) { + ok + errors + result { + ...QuestionResponse + } + } + } + } + } +`; type FormType = PartialForm; type FormSchema = ObjectSchema; type FormSchemaFields = ReturnType; @@ -67,6 +105,10 @@ const schema: FormSchema = { required: true, requiredValidation: requiredStringCondition, }, + group: { + required: true, + requiredValidation: requiredStringCondition, + }, hint: {}, }), }; @@ -74,16 +116,67 @@ const schema: FormSchema = { interface Props { projectId: string; questionnaireId: string; + questionId?: string; } function IntegerQuestionForm(props: Props) { const { projectId, questionnaireId, + questionId, } = props; const alert = useAlert(); + const initialFormValue: FormType = { + type: 'INTEGER' as QuestionTypeEnum, + questionnaire: questionnaireId, + }; + + const { + pristine, + validate, + value: formValue, + error: formError, + setFieldValue, + setValue, + setError, + } = useForm(schema, { value: initialFormValue }); + + const fieldError = getErrorObject(formError); + + const questionInfoVariables = useMemo(() => { + if (isNotDefined(projectId) || isNotDefined(questionId)) { + return undefined; + } + return ({ + projectId, + questionId, + }); + }, [ + projectId, + questionId, + ]); + + useQuery( + QUESTION_INFO, + { + skip: isNotDefined(questionInfoVariables), + variables: questionInfoVariables, + onCompleted: (response) => { + const questionResponse = response.private.projectScope?.question; + setValue({ + name: questionResponse?.name, + type: questionResponse?.type, + questionnaire: questionResponse?.questionnaireId, + label: questionResponse?.label, + group: questionResponse?.groupId, + hint: questionResponse?.hint, + }); + }, + }, + ); + const [ triggerQuestionCreate, { loading: createQuestionPending }, @@ -115,39 +208,67 @@ function IntegerQuestionForm(props: Props) { }, }, ); - const initialFormValue: FormType = { - type: 'INTEGER' as QuestionTypeEnum, - questionnaire: questionnaireId, - name: randomString(), - }; - - const { - pristine, - validate, - value: formValue, - error: formError, - setFieldValue, - setError, - } = useForm(schema, { value: initialFormValue }); - const fieldError = getErrorObject(formError); + const [ + triggerQuestionUpdate, + { loading: updateQuestionPending }, + ] = useMutation( + UPDATE_INTEGER_QUESTION, + { + onCompleted: (questionResponse) => { + const response = questionResponse?.private?.projectScope?.updateQuestion; + if (!response) { + return; + } + if (response.ok) { + alert.show( + 'Question updated successfully.', + { variant: 'success' }, + ); + } else { + alert.show( + 'Failed to update question.', + { variant: 'error' }, + ); + } + }, + onError: () => { + alert.show( + 'Failed to update question.', + { variant: 'error' }, + ); + }, + }, + ); const handleQuestionSubmit = useCallback(() => { const handler = createSubmitHandler( validate, setError, (valueFromForm) => { - triggerQuestionCreate({ - variables: { - projectId, - input: valueFromForm as QuestionCreateInput, - }, - }); + if (isDefined(questionId)) { + triggerQuestionUpdate({ + variables: { + projectId, + questionId, + input: valueFromForm as QuestionUpdateInput, + }, + }); + } else { + triggerQuestionCreate({ + variables: { + projectId, + input: valueFromForm as QuestionCreateInput, + }, + }); + } }, ); handler(); }, [ triggerQuestionCreate, + triggerQuestionUpdate, + questionId, projectId, setError, validate, @@ -176,12 +297,32 @@ function IntegerQuestionForm(props: Props) { error={fieldError?.hint} onChange={setFieldValue} /> + + diff --git a/src/views/QuestionnaireEdit/NoteQuestionForm/index.tsx b/src/views/QuestionnaireEdit/NoteQuestionForm/index.tsx index 54af7fd..56ae964 100644 --- a/src/views/QuestionnaireEdit/NoteQuestionForm/index.tsx +++ b/src/views/QuestionnaireEdit/NoteQuestionForm/index.tsx @@ -1,13 +1,18 @@ -import { useCallback } from 'react'; +import { useCallback, useMemo } from 'react'; import { - randomString, + isDefined, + isNotDefined, } from '@togglecorp/fujs'; import { TextInput, Button, useAlert, } from '@the-deep/deep-ui'; -import { gql, useMutation } from '@apollo/client'; +import { + gql, + useMutation, + useQuery, +} from '@apollo/client'; import { ObjectSchema, createSubmitHandler, @@ -20,14 +25,25 @@ import { import { CreateNoteQuestionMutation, CreateNoteQuestionMutationVariables, + UpdateNoteQuestionMutation, + UpdateNoteQuestionMutationVariables, + QuestionInfoQuery, + QuestionInfoQueryVariables, QuestionCreateInput, + QuestionUpdateInput, QuestionTypeEnum, } from '#generated/types'; import NoteQuestionPreview from '#components/questionPreviews/NoteQuestionPreview'; +import PillarSelectInput from '#components/PillarSelectInput'; +import { + QUESTION_FRAGMENT, + QUESTION_INFO, +} from '../queries.ts'; import styles from './index.module.css'; const CREATE_NOTE_QUESTION = gql` + ${QUESTION_FRAGMENT} mutation CreateNoteQuestion( $projectId: ID!, $input: QuestionCreateInput!, @@ -39,6 +55,33 @@ const CREATE_NOTE_QUESTION = gql` ) { ok errors + result { + ...QuestionResponse + } + } + } + } + } +`; + +const UPDATE_NOTE_QUESTION = gql` + ${QUESTION_FRAGMENT} + mutation UpdateNoteQuestion( + $projectId: ID!, + $questionId: ID!, + $input: QuestionUpdateInput!, + ) { + private { + projectScope(pk: $projectId) { + updateQuestion ( + data: $input + id: $questionId, + ) { + ok + errors + result { + ...QuestionResponse + } } } } @@ -67,22 +110,77 @@ const schema: FormSchema = { required: true, requiredValidation: requiredStringCondition, }, + group: { + required: true, + requiredValidation: requiredStringCondition, + }, }), }; interface Props { projectId: string; questionnaireId: string; + questionId?: string; } function NoteQuestionForm(props: Props) { const { projectId, questionnaireId, + questionId, } = props; const alert = useAlert(); + const initialFormValue: FormType = { + type: 'NOTE' as QuestionTypeEnum, + questionnaire: questionnaireId, + }; + + const { + pristine, + validate, + value: formValue, + error: formError, + setFieldValue, + setValue, + setError, + } = useForm(schema, { value: initialFormValue }); + + const fieldError = getErrorObject(formError); + + const questionInfoVariables = useMemo(() => { + if (isNotDefined(projectId) || isNotDefined(questionId)) { + return undefined; + } + return ({ + projectId, + questionId, + }); + }, [ + projectId, + questionId, + ]); + + useQuery( + QUESTION_INFO, + { + skip: isNotDefined(questionInfoVariables), + variables: questionInfoVariables, + onCompleted: (response) => { + const questionResponse = response.private.projectScope?.question; + setValue({ + name: questionResponse?.name, + type: questionResponse?.type, + questionnaire: questionResponse?.questionnaireId, + label: questionResponse?.label, + group: questionResponse?.groupId, + hint: questionResponse?.hint, + }); + }, + }, + ); + const [ triggerQuestionCreate, { loading: createQuestionPending }, @@ -114,39 +212,67 @@ function NoteQuestionForm(props: Props) { }, }, ); - const initialFormValue: FormType = { - type: 'NOTE' as QuestionTypeEnum, - questionnaire: questionnaireId, - name: randomString(), - }; - - const { - pristine, - validate, - value: formValue, - error: formError, - setFieldValue, - setError, - } = useForm(schema, { value: initialFormValue }); - const fieldError = getErrorObject(formError); + const [ + triggerQuestionUpdate, + { loading: updateQuestionPending }, + ] = useMutation( + UPDATE_NOTE_QUESTION, + { + onCompleted: (questionResponse) => { + const response = questionResponse?.private?.projectScope?.updateQuestion; + if (!response) { + return; + } + if (response.ok) { + alert.show( + 'Question updated successfully.', + { variant: 'success' }, + ); + } else { + alert.show( + 'Failed to update question.', + { variant: 'error' }, + ); + } + }, + onError: () => { + alert.show( + 'Failed to update question.', + { variant: 'error' }, + ); + }, + }, + ); const handleQuestionSubmit = useCallback(() => { const handler = createSubmitHandler( validate, setError, (valueFromForm) => { - triggerQuestionCreate({ - variables: { - projectId, - input: valueFromForm as QuestionCreateInput, - }, - }); + if (isDefined(questionId)) { + triggerQuestionUpdate({ + variables: { + projectId, + questionId, + input: valueFromForm as QuestionUpdateInput, + }, + }); + } else { + triggerQuestionCreate({ + variables: { + projectId, + input: valueFromForm as QuestionCreateInput, + }, + }); + } }, ); handler(); }, [ triggerQuestionCreate, + triggerQuestionUpdate, + questionId, projectId, setError, validate, @@ -166,12 +292,32 @@ function NoteQuestionForm(props: Props) { error={fieldError?.label} onChange={setFieldValue} /> + + diff --git a/src/views/QuestionnaireEdit/QuestionPreview/index.module.css b/src/views/QuestionnaireEdit/QuestionPreview/index.module.css index 4398b11..c8f3629 100644 --- a/src/views/QuestionnaireEdit/QuestionPreview/index.module.css +++ b/src/views/QuestionnaireEdit/QuestionPreview/index.module.css @@ -1,9 +1,15 @@ .preview { border: none !important; background-color: var(--dui-color-background); + overflow-y: auto; gap: var(--dui-spacing-large); + .question-wrapper { + padding: var(--dui-spacing-medium); + } + .question-item { + flex-grow: 1; padding: var(--dui-spacing-extra-large); } } diff --git a/src/views/QuestionnaireEdit/QuestionPreview/index.tsx b/src/views/QuestionnaireEdit/QuestionPreview/index.tsx index 7ef1a3a..8f78eb7 100644 --- a/src/views/QuestionnaireEdit/QuestionPreview/index.tsx +++ b/src/views/QuestionnaireEdit/QuestionPreview/index.tsx @@ -1,8 +1,16 @@ +import { useCallback } from 'react'; +import { + IoEllipsisVertical, +} from 'react-icons/io5'; import { isNotDefined, + isDefined, } from '@togglecorp/fujs'; import { TabPanel, + Element, + QuickActionDropdownMenu, + DropdownMenuItem, } from '@the-deep/deep-ui'; import { @@ -13,6 +21,11 @@ import IntegerQuestionPreview from '#components/questionPreviews/IntegerQuestion import RankQuestionPreview from '#components/questionPreviews/RankQuestionPreview'; import DateQuestionPreview from '#components/questionPreviews/DateQuestionPreview'; import TimeQuestionPreview from '#components/questionPreviews/TimeQuestionPreview'; +import NoteQuestionPreview from '#components/questionPreviews/NoteQuestionPreview'; +import ImageQuestionPreview from '#components/questionPreviews/ImageQuestionPreview'; +import FileQuestionPreview from '#components/questionPreviews/FileQuestionPreview'; +import SelectOneQuestionPreview from '#components/questionPreviews/SelectOneQuestionPreview'; +import SelectMultipleQuestionPreview from '#components/questionPreviews/SelectMultipleQuestionPreview'; import styles from './index.module.css'; @@ -20,13 +33,32 @@ type Question = NonNullable void; + setSelectedQuestionType: React.Dispatch>; + projectId: string | undefined; + setActiveQuestionId: React.Dispatch>; } function QuestionPreview(props: QuestionProps) { const { question, + showAddQuestionPane, + setSelectedQuestionType, + setActiveQuestionId, + projectId, } = props; + const handleEditQuestionClick = useCallback((val: string) => { + showAddQuestionPane(); + setSelectedQuestionType(question.type); + setActiveQuestionId(val); + }, [ + showAddQuestionPane, + setSelectedQuestionType, + question.type, + setActiveQuestionId, + ]); + if (isNotDefined(question.groupId)) { return null; } @@ -36,41 +68,96 @@ function QuestionPreview(props: QuestionProps) { name={question.groupId} className={styles.preview} > - {(question.type === 'TEXT') && ( - - )} - {(question.type === 'INTEGER') && ( - - )} - {(question.type === 'RANK') && ( - - )} - {(question.type === 'DATE') && ( - - )} - {(question.type === 'TIME') && ( - - )} + } + variant="secondary" + > + + Edit question + + + )} + > + {(question.type === 'TEXT') && ( + + )} + {(question.type === 'INTEGER') && ( + + )} + {(question.type === 'RANK') && ( + + )} + {(question.type === 'DATE') && ( + + )} + {(question.type === 'TIME') && ( + + )} + {(question.type === 'NOTE') && ( + + )} + {(question.type === 'FILE') && ( + + )} + {(question.type === 'IMAGE') && ( + + )} + {(question.type === 'SELECT_ONE') && isDefined(projectId) && ( + + )} + {(question.type === 'SELECT_MULTIPLE') && isDefined(projectId) && ( + + )} + ); } diff --git a/src/views/QuestionnaireEdit/RankQuestionForm/index.tsx b/src/views/QuestionnaireEdit/RankQuestionForm/index.tsx index 1a7995c..8783f48 100644 --- a/src/views/QuestionnaireEdit/RankQuestionForm/index.tsx +++ b/src/views/QuestionnaireEdit/RankQuestionForm/index.tsx @@ -1,13 +1,18 @@ -import { useCallback } from 'react'; +import { useCallback, useMemo } from 'react'; import { - randomString, + isDefined, + isNotDefined, } from '@togglecorp/fujs'; import { TextInput, Button, useAlert, } from '@the-deep/deep-ui'; -import { gql, useMutation } from '@apollo/client'; +import { + gql, + useMutation, + useQuery, +} from '@apollo/client'; import { ObjectSchema, createSubmitHandler, @@ -20,14 +25,25 @@ import { import { CreateRankQuestionMutation, CreateRankQuestionMutationVariables, + UpdateRankQuestionMutation, + UpdateRankQuestionMutationVariables, + QuestionInfoQuery, + QuestionInfoQueryVariables, QuestionCreateInput, + QuestionUpdateInput, QuestionTypeEnum, } from '#generated/types'; import TextQuestionPreview from '#components/questionPreviews/TextQuestionPreview'; +import PillarSelectInput from '#components/PillarSelectInput'; +import { + QUESTION_FRAGMENT, + QUESTION_INFO, +} from '../queries.ts'; import styles from './index.module.css'; const CREATE_RANK_QUESTION = gql` + ${QUESTION_FRAGMENT} mutation CreateRankQuestion( $projectId: ID!, $input: QuestionCreateInput!, @@ -39,6 +55,33 @@ const CREATE_RANK_QUESTION = gql` ) { ok errors + result { + ...QuestionResponse + } + } + } + } + } +`; + +const UPDATE_RANK_QUESTION = gql` + ${QUESTION_FRAGMENT} + mutation UpdateRankQuestion( + $projectId: ID!, + $questionId: ID!, + $input: QuestionUpdateInput!, + ) { + private { + projectScope(pk: $projectId) { + updateQuestion ( + data: $input + id: $questionId, + ) { + ok + errors + result { + ...QuestionResponse + } } } } @@ -67,6 +110,10 @@ const schema: FormSchema = { required: true, requiredValidation: requiredStringCondition, }, + group: { + required: true, + requiredValidation: requiredStringCondition, + }, hint: {}, }), }; @@ -74,16 +121,67 @@ const schema: FormSchema = { interface Props { projectId: string; questionnaireId: string; + questionId?: string; } function RankQuestionForm(props: Props) { const { projectId, questionnaireId, + questionId, } = props; const alert = useAlert(); + const initialFormValue: FormType = { + type: 'RANK' as QuestionTypeEnum, + questionnaire: questionnaireId, + }; + + const { + pristine, + validate, + value: formValue, + error: formError, + setFieldValue, + setValue, + setError, + } = useForm(schema, { value: initialFormValue }); + + const fieldError = getErrorObject(formError); + + const questionInfoVariables = useMemo(() => { + if (isNotDefined(projectId) || isNotDefined(questionId)) { + return undefined; + } + return ({ + projectId, + questionId, + }); + }, [ + projectId, + questionId, + ]); + + useQuery( + QUESTION_INFO, + { + skip: isNotDefined(questionInfoVariables), + variables: questionInfoVariables, + onCompleted: (response) => { + const questionResponse = response.private.projectScope?.question; + setValue({ + name: questionResponse?.name, + type: questionResponse?.type, + questionnaire: questionResponse?.questionnaireId, + label: questionResponse?.label, + group: questionResponse?.groupId, + hint: questionResponse?.hint, + }); + }, + }, + ); + const [ triggerQuestionCreate, { loading: createQuestionPending }, @@ -115,39 +213,66 @@ function RankQuestionForm(props: Props) { }, }, ); - const initialFormValue: FormType = { - type: 'RANK' as QuestionTypeEnum, - questionnaire: questionnaireId, - name: randomString(), - }; - - const { - pristine, - validate, - value: formValue, - error: formError, - setFieldValue, - setError, - } = useForm(schema, { value: initialFormValue }); - - const fieldError = getErrorObject(formError); + const [ + triggerQuestionUpdate, + { loading: updateQuestionPending }, + ] = useMutation( + UPDATE_RANK_QUESTION, + { + onCompleted: (questionResponse) => { + const response = questionResponse?.private?.projectScope?.updateQuestion; + if (!response) { + return; + } + if (response.ok) { + alert.show( + 'Question updated successfully.', + { variant: 'success' }, + ); + } else { + alert.show( + 'Failed to update question.', + { variant: 'error' }, + ); + } + }, + onError: () => { + alert.show( + 'Failed to update question.', + { variant: 'error' }, + ); + }, + }, + ); const handleQuestionSubmit = useCallback(() => { const handler = createSubmitHandler( validate, setError, (valueFromForm) => { - triggerQuestionCreate({ - variables: { - projectId, - input: valueFromForm as QuestionCreateInput, - }, - }); + if (isDefined(questionId)) { + triggerQuestionUpdate({ + variables: { + projectId, + questionId, + input: valueFromForm as QuestionUpdateInput, + }, + }); + } else { + triggerQuestionCreate({ + variables: { + projectId, + input: valueFromForm as QuestionCreateInput, + }, + }); + } }, ); handler(); }, [ triggerQuestionCreate, + triggerQuestionUpdate, + questionId, projectId, setError, validate, @@ -175,12 +300,32 @@ function RankQuestionForm(props: Props) { error={fieldError?.hint} onChange={setFieldValue} /> + + diff --git a/src/views/QuestionnaireEdit/TextQuestionForm/index.tsx b/src/views/QuestionnaireEdit/TextQuestionForm/index.tsx index cb57a84..5309ee9 100644 --- a/src/views/QuestionnaireEdit/TextQuestionForm/index.tsx +++ b/src/views/QuestionnaireEdit/TextQuestionForm/index.tsx @@ -1,13 +1,14 @@ -import { useCallback } from 'react'; +import { useCallback, useMemo } from 'react'; import { - randomString, + isNotDefined, + isDefined, } from '@togglecorp/fujs'; import { TextInput, Button, useAlert, } from '@the-deep/deep-ui'; -import { gql, useMutation } from '@apollo/client'; +import { gql, useMutation, useQuery } from '@apollo/client'; import { ObjectSchema, createSubmitHandler, @@ -20,14 +21,25 @@ import { import { CreateTextQuestionMutation, CreateTextQuestionMutationVariables, + UpdateTextQuestionMutation, + UpdateTextQuestionMutationVariables, + QuestionInfoQuery, + QuestionInfoQueryVariables, QuestionCreateInput, + QuestionUpdateInput, QuestionTypeEnum, } from '#generated/types'; import TextQuestionPreview from '#components/questionPreviews/TextQuestionPreview'; +import PillarSelectInput from '#components/PillarSelectInput'; +import { + QUESTION_FRAGMENT, + QUESTION_INFO, +} from '../queries.ts'; import styles from './index.module.css'; const CREATE_TEXT_QUESTION = gql` + ${QUESTION_FRAGMENT} mutation CreateTextQuestion( $projectId: ID!, $input: QuestionCreateInput!, @@ -39,6 +51,33 @@ const CREATE_TEXT_QUESTION = gql` ) { ok errors + result { + ...QuestionResponse + } + } + } + } + } +`; + +const UPDATE_TEXT_QUESTION = gql` + ${QUESTION_FRAGMENT} + mutation UpdateTextQuestion( + $projectId: ID!, + $questionId: ID!, + $input: QuestionUpdateInput!, + ) { + private { + projectScope(pk: $projectId) { + updateQuestion ( + data: $input + id: $questionId, + ) { + ok + errors + result { + ...QuestionResponse + } } } } @@ -67,6 +106,10 @@ const schema: FormSchema = { required: true, requiredValidation: requiredStringCondition, }, + group: { + required: true, + requiredValidation: requiredStringCondition, + }, hint: {}, }), }; @@ -74,16 +117,67 @@ const schema: FormSchema = { interface Props { projectId: string; questionnaireId: string; + questionId?: string; } function TextQuestionForm(props: Props) { const { projectId, questionnaireId, + questionId, } = props; const alert = useAlert(); + const initialFormValue: FormType = { + type: 'TEXT' as QuestionTypeEnum, + questionnaire: questionnaireId, + }; + + const { + pristine, + validate, + value: formValue, + error: formError, + setFieldValue, + setValue, + setError, + } = useForm(schema, { value: initialFormValue }); + + const fieldError = getErrorObject(formError); + + const questionInfoVariables = useMemo(() => { + if (isNotDefined(projectId) || isNotDefined(questionId)) { + return undefined; + } + return ({ + projectId, + questionId, + }); + }, [ + projectId, + questionId, + ]); + + useQuery( + QUESTION_INFO, + { + skip: isNotDefined(questionInfoVariables), + variables: questionInfoVariables, + onCompleted: (response) => { + const questionResponse = response.private.projectScope?.question; + setValue({ + name: questionResponse?.name, + type: questionResponse?.type, + questionnaire: questionResponse?.questionnaireId, + label: questionResponse?.label, + group: questionResponse?.groupId, + hint: questionResponse?.hint, + }); + }, + }, + ); + const [ triggerQuestionCreate, { loading: createQuestionPending }, @@ -115,39 +209,67 @@ function TextQuestionForm(props: Props) { }, }, ); - const initialFormValue: FormType = { - type: 'TEXT' as QuestionTypeEnum, - questionnaire: questionnaireId, - name: randomString(), - }; - - const { - pristine, - validate, - value: formValue, - error: formError, - setFieldValue, - setError, - } = useForm(schema, { value: initialFormValue }); - const fieldError = getErrorObject(formError); + const [ + triggerQuestionUpdate, + { loading: updateQuestionPending }, + ] = useMutation( + UPDATE_TEXT_QUESTION, + { + onCompleted: (questionResponse) => { + const response = questionResponse?.private?.projectScope?.updateQuestion; + if (!response) { + return; + } + if (response.ok) { + alert.show( + 'Question updated successfully.', + { variant: 'success' }, + ); + } else { + alert.show( + 'Failed to update question.', + { variant: 'error' }, + ); + } + }, + onError: () => { + alert.show( + 'Failed to update question.', + { variant: 'error' }, + ); + }, + }, + ); const handleQuestionSubmit = useCallback(() => { const handler = createSubmitHandler( validate, setError, (valueFromForm) => { - triggerQuestionCreate({ - variables: { - projectId, - input: valueFromForm as QuestionCreateInput, - }, - }); + if (isDefined(questionId)) { + triggerQuestionUpdate({ + variables: { + projectId, + questionId, + input: valueFromForm as QuestionUpdateInput, + }, + }); + } else { + triggerQuestionCreate({ + variables: { + projectId, + input: valueFromForm as QuestionCreateInput, + }, + }); + } }, ); handler(); }, [ triggerQuestionCreate, + triggerQuestionUpdate, + questionId, projectId, setError, validate, @@ -175,12 +297,32 @@ function TextQuestionForm(props: Props) { error={fieldError?.hint} onChange={setFieldValue} /> + + diff --git a/src/views/QuestionnaireEdit/TimeQuestionForm/index.tsx b/src/views/QuestionnaireEdit/TimeQuestionForm/index.tsx index 910e7b9..69405c0 100644 --- a/src/views/QuestionnaireEdit/TimeQuestionForm/index.tsx +++ b/src/views/QuestionnaireEdit/TimeQuestionForm/index.tsx @@ -1,13 +1,18 @@ -import { useCallback } from 'react'; +import { useCallback, useMemo } from 'react'; import { - randomString, + isDefined, + isNotDefined, } from '@togglecorp/fujs'; import { Button, TextInput, useAlert, } from '@the-deep/deep-ui'; -import { gql, useMutation } from '@apollo/client'; +import { + gql, + useMutation, + useQuery, +} from '@apollo/client'; import { ObjectSchema, createSubmitHandler, @@ -20,14 +25,25 @@ import { import { CreateTimeQuestionMutation, CreateTimeQuestionMutationVariables, + UpdateTimeQuestionMutation, + UpdateTimeQuestionMutationVariables, + QuestionInfoQuery, + QuestionInfoQueryVariables, QuestionCreateInput, + QuestionUpdateInput, QuestionTypeEnum, } from '#generated/types'; import TimeQuestionPreview from '#components/questionPreviews/TimeQuestionPreview'; +import PillarSelectInput from '#components/PillarSelectInput'; +import { + QUESTION_FRAGMENT, + QUESTION_INFO, +} from '../queries.ts'; import styles from './index.module.css'; const CREATE_TIME_QUESTION = gql` + ${QUESTION_FRAGMENT} mutation CreateTimeQuestion( $projectId: ID!, $input: QuestionCreateInput!, @@ -39,6 +55,33 @@ const CREATE_TIME_QUESTION = gql` ) { ok errors + result { + ...QuestionResponse + } + } + } + } + } +`; + +const UPDATE_TIME_QUESTION = gql` + ${QUESTION_FRAGMENT} + mutation UpdateTimeQuestion( + $projectId: ID!, + $questionId: ID!, + $input: QuestionUpdateInput!, + ) { + private { + projectScope(pk: $projectId) { + updateQuestion ( + data: $input + id: $questionId, + ) { + ok + errors + result { + ...QuestionResponse + } } } } @@ -67,6 +110,10 @@ const schema: FormSchema = { required: true, requiredValidation: requiredStringCondition, }, + group: { + required: true, + requiredValidation: requiredStringCondition, + }, hint: {}, }), }; @@ -74,16 +121,67 @@ const schema: FormSchema = { interface Props { projectId: string; questionnaireId: string; + questionId?: string; } function TimeQuestionForm(props: Props) { const { projectId, questionnaireId, + questionId, } = props; const alert = useAlert(); + const initialFormValue: FormType = { + type: 'TIME' as QuestionTypeEnum, + questionnaire: questionnaireId, + }; + + const { + pristine, + validate, + value: formValue, + error: formError, + setFieldValue, + setValue, + setError, + } = useForm(schema, { value: initialFormValue }); + + const fieldError = getErrorObject(formError); + + const questionInfoVariables = useMemo(() => { + if (isNotDefined(projectId) || isNotDefined(questionId)) { + return undefined; + } + return ({ + projectId, + questionId, + }); + }, [ + projectId, + questionId, + ]); + + useQuery( + QUESTION_INFO, + { + skip: isNotDefined(questionInfoVariables), + variables: questionInfoVariables, + onCompleted: (response) => { + const questionResponse = response.private.projectScope?.question; + setValue({ + name: questionResponse?.name, + type: questionResponse?.type, + questionnaire: questionResponse?.questionnaireId, + label: questionResponse?.label, + group: questionResponse?.groupId, + hint: questionResponse?.hint, + }); + }, + }, + ); + const [ triggerQuestionCreate, { loading: createQuestionPending }, @@ -115,39 +213,67 @@ function TimeQuestionForm(props: Props) { }, }, ); - const initialFormValue: FormType = { - type: 'TIME' as QuestionTypeEnum, - questionnaire: questionnaireId, - name: randomString(), - }; - - const { - pristine, - validate, - value: formValue, - error: formError, - setFieldValue, - setError, - } = useForm(schema, { value: initialFormValue }); - const fieldError = getErrorObject(formError); + const [ + triggerQuestionUpdate, + { loading: updateQuestionPending }, + ] = useMutation( + UPDATE_TIME_QUESTION, + { + onCompleted: (questionResponse) => { + const response = questionResponse?.private?.projectScope?.updateQuestion; + if (!response) { + return; + } + if (response.ok) { + alert.show( + 'Question updated successfully.', + { variant: 'success' }, + ); + } else { + alert.show( + 'Failed to update question.', + { variant: 'error' }, + ); + } + }, + onError: () => { + alert.show( + 'Failed to update question.', + { variant: 'error' }, + ); + }, + }, + ); const handleQuestionSubmit = useCallback(() => { const handler = createSubmitHandler( validate, setError, (valueFromForm) => { - triggerQuestionCreate({ - variables: { - projectId, - input: valueFromForm as QuestionCreateInput, - }, - }); + if (isDefined(questionId)) { + triggerQuestionUpdate({ + variables: { + projectId, + questionId, + input: valueFromForm as QuestionUpdateInput, + }, + }); + } else { + triggerQuestionCreate({ + variables: { + projectId, + input: valueFromForm as QuestionCreateInput, + }, + }); + } }, ); handler(); }, [ triggerQuestionCreate, + triggerQuestionUpdate, + questionId, projectId, setError, validate, @@ -176,12 +302,32 @@ function TimeQuestionForm(props: Props) { error={fieldError?.hint} onChange={setFieldValue} /> + + diff --git a/src/views/QuestionnaireEdit/index.tsx b/src/views/QuestionnaireEdit/index.tsx index d1218b9..2e57b33 100644 --- a/src/views/QuestionnaireEdit/index.tsx +++ b/src/views/QuestionnaireEdit/index.tsx @@ -57,9 +57,11 @@ import ImageQuestionForm from './ImageQuestionForm'; import SelectOneQuestionForm from './SelectOneQuestionForm'; import SelectMultipleQuestionForm from './SelectMultipleQuestionForm'; +import { + QUESTION_FRAGMENT, +} from './queries.ts'; import QuestionTypeItem, { QuestionType } from './QuestionTypeItem'; import QuestionPreview from './QuestionPreview'; -import SelectMultipleQuestionForm from './SelectMultipleQuestionForm'; import styles from './index.module.css'; @@ -99,10 +101,11 @@ const QUESTIONNAIRE = gql` `; const QUESTIONS_BY_GROUP = gql` + ${QUESTION_FRAGMENT} query QuestionsByGroup( $projectId: ID!, $questionnaireId: ID!, - $groupId: DjangoModelFilterInput, + $groupId: ID!, ) { private { projectScope(pk: $projectId) { @@ -112,7 +115,9 @@ const QUESTIONS_BY_GROUP = gql` questionnaire: { pk: $questionnaireId, }, - group: $groupId, + group: { + pk: $groupId, + }, includeChildGroup: true, } order: { @@ -123,14 +128,7 @@ const QUESTIONS_BY_GROUP = gql` limit offset items { - createdAt - hint - id - groupId - label - name - type - questionnaireId + ...QuestionResponse } } } @@ -217,6 +215,11 @@ export function Component() { setSelectedQuestionType, ] = useState(); + const [ + activeQuestionId, + setActiveQuestionId, + ] = useState(); + const [ selectedGroups, setSelectedGroups, @@ -292,7 +295,7 @@ export function Component() { return ({ projectId, questionnaireId, - activeGroupTab: finalSelectedTab, + groupId: finalSelectedTab, }); }, [ projectId, @@ -319,7 +322,14 @@ export function Component() { const questionRendererParams = useCallback((_: string, data: Question) => ({ question: data, - }), []); + showAddQuestionPane, + setSelectedQuestionType, + projectId, + setActiveQuestionId, + }), [ + showAddQuestionPane, + projectId, + ]); const groupTabRenderParams = useCallback((_: string, datum: QuestionGroup) => ({ children: datum.label, @@ -438,18 +448,21 @@ export function Component() { )} {(selectedQuestionType === 'INTEGER') && ( )} {(selectedQuestionType === 'RANK') && ( )} {(selectedQuestionType === 'SELECT_ONE') && ( @@ -468,30 +481,35 @@ export function Component() { )} {(selectedQuestionType === 'TIME') && ( )} {(selectedQuestionType === 'NOTE') && ( )} {(selectedQuestionType === 'FILE') && ( )} {(selectedQuestionType === 'IMAGE') && ( )} diff --git a/src/views/QuestionnaireEdit/queries.ts b/src/views/QuestionnaireEdit/queries.ts new file mode 100644 index 0000000..4a8f963 --- /dev/null +++ b/src/views/QuestionnaireEdit/queries.ts @@ -0,0 +1,54 @@ +import { gql } from '@apollo/client'; + +export const QUESTION_FRAGMENT = gql` + fragment QuestionResponse on QuestionType { + id + label + name + type + hint + groupId + questionnaireId + choiceCollection { + id + choices { + collectionId + id + label + name + } + } + } +`; + +export const QUESTION_INFO = gql` + query QuestionInfo ( + $projectId: ID!, + $questionId: ID!, + ) { + private { + projectScope(pk: $projectId) { + question(pk: $questionId) { + id + label + name + type + hint + groupId + questionnaireId + choiceCollection { + id + choices { + collectionId + id + label + name + } + } + } + } + } + } +`; + +export default QUESTION_INFO;