diff --git a/cypress/fixtures/sims.json b/cypress/fixtures/sims.json
index d90944274..5deba54ba 100644
--- a/cypress/fixtures/sims.json
+++ b/cypress/fixtures/sims.json
@@ -9,6 +9,44 @@
"rangeType": "DATE",
"idAttribute": "S.2.2",
"value": "2019-01-01T11:00:00.000Z"
+ },
+ {
+ "rangeType": "RICH_TEXT",
+ "idAttribute": "S.3.1",
+ "documents": [
+ {
+ "uri": "uri1-bis",
+ "url": "http://google.fr?q=url-1",
+ "updatedDate": "01/01/2019",
+ "labelLg1": "labelLg1-0",
+ "labelLg2": "labelLg2-0",
+ "lang": "fr",
+ "descriptionLg1": "descriptionLg1",
+ "descriptionLg2": "descriptionLg2"
+ },
+ {
+ "uri": "uri2-bis",
+ "url": "http://google.fr?q=url-2",
+ "updatedDate": "01/02/2019",
+ "labelLg1": "labelLg1-1",
+ "labelLg2": "labelLg2-1",
+ "descriptionLg1": "descriptionLg1-2",
+ "descriptionLg2": "descriptionLg2-2"
+ },
+ {
+ "uri": "uri3-bis",
+ "url": "http://google.fr?q=url-2",
+ "labelLg1": "labelLg1-2",
+ "labelLg2": "labelLg2-2",
+ "lang": "fr",
+ "descriptionLg1": "descriptionLg1-2",
+ "descriptionLg2": "descriptionLg2-2"
+ }
+ ]
+ },
+ {
+ "rangeType": "RICH_TEXT",
+ "idAttribute": "S.3.2"
}
],
"labelLg2": "Annual production survey 2012 SIMS",
diff --git a/cypress/integration/family.spec.js b/cypress/integration/family.spec.js
index 102c74661..73f07c336 100644
--- a/cypress/integration/family.spec.js
+++ b/cypress/integration/family.spec.js
@@ -22,7 +22,7 @@ describe('Family page', () => {
familiesPage.go();
familiesPage.getFamiliesList().should('have.length', 10);
familiesPage.getPaginationBlock().should('have.length', 7);
- familiesPage.search('ZENIKA');
+ familiesPage.search('FAKE DATA');
familiesPage.getFamiliesList().should('have.length', 0);
familiesPage.resetSearch();
familiesPage.getFamiliesList().should('have.length', 10);
diff --git a/cypress/integration/operations_list.spec.js b/cypress/integration/operations_list.spec.js
index ba9a2e1dc..e04bafe08 100644
--- a/cypress/integration/operations_list.spec.js
+++ b/cypress/integration/operations_list.spec.js
@@ -23,7 +23,7 @@
cy.get('.list-group').should('be.visible');
cy.get('.pagination').should('be.visible');
- cy.get('input').type('Zenika');
+ cy.get('input').type('FAKE DATA');
cy.get('h4').should(h4 => {
expect(h4.first()).to.contain('0');
});
diff --git a/cypress/integration/po/sims.po.js b/cypress/integration/po/sims.po.js
index cb373a711..e9c210c19 100644
--- a/cypress/integration/po/sims.po.js
+++ b/cypress/integration/po/sims.po.js
@@ -6,13 +6,17 @@ export class SimsViewPage {
getUpdateButton() {
return 'div:nth-child(4) > a';
}
+
+ getDocumentsBlocForRubric(rubricId) {
+ return cy.get(rubricId);
+ }
}
export class SimsEditPage {
getTitle() {
return '.page-title-operations';
}
-
+ p;
getCancelButton() {
return '.btn-line div:first > button';
}
diff --git a/cypress/integration/serie.spec.js b/cypress/integration/serie.spec.js
index 48297b129..80e67bd6b 100644
--- a/cypress/integration/serie.spec.js
+++ b/cypress/integration/serie.spec.js
@@ -138,69 +138,4 @@ describe('Series page', () => {
cy.get('.row:first-of-type > div.form-group').should('have.length', 2);
cy.get('label span.boldRed').should('have.length', 2);
});
-
- it('should handle multi Select component', () => {
- cy.server()
- .fixture('series')
- .then(json => {
- cy.route(Cypress.env('API') + 'operations/series/s1161', json);
- })
-
- .visit('/operations/series', {
- onBeforeLoad(win) {
- delete win.fetch;
- win.eval(polyfill);
- win.fetch = win.unfetch;
- },
- });
-
- cy.get('.list-group a')
- .first()
- .click();
-
- cy.url().should('include', '/operations/series/');
-
- cy.get('.btn-line a')
- .first()
- .click();
-
- cy.url().should('include', '/modify');
-
- cy.get('.Select--multi')
- .eq(1)
- .as('firstMultiSelect')
- .get('@firstMultiSelect')
- .find('.Select-multi-value-wrapper')
- .children('.Select-value')
- .then(children => {
- cy.get('@firstMultiSelect')
- .find('.Select-value')
- .should('have.length', children.length);
-
- cy.get('@firstMultiSelect').click();
- cy.get('@firstMultiSelect').click();
-
- cy.get('@firstMultiSelect')
- .get('.Select-option')
- .first()
- .should('be.visible');
-
- cy.get('@firstMultiSelect')
- .get('.Select-option')
- .first()
- .click();
-
- cy.get('@firstMultiSelect')
- .find('.Select-value')
- .should('have.length', children.length + 1);
-
- cy.get('@firstMultiSelect')
- .find('.Select-value-icon')
- .first()
- .click();
- cy.get('@firstMultiSelect')
- .find('.Select-value')
- .should('have.length', children.length);
- });
- });
});
diff --git a/cypress/integration/sims.spec.js b/cypress/integration/sims.spec.js
index 42a165765..52aeeaafd 100644
--- a/cypress/integration/sims.spec.js
+++ b/cypress/integration/sims.spec.js
@@ -37,6 +37,7 @@ describe('SIMS Page', function() {
// Visu Page
cy.url().should('contains', '/sims/1512');
cy.get(simsViewPage.getTitle()).should('exist');
+
cy.get(simsViewPage.getUpdateButton()).click();
// Update page
diff --git a/docs/en/getting-started.md b/docs/en/getting-started.md
index 82e48babd..640dfb9ea 100644
--- a/docs/en/getting-started.md
+++ b/docs/en/getting-started.md
@@ -31,3 +31,16 @@ If you're new to JavaScript, you might need to first install [node](https://node
`yarn start` will launch the `dev` command defined in the `scripts` section of the same `package.json` file. This command will launch a local web server serving the main HTML file ([src/js/index.html](https://github.com/InseeFr/Bauhaus/blob/master/public/index.html)) and all the relevant assets.
`yarn build` will launch the compilation with some optimizations for production. It copies all the static assets and the resulting bundle file in the `dist` folder.
+
+## Project Structure
+
+In this paragraph, we will try to explain the rules we defined and try to follow when talking about the structure of the project.
+
+### SCSS Mixin
+
+If you have to define SCSS mixin, you have to define them in the `src/styles/mixin.scss` file, and import them in the stylesheet of the React component.
+
+### I18N
+
+In order to avoid big i18n file, we try to split this file in smaller files, based on `page` or `feature`. For example, we have a `src/js/i18n/dictionary/operations/documents.js` file for all messages dedicated to the documents feature.
+This files have to be imported directly or not in the main file `js/i18n/dictionary/app.js`.
\ No newline at end of file
diff --git a/docs/fr/getting-started.md b/docs/fr/getting-started.md
index f8f2606f3..e3d74e7ec 100644
--- a/docs/fr/getting-started.md
+++ b/docs/fr/getting-started.md
@@ -31,3 +31,16 @@ Si vous débutez avec ces technologies, vous aurez vraisemblablement besoin d'in
`yarn start` démarre un serveur de développement qui sert la page d'accueil de l'application ([src/js/index.html](https://github.com/InseeFr/Bauhaus/blob/master/public/index.html)) et toutes les ressources nécessaires.
`yarn build` lance la compilation du code avec des optimisations pour la mise en production. Elle copie toutes les ressources statiques et le fichier `JavaScript` compilé dans le dossier `dist`.
+
+## Structure du projet
+
+Dans ce paragraphe, nous allons définir les quelques règles que nous essayons de suivre concernant la structure du projet.
+
+### Mixin SCSS
+
+Si vous devez définir des mixins SCSS, vous devez les implémenter dans le fichier `src/styles/mixin.scss` et ensuite importer ce fichier lorsque vous souhaitez l'utiliser.
+
+### I18N
+
+Dans le but d'éviter d'avoir un fichier d'i18n trop gros, nous avons commencer à découper ce fichier par `page` ou `fontionnalité`. Il y a par exemple un fichier `src/js/i18n/dictionary/operations/documents.js` pour tous les messages utilisés dans la fonctionnalité de gestions des documents.
+Ces fichiers doivent ensuite être importés directement ou indirectement dans le fichier principal `js/i18n/dictionary/app.js`
\ No newline at end of file
diff --git a/jsconfig.json b/jsconfig.json
index 950a3d39d..bfa9a0471 100644
--- a/jsconfig.json
+++ b/jsconfig.json
@@ -1,5 +1,8 @@
{
"compilerOptions": {
- "baseUrl": "./src"
- }
+ "baseUrl": "./src",
+ "checkJs": true,
+ "jsx": "preserve"
+ },
+ "exclude": ["cypress/**/*.js"]
}
diff --git a/package-lock.json b/package-lock.json
index aaaef79f1..18c8dc772 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2546,6 +2546,30 @@
"unified": "6.2.0"
}
},
+ "@types/cypress": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@types/cypress/-/cypress-1.1.3.tgz",
+ "integrity": "sha512-OXe0Gw8LeCflkG1oPgFpyrYWJmEKqYncBsD/J0r17r0ETx/TnIGDNLwXt/pFYSYuYTpzcq1q3g62M9DrfsBL4g==",
+ "dev": true,
+ "requires": {
+ "cypress": "3.2.0"
+ }
+ },
+ "@types/jest": {
+ "version": "24.0.12",
+ "resolved": "https://registry.npmjs.org/@types/jest/-/jest-24.0.12.tgz",
+ "integrity": "sha512-60sjqMhat7i7XntZckcSGV8iREJyXXI6yFHZkSZvCPUeOnEJ/VP1rU/WpEWQ56mvoh8NhC+sfKAuJRTyGtCOow==",
+ "dev": true,
+ "requires": {
+ "@types/jest-diff": "20.0.1"
+ }
+ },
+ "@types/jest-diff": {
+ "version": "20.0.1",
+ "resolved": "https://registry.npmjs.org/@types/jest-diff/-/jest-diff-20.0.1.tgz",
+ "integrity": "sha512-yALhelO3i0hqZwhjtcr6dYyaLoCHbAMshwtj6cGxTvHZAKXHsYGdff6E8EPw3xLKY0ELUTQ69Q1rQiJENnccMA==",
+ "dev": true
+ },
"@types/node": {
"version": "11.9.4",
"resolved": "https://registry.npmjs.org/@types/node/-/node-11.9.4.tgz",
@@ -4366,9 +4390,9 @@
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
},
"bootstrap": {
- "version": "3.3.7",
- "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-3.3.7.tgz",
- "integrity": "sha1-WjiTlFSfIzMIdaOxUGVldPip63E="
+ "version": "3.4.1",
+ "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-3.4.1.tgz",
+ "integrity": "sha512-yN5oZVmRCwe5aKwzRj6736nSmKDX7pLYwsXiCj/EYmo16hODaBiT4En5btW/jhBF/seV+XMx3aYwukYC3A49DA=="
},
"boundary": {
"version": "1.0.1",
diff --git a/package.json b/package.json
index f0a0254fe..2d2184d5d 100755
--- a/package.json
+++ b/package.json
@@ -37,7 +37,7 @@
},
"dependencies": {
"babel-polyfill": "6.26.0",
- "bootstrap": "3.3.7",
+ "bootstrap": "3.4.1",
"dompurify": "0.8.7",
"draft-js": "0.10.4",
"draft-js-export-html": "1.2.0",
@@ -79,6 +79,8 @@
"@storybook/addon-links": "4.1.12",
"@storybook/addons": "4.1.12",
"@storybook/react": "4.1.12",
+ "@types/cypress": "^1.1.3",
+ "@types/jest": "^24.0.12",
"babel-loader": "8.0.5",
"concurrently": "3.5.1",
"coveralls": "3.0.0",
diff --git a/src/app.scss b/src/app.scss
index 8404ef660..8e0e1b090 100644
--- a/src/app.scss
+++ b/src/app.scss
@@ -1,3 +1,5 @@
+@import 'src/styles/mixin.scss';
+
// Define concepts colors
$color_concepts_1: #044173;
$color_concepts_2: #234ca5;
@@ -55,18 +57,6 @@ a {
margin: 0;
}
-@mixin btn-group-vertical() {
- border-radius: 6px;
- border-width: 2px;
- border-style: solid;
- margin-top: 200px;
-
- .btn {
- margin-top: 15px;
- margin-bottom: 15px;
- }
-}
-
.btn-group-vertical {
border-color: $color_concepts_3;
@include btn-group-vertical();
diff --git a/src/js/actions/constants/operations.js b/src/js/actions/constants/operations.js
index e329cb735..978a8aea8 100644
--- a/src/js/actions/constants/operations.js
+++ b/src/js/actions/constants/operations.js
@@ -10,3 +10,4 @@ export * from './operations/series';
export * from './operations/codeList';
export * from './operations/organisations';
export * from './operations/sims';
+export * from './operations/documents';
diff --git a/src/js/actions/constants/operations/documents.js b/src/js/actions/constants/operations/documents.js
new file mode 100644
index 000000000..2690d3445
--- /dev/null
+++ b/src/js/actions/constants/operations/documents.js
@@ -0,0 +1,5 @@
+export const LOAD_OPERATIONS_DOCUMENTS = 'LOAD_OPERATIONS_DOCUMENTS';
+export const LOAD_OPERATIONS_DOCUMENTS_SUCCESS =
+ 'LOAD_OPERATIONS_DOCUMENTS_SUCCESS';
+export const LOAD_OPERATIONS_DOCUMENTS_FAILURE =
+ 'LOAD_OPERATIONS_DOCUMENTS_FAILURE';
diff --git a/src/js/actions/operations/documents/list.js b/src/js/actions/operations/documents/list.js
new file mode 100644
index 000000000..a5fb8d92b
--- /dev/null
+++ b/src/js/actions/operations/documents/list.js
@@ -0,0 +1,79 @@
+import api from 'js/remote-api/operations-api';
+import * as A from 'js/actions/constants';
+import { sortArray } from 'js/utils/array-utils';
+
+const sortByLabel = sortArray('label');
+
+export default () => dispatch => {
+ dispatch({
+ type: A.LOAD_OPERATIONS_DOCUMENTS,
+ payload: {},
+ });
+ return api.getDocumentsList().then(
+ results =>
+ dispatch({
+ type: A.LOAD_OPERATIONS_DOCUMENTS_SUCCESS,
+ payload: { results: sortByLabel(results) },
+ }),
+ err => {
+ const results = [
+ {
+ uri: 'uri1-bis',
+ url: 'http://google.fr?q=url-1',
+ updatedDate: '01/01/2019',
+ lang: 'fr',
+ labelLg1: 'Document 1 - label en Langue 1',
+ labelLg2: 'Document 1 - label en Langue 2',
+ descriptionLg1: 'Description 1 en Langue 1',
+ descriptionLg2: 'Description 1 en Langue 2',
+ },
+ {
+ uri: 'uri2-bis',
+ url: 'http://google.fr?q=url-2',
+ updatedDate: '01/02/2019',
+ labelLg1: 'Document 2 - label en Langue 1',
+ labelLg2: 'Document 2 - label en Langue 2',
+ descriptionLg1: 'Description 2 en Langue 1',
+ descriptionLg2: 'Description 2 en Langue 2',
+ },
+ {
+ uri: 'uri3-bis',
+ url: 'http://google.fr?q=url-2',
+ labelLg1: 'Document 3 - label en Langue 1',
+ labelLg2: 'Document 3 - label en Langue 2',
+ descriptionLg1: 'Description 3 en Langue 1',
+ descriptionLg2: 'Description 3 en Langue 2',
+ lang: 'fr',
+ },
+
+ {
+ uri: 'uri5-bis',
+ url: 'http://google.fr?q=url-2',
+ labelLg1: 'Document 5 - label en Langue 1',
+ labelLg2: 'Document 5 - label en Langue 2',
+ descriptionLg1: 'Description 5 en Langue 1',
+ descriptionLg2: 'Description 5 en Langue 2',
+ lang: 'fr',
+ },
+ {
+ uri: 'uri4-bis',
+ url: 'http://google.fr?q=url-2',
+ updatedDate: '01/02/2019',
+ labelLg1: 'Document 4 - label en Langue 1',
+ labelLg2: 'Document 4 - label en Langue 2',
+ descriptionLg1: 'Description 4 en Langue 1',
+ descriptionLg2: 'Description 4 en Langue 2',
+ },
+ ];
+ dispatch({
+ type: A.LOAD_OPERATIONS_DOCUMENTS_SUCCESS,
+ payload: { results: sortByLabel(results) },
+ });
+
+ /*dispatch({
+ type: A.LOAD_OPERATIONS_DOCUMENTS_FAILURE,
+ payload: { err },
+ })*/
+ }
+ );
+};
diff --git a/src/js/actions/operations/families/item.js b/src/js/actions/operations/families/item.js
index 0d4ef87f7..29b3747f4 100644
--- a/src/js/actions/operations/families/item.js
+++ b/src/js/actions/operations/families/item.js
@@ -11,7 +11,10 @@ export const saveFamily = (family, callback) => dispatch => {
results => {
dispatch({
type: A.SAVE_OPERATIONS_FAMILY_SUCCESS,
- payload: family,
+ payload: {
+ ...family,
+ id: family.id ? family.id : results
+ },
});
callback(results);
},
diff --git a/src/js/actions/operations/series/item.js b/src/js/actions/operations/series/item.js
index df1f84ab6..9b8035db4 100644
--- a/src/js/actions/operations/series/item.js
+++ b/src/js/actions/operations/series/item.js
@@ -1,19 +1,22 @@
import api from 'js/remote-api/operations-api';
import * as A from 'js/actions/constants';
-export const saveSerie = (serie, callback) => dispatch => {
+export const saveSerie = (series, callback) => dispatch => {
dispatch({
type: A.SAVE_OPERATIONS_SERIE,
- payload: serie,
+ payload: series,
});
- const method = serie.id ? 'putSeries' : 'postSeries';
- return api[method](serie).then(
- results => {
+ const method = series.id ? 'putSeries' : 'postSeries';
+ return api[method](series).then(
+ id => {
dispatch({
type: A.SAVE_OPERATIONS_SERIE_SUCCESS,
- payload: serie,
+ payload: {
+ ...series,
+ id
+ },
});
- callback(results);
+ callback(id);
},
err => {
dispatch({
diff --git a/src/js/actions/operations/series/item.spec.js b/src/js/actions/operations/series/item.spec.js
index c5c93c538..7a2b86913 100644
--- a/src/js/actions/operations/series/item.spec.js
+++ b/src/js/actions/operations/series/item.spec.js
@@ -43,7 +43,7 @@ describe('Serie actions', () => {
describe('save a serie', () => {
it('should call dispatch SAVE_OPERATIONS_SERIE_SUCCESS action with the udpated serie', async () => {
api.putSeries = function(id) {
- return Promise.resolve('');
+ return Promise.resolve('1');
};
const serie = { label: 'aaa', id: '1' };
await saveSerie(serie, () => {})(dispatch);
diff --git a/src/js/actions/operations/sims/item.js b/src/js/actions/operations/sims/item.js
index f2de6e52d..49da9ac03 100644
--- a/src/js/actions/operations/sims/item.js
+++ b/src/js/actions/operations/sims/item.js
@@ -3,6 +3,18 @@ import * as A from 'js/actions/constants';
import { LOADING } from 'js/constants';
import { getLabelsFromOperation } from 'js/utils/msd';
+/**
+ * @typedef {Object} SimsDocuments
+ * @property {string} uri
+ * @property {string} url
+ * @property {string=} updatedDate
+ * @property {string} labelLg1
+ * @property {string} labelLg2
+ * @property {string=} lang
+ * @property {string} descriptionLg1
+ * @property {string} descriptionLg2
+ */
+
export const saveSims = (sims, callback) => (dispatch, getState) => {
let promise = Promise.resolve(sims);
@@ -61,11 +73,43 @@ export default id => (dispatch, getState) => {
...results,
operationsWithoutSims,
rubrics: results.rubrics.reduce((acc, rubric) => {
+ // TO BE DELETED
+ const documents = [
+ {
+ uri: 'uri1-bis',
+ url: 'http://google.fr?q=url-1',
+ updatedDate: '01/01/2019',
+ lang: 'fr',
+ labelLg1: 'Document 1 - label en Langue 1',
+ labelLg2: 'Document 1 - label en Langue 2',
+ descriptionLg1: 'Description 1 en Langue 1',
+ descriptionLg2: 'Description 1 en Langue 2',
+ },
+ {
+ uri: 'uri2-bis',
+ url: 'http://google.fr?q=url-2',
+ updatedDate: '01/02/2019',
+ labelLg1: 'Document 2 - label en Langue 1',
+ labelLg2: 'Document 2 - label en Langue 2',
+ descriptionLg1: 'Description 2 en Langue 1',
+ descriptionLg2: 'Description 2 en Langue 2',
+ },
+ {
+ uri: 'uri3-bis',
+ url: 'http://google.fr?q=url-2',
+ labelLg1: 'Document 3 - label en Langue 1',
+ labelLg2: 'Document 3 - label en Langue 2',
+ descriptionLg1: 'Description 3 en Langue 1',
+ descriptionLg2: 'Description 3 en Langue 2',
+ lang: 'fr',
+ },
+ ];
return {
...acc,
[rubric.idAttribute]: {
...rubric,
idMas: rubric.idAttribute,
+ documents: rubric.idAttribute === 'S.3.1' ? documents : [],
},
};
}, {}),
diff --git a/src/js/components/administration/roles/home-container.js b/src/js/components/administration/roles/home-container.js
index bcc5baba0..90122f10e 100644
--- a/src/js/components/administration/roles/home-container.js
+++ b/src/js/components/administration/roles/home-container.js
@@ -108,4 +108,7 @@ const mapDispatchToProps = {
deleteRole,
};
-export default connect(mapStateToProps, mapDispatchToProps)(RolesContainer);
+export default connect(
+ mapStateToProps,
+ mapDispatchToProps
+)(RolesContainer);
diff --git a/src/js/components/operations/msd/documents/documents-bloc/index.js b/src/js/components/operations/msd/documents/documents-bloc/index.js
new file mode 100644
index 000000000..cb76db4ac
--- /dev/null
+++ b/src/js/components/operations/msd/documents/documents-bloc/index.js
@@ -0,0 +1,171 @@
+import React, { useState, Component } from 'react';
+import { connect } from 'react-redux';
+import { sortArray } from 'js/utils/array-utils';
+import D from 'js/i18n';
+import loadDocuments from 'js/actions/operations/documents/list';
+import './style.scss';
+import { getLang } from 'js/i18n/build-dictionary';
+import { LOADED, NOT_LOADED } from 'js/constants';
+/**
+ * @typedef {Object} DocumentsBlocProps
+ * @property {import('js/actions/operations/sims/item').SimsDocuments[]=} documents
+ * @property {String=} localPrefix
+ * @property {Boolean=} editMode
+ * @property {(string) => void } deleteHandler
+ * @property {(string) => void } addHandler
+ * @property {import('js/actions/operations/sims/item').SimsDocuments[]} documentStores
+ */
+
+/**
+ * This component will display a list of documents associated
+ * to a RICH_TEXT typed rubric of a SIMS
+ *
+ * @param {DocumentsBlocProps} props
+ */
+export function DocumentsBloc({
+ documents = [],
+ localPrefix = 'Lg1',
+ editMode = false,
+ deleteHandler,
+ addHandler,
+ documentStores = [],
+}) {
+ const currentDocuments = sortArray(`label${localPrefix}`)(documents);
+ const currentDocumentsIds = currentDocuments.map(doc => doc.uri);
+ const otherDocuments = sortArray(`label${localPrefix}`)(
+ documentStores.filter(
+ document => !currentDocumentsIds.includes(document.uri)
+ )
+ );
+ const isSecondLang = localPrefix === 'Lg2';
+ const [panelStatus, setPanelStatus] = useState(false);
+
+ function addAsideToTheDocument(document) {
+ let updatedDate;
+ if (document.updatedDate) {
+ updatedDate = new Intl.DateTimeFormat(getLang()).format(
+ new Date(document.updatedDate)
+ );
+ }
+ const aside = [document.lang, updatedDate].filter(val => !!val).join('-');
+ return {
+ ...document,
+ aside,
+ };
+ }
+
+ const defaultBtnBlocFunction = document => (
+
+ );
+
+ function displayHTMLForDocument(
+ document,
+ btnBlocFunction = defaultBtnBlocFunction
+ ) {
+ return (
+
+
+ {document[`label${localPrefix}`]}
+
+ ({document.aside})
+ {editMode && !isSecondLang && btnBlocFunction(document)}
+
+ );
+ }
+ return (
+ <>
+ {documents && documents.length > 0 && (
+
+ {currentDocuments
+ .map(addAsideToTheDocument)
+ .map(document => displayHTMLForDocument(document))}
+
+ )}
+
+ {editMode && !isSecondLang && otherDocuments.length > 0 && (
+
+
+
+
+ {panelStatus && (
+
+
+ {otherDocuments.map(addAsideToTheDocument).map(document => {
+ return displayHTMLForDocument(document, document => (
+
+ ));
+ })}
+
+
+ )}
+
+ )}
+ >
+ );
+}
+
+class DocumentsBlocContainer extends Component {
+ componentWillMount() {
+ if (this.props.documentStoresStatus === NOT_LOADED) {
+ this.props.loadDocuments();
+ }
+ }
+ render() {
+ return (
+ this.props.documentStoresStatus === LOADED && (
+
+ )
+ );
+ }
+}
+
+const mapDispatchToProps = {
+ loadDocuments,
+};
+
+const mapStateToProps = state => {
+ return {
+ documentStoresStatus: state.operationsDocuments.status,
+ documentStores: state.operationsDocuments.results,
+ };
+};
+
+export default connect(
+ mapStateToProps,
+ mapDispatchToProps
+)(DocumentsBlocContainer);
diff --git a/src/js/components/operations/msd/documents/documents-bloc/index.spec.js b/src/js/components/operations/msd/documents/documents-bloc/index.spec.js
new file mode 100644
index 000000000..ada0d0164
--- /dev/null
+++ b/src/js/components/operations/msd/documents/documents-bloc/index.spec.js
@@ -0,0 +1,176 @@
+import React from 'react';
+import { shallow } from 'enzyme';
+import { DocumentsBloc } from './index';
+import { sortArray } from 'js/utils/array-utils';
+import { getLang } from 'js/i18n/build-dictionary';
+
+jest.mock('js/components/operations/msd/utils');
+
+const documents = [
+ {
+ uri: 'uri1-bis',
+ url: 'http://google.fr?q=url-1',
+ updatedDate: '2019-03-04T10:00:00.000Z',
+ labelLg1: 'B labelLg1-0',
+ labelLg2: 'B labelLg2-0',
+ lang: 'fr',
+ descriptionLg1: 'descriptionLg1',
+ descriptionLg2: 'descriptionLg2',
+ aside: `fr-${new Intl.DateTimeFormat(getLang()).format(
+ new Date('2019-03-04T10:00:00.000Z')
+ )}`,
+ },
+ {
+ uri: 'uri2-bis',
+ url: 'http://google.fr?q=url-2',
+ updatedDate: '2019-04-04T10:00:00.000Z',
+ labelLg1: 'A labelLg1-1',
+ labelLg2: 'A labelLg2-1',
+ descriptionLg1: 'descriptionLg1-2',
+ descriptionLg2: 'descriptionLg2-2',
+ aside: `${new Intl.DateTimeFormat(getLang()).format(
+ new Date('2019-04-04T10:00:00.000Z')
+ )}`,
+ },
+ {
+ uri: 'uri3-bis',
+ url: 'http://google.fr?q=url-2',
+ labelLg1: 'Z labelLg1-2',
+ labelLg2: 'Z labelLg2-2',
+ lang: 'fr',
+ descriptionLg1: 'descriptionLg1-2',
+ descriptionLg2: 'descriptionLg2-2',
+ aside: 'fr',
+ },
+];
+
+describe('DocumentsBloc', () => {
+ it('should display nothing if the documents props is not defined', () => {
+ const general = shallow();
+ expect(general.find('.documentsbloc')).toHaveLength(0);
+ });
+ it('should display nothing if the documents props is an empty array', () => {
+ const general = shallow();
+ expect(general.find('.documentsbloc')).toHaveLength(0);
+ });
+
+ it('should display nothing if the documents props is an empty array', () => {
+ const general = shallow(
+
+ );
+ expect(general.find('li')).toHaveLength(3);
+ });
+
+ it('should display the Lg1 label and description ordered by label', () => {
+ const general = shallow(
+
+ );
+ const orderedList = sortArray('labelLg1')(documents);
+
+ general.find('li').forEach((li, i) => {
+ expect(li.html()).toEqual(
+ `${
+ orderedList[i].labelLg1
+ }(${orderedList[i].aside})`
+ );
+ });
+ });
+ it('should display the Lg2 label and description ordered by label', () => {
+ const general = shallow(
+
+ );
+ const orderedList = sortArray('labelLg2')(documents);
+
+ general.find('li').forEach((li, i) => {
+ expect(li.html()).toEqual(
+ `${
+ orderedList[i].labelLg2
+ }(${orderedList[i].aside})`
+ );
+ });
+ });
+
+ describe.each`
+ lang | expectedEdit | expectedView
+ ${'Lg2'} | ${0} | ${0}
+ ${'Lg1'} | ${3} | ${0}
+ `('$a + $b', ({ lang, expectedEdit, expectedView }) => {
+ it('should not display delete buttons', () => {
+ const general = shallow(
+
+ );
+
+ expect(general.find('.documentsbloc__delete')).toHaveLength(expectedView);
+ });
+
+ it('should display zero delete buttons', () => {
+ const general = shallow(
+
+ );
+
+ expect(general.find('.documentsbloc__delete')).toHaveLength(expectedEdit);
+ });
+ });
+
+ it('should not display the Add Document button if there is not more document to add', () => {
+ const general = shallow(
+
+ );
+
+ expect(general.find('.documentsbloc__add')).toHaveLength(0);
+ });
+ it('should display the Add Document button if there is more than on document available', () => {
+ const general = shallow(
+
+ );
+
+ expect(general.find('.documentsbloc__add')).toHaveLength(0);
+ });
+
+ it('should not display the Add Document button for Lg2', () => {
+ const general = shallow(
+
+ );
+
+ expect(general.find('.documentsbloc__add')).toHaveLength(0);
+ });
+});
diff --git a/src/js/components/operations/msd/documents/documents-bloc/style.scss b/src/js/components/operations/msd/documents/documents-bloc/style.scss
new file mode 100644
index 000000000..9eab727b1
--- /dev/null
+++ b/src/js/components/operations/msd/documents/documents-bloc/style.scss
@@ -0,0 +1,17 @@
+@import 'src/styles/mixin.scss';
+
+.documentsbloc {
+ i {
+ margin-left: 10px;
+ }
+
+ &__btn {
+ @include reset-btn();
+ }
+ &__delete {
+ float: right;
+ }
+ &__filepicker {
+ padding-left: 0;
+ }
+}
diff --git a/src/js/components/operations/msd/layout/style.scss b/src/js/components/operations/msd/layout/style.scss
index 30129e882..9168e6a6c 100644
--- a/src/js/components/operations/msd/layout/style.scss
+++ b/src/js/components/operations/msd/layout/style.scss
@@ -70,19 +70,6 @@
}
}
-@mixin panel-trigger() {
- margin-top: 20px;
- border-top-left-radius: 2px;
- border-bottom-left-radius: 2px;
- padding: 6px 5px 2px 5px;
- position: absolute;
- margin-top: 20px;
- background-color: $color_operations_3;
- color: #fff;
- text-align: center;
- text-transform: uppercase;
-}
-
.msd__panel-trigger {
&_right {
@include panel-trigger();
diff --git a/src/js/components/operations/msd/pages/sims-creation/sims-field.js b/src/js/components/operations/msd/pages/sims-creation/sims-field.js
index d80184ba6..8faa9c3fe 100644
--- a/src/js/components/operations/msd/pages/sims-creation/sims-field.js
+++ b/src/js/components/operations/msd/pages/sims-creation/sims-field.js
@@ -9,6 +9,7 @@ import SelectRmes from 'js/components/shared/select-rmes';
import { Note } from 'js/components/shared/note/note';
import './sims-field.scss';
+import DocumentsBloc from '../../documents/documents-bloc';
const { RICH_TEXT, TEXT, DATE, CODE_LIST, ORGANIZATION } = rangeType;
@@ -21,20 +22,52 @@ class Field extends PureComponent {
secondLang: PropTypes.bool,
};
- handleTextInput = value => {
+ _handleChange(override) {
this.props.handleChange({
id: this.props.msd.idMas,
- override: { [this.props.secondLang ? 'labelLg2' : 'labelLg1']: value },
+ override,
+ });
+ }
+
+ handleTextInput = value => {
+ this._handleChange({
+ [this.props.secondLang ? 'labelLg2' : 'labelLg1']: value,
});
};
+
handleCodeListInput = value => {
- this.props.handleChange({
- id: this.props.msd.idMas,
- override: { codeList: this.props.msd.codeList, value },
- });
+ this._handleChange({ codeList: this.props.msd.codeList, value });
};
+
+ /**
+ * @param {String} value The new value of the date input
+ */
handleDateInput = value => {
- this.props.handleChange({ id: this.props.msd.idMas, override: { value } });
+ this._handleChange({ value });
+ };
+
+ /**
+ * Handler when the user click on a button in order to delete a document
+ * @param {String} uri The uri of the document that we should remove
+ */
+ handleDeleteDocument = uri => {
+ const documents = this.props.currentSection.documents || [];
+
+ this._handleChange({
+ documents: documents.filter(doc => doc.uri !== uri),
+ });
+ };
+
+ /**
+ * Handler when the user add a new document to a rubric
+ * @param {import('js/actions/operations/sims/item').SimsDocuments} document
+ */
+ handleAddDocument = document => {
+ const documents = this.props.currentSection.documents || [];
+
+ this._handleChange({
+ documents: [...documents, document],
+ });
};
render() {
@@ -98,11 +131,20 @@ class Field extends PureComponent {
/>
)}
{msd.rangeType === RICH_TEXT && (
-
+ <>
+
+
+ >
)}
{msd.rangeType === CODE_LIST && codesList && (
diff --git a/src/js/components/operations/msd/pages/sims-visualisation/index.js b/src/js/components/operations/msd/pages/sims-visualisation/index.js
index dbf8c3854..8acc16861 100644
--- a/src/js/components/operations/msd/pages/sims-visualisation/index.js
+++ b/src/js/components/operations/msd/pages/sims-visualisation/index.js
@@ -7,6 +7,7 @@ import CheckSecondLang from 'js/components/shared/second-lang-checkbox';
import Button from 'js/components/shared/button';
import { markdownToHtml } from 'js/utils/html';
import { Note } from 'js/components/shared/note/note';
+import DocumentsBloc from 'js/components/operations/msd/documents/documents-bloc/index.js';
const { RICH_TEXT, TEXT, DATE, CODE_LIST, ORGANIZATION } = rangeType;
@@ -40,13 +41,19 @@ export default function SimsVisualisation({
currentSection.rangeType === DATE &&
stringToDate(currentSection.value)}
{currentSection.rangeType === RICH_TEXT && (
-
+ <>
+
+
+ >
)}
{currentSection.rangeType === CODE_LIST &&
codesLists[currentSection.codeList] && (
diff --git a/src/js/i18n/build-dictionary.js b/src/js/i18n/build-dictionary.js
index deb79e0cc..8a2ed26c7 100644
--- a/src/js/i18n/build-dictionary.js
+++ b/src/js/i18n/build-dictionary.js
@@ -1,7 +1,7 @@
import appD from './dictionary/app';
import conceptsD from './dictionary/concepts';
import classificationsD from './dictionary/classifications';
-import operationsD from './dictionary/operations';
+import operationsD from './dictionary/operations/index.js';
import 'moment/locale/en-gb';
import 'moment/locale/fr';
diff --git a/src/js/i18n/dictionary/app.js b/src/js/i18n/dictionary/app.js
index 4bed51e90..bb5839a37 100644
--- a/src/js/i18n/dictionary/app.js
+++ b/src/js/i18n/dictionary/app.js
@@ -1,3 +1,4 @@
+import btnD from './generic/btn';
const dictionary = {
welcome: {
fr: 'Application de gestion des métadonnées de référence',
@@ -219,91 +220,7 @@ const dictionary = {
fr: 'Liens',
en: 'Links',
},
- // Buttons
- btnReturn: {
- fr: 'Retour',
- en: 'Back',
- },
- btnReturnCurrent: {
- fr: 'Retour à la version courante',
- en: 'Back to current version',
- },
- btnNewMale: {
- fr: 'Nouveau',
- en: 'New',
- },
- btnNewFemale: {
- fr: 'Nouvelle',
- en: 'New',
- },
- btnUpdate: {
- fr: 'Modifier',
- en: 'Update',
- },
- btnDuplicate: {
- fr: 'Dupliquer',
- en: 'Duplicate',
- },
- btnSave: {
- fr: 'Sauvegarder',
- en: 'Save',
- },
- btnCompare: {
- fr: 'Comparer',
- en: 'Compare',
- },
- btnCancel: {
- fr: 'Annuler',
- en: 'Cancel',
- },
- btnClose: {
- fr: 'Fermer',
- en: 'Close',
- },
- btnAdd: {
- fr: 'Ajouter',
- en: 'Add',
- },
- btnMinorVersion: {
- fr: 'Ecraser la version',
- en: 'Overwrite version',
- },
- btnMajorVersion: {
- fr: 'Nouvelle version',
- en: 'New version',
- },
- btnExport: {
- fr: 'Exporter',
- en: 'Export',
- },
- btnPdf: {
- fr: 'Exporter en PDF',
- en: 'Export as PDF',
- },
- btnOdt: {
- fr: 'Exporter en ODT',
- en: 'Export as ODT',
- },
- btnSend: {
- fr: 'Envoyer',
- en: 'Send',
- },
- btnValid: {
- fr: 'Publier',
- en: 'Publish',
- },
- btnReinitialize: {
- fr: 'Réinitialiser',
- en: 'Reinitialize',
- },
- btnTree: {
- fr: "Voir l'arborescence",
- en: 'View tree',
- },
- btnDelete: {
- fr: 'Supprimer',
- en: 'Delete',
- },
+ ...btnD,
// Links
narrowerTitle: {
fr: 'Parent',
diff --git a/src/js/i18n/dictionary/generic/btn.js b/src/js/i18n/dictionary/generic/btn.js
new file mode 100644
index 000000000..16d0949f8
--- /dev/null
+++ b/src/js/i18n/dictionary/generic/btn.js
@@ -0,0 +1,86 @@
+export default {
+ btnReturn: {
+ fr: 'Retour',
+ en: 'Back',
+ },
+ btnReturnCurrent: {
+ fr: 'Retour à la version courante',
+ en: 'Back to current version',
+ },
+ btnNewMale: {
+ fr: 'Nouveau',
+ en: 'New',
+ },
+ btnNewFemale: {
+ fr: 'Nouvelle',
+ en: 'New',
+ },
+ btnUpdate: {
+ fr: 'Modifier',
+ en: 'Update',
+ },
+ btnDuplicate: {
+ fr: 'Dupliquer',
+ en: 'Duplicate',
+ },
+ btnSave: {
+ fr: 'Sauvegarder',
+ en: 'Save',
+ },
+ btnCompare: {
+ fr: 'Comparer',
+ en: 'Compare',
+ },
+ btnCancel: {
+ fr: 'Annuler',
+ en: 'Cancel',
+ },
+ btnClose: {
+ fr: 'Fermer',
+ en: 'Close',
+ },
+ btnAdd: {
+ fr: 'Ajouter',
+ en: 'Add',
+ },
+ btnMinorVersion: {
+ fr: 'Ecraser la version',
+ en: 'Overwrite version',
+ },
+ btnMajorVersion: {
+ fr: 'Nouvelle version',
+ en: 'New version',
+ },
+ btnExport: {
+ fr: 'Exporter',
+ en: 'Export',
+ },
+ btnPdf: {
+ fr: 'Exporter en PDF',
+ en: 'Export as PDF',
+ },
+ btnOdt: {
+ fr: 'Exporter en ODT',
+ en: 'Export as ODT',
+ },
+ btnSend: {
+ fr: 'Envoyer',
+ en: 'Send',
+ },
+ btnValid: {
+ fr: 'Valider',
+ en: 'Validate',
+ },
+ btnReinitialize: {
+ fr: 'Réinitialiser',
+ en: 'Reinitialize',
+ },
+ btnTree: {
+ fr: "Voir l'arborescence",
+ en: 'View tree',
+ },
+ btnDelete: {
+ fr: 'Supprimer',
+ en: 'Delete',
+ },
+};
diff --git a/src/js/i18n/dictionary/operations/documents.js b/src/js/i18n/dictionary/operations/documents.js
new file mode 100644
index 000000000..e5f3a3fdd
--- /dev/null
+++ b/src/js/i18n/dictionary/operations/documents.js
@@ -0,0 +1,6 @@
+export default {
+ addDocument: {
+ fr: 'Ajoutez un document',
+ en: 'Add a document',
+ },
+};
diff --git a/src/js/i18n/dictionary/operations.js b/src/js/i18n/dictionary/operations/index.js
similarity index 97%
rename from src/js/i18n/dictionary/operations.js
rename to src/js/i18n/dictionary/operations/index.js
index 77b05cece..f7288bf9f 100644
--- a/src/js/i18n/dictionary/operations.js
+++ b/src/js/i18n/dictionary/operations/index.js
@@ -1,3 +1,5 @@
+import documentsD from 'js/i18n/dictionary/operations/documents';
+
const dictionary = {
operationsTitle: {
fr: 'Opérations',
@@ -180,6 +182,7 @@ const dictionary = {
en: '{{OPERATION_LABEL}} SIMS',
fr: "SIMS de l'opération {{OPERATION_LABEL}}",
},
+ ...documentsD,
};
export default dictionary;
diff --git a/src/js/reducers/operations/documents.js b/src/js/reducers/operations/documents.js
new file mode 100644
index 000000000..2db4a64b7
--- /dev/null
+++ b/src/js/reducers/operations/documents.js
@@ -0,0 +1,35 @@
+import { LOADED, LOADING, ERROR, NOT_LOADED } from 'js/constants';
+import {
+ LOAD_OPERATIONS_DOCUMENTS,
+ LOAD_OPERATIONS_DOCUMENTS_SUCCESS,
+ LOAD_OPERATIONS_DOCUMENTS_FAILURE,
+} from 'js/actions/constants/operations/documents';
+
+/**
+ *
+ * @param {SimsDoc} state
+ * @param {*} action
+ */
+export const operationsDocuments = function(
+ state = { status: NOT_LOADED },
+ action
+) {
+ switch (action.type) {
+ case LOAD_OPERATIONS_DOCUMENTS:
+ return {
+ status: LOADING,
+ };
+ case LOAD_OPERATIONS_DOCUMENTS_SUCCESS:
+ return {
+ status: LOADED,
+ results: action.payload.results,
+ };
+ case LOAD_OPERATIONS_DOCUMENTS_FAILURE:
+ return {
+ status: ERROR,
+ err: action.payload.err,
+ };
+ default:
+ return state;
+ }
+};
diff --git a/src/js/reducers/operations/index.js b/src/js/reducers/operations/index.js
index aebafd371..e895bf76f 100644
--- a/src/js/reducers/operations/index.js
+++ b/src/js/reducers/operations/index.js
@@ -1,7 +1,15 @@
import * as A from 'js/actions/constants';
import { LOADED, LOADING, ERROR } from 'js/constants';
import * as currentReducers from 'js/reducers/operations/current';
+import * as documentsReducers from 'js/reducers/operations/documents';
+import { sortArray } from 'js/utils/array-utils';
+const sortByLabel = sortArray('label');
+
+/**
+ *
+ * @param {Array} param List of Redux event
+ */
function makeReducers([
GET_ITEMS,
GET_ITEMS_SUCCESS,
@@ -26,9 +34,23 @@ function makeReducers([
};
case SAVE_ITEM_SUCCESS:
if (!state.results) return state;
+
+ /**
+ * When we add / update a new object, we must first remove this updated item from
+ * the previous list.
+ *
+ * Finally, we should sort by label again
+ */
+ const tail = state.results.filter(obj => obj.id !== action.payload.id);
return {
status: state.status,
- results: [...state.results, action.payload],
+ results: sortByLabel([
+ ...tail,
+ {
+ id: action.payload.id,
+ label: action.payload.prefLabelLg1,
+ },
+ ]),
};
default:
return state;
@@ -36,14 +58,22 @@ function makeReducers([
};
}
-const operationsSeriesList = makeReducers([
- A.LOAD_OPERATIONS_SERIES_LIST,
- A.LOAD_OPERATIONS_SERIES_LIST_SUCCESS,
- A.LOAD_OPERATIONS_SERIES_LIST_FAILURE,
-]);
+/**
+ * @typedef {Object} ActionType
+ * @property {string} types
+ * @property {Object} payload
+ */
+/**
+ * Reducer to store the state of any asynchronous operations.
+ * The boolean state is used to display / hide a spinner
+ *
+ * @param {Boolean} state
+ * @param {ActionType} action
+ * @returns {Boolean}
+ */
const operationsAsyncTask = function(state = false, action) {
- switch (action.type) {
+ switch (action.types) {
case A.SAVE_OPERATIONS_INDICATOR:
case A.SAVE_OPERATIONS_SERIE:
case A.SAVE_OPERATIONS_FAMILY:
@@ -63,17 +93,28 @@ const operationsAsyncTask = function(state = false, action) {
return state;
}
};
-const operationsOperationsList = makeReducers([
- A.LOAD_OPERATIONS_OPERATIONS_LIST,
- A.LOAD_OPERATIONS_OPERATIONS_LIST_SUCCESS,
- A.LOAD_OPERATIONS_OPERATIONS_LIST_FAILURE,
-]);
const operationsFamiliesList = makeReducers([
A.LOAD_OPERATIONS_FAMILIES_LIST,
A.LOAD_OPERATIONS_FAMILIES_LIST_SUCCESS,
A.LOAD_OPERATIONS_FAMILIES_LIST_FAILURE,
+ A.SAVE_OPERATIONS_FAMILY_SUCCESS,
]);
+
+const operationsSeriesList = makeReducers([
+ A.LOAD_OPERATIONS_SERIES_LIST,
+ A.LOAD_OPERATIONS_SERIES_LIST_SUCCESS,
+ A.LOAD_OPERATIONS_SERIES_LIST_FAILURE,
+ A.SAVE_OPERATIONS_SERIE_SUCCESS,
+]);
+
+const operationsOperationsList = makeReducers([
+ A.LOAD_OPERATIONS_OPERATIONS_LIST,
+ A.LOAD_OPERATIONS_OPERATIONS_LIST_SUCCESS,
+ A.LOAD_OPERATIONS_OPERATIONS_LIST_FAILURE,
+ A.SAVE_OPERATIONS_OPERATION_SUCCESS,
+]);
+
const operationsMetadataStructureList = makeReducers([
A.LOAD_OPERATIONS_METADATASTRUCTURE_LIST,
A.LOAD_OPERATIONS_METADATASTRUCTURE_LIST_SUCCESS,
@@ -94,4 +135,5 @@ export default {
operationsIndicatorsList,
operationsAsyncTask,
...currentReducers,
+ ...documentsReducers,
};
diff --git a/src/js/remote-api/operations-api.js b/src/js/remote-api/operations-api.js
index 2b817ef09..6a225d70c 100644
--- a/src/js/remote-api/operations-api.js
+++ b/src/js/remote-api/operations-api.js
@@ -9,6 +9,7 @@ const api = {
getSeriesList: () => ['series'],
getOperationsList: () => ['operations'],
getFamiliesList: () => ['families'],
+ getDocumentsList: () => ['documents'],
getMetadataStructureList: () => ['metadataStructureDefinition'],
getMetadataAttributesList: () => ['metadataAttributes'],
@@ -33,7 +34,7 @@ const api = {
},
body: JSON.stringify(family),
},
- () => {},
+ res => Promise.resolve(family.id),
],
postFamily: family => [
`family`,
@@ -43,7 +44,7 @@ const api = {
},
body: JSON.stringify(family),
},
- res => res.text().then(id => id),
+ res => res.text(),
],
putSeries: series => [
`series/${series.id}`,
@@ -53,7 +54,7 @@ const api = {
},
body: JSON.stringify(series),
},
- () => {},
+ res => Promise.resolve(series.id),
],
postSeries: series => [
`series`,
@@ -63,7 +64,7 @@ const api = {
},
body: JSON.stringify(series),
},
- res => res.text().then(id => id),
+ res => res.text(),
],
putOperation: operation => [
`operation/${operation.id}`,
@@ -73,7 +74,7 @@ const api = {
},
body: JSON.stringify(operation),
},
- () => {},
+ res => Promise.resolve(operation.id),
],
postOperation: operation => [
`operation`,
@@ -83,7 +84,7 @@ const api = {
},
body: JSON.stringify(operation),
},
- res => res.text().then(id => id),
+ res => res.text(),
],
getOperationsWithoutReport: idSerie => [
`series/${idSerie}/operationsWithoutReport`,
diff --git a/src/styles/mixin.scss b/src/styles/mixin.scss
new file mode 100644
index 000000000..fd24d39c2
--- /dev/null
+++ b/src/styles/mixin.scss
@@ -0,0 +1,49 @@
+@mixin panel-trigger() {
+ margin-top: 20px;
+ border-top-left-radius: 2px;
+ border-bottom-left-radius: 2px;
+ padding: 6px 5px 2px 5px;
+ position: absolute;
+ margin-top: 20px;
+ background-color: $color_operations_3;
+ color: #fff;
+ text-align: center;
+ text-transform: uppercase;
+}
+
+
+@mixin reset-btn() {
+ border: none;
+ margin: 0;
+ padding: 0;
+ width: auto;
+ overflow: visible;
+
+ background: transparent;
+
+ /* inherit font & color from ancestor */
+ color: inherit;
+ font: inherit;
+
+ /* Normalize `line-height`. Cannot be changed from `normal` in Firefox 4+. */
+ line-height: normal;
+
+ /* Corrects font smoothing for webkit */
+ -webkit-font-smoothing: inherit;
+ -moz-osx-font-smoothing: inherit;
+
+ /* Corrects inability to style clickable `input` types in iOS */
+ -webkit-appearance: none;
+}
+
+@mixin btn-group-vertical() {
+ border-radius: 6px;
+ border-width: 2px;
+ border-style: solid;
+ margin-top: 200px;
+
+ .btn {
+ margin-top: 15px;
+ margin-bottom: 15px;
+ }
+}
\ No newline at end of file