Update prototype page

This commit is contained in:
Hazem Krimi
2021-06-14 02:20:01 +01:00
parent 87be156f83
commit 541597a6f5
2 changed files with 214 additions and 65 deletions
+214 -48
View File
@@ -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 { 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 { Redirect } from 'react-router';
import { roleVar } from '../../graphql/state'; import { roleVar } from '../../graphql/state';
import { Empty, ArrowLeft, Edit, Check } from '../../assets'; import { Empty, ArrowLeft, Edit, CheckCircle } from '../../assets';
import { import {
Box, Box,
Text, Text,
@@ -11,22 +23,39 @@ import {
Spinner, Spinner,
FrontendFeatureCard, FrontendFeatureCard,
BackendFeatureCard, BackendFeatureCard,
Alert,
} from '../../components'; } from '../../components';
import { Wrapper } from './styles'; import { Wrapper } from './styles';
import { import {
AddPrototypeMutation,
AddPrototypeMutationVariables,
GetPrototypeByIdQuery,
GetPrototypeByIdQueryVariables,
GetTemplateByIdQuery, GetTemplateByIdQuery,
GetTemplateByIdQueryVariables, GetTemplateByIdQueryVariables,
ProtoTypeOutput,
TemplateOutput, TemplateOutput,
UpdatePrototypeMutation,
UpdatePrototypeMutationVariables,
} from '../../graphql/types'; } from '../../graphql/types';
import { GET_TEMPLATE_BY_ID } from '../../graphql/template.api'; 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 Prototype = () => {
const role = useReactiveVar(roleVar); const role = useReactiveVar(roleVar);
const history = useHistory(); const history = useHistory();
const { id } = useParams<{ id: string }>(); const { id } = useParams<{ id: string }>();
const [template, setTemplate] = useState<TemplateOutput>(); const [template, setTemplate] = useState<TemplateOutput>();
const [prototype, setPrototype] = useState<Array<ProtoTypeOutput>>();
const [elements, setElements] = useState<Elements>([]);
const [editing, setEditing] = useState<boolean>(false); const [editing, setEditing] = useState<boolean>(false);
const [error, setError] = useState<string>('');
const [success, setSuccess] = useState<boolean>(false);
const diagramParentRef = useRef<HTMLDivElement>(null);
const [getTemplate, { loading: templateLoading }] = useLazyQuery< const [getTemplate, { loading: templateLoading }] = useLazyQuery<
GetTemplateByIdQuery, GetTemplateByIdQuery,
@@ -38,17 +67,157 @@ const Prototype = () => {
fetchPolicy: 'network-only', 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(() => { useEffect(() => {
if (id) { if (id) {
getTemplate({ variables: { id } }); getTemplate({ variables: { id } });
getPrototype({ variables: { id } });
} }
// eslint-disable-next-line // eslint-disable-next-line
}, [id]); }, [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: <FrontendFeatureCard feature={feature} />,
},
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<Edge> = [];
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<any>) =>
setElements((els) => removeElements(elementsToRemove, els));
const onConnect = (params: Edge<any> | 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' ? ( return role === 'productOwner' || role === 'developer' ? (
<> <>
{!templateLoading ? ( {!templateLoading && !prototypeLoading ? (
<> <>
{template ? ( {template ? (
<Wrapper color={role}> <Wrapper color={role}>
@@ -59,7 +228,7 @@ const Prototype = () => {
alignItems='center' alignItems='center'
marginBottom='20px' marginBottom='20px'
> >
<Box> <Box marginRight='50px'>
<Button <Button
text='Back' text='Back'
color={role || 'client'} color={role || 'client'}
@@ -71,6 +240,13 @@ const Prototype = () => {
Prototype Prototype
</Text> </Text>
</Box> </Box>
{success && (
<Alert
color='success'
text='Prototype updated successfully'
/>
)}
{error && <Alert color='error' text={error} />}
</Box> </Box>
{template.features && ( {template.features && (
<> <>
@@ -86,55 +262,45 @@ const Prototype = () => {
</Box> </Box>
<Box <Box
display='grid' display='grid'
gridTemplateColumns='repeat(2, auto)'
rowGap='100px'
alignItems='stretch'
background='#F9FAFA' background='#F9FAFA'
boxShadow='1px 1px 10px rgba(50, 59, 105, 0.25)' boxShadow='1px 1px 10px rgba(50, 59, 105, 0.25)'
borderRadius='10px' borderRadius='10px'
padding='30px 120px' width='100%'
position='relative' height='400px'
ref={diagramParentRef}
> >
{template.features.map((feature, index) => {
if (
feature.featureType === 'frontend' ||
feature.featureType === 'fullstack'
) {
return (
<FrontendFeatureCard
feature={feature}
key={feature.id}
className={
index === 0 || index % 2 === 0
? 'frontend-feature-even'
: 'frontend-feature-odd'
}
/>
);
}
return null;
})}
<Box <Box
position='absolute' width={
top='30px' diagramParentRef.current
right='30px' ? `${
width='35px' getComputedStyle(diagramParentRef.current)
height='35px' ?.width
padding='10px' }}px`
borderRadius='10px' : '100%'
background={
!editing
? theme.colors.white.main
: theme.colors[role].main
} }
boxShadow='1px 1px 5px rgba(50, 59, 105, 0.25)' height='auto'
display='flex'
alignItems='center'
justifyContent='center'
cursor='pointer'
onClick={() => setEditing(!editing)}
> >
{!editing ? <Edit /> : <Check />} <ReactFlow
elements={elements}
onElementsRemove={onElementsRemove}
onConnect={onConnect}
deleteKeyCode={46}
edgeTypes={{ arrowHeadType: 'arrow' }}
>
{role === 'developer' && (
<>
<MiniMap />
<Controls
showInteractive={false}
showFitView={false}
>
<ControlButton onClick={handleEditPrototype}>
{!editing ? <Edit /> : <CheckCircle />}
</ControlButton>
</Controls>
</>
)}
</ReactFlow>
</Box> </Box>
</Box> </Box>
</Box> </Box>
-17
View File
@@ -9,21 +9,4 @@ export const Wrapper = styled.div<WrapperProps>`
fill: ${({ theme, color }) => fill: ${({ theme, color }) =>
color ? theme.colors[color].main : theme.colors.client.main}; color ? theme.colors[color].main : theme.colors.client.main};
} }
.frontend-feature-odd {
justify-self: flex-end;
}
.frontend-feature-even {
justify-self: flex-start;
}
.frontend-feature-even,
.frontend-feature-odd {
&:hover {
border: 2px solid
${({ theme, color }) =>
color ? theme.colors[color].main : theme.colors.client.main};
}
}
`; `;