Skip to content

Commit

Permalink
[173] Migrate the projects view to MaterialUI and XState
Browse files Browse the repository at this point in the history
Bug: #173
Signed-off-by: Stéphane Bégaudeau <stephane.begaudeau@obeo.fr>
Signed-off-by: Florian Barbin <florian.barbin@obeo.fr>
  • Loading branch information
sbegaudeau committed Feb 23, 2021
1 parent abdf2bf commit 1299031
Show file tree
Hide file tree
Showing 25 changed files with 1,148 additions and 948 deletions.
8 changes: 0 additions & 8 deletions frontend/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ export * from './modals/new-document/NewDocumentModal';
export * from './modals/new-object/NewObjectModal';
export * from './modals/new-representation/NewRepresentationModal';
export * from './modals/new-root-object/NewRootObjectModal';
export * from './modals/rename-project/RenameProjectModal';
export * from './modals/share-diagram/ShareDiagramModal';
export * from './modals/upload-document/UploadDocumentModal';
export * from './navbar/LoggedInNavbar';
Expand Down Expand Up @@ -111,14 +110,7 @@ export * from './views/FormContainer';
export * from './views/modelers/ModelersView';
export * from './views/new-modeler/NewModelerView';
export * from './views/new-project/NewProjectView';
export * from './views/projects/ProjectActionsContextMenu';
export * from './views/projects/ProjectCard';
export * from './views/projects/ProjectsEmptyView';
export * from './views/projects/ProjectsErrorView';
export * from './views/projects/ProjectsLoadedView';
export * from './views/projects/ProjectsLoadingView';
export * from './views/projects/ProjectsView';
export * from './views/projects/ProjectsViewContainer';
export * from './views/upload-project/UploadProjectView';
export * from './views/View';
export * from './workbench/OnboardArea';
Expand Down
37 changes: 0 additions & 37 deletions frontend/src/modals/delete-project/DeleteProjectModal.module.css

This file was deleted.

159 changes: 107 additions & 52 deletions frontend/src/modals/delete-project/DeleteProjectModal.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2019, 2020 Obeo.
* Copyright (c) 2019, 2021 Obeo.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
Expand All @@ -11,83 +11,138 @@
* Obeo - initial API and implementation
*******************************************************************************/
import { useMutation } from '@apollo/client';
import { Banner } from 'core/banner/Banner';
import { Buttons, DangerButton } from 'core/button/Button';
import { Text } from 'core/text/Text';
import Button from '@material-ui/core/Button';
import Dialog from '@material-ui/core/Dialog';
import DialogActions from '@material-ui/core/DialogActions';
import DialogContent from '@material-ui/core/DialogContent';
import DialogContentText from '@material-ui/core/DialogContentText';
import DialogTitle from '@material-ui/core/DialogTitle';
import IconButton from '@material-ui/core/IconButton';
import Snackbar from '@material-ui/core/Snackbar';
import CloseIcon from '@material-ui/icons/Close';
import { useMachine } from '@xstate/react';
import gql from 'graphql-tag';
import { Modal } from 'modals/Modal';
import PropTypes from 'prop-types';
import React, { useEffect, useState } from 'react';
import styles from './DeleteProjectModal.module.css';
import {
DeleteProjectModalProps,
GQLDeleteProjectMutationData,
GQLDeleteProjectPayload,
GQLErrorPayload,
} from 'modals/delete-project/DeleteProjectModal.types';
import {
DeleteProjectModalContext,
DeleteProjectModalEvent,
deleteProjectModalMachine,
HandleResponseEvent,
HideToastEvent,
RequestProjectDeletionEvent,
SchemaValue,
ShowToastEvent,
} from 'modals/delete-project/DeleteProjectModalMachine';
import React, { useEffect } from 'react';

const deleteProjectMutation = gql`
mutation deleteProject($input: DeleteProjectInput!) {
deleteProject(input: $input) {
__typename
... on DeleteProjectSuccessPayload {
viewer {
id
}
}
... on ErrorPayload {
message
}
}
}
`;

const propTypes = {
projectId: PropTypes.string.isRequired,
onProjectDeleted: PropTypes.func.isRequired,
onClose: PropTypes.func.isRequired,
};
const isErrorPayload = (payload: GQLDeleteProjectPayload): payload is GQLErrorPayload =>
payload.__typename === 'ErrorPayload';
export const DeleteProjectModal = ({ projectId, onDelete, onClose }: DeleteProjectModalProps) => {
const [{ value, context }, dispatch] = useMachine<DeleteProjectModalContext, DeleteProjectModalEvent>(
deleteProjectModalMachine
);
const { toast, deleteProjectModal } = value as SchemaValue;
const { message } = context;

const [deleteProject, { loading, data, error }] = useMutation<GQLDeleteProjectMutationData>(deleteProjectMutation);
useEffect(() => {
if (!loading) {
if (error) {
const showToastEvent: ShowToastEvent = {
type: 'SHOW_TOAST',
message: error.message,
};
dispatch(showToastEvent);
}
if (data) {
const event: HandleResponseEvent = { type: 'HANDLE_RESPONSE', data };
dispatch(event);

const { deleteProject } = data;
if (isErrorPayload(deleteProject)) {
const { message } = deleteProject;
const showToastEvent: ShowToastEvent = { type: 'SHOW_TOAST', message };
dispatch(showToastEvent);
}
}
}
}, [loading, data, error, onDelete, dispatch]);

const onDeleteProject = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
const requestProjectDeletionEvent: RequestProjectDeletionEvent = { type: 'REQUEST_PROJECT_DELETION' };
dispatch(requestProjectDeletionEvent);

export const DeleteProjectModal = ({ projectId, onProjectDeleted, onClose }) => {
const [errorMessage, setErrorMessage] = useState('');
const [performProjectDeletion, { loading, data, error }] = useMutation(deleteProjectMutation);
const onDeleteProject = async (event) => {
event.preventDefault();
const variables = {
input: {
projectId,
},
};
performProjectDeletion({ variables });
deleteProject({ variables });
};

useEffect(() => {
if (!loading) {
if (error) {
setErrorMessage(error.message);
} else if (data?.deleteProject) {
const { deleteProject } = data;
if (deleteProject.__typename === 'DeleteProjectSuccessPayload') {
onProjectDeleted();
} else if (deleteProject.__typename === 'ErrorPayload') {
setErrorMessage(deleteProject.message);
}
}
if (deleteProjectModal === 'success') {
onDelete();
}
}, [loading, data, error, onProjectDeleted]);
}, [deleteProjectModal, onDelete]);

let bannerContent = null;
if (errorMessage) {
bannerContent = <Banner data-testid="banner" content={errorMessage} />;
}
return (
<Modal title="Delete the project" onClose={onClose}>
<div className={styles.container}>
<div className={styles.content}>
<Text className={styles.subtitle}>
<>
<Dialog open={true} onClose={onClose} aria-labelledby="dialog-title">
<DialogTitle id="dialog-title">Delete the project</DialogTitle>
<DialogContent>
<DialogContentText>
This action will delete everything in the project, it cannot be reversed.
</Text>
<div className={styles.bannerArea}>{bannerContent}</div>
</div>
<Buttons>
<DangerButton type="button" onClick={onDeleteProject} label="Delete" data-testid="delete-project" />
</Buttons>
</div>
</Modal>
</DialogContentText>
</DialogContent>
<DialogActions>
<Button
variant="contained"
disabled={deleteProjectModal !== 'idle'}
onClick={onDeleteProject}
color="primary"
data-testid="delete-project">
Delete
</Button>
</DialogActions>
</Dialog>
<Snackbar
anchorOrigin={{
vertical: 'bottom',
horizontal: 'right',
}}
open={toast === 'visible'}
autoHideDuration={3000}
onClose={() => dispatch({ type: 'HIDE_TOAST' } as HideToastEvent)}
message={message}
action={
<IconButton
size="small"
aria-label="close"
color="inherit"
onClick={() => dispatch({ type: 'HIDE_TOAST' } as HideToastEvent)}>
<CloseIcon fontSize="small" />
</IconButton>
}
data-testid="error"
/>
</>
);
};
DeleteProjectModal.propTypes = propTypes;
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2019, 2020 Obeo.
* Copyright (c) 2021 Obeo.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
Expand All @@ -10,15 +10,20 @@
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
.errorMessage {
display: grid;
grid-template-columns: 1fr;
grid-template-rows: 1fr;
justify-items: center;
padding-top: 64px;
export interface DeleteProjectModalProps {
projectId: string;
onDelete: () => void;
onClose: () => void;
}

.message {
font-size: var(--font-size-4);
color: var(--daintree-lighten-30);
export interface GQLDeleteProjectMutationData {
deleteProject: GQLDeleteProjectPayload;
}

export interface GQLDeleteProjectPayload {
__typename: string;
}

export interface GQLErrorPayload extends GQLDeleteProjectPayload {
message: string;
}
Loading

0 comments on commit 1299031

Please sign in to comment.