Skip to content

Commit

Permalink
Merge pull request #50 from EmmanuelDemey/master
Browse files Browse the repository at this point in the history
Upload Document
  • Loading branch information
Evargalo authored Jun 7, 2019
2 parents 0cecc17 + 57924fc commit 8db6f06
Show file tree
Hide file tree
Showing 16 changed files with 4,430 additions and 4,118 deletions.
8,112 changes: 4,088 additions & 4,024 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
"react-d3-tree": "1.10.3",
"react-dom": "16.8.6",
"react-draft-wysiwyg": "1.12.0",
"react-dropzone": "^10.1.5",
"react-loading": "2.0.3",
"react-modal": "3.1.8",
"react-redux": "5.1.1",
Expand Down
16 changes: 16 additions & 0 deletions src/app.scss
Original file line number Diff line number Diff line change
Expand Up @@ -211,3 +211,19 @@ footer {
font-weight: bold;
}
}

.dropzone {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
padding: 20px;
border-width: 2px;
border-radius: 2px;
border-color: #eeeeee;
border-style: dashed;
background-color: #fafafa;
color: #bdbdbd;
outline: none;
transition: border 0.24s ease-in-out;
}
36 changes: 31 additions & 5 deletions src/js/actions/operations/documents/item.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,45 @@
import api from 'js/remote-api/api';
import * as A from 'js/actions/constants';
import { LINK, DOCUMENT } from 'js/components/operations/document/utils';

export const saveDocument = (document, callback) => dispatch => {
export const saveDocument = (document, type, files, callback) => dispatch => {
dispatch({
type: A.SAVE_OPERATIONS_DOCUMENT,
payload: document,
});
const method = document.id ? 'putDocument' : 'postLink';
const method = document.id
? 'putDocument'
: type === LINK
? 'postLink'
: 'postDocument';

let body = document;

/**
* If the document has no id, this is a creation
* We have to send FormData kind of HTTP request.
* Only File-type document has a file to upload
*/
if (!document.id) {
const formData = new FormData();
formData.append('body', JSON.stringify(document));

if (type === DOCUMENT && files[0]) {
formData.append('file', files[0], files[0].name);
}
body = formData;
}
return api[method](body).then(

let promise;
if (type === DOCUMENT && document.id && files[0] && files[0].size) {
const formData = new FormData();
formData.append('file', files[0], files[0].name);
promise = api.putDocumentFile(document, formData);
} else {
promise = api[method](body);
}

return promise.then(
results => {
dispatch({
type: A.SAVE_OPERATIONS_DOCUMENT_SUCCESS,
Expand All @@ -22,14 +48,14 @@ export const saveDocument = (document, callback) => dispatch => {
id: document.id ? document.id : results,
},
});
callback(results);
callback(null, results);
},
err => {
dispatch({
type: A.SAVE_OPERATIONS_DOCUMENT_FAILURE,
payload: { err },
});
callback();
callback(err);
}
);
};
Expand Down
81 changes: 81 additions & 0 deletions src/js/actions/operations/documents/item.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { saveDocument } from './item';
import * as A from 'js/actions/constants';
import api from 'js/remote-api/api';
import { DOCUMENT, LINK } from 'js/components/operations/document/utils';

const dispatch = jest.fn();
jest.mock('js/remote-api/api');

describe('Document actions', () => {
beforeEach(() => dispatch.mockClear());

describe('save a document', () => {
it('should call putDocument', async () => {
api.putDocument = function(id) {
return Promise.resolve('putDocument');
};
const document = { id: '1' };
await saveDocument(document, DOCUMENT, [], () => {})(dispatch);
expect(dispatch).toHaveBeenCalledWith({
type: A.SAVE_OPERATIONS_DOCUMENT,
payload: { id: '1' },
});
expect(dispatch).toHaveBeenLastCalledWith({
type: A.SAVE_OPERATIONS_DOCUMENT_SUCCESS,
payload: { id: '1' },
});
});
it('should call postDocument', async () => {
api.postDocument = function(id) {
return Promise.resolve('postLink');
};
const document = { label: 'label' };
await saveDocument(document, DOCUMENT, [], () => {})(dispatch);
expect(dispatch).toHaveBeenCalledWith({
type: A.SAVE_OPERATIONS_DOCUMENT,
payload: { label: 'label' },
});
expect(dispatch).toHaveBeenLastCalledWith({
type: A.SAVE_OPERATIONS_DOCUMENT_SUCCESS,
payload: { id: 'postLink', label: 'label' },
});
});

it('should call postLink', async () => {
api.postLink = function(id) {
return Promise.resolve('postLink');
};
const document = { label: 'label' };
await saveDocument(document, LINK, [], () => {})(dispatch);
expect(dispatch).toHaveBeenCalledWith({
type: A.SAVE_OPERATIONS_DOCUMENT,
payload: { label: 'label' },
});
expect(dispatch).toHaveBeenLastCalledWith({
type: A.SAVE_OPERATIONS_DOCUMENT_SUCCESS,
payload: { id: 'postLink', label: 'label' },
});
});

it('should call putDocumentFile', async () => {
api.putDocumentFile = function(id) {
return Promise.resolve('putDocumentFile');
};
const document = { id: 'id', label: 'label' };
await saveDocument(
document,
DOCUMENT,
[new File(['content'], 'filename', { type: 'text/html' })],
() => {}
)(dispatch);
expect(dispatch).toHaveBeenCalledWith({
type: A.SAVE_OPERATIONS_DOCUMENT,
payload: { id: 'id', label: 'label' },
});
expect(dispatch).toHaveBeenLastCalledWith({
type: A.SAVE_OPERATIONS_DOCUMENT_SUCCESS,
payload: { id: 'id', label: 'label' },
});
});
});
});
143 changes: 107 additions & 36 deletions src/js/components/operations/document/edition/edition.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ import PropTypes from 'prop-types';
import EditorMarkdown from 'js/components/shared/editor-html/editor-markdown';
import Button from 'js/components/shared/button';
import { validate } from 'js/components/operations/document/edition/validation';
import { LINK, DOCUMENT } from '../utils';
import Dropzone from 'react-dropzone';
import Loading from 'js/components/shared/loading';

const defaultDocument = {
labelLg1: '',
labelLg2: '',
Expand All @@ -21,53 +25,83 @@ class OperationsDocumentationEdition extends Component {
document: PropTypes.object.isRequired,
langs: PropTypes.object.isRequired,
saveDocument: PropTypes.func.isRequired,
type: PropTypes.oneOf([LINK, DOCUMENT]),
};

constructor(props) {
super(props);
this.state = {
this.state = this.setInitialState(props);
}

componentWillReceiveProps(nextProps) {
this.setState(this.setInitialState(nextProps));
}

setInitialState = props => {
return {
serverSideError: '',
document: {
...defaultDocument,
...props.document,
},
files: props.document.url ? [{ name: props.document.url }] : [],
};
}
};

componentWillReceiveProps(nextProps) {
uploadFile = files => {
this.setState({
document: {
...defaultDocument,
...nextProps.document,
},
serverSideError: '',
files,
});
}
};
removeFile = () => {
this.setState({
serverSideError: '',
files: [],
});
};

onChange = e => {
this.setState({
serverSideError: '',
document: {
...this.state.document,
[e.target.id]: e.target.value,
},
});
};

onSubmit = () => {
this.props.saveDocument(
this.state.document,
(id = this.state.document.id) => {
this.props.history.push(`/operations/document/${id}`);
this.props.type,
this.state.files,
(err, id = this.state.document.id) => {
if (!err) {
this.props.history.push(`/operations/document/${id}`);
} else {
this.setState({
serverSideError: err,
});
}
}
);
};

render() {
const {
langs: { lg1, lg2 },
type,
} = this.props;
const { document } = this.state;
const isEditing = !!document.id;

const errors = validate(document);
if (this.props.operationsAsyncTask)
return <Loading textType="saving" context="operations" />;

const { document, files, serverSideError } = this.state;
const isEditing = !!document.id;

const errors = validate(document, type, files);
const globalError = errors.errorMessage || serverSideError;
return (
<div className="container editor-container">
{isEditing && (
Expand Down Expand Up @@ -102,15 +136,11 @@ class OperationsDocumentationEdition extends Component {

<div className="col-md-8 centered">
<div
style={{ visibility: errors.errorMessage ? 'visible' : 'hidden' }}
style={{ visibility: globalError ? 'visible' : 'hidden' }}
className="alert alert-danger bold"
role="alert"
>
{/* HACK: if no content, the line height is set to 0 and the rest
of the page moves a little */}
{errors.errorMessage || (
<span style={{ whiteSpace: 'pre-wrap' }}> </span>
)}
{globalError}
</div>
</div>
<Button
Expand Down Expand Up @@ -183,22 +213,65 @@ class OperationsDocumentationEdition extends Component {
/>
</div>
</div>
<div className="row">
<div className="col-md-12 form-group">
<label htmlFor="url">
<NoteFlag text={D.titleLink} lang={lg1} />
<span className="boldRed">*</span>
</label>
<input
type="text"
className="form-control"
id="url"
value={document.url}
onChange={this.onChange}
aria-invalid={errors.fields.url}
/>
{type === LINK && (
<div className="row">
<div className="col-md-12 form-group">
<label htmlFor="url">
<NoteFlag text={D.titleLink} lang={lg1} />
<span className="boldRed">*</span>
</label>
<input
type="text"
className="form-control"
id="url"
value={document.url}
onChange={this.onChange}
aria-invalid={errors.fields.url}
/>
</div>
</div>
</div>
)}

{type === DOCUMENT && files.length === 0 && (
<div className="row">
<div className="col-md-12 form-group">
<Dropzone onDrop={this.uploadFile} multiple={false}>
{({ getRootProps, getInputProps }) => (
<div
{...getRootProps({
className: 'dropzone',
onDrop: event => event.stopPropagation(),
})}
>
<input
{...getInputProps()}
aria-invalid={errors.fields.file}
/>
<p>{D.drag}</p>
</div>
)}
</Dropzone>
</div>
</div>
)}

{type === DOCUMENT && files.length > 0 && (
<div className="panel panel-default">
{files.map(file => (
<div className="panel-body" key={file.name}>
{file.name}
<button
onClick={this.removeFile}
type="button"
className="close"
aria-label="Close"
>
<span aria-hidden="true">&times;</span>
</button>
</div>
))}
</div>
)}
<div className="row">
<div className="col-md-12 form-group">
<label htmlFor="lang">
Expand All @@ -218,8 +291,6 @@ class OperationsDocumentationEdition extends Component {
</form>
</div>
);
// TODO n'afficher que l'URL pour les liens
// TODO ajouter un select pour savoir si c'est un lien ou un document qu'on souhaite creer
}
}

Expand Down
Loading

0 comments on commit 8db6f06

Please sign in to comment.