From b2963f9725f92e6b8107eec08586646c5b4df62f Mon Sep 17 00:00:00 2001 From: jasquat Date: Tue, 19 Jul 2022 14:39:13 -0400 Subject: [PATCH] added ability to add and edit json files for forms --- cypress/e2e/process_models.cy.js | 18 ++- src/routes/AdminRoutes.tsx | 6 +- src/routes/ProcessModelShow.tsx | 189 ++++++++++++++++++------------- src/routes/ReactFormEditor.tsx | 143 ++++++++++++++++++++++- 4 files changed, 276 insertions(+), 80 deletions(-) diff --git a/cypress/e2e/process_models.cy.js b/cypress/e2e/process_models.cy.js index e131eef..28d6691 100644 --- a/cypress/e2e/process_models.cy.js +++ b/cypress/e2e/process_models.cy.js @@ -35,14 +35,16 @@ describe('process-models', () => { cy.contains(modelId).should('not.exist'); }); - it('can create new bpmn and dmn files', () => { + it('can create new bpmn, dmn, and json files', () => { const uuid = () => Cypress._.random(0, 1e6); const id = uuid(); const groupId = 'acceptance-tests-group-one'; const modelDisplayName = `Test Model 2 ${id}`; const modelId = `test-model-2-${id}`; + const bpmnFileName = `bpmn_test_file_${id}`; const dmnFileName = `dmn_test_file_${id}`; + const jsonFileName = `json_test_file_${id}`; cy.contains(groupId).click(); cy.createModel(groupId, modelId, modelDisplayName); @@ -54,6 +56,7 @@ describe('process-models', () => { cy.contains(`Process Model: ${modelId}`); cy.contains(`${bpmnFileName}.bpmn`).should('not.exist'); cy.contains(`${dmnFileName}.dmn`).should('not.exist'); + cy.contains(`${jsonFileName}.json`).should('not.exist'); // add new bpmn file cy.contains('Add New BPMN File').click(); @@ -84,6 +87,19 @@ describe('process-models', () => { cy.contains(`Process Model: ${modelId}`); cy.contains(`${dmnFileName}.dmn`).should('exist'); + // add new json file + cy.contains('Add New JSON File').click(); + cy.contains(/^Process Model File$/); + // Some reason, cypress evals json strings so we have to escape it it with '{{}' + cy.get('.view-line').type('{{} "test_key": "test_value" }'); + cy.contains('Save').click(); + cy.get('input[name=file_name]').type(jsonFileName); + cy.contains('Save Changes').click(); + cy.contains(`Process Model File: ${jsonFileName}`); + cy.contains(modelId).click(); + cy.contains(`Process Model: ${modelId}`); + cy.contains(`${jsonFileName}.json`).should('exist'); + cy.contains('Edit process model').click(); cy.contains('Delete process model').click(); cy.url().should('include', `process-groups/${groupId}`); diff --git a/src/routes/AdminRoutes.tsx b/src/routes/AdminRoutes.tsx index 6f15ba5..58f83c6 100644 --- a/src/routes/AdminRoutes.tsx +++ b/src/routes/AdminRoutes.tsx @@ -90,7 +90,11 @@ export default function AdminRoutes() { element={} /> } + /> + } /> diff --git a/src/routes/ProcessModelShow.tsx b/src/routes/ProcessModelShow.tsx index ba32c65..49156f0 100644 --- a/src/routes/ProcessModelShow.tsx +++ b/src/routes/ProcessModelShow.tsx @@ -4,7 +4,6 @@ import { Button, Stack } from 'react-bootstrap'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; import FileInput from '../components/FileInput'; import HttpService from '../services/HttpService'; -// import ReactFormBuilder from '../components/ReactFormBuilder'; export default function ProcessModelShow() { const params = useParams(); @@ -55,31 +54,119 @@ export default function ProcessModelShow() { setReloadModel(true); }; - if (Object.keys(processModel).length > 1) { - const processModelFilesTag = (processModel as any).files.map( - (fileBpmn: any) => { - if (fileBpmn.name.match(/\.(dmn|bpmn)$/)) { - let primarySuffix = ''; - if (fileBpmn.name === (processModel as any).primary_file_name) { - primarySuffix = '- Primary File'; - } - return ( -
  • - - {fileBpmn.name} - - {primarySuffix} -
  • - ); + const processModelFileList = () => { + let constructedTag; + const tags = (processModel as any).files.map((processModelFile: any) => { + if (processModelFile.name.match(/\.(dmn|bpmn)$/)) { + let primarySuffix = ''; + if (processModelFile.name === (processModel as any).primary_file_name) { + primarySuffix = '- Primary File'; } - return
  • {fileBpmn.name}
  • ; + constructedTag = ( +
  • + + {processModelFile.name} + + {primarySuffix} +
  • + ); + } else if (processModelFile.name.match(/\.(json)$/)) { + constructedTag = ( +
  • + + {processModelFile.name} + +
  • + ); + } else { + constructedTag = ( +
  • {processModelFile.name}
  • + ); } - ); + return constructedTag; + }); + return
      {tags}
    ; + }; + + const processInstancesUl = () => { + return ( +
      +
    • + + List + +
    • +
    • + + Reports + +
    • +
    + ); + }; + + const processModelButtons = () => { + return ( + + + + + + + + ); + }; + + if (Object.keys(processModel).length > 1) { return (

    - - - - - - + {processModelButtons()}

    Process Instances

    -
      -
    • - - List - -
    • -
    • - - Reports - -
    • -
    + {processInstancesUl()}

    Files

    -
      {processModelFilesTag}
    + {processModelFileList()}
    ); } - // } diff --git a/src/routes/ReactFormEditor.tsx b/src/routes/ReactFormEditor.tsx index 50961aa..987b040 100644 --- a/src/routes/ReactFormEditor.tsx +++ b/src/routes/ReactFormEditor.tsx @@ -1,7 +1,146 @@ -import { useEffect } from 'react'; +import { useEffect, useState } from 'react'; +import Editor from '@monaco-editor/react'; +import { useNavigate, useParams } from 'react-router-dom'; +import { Button, Modal } from 'react-bootstrap'; +import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; +import HttpService from '../services/HttpService'; +// NOTE: This is mostly the same as ProcessModelEditDiagram and if we go this route could +// possibly be merged into it. I'm leaving as a separate file now in case it does +// end up diverging greatly export default function ReactFormEditor() { - useEffect(() => {}, []); + const params = useParams(); + const [showFileNameEditor, setShowFileNameEditor] = useState(false); + const [newFileName, setNewFileName] = useState(''); + const handleShowFileNameEditor = () => setShowFileNameEditor(true); + const navigate = useNavigate(); + const [processModelFile, setProcessModelFile] = useState(null); + const [processModelFileContents, setProcessModelFileContents] = useState(''); + + useEffect(() => { + const processResult = (result: any) => { + setProcessModelFile(result); + setProcessModelFileContents(result.file_contents); + }; + + if (params.file_name) { + HttpService.makeCallToBackend({ + path: `/process-models/${params.process_group_id}/${params.process_model_id}/file/${params.file_name}`, + successCallback: processResult, + }); + } + }, [params]); + + const navigateToProcessModelFile = (_result: any) => { + if (!params.file_name) { + const fileNameWithExtension = `${newFileName}.json`; + navigate( + `/admin/process-models/${params.process_group_id}/${params.process_model_id}/form/${fileNameWithExtension}` + ); + } + }; + + const saveFile = () => { + let url = `/process-models/${params.process_group_id}/${params.process_model_id}/file`; + let httpMethod = 'PUT'; + let fileNameWithExtension = params.file_name; + + if (newFileName) { + fileNameWithExtension = `${newFileName}.json`; + httpMethod = 'POST'; + } else { + url += `/${fileNameWithExtension}`; + } + if (!fileNameWithExtension) { + handleShowFileNameEditor(); + return; + } + + const file = new File( + [processModelFileContents], + fileNameWithExtension || '' + ); + const formData = new FormData(); + formData.append('file', file); + formData.append('fileName', file.name); + + HttpService.makeCallToBackend({ + path: url, + successCallback: navigateToProcessModelFile, + httpMethod, + postBody: formData, + }); + }; + + const handleFileNameCancel = () => { + setShowFileNameEditor(false); + setNewFileName(''); + }; + + const handleFileNameSave = (event: any) => { + event.preventDefault(); + setShowFileNameEditor(false); + saveFile(); + }; + + const newFileNameBox = () => { + const fileExtension = '.json'; + return ( + + + Process Model File Name + +
    + + + setNewFileName(e.target.value)} + autoFocus + /> + {fileExtension} + + + + + +
    +
    + ); + }; + + if (processModelFile || !params.file_name) { + return ( +
    + +

    + Process Model File + {processModelFile ? `: ${(processModelFile as any).name}` : ''} +

    + {newFileNameBox()} + + setProcessModelFileContents(value || '')} + /> +
    + ); + } return
    ; }