diff --git a/src/pages/Prototype/index.tsx b/src/pages/Prototype/index.tsx index 6421e2f..2d58e0a 100644 --- a/src/pages/Prototype/index.tsx +++ b/src/pages/Prototype/index.tsx @@ -1,9 +1,21 @@ -import { useEffect, useState } from 'react'; +import ReactFlow, { + removeElements, + addEdge, + MiniMap, + Controls, + ControlButton, + FlowElement, + Elements, + Connection, + Edge, + ArrowHeadType, +} from 'react-flow-renderer'; +import { useEffect, useState, useRef } from 'react'; import { useHistory, useParams } from 'react-router-dom'; -import { useLazyQuery, useReactiveVar } from '@apollo/client'; +import { useLazyQuery, useMutation, useReactiveVar } from '@apollo/client'; import { Redirect } from 'react-router'; import { roleVar } from '../../graphql/state'; -import { Empty, ArrowLeft, Edit, Check } from '../../assets'; +import { Empty, ArrowLeft, Edit, CheckCircle } from '../../assets'; import { Box, Text, @@ -11,22 +23,39 @@ import { Spinner, FrontendFeatureCard, BackendFeatureCard, + Alert, } from '../../components'; import { Wrapper } from './styles'; import { + AddPrototypeMutation, + AddPrototypeMutationVariables, + GetPrototypeByIdQuery, + GetPrototypeByIdQueryVariables, GetTemplateByIdQuery, GetTemplateByIdQueryVariables, + ProtoTypeOutput, TemplateOutput, + UpdatePrototypeMutation, + UpdatePrototypeMutationVariables, } from '../../graphql/types'; import { GET_TEMPLATE_BY_ID } from '../../graphql/template.api'; -import { theme } from '../../themes'; +import { + ADD_PROTOTYPE, + GET_PROTOTYPE_BY_ID, + UPDATE_PROTOTYPE, +} from '../../graphql/prototype.api'; const Prototype = () => { const role = useReactiveVar(roleVar); const history = useHistory(); const { id } = useParams<{ id: string }>(); const [template, setTemplate] = useState(); + const [prototype, setPrototype] = useState>(); + const [elements, setElements] = useState([]); const [editing, setEditing] = useState(false); + const [error, setError] = useState(''); + const [success, setSuccess] = useState(false); + const diagramParentRef = useRef(null); const [getTemplate, { loading: templateLoading }] = useLazyQuery< GetTemplateByIdQuery, @@ -38,17 +67,157 @@ const Prototype = () => { fetchPolicy: 'network-only', }); + const [getPrototype, { loading: prototypeLoading }] = useLazyQuery< + GetPrototypeByIdQuery, + GetPrototypeByIdQueryVariables + >(GET_PROTOTYPE_BY_ID, { + onCompleted({ getPrototypeById }) { + setPrototype(getPrototypeById.prototype); + }, + }); + + const [addPrototype] = useMutation< + AddPrototypeMutation, + AddPrototypeMutationVariables + >(ADD_PROTOTYPE, { + onCompleted({ addPrototype: addPrototypeResult }) { + setPrototype(addPrototypeResult.prototype); + setSuccess(true); + setTimeout(() => setSuccess(false), 3000); + }, + onError({ graphQLErrors }) { + setError(graphQLErrors[0]?.extensions?.info); + setTimeout(() => setError(''), 3000); + }, + }); + + const [updatePrototype] = useMutation< + UpdatePrototypeMutation, + UpdatePrototypeMutationVariables + >(UPDATE_PROTOTYPE, { + onCompleted({ updatePrototype: updatePrototypeResult }) { + setPrototype(updatePrototypeResult.prototype); + setSuccess(true); + setTimeout(() => setSuccess(false), 3000); + }, + }); + useEffect(() => { if (id) { getTemplate({ variables: { id } }); + getPrototype({ variables: { id } }); } // eslint-disable-next-line }, [id]); + useEffect(() => { + if (template && template.features) { + const initialElements = template.features.map((feature, index) => { + if (['frontend', 'fullstack'].includes(feature.featureType)) { + return { + id: feature.id, + type: 'default', + data: { + label: , + }, + position: { x: index * 100, y: index * 200 }, + style: { + width: 'auto', + }, + connectable: role === 'developer' && editing, + } as FlowElement; + } + return {} as FlowElement; + }); + + if (initialElements) setElements(initialElements); + } + + if (prototype) { + const initialElements: Array = []; + prototype.forEach((link) => { + link.connections.forEach((connection) => { + initialElements.push({ + id: `edge-${link.feature.id}`, + source: link.feature.id, + target: connection.to, + arrowHeadType: ArrowHeadType.ArrowClosed, + className: 'normal-edge', + }); + }); + }); + + if (initialElements) setElements((els) => [...els, ...initialElements]); + } + + // eslint-disable-next-line + }, [template, prototype, editing]); + + const onElementsRemove = (elementsToRemove: Elements) => + setElements((els) => removeElements(elementsToRemove, els)); + const onConnect = (params: Edge | Connection) => + setElements((els) => + addEdge({ ...params, arrowHeadType: ArrowHeadType.ArrowClosed }, els) + ); + + const handleEditPrototype = () => { + if (editing) { + const prototypeInput = elements + // @ts-ignore + .filter((element) => element.source || element.target) + .map((element) => { + if ( + element.hasOwnProperty('source') || + element.hasOwnProperty('target') + ) { + return { + // @ts-ignore + featureId: element.source, + connections: [ + { + // @ts-ignore + to: element.target, + releations: { back: false, forword: true }, + }, + ], + }; + } + return {}; + }); + if (prototypeInput && prototypeInput.length > 0) { + if (prototype) { + updatePrototype({ + variables: { + prototype: { + templateId: id, + // @ts-ignore + prototype: prototypeInput, + }, + }, + }); + } else { + addPrototype({ + variables: { + prototype: { + templateId: id, + // @ts-ignore + prototype: prototypeInput, + }, + }, + }); + } + } + + setEditing(false); + } else { + setEditing(true); + } + }; + return role === 'productOwner' || role === 'developer' ? ( <> - {!templateLoading ? ( + {!templateLoading && !prototypeLoading ? ( <> {template ? ( @@ -59,7 +228,7 @@ const Prototype = () => { alignItems='center' marginBottom='20px' > - +