From baf93aed0342f660aaddc9ca11af6b8a7cf2b035 Mon Sep 17 00:00:00 2001 From: Matija Obreza Date: Wed, 22 Aug 2018 10:02:59 +0200 Subject: [PATCH 1/2] npm update --- package-lock.json | 169 ++-------------------------------------------- 1 file changed, 7 insertions(+), 162 deletions(-) diff --git a/package-lock.json b/package-lock.json index 012a099..941e10a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -502,12 +502,6 @@ "util": "0.10.3" } }, - "assert-plus": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", - "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=", - "dev": true - }, "assets": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/assets/-/assets-3.0.0.tgz", @@ -667,12 +661,6 @@ } } }, - "aws-sign2": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", - "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=", - "dev": true - }, "aws4": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.7.0.tgz", @@ -1671,15 +1659,6 @@ "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", "dev": true }, - "boom": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", - "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", - "dev": true, - "requires": { - "hoek": "2.x.x" - } - }, "brace-expansion": { "version": "1.1.9", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.9.tgz", @@ -2700,15 +2679,6 @@ "which": "^1.2.9" } }, - "cryptiles": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", - "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", - "dev": true, - "requires": { - "boom": "2.x.x" - } - }, "crypto-browserify": { "version": "3.12.0", "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", @@ -5186,34 +5156,6 @@ "integrity": "sha1-/Xqtcmvxpf0W38KbL3pmAdJxOcQ=", "dev": true }, - "har-schema": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-1.0.5.tgz", - "integrity": "sha1-0mMTX0MwfALGAq/I/pWXDAFRNp4=", - "dev": true - }, - "har-validator": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-4.2.1.tgz", - "integrity": "sha1-M0gdDxu/9gDdID11gSpqX7oALio=", - "dev": true, - "requires": { - "ajv": "^4.9.1", - "har-schema": "^1.0.5" - }, - "dependencies": { - "ajv": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", - "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", - "dev": true, - "requires": { - "co": "^4.6.0", - "json-stable-stringify": "^1.0.1" - } - } - } - }, "has": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", @@ -5302,18 +5244,6 @@ "minimalistic-assert": "^1.0.1" } }, - "hawk": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", - "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", - "dev": true, - "requires": { - "boom": "2.x.x", - "cryptiles": "2.x.x", - "hoek": "2.x.x", - "sntp": "1.x.x" - } - }, "he": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", @@ -5343,12 +5273,6 @@ "minimalistic-crypto-utils": "^1.0.1" } }, - "hoek": { - "version": "2.16.3", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", - "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", - "dev": true - }, "hoist-non-react-statics": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.3.1.tgz", @@ -5661,17 +5585,6 @@ } } }, - "http-signature": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", - "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", - "dev": true, - "requires": { - "assert-plus": "^0.2.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, "https-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", @@ -7536,9 +7449,9 @@ "dev": true }, "node-gyp": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.7.0.tgz", - "integrity": "sha512-qDQE/Ft9xXP6zphwx4sD0t+VhwV7yFaloMpfbL2QnnDZcyaiakWlLdtFGGQfTAwpFHdpbRhRxVhIHN1OKAjgbg==", + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", + "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==", "dev": true, "requires": { "fstream": "^1.0.0", @@ -7548,60 +7461,13 @@ "nopt": "2 || 3", "npmlog": "0 || 1 || 2 || 3 || 4", "osenv": "0", - "request": ">=2.9.0 <2.82.0", + "request": "^2.87.0", "rimraf": "2", "semver": "~5.3.0", "tar": "^2.0.0", "which": "1" }, "dependencies": { - "form-data": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.1.4.tgz", - "integrity": "sha1-M8GDrPGTJ27KqYFDpp6Uv+4XUNE=", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.5", - "mime-types": "^2.1.12" - } - }, - "qs": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.4.0.tgz", - "integrity": "sha1-E+JtKK1rD/qpExLNO/cI7TUecjM=", - "dev": true - }, - "request": { - "version": "2.81.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.81.0.tgz", - "integrity": "sha1-xpKJRqDgbF+Nb4qTM0af/aRimKA=", - "dev": true, - "requires": { - "aws-sign2": "~0.6.0", - "aws4": "^1.2.1", - "caseless": "~0.12.0", - "combined-stream": "~1.0.5", - "extend": "~3.0.0", - "forever-agent": "~0.6.1", - "form-data": "~2.1.1", - "har-validator": "~4.2.1", - "hawk": "~3.1.3", - "http-signature": "~1.1.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.7", - "oauth-sign": "~0.8.1", - "performance-now": "^0.2.0", - "qs": "~6.4.0", - "safe-buffer": "^5.0.1", - "stringstream": "~0.0.4", - "tough-cookie": "~2.3.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.0.0" - } - }, "semver": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", @@ -8246,12 +8112,6 @@ "sha.js": "^2.4.8" } }, - "performance-now": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", - "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=", - "dev": true - }, "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", @@ -14180,15 +14040,6 @@ } } }, - "sntp": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", - "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", - "dev": true, - "requires": { - "hoek": "2.x.x" - } - }, "sockjs": { "version": "0.3.19", "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz", @@ -14607,12 +14458,6 @@ "is-hexadecimal": "^1.0.0" } }, - "stringstream": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.6.tgz", - "integrity": "sha512-87GEBAkegbBcweToUrdzf3eLhWNg06FJTebl4BVJz/JgWy8CvEr9dRtX5qWphiynMSQlxxi+QqN0z5T32SLlhA==", - "dev": true - }, "strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", @@ -15982,9 +15827,9 @@ } }, "url-parse": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.1.tgz", - "integrity": "sha512-x95Td74QcvICAA0+qERaVkRpTGKyBHHYdwL2LXZm5t/gBtCB9KQSO/0zQgSTYEV1p0WcvSg79TLNPSvd5IDJMQ==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.3.tgz", + "integrity": "sha512-rh+KuAW36YKo0vClhQzLLveoj8FwPJNu65xLb7Mrt+eZht0IPT0IXgSv8gcMegZ6NvjJUALf6Mf25POlMwD1Fw==", "dev": true, "requires": { "querystringify": "^2.0.0", -- GitLab From e5a386f64a379fa31170a943ba05d2c6d58ad139 Mon Sep 17 00:00:00 2001 From: "v.pavlov" Date: Mon, 20 Aug 2018 20:54:28 +0300 Subject: [PATCH 2/2] Dashboard: Actions on selected elements Fixed ssr and refactor --- src/actions/dashboard.ts | 233 ++++++++++++++++++ src/actions/dataset.ts | 25 +- src/actions/descriptorList.ts | 9 + src/actions/descriptors.ts | 18 ++ src/constants/dashboard.ts | 8 + src/reducers/dashboard.ts | 49 ++++ src/reducers/index.ts | 2 + src/ui/common/Tabs.tsx | 63 ++++- src/ui/pages/dashboard/DashboardPage.tsx | 73 ++++-- src/ui/pages/dashboard/MyDataPage.tsx | 116 ++++++--- .../dashboard/c/DashboardActionsArea.tsx | 49 ++++ .../pages/dashboard/c/DashboardTableRow.tsx | 24 +- src/ui/pages/dashboard/c/MyDataTable.tsx | 45 +++- 13 files changed, 645 insertions(+), 69 deletions(-) create mode 100644 src/actions/dashboard.ts create mode 100644 src/reducers/dashboard.ts create mode 100644 src/ui/pages/dashboard/c/DashboardActionsArea.tsx diff --git a/src/actions/dashboard.ts b/src/actions/dashboard.ts new file mode 100644 index 0000000..fe7d83c --- /dev/null +++ b/src/actions/dashboard.ts @@ -0,0 +1,233 @@ +import * as _ from 'lodash'; + +// utilities +import {dereferenceReferences} from 'utilities'; +import {log} from 'utilities/debug'; + +// constants +import {ADD_TO_EDIT_LIST, RECEIVE_EDIT_LIST, RECEIVE_STATIC_PAGE, REMOVE_FROM_EDIT_LIST, RECEIVE_IS_EDIT_MODE} from 'constants/dashboard'; + +// Models +import {Dataset, IDatasetFilter} from 'model/dataset.model'; +import {Descriptor, DescriptorList, IDescriptorFilter, IDescriptorListFilter} from 'model/descriptor.model'; +import {Page} from 'model/common.model'; +import {Partner} from 'model/partner.model'; + +// service +import {DatasetService} from 'service/DatasetService'; +import {DescriptorService} from 'service/DescriptorService'; +import {DescriptorListService} from 'service/DescriptorListService'; + +// actions +import {addFilterCode} from 'actions/filterCode'; +import {loadDescriptorListTitles} from 'actions/uuidDecoder'; + + +export const setEditMode = (payload: boolean) => (dispatch) => { + dispatch({type: RECEIVE_IS_EDIT_MODE, payload}); +}; + +const receivePaged = (paged: Page | Page | Page) => (dispatch) => { + dispatch({type: RECEIVE_STATIC_PAGE, payload: paged}); +}; + +const addToList = (payload: Dataset | Descriptor | DescriptorList) => (dispatch) => { + dispatch({type: ADD_TO_EDIT_LIST, payload}); +}; + +const removeFromList = (payload: Dataset | Descriptor | DescriptorList) => (dispatch) => { + dispatch({type: REMOVE_FROM_EDIT_LIST, payload}); +}; + +const clearEditList = () => (dispatch) => { + dispatch({type: RECEIVE_EDIT_LIST, payload: []}); +}; + +export const onPageChange = () => (dispatch) => { + dispatch(setEditMode(false)); + dispatch(clearEditList()); + dispatch(receivePaged(null)); +}; + +const receiveNewStaticPage = (paged: any) => (dispatch) => { + dispatch(receivePaged(paged)); +}; + +const receiveAndPushStaticPage = (paged: any) => (dispatch, getState) => { + const oldContent = getState().dashboard.staticPaged && getState().dashboard.staticPaged.content || []; + dispatch(receivePaged({...paged, content: [...oldContent, ...paged.content]})); +}; + +const refreshPageItem = (item: any) => (dispatch, getState) => { + const staticPage = getState().dashboard.staticPaged; + const toUpdateIndex = _.findIndex(staticPage.content, (contentItem) => contentItem.uuid === item.uuid); + + if (toUpdateIndex !== -1) { + staticPage.content[toUpdateIndex] = _.merge(staticPage.content[toUpdateIndex], item); + } + + return dispatch(receiveNewStaticPage(staticPage)); +}; + +const removePageItem = (item: any) => (dispatch, getState) => { + const staticPage = getState().dashboard.staticPaged; + + _.remove(staticPage.content, (contentItem) => contentItem.uuid === item.uuid); + staticPage.numberOfElements = staticPage.numberOfElements - 1; + staticPage.totalElements = staticPage.totalElements - 1; + + return dispatch(receiveNewStaticPage(staticPage)); +}; + +export const addToEditList = (item: Dataset | Descriptor | DescriptorList) => (dispatch, getState) => { + const list = getState().dashboard.toEditList; + if (!list.includes(item)) { + dispatch(addToList(item)); + } +}; + +export const removeFromEditList = (item: Dataset | Descriptor | DescriptorList) => (dispatch, getState) => { + const list = getState().dashboard.toEditList; + if (list.includes(item)) { + dispatch(removeFromList(item)); + } +}; + +export const publishAll = (publishOne) => (dispatch, getState) => { + const toEditList = getState().dashboard.toEditList; + + toEditList.map((item) => { + if (!item.published || item.state === 'DRAFT') { + dispatch(publishOne(item)) + .then((published) => { + dispatch(refreshPageItem(published)); + return dispatch(removeFromList(published)); + }); + } else { + dispatch(removeFromList(item)); + } + }); +}; + +export const unpublishAll = (publishOne) => (dispatch, getState) => { + const toEditList = getState().dashboard.toEditList; + + toEditList.map((item) => { + if (item.published || item.state === 'PUBLISHED') { + dispatch(publishOne(item, false)) + .then((unpublished) => { + dispatch(refreshPageItem(unpublished)); + return dispatch(removeFromList(unpublished)); + + + }); + } else { + dispatch(removeFromList(item)); + } + }); +}; + +export const deleteAll = (deleteOne) => (dispatch, getState) => { + const toEditList = getState().dashboard.toEditList; + + toEditList.map((item) => { + dispatch(deleteOne(item)) + .then((deleted) => { + dispatch(removePageItem(deleted)); + return dispatch(removeFromList(deleted)); + }); + }); +}; + +export const staticListMyDatasets = (page?, results?, sortBy?, filter?: string | IDatasetFilter, order?) => { + return (dispatch, getState) => { + const token = getState().login.access_token; + + return DatasetService.listMyDatasets(token, page, results, sortBy, filter, order) + .then((paged) => { + dispatch(onPageChange()); + dispatch(receiveAndPushStaticPage(paged)); + return dispatch(addFilterCode(paged.filterCode, paged.filter)); + }) + .catch((error) => { + log('Error', error); + }); + }; +}; + +export const staticListMyDescriptors = (page?, results?, sortBy?, filter?: string | IDescriptorFilter, order?) => { + return (dispatch, getState) => { + const token = getState().login.access_token; + + return DescriptorService.listMyDescriptors(token, page, results, sortBy, filter, order) + .then((paged) => { + dereferenceReferences(paged.content, 'owner', (o) => new Partner(o)); + dispatch(receiveAndPushStaticPage(paged)); + return dispatch(addFilterCode(paged.filterCode, paged.filter)); + }) + .catch((error) => { + log('Error', error); + }); + }; +}; + +export const staticListMyDescriptorLists = (page?, results?, sortBy?, filter?: string | IDescriptorListFilter, order?) => { + + return (dispatch, getState) => { + log('Loading my descriptor lists'); + + const token = getState().login.access_token; + + return DescriptorListService.listMyDescriptorLists(token, page, results, sortBy, filter, order) + // receive the current descriptor list + .then((descriptorLists) => { + dispatch(receiveAndPushStaticPage(descriptorLists)); + return dispatch(addFilterCode(descriptorLists.filterCode, descriptorLists.filter)); + }).catch((error) => { + log(`Error loading my descriptor lists`, error); + }); + }; +}; + +export const promiseStaticListMyDatasets = (page?, results?, sortBy?, filter?, order?) => (dispatch, getState) => { + const token = getState().login.access_token; + + return DescriptorService.listMyDescriptors(token, page, results, sortBy, filter, order) + .then((paged) => { + dereferenceReferences(paged.content, 'owner', (o) => new Partner(o)); + dispatch(receiveAndPushStaticPage(paged)); + return {...paged, content: []}; + }) + .catch((error) => { + log('Error', error); + return error; + }); +}; + +export const promiseStaticListMyDescriptors = (page?, results?, sortBy?, filter?, order?) => (dispatch, getState) => { + const token = getState().login.access_token; + + return DescriptorService.listMyDescriptors(token, page, results, sortBy, filter, order) + .then((paged) => { + dereferenceReferences(paged.content, 'owner', (o) => new Partner(o)); + dispatch(receiveAndPushStaticPage(paged)); + return {...paged, content: []}; + }) + .catch((error) => { + log('Error', error); + return error; + }); +}; + +export const promiseStaticListMyDescriptorLists = (page?, results?, sortBy?, filter?, order?) => (dispatch, getState) => { + const token = getState().login.access_token; + + return DescriptorListService.listDescriptorLists(token, page, results, sortBy, filter, order) + .then((paged) => { + dispatch(loadDescriptorListTitles(paged.content)); + return {...paged, content: []}; + }).catch((error) => { + log('Error', error); + return error; + }); +}; diff --git a/src/actions/dataset.ts b/src/actions/dataset.ts index 9b8114e..88b8da1 100644 --- a/src/actions/dataset.ts +++ b/src/actions/dataset.ts @@ -176,6 +176,29 @@ function publishDataset(dataset: Dataset, published: boolean = true) { }; } +// Publish dataset without redirect +function publishDatasetAction(dataset: Dataset, published: boolean = true) { + return (dispatch, getState) => { + const token = getState().login.access_token; + + return DatasetService.publishDataset(token, dataset, published) + .catch((error) => { + log('Publish error', error); + }); + }; +} + +function deleteDatasetPromise(dataset: Dataset) { + return (dispatch, getState) => { + const token = getState().login.access_token; + + return DatasetService.deleteDataset(token, dataset) + .catch((error) => { + log('Delete error', error); + }); + }; +} + function deleteDataset(dataset: Dataset) { return (dispatch, getState) => { const token = getState().login.access_token; @@ -241,7 +264,7 @@ function removeDescriptorsFromDataset(dataset: Dataset, descriptorUuids: string[ export { listMyDatasets, listDatasetsRequest, loadDataset, createDataset, saveDataset, deleteDataset, publishDataset, receiveDataset, editDataset, showDataset, updateDatasetAccessionIdentifiers, - removeDescriptorsFromDataset, addDescriptorsToDataset, + removeDescriptorsFromDataset, addDescriptorsToDataset, publishDatasetAction, deleteDatasetPromise, }; export const uploadRepositoryFileRequest = (datasetUUID: string, file: File) => (dispatch, getState) => { diff --git a/src/actions/descriptorList.ts b/src/actions/descriptorList.ts index 3866db4..7b4a8c7 100644 --- a/src/actions/descriptorList.ts +++ b/src/actions/descriptorList.ts @@ -198,6 +198,10 @@ export const saveDescriptorList = (descriptorList: DescriptorList) => (dispatch, }); }; +export const deleteDescriptorListPromise = (descriptorList: DescriptorList) => (dispatch, getState) => { + return DescriptorListService.deleteDescriptorList(getState().login.access_token, descriptorList); +}; + // Publish the descriptor list export const deleteDescriptorList = (descriptorList: DescriptorList) => (dispatch, getState) => { return DescriptorListService.deleteDescriptorList(getState().login.access_token, descriptorList) @@ -218,6 +222,11 @@ export const publishDescriptorList = (descriptorList: DescriptorList, published? }); }; +// Publish the descriptor list without redirect +export const publishDescriptorListAction = (descriptorList: DescriptorList, published?: boolean) => (dispatch, getState) => { + return DescriptorListService.publishDescriptorList(getState().login.access_token, descriptorList, published); +}; + // Add a descriptor to the descriptor list export const addDescriptorToDescriptorList = (descriptorList: DescriptorList, descriptor: Descriptor) => (dispatch, getState) => { return DescriptorListService.addDescriptor(getState().login.access_token, descriptorList, [ descriptor.uuid ]) diff --git a/src/actions/descriptors.ts b/src/actions/descriptors.ts index 3f628ed..955249d 100644 --- a/src/actions/descriptors.ts +++ b/src/actions/descriptors.ts @@ -122,6 +122,14 @@ export const copyDescriptor = (descriptor: Descriptor) => (dispatch, getState) = }); }; + +export const deleteDescriptorPromise = (descriptor: Descriptor) => (dispatch, getState) => { + return DescriptorService.deleteDescriptor(getState().login.access_token, descriptor) + .catch((error) => { + log('Error', error); + }); +}; + // Delete a record export const deleteDescriptor = (descriptor: Descriptor) => (dispatch, getState) => { return DescriptorService.deleteDescriptor(getState().login.access_token, descriptor) @@ -227,3 +235,13 @@ export function publishDescriptor(descriptor: Descriptor, published: boolean = t }); }; } + +// Publish action without redirect +export function publishDescriptorAction(descriptor: Descriptor, published: boolean = true) { + return (dispatch, getState) => { + + const token = getState().login.access_token; + + return DescriptorService.publishDescriptor(token, descriptor, published); + }; +} diff --git a/src/constants/dashboard.ts b/src/constants/dashboard.ts index 2c53837..dd537a4 100644 --- a/src/constants/dashboard.ts +++ b/src/constants/dashboard.ts @@ -1 +1,9 @@ export const DASHBOARD_FILTERFORM = 'Form/DASHBOARD_FILTERFORM'; + +export const RECEIVE_IS_EDIT_MODE = 'dashboard/RECEIVE_IS_EDIT_MODE'; + +export const ADD_TO_EDIT_LIST = 'dashboard/ADD_TO_EDIT_LIST'; +export const REMOVE_FROM_EDIT_LIST = 'dashboard/REMOVE_FROM_EDIT_LIST'; +export const RECEIVE_EDIT_LIST = 'dashboard/RECEIVE_EDIT_LIST'; + +export const RECEIVE_STATIC_PAGE = 'dashboard/RECEIVE_STATIC_PAGE'; diff --git a/src/reducers/dashboard.ts b/src/reducers/dashboard.ts new file mode 100644 index 0000000..04e68a9 --- /dev/null +++ b/src/reducers/dashboard.ts @@ -0,0 +1,49 @@ +import update from 'immutability-helper'; +import {ADD_TO_EDIT_LIST, RECEIVE_STATIC_PAGE, RECEIVE_EDIT_LIST, REMOVE_FROM_EDIT_LIST, RECEIVE_IS_EDIT_MODE} from 'constants/dashboard'; +import {Descriptor, DescriptorList} from 'model/descriptor.model'; +import {Page} from 'model/common.model'; +import {Dataset} from 'model/dataset.model'; + +const INITIAL_STATE: { + isEditMode: boolean, + staticPaged: Page | Page | Page, + toEditList: object[], +} = { + isEditMode: false, + staticPaged: null, + toEditList: [], +}; + +export default function dashboard(state = INITIAL_STATE, action: { type?: string, payload?: any } = {type: '', payload: null}) { + + switch (action.type) { + case RECEIVE_IS_EDIT_MODE: { + return update(state, { + isEditMode: { $set: action.payload }, + }); + } + case RECEIVE_EDIT_LIST: { + return update(state, { + toEditList: { $set: action.payload }, + }); + } + case ADD_TO_EDIT_LIST: { + return update(state, { + toEditList: { $push: [action.payload] }, + }); + } + case REMOVE_FROM_EDIT_LIST: { + const index = state.toEditList.indexOf(action.payload); + return update(state, { + toEditList: {$splice: [[index, 1]]}, + }); + } + case RECEIVE_STATIC_PAGE: { + return update(state, { + staticPaged: {$set: action.payload}, + }); + } + default: + return state; + } +} diff --git a/src/reducers/index.ts b/src/reducers/index.ts index 0259005..abf0199 100644 --- a/src/reducers/index.ts +++ b/src/reducers/index.ts @@ -19,6 +19,7 @@ import userProfile from './userProfile'; import snackbar from './snackbar'; import filterCode from './filterCode'; import uuidDecoder from './uuidDecoder'; +import dashboard from './dashboard'; const rootReducer = combineReducers({ lookups, @@ -39,6 +40,7 @@ const rootReducer = combineReducers({ snackbar, filterCode, uuidDecoder, + dashboard, routing: routerReducer, form: formReducer, }); diff --git a/src/ui/common/Tabs.tsx b/src/ui/common/Tabs.tsx index ea24caa..6078dfa 100644 --- a/src/ui/common/Tabs.tsx +++ b/src/ui/common/Tabs.tsx @@ -11,6 +11,52 @@ import {bindActionCreators} from 'redux'; import { navigateTo } from 'actions/navigation'; import MuiTabs from '@material-ui/core/Tabs'; import MuiTab from '@material-ui/core/Tab'; +import {withStyles} from '@material-ui/core/styles'; +import Grid from '@material-ui/core/Grid'; + +/*tslint:disable*/ +const styles = (theme) => ({ + root: { + width: '100%', + display: 'flex' as 'flex', + height: 'auto', + borderBottom: '1px #ccc solid', + }, + actionsArea: { + display: 'flex' as 'flex', + alignItems: 'center' as 'center', + position: 'sticky' as 'sticky', + overflow: 'hidden' as 'hidden', + height: '48px', + left: '100%', + padding: '0 16px', + '& > * > *': { + margin: '8px', + [theme.breakpoints.down('sm')]: { + width: '100%', + margin: '8px 0', + }, + }, + [theme.breakpoints.down('sm')]: { + position: 'initial' as 'initial', + overflow: 'initial' as 'initial', + display: 'initial' as 'initial', + padding: '0', + height: 'auto' as 'auto', + marginTop: '48px', + width: '100%', + '& > *': { + width: '100%', + } + }, + }, + tabsArea: { + [theme.breakpoints.down('sm')]: { + position: 'absolute' as 'absolute', + }, + } +}); +/*tslint:enable*/ interface ITabProps extends React.Props { name?: string; @@ -29,7 +75,7 @@ class Tab extends React.Component { class Tabs extends React.Component { public render() { - const { tab, children, navigateTo } = this.props; + const { tab, children, navigateTo, actions, classes } = this.props; const tabs = (children as Tab[]).map((ch) => { return { @@ -48,9 +94,16 @@ class Tabs extends React.Component { }; return ( - - { tabs.map((tab) => ) } - + + + + { tabs.map((tab) => ) } + + + + { actions } + + ); } } @@ -64,6 +117,6 @@ const mapDispatchToProps = (dispatch) => bindActionCreators({ navigateTo, }, dispatch); -const tabs = connect(mapStateToProps, mapDispatchToProps)(Tabs); +const tabs = connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(Tabs)); export { tabs as default, Tab }; diff --git a/src/ui/pages/dashboard/DashboardPage.tsx b/src/ui/pages/dashboard/DashboardPage.tsx index f342501..804e1f8 100644 --- a/src/ui/pages/dashboard/DashboardPage.tsx +++ b/src/ui/pages/dashboard/DashboardPage.tsx @@ -4,14 +4,15 @@ import { bindActionCreators } from 'redux'; import { parse } from 'query-string'; import { filterCodeToUrl } from 'actions/filterCode'; -import { listMyDatasets, promiseListMyDatasets } from 'actions/dataset'; -import { listMyDescriptors, promiseListMyDescriptors} from 'actions/descriptors'; -import { listMyDescriptorLists, promiseListMyDescriptorLists } from 'actions/descriptorList'; +import { deleteDatasetPromise, publishDatasetAction} from 'actions/dataset'; +import { deleteDescriptorPromise, publishDescriptorAction} from 'actions/descriptors'; +import { deleteDescriptorListPromise, publishDescriptorListAction} from 'actions/descriptorList'; import { setPageTitle } from 'actions/pageTitle'; -import { Page } from 'model/common.model'; -import { Dataset } from 'model/dataset.model'; -import { Descriptor, DescriptorList } from 'model/descriptor.model'; - +import { + setEditMode, addToEditList, deleteAll, publishAll, removeFromEditList, unpublishAll, onPageChange, + staticListMyDatasets, staticListMyDescriptors, staticListMyDescriptorLists, + promiseStaticListMyDescriptors, promiseStaticListMyDescriptorLists, promiseStaticListMyDatasets, +} from 'actions/dashboard'; import { BaseMyDataPage } from './MyDataPage'; import MyDataTable from './c/MyDataTable'; @@ -52,25 +53,34 @@ class AdministrationDashboard extends BaseMyDataPage { public render() { - const {tab, datasets, descriptors, descriptorLists, pagination, promiseListMyDatasets, promiseListMyDescriptors, promiseListMyDescriptorLists} = this.props; - + const {tab, paged, pagination, promiseStaticListMyDatasets, promiseStaticListMyDescriptors, promiseStaticListMyDescriptorLists, + addToEditList, removeFromEditList, deleteAll, publishAll, unpublishAll, isEditMode, setEditMode, editList } = this.props; // console.log('Dash', tab, filterCode, filter); - let paged: Page = datasets as Page; let promiseListData; + let deleteAllAction; + let publishAllAction; + let unpublishAllAction; switch (tab) { case 'descriptorlists': - paged = descriptorLists as Page; - promiseListData = promiseListMyDescriptorLists; + promiseListData = promiseStaticListMyDescriptorLists; + deleteAllAction = () => { deleteAll(deleteDescriptorListPromise); }; + publishAllAction = () => { publishAll(publishDescriptorListAction); }; + unpublishAllAction = () => { unpublishAll(publishDescriptorListAction); }; break; case 'descriptors': - paged = descriptors as Page; - promiseListData = promiseListMyDescriptors; + promiseListData = promiseStaticListMyDescriptors; + deleteAllAction = () => { deleteAll(deleteDescriptorPromise); }; + publishAllAction = () => { publishAll(publishDescriptorAction); }; + unpublishAllAction = () => { unpublishAll(publishDescriptorAction); }; break; case 'datasets': default: - promiseListData = promiseListMyDatasets; + promiseListData = promiseStaticListMyDatasets; + deleteAllAction = () => { deleteAll(deleteDatasetPromise); }; + publishAllAction = () => { publishAll(publishDatasetAction); }; + unpublishAllAction = () => { unpublishAll(publishDatasetAction); }; break; } @@ -84,6 +94,14 @@ class AdministrationDashboard extends BaseMyDataPage { onPaginationChange={ this.onPaginationChange } pagination={ pagination } promiseListData={ promiseListData } + deleteAllAction={ deleteAllAction } + publishAllAction={ publishAllAction } + unpublishAllAction={ unpublishAllAction } + setEditState={ setEditMode } + editList={ editList } + isEditMode={ isEditMode } + addToEditList={ addToEditList } + removeFromEditList={ removeFromEditList } /> @@ -102,18 +120,25 @@ const mapStateToProps = (state, ownProps) => ({ }, title: ownProps.route.extraProps.title, // route-configured tab: ownProps.match.params.tab || 'datasets', // current tab, or ownProps.location.pathname - datasets: state.datasets.paged, - descriptors: state.descriptors.paged, - descriptorLists: state.descriptorList.paged, + paged: state.dashboard.staticPaged, + editList: state.dashboard.toEditList, + isEditMode: state.dashboard.isEditMode, }); const mapDispatchToProps = (dispatch) => bindActionCreators({ - listDatasets: listMyDatasets, - listDescriptors: listMyDescriptors, - listDescriptorLists: listMyDescriptorLists, - promiseListMyDatasets, - promiseListMyDescriptors, - promiseListMyDescriptorLists, + listDatasets: staticListMyDatasets, + listDescriptors: staticListMyDescriptors, + listDescriptorLists: staticListMyDescriptorLists, + addToEditList, + removeFromEditList, + onPageChange, + deleteAll, + publishAll, + unpublishAll, + setEditMode, + promiseStaticListMyDatasets, + promiseStaticListMyDescriptors, + promiseStaticListMyDescriptorLists, setPageTitle, filterCodeToUrl, }, dispatch); diff --git a/src/ui/pages/dashboard/MyDataPage.tsx b/src/ui/pages/dashboard/MyDataPage.tsx index 96768ea..9e4902f 100644 --- a/src/ui/pages/dashboard/MyDataPage.tsx +++ b/src/ui/pages/dashboard/MyDataPage.tsx @@ -9,10 +9,15 @@ import {Page} from 'model/common.model'; import { parse } from 'query-string'; import { filterCodeToUrl } from 'actions/filterCode'; -import {listMyDatasets, promiseListMyDatasets} from 'actions/dataset'; -import {listMyDescriptors, promiseListMyDescriptors} from 'actions/descriptors'; -import {listMyDescriptorLists, promiseListDescriptorLists} from 'actions/descriptorList'; +import {deleteDataset, publishDatasetAction} from 'actions/dataset'; +import {deleteDescriptor, publishDescriptorAction} from 'actions/descriptors'; +import {deleteDescriptorList, publishDescriptorListAction} from 'actions/descriptorList'; import {setPageTitle} from 'actions/pageTitle'; +import { + setEditMode, addToEditList, deleteAll, publishAll, removeFromEditList, unpublishAll, onPageChange, + staticListMyDatasets, staticListMyDescriptors, staticListMyDescriptorLists, + promiseStaticListMyDescriptors, promiseStaticListMyDescriptorLists, promiseStaticListMyDatasets, +} from 'actions/dashboard'; import ContentHeaderWithButton from 'ui/common/heading/ContentHeaderWithButton'; import BackButton from 'ui/common/buttons/BackButton'; @@ -23,19 +28,25 @@ interface IDataPublishedContainerProps extends React.ClassAttributes { title: string; tab?: string; pagination: any; - datasets: Page; - descriptors: Page; - descriptorLists: Page; + paged: Page | Page | Page; preFilter?: object; basePath: string; filterCodeToUrl: any; listDescriptorLists: any; listDescriptors: any; listDatasets: any; - promiseListMyDatasets: any; - promiseListDescriptors: any; - promiseListDescriptorLists: any; + promiseStaticListMyDatasets: any; + promiseStaticListMyDescriptors: any; + promiseStaticListMyDescriptorLists: any; setPageTitle: (title: string) => void; + addToEditList: (item: Dataset | Descriptor | DescriptorList) => void; + removeFromEditList: (item: Dataset | Descriptor | DescriptorList) => void; + onPageChange: () => void; + deleteAll: (deleteOne: (page?, results?, sortBy?, filter?, order?) => Promise | Promise | Promise) => void; + publishAll: (publishOne: (page?, results?, sortBy?, filter?, order?) => Promise | Promise | Promise) => void; + setEditState: (state: boolean) => void; + editList: Dataset[] | Descriptor[] | DescriptorList[]; + isEditMode: boolean; } class BaseMyDataPage extends React.Component { @@ -49,10 +60,10 @@ class BaseMyDataPage extends React.Component extends React.Component extends React.Component extends React.Component { } public render() { - const {title, tab, basePath, pagination, promiseListMyDatasets, promiseListMyDescriptors, promiseListDescriptorLists} = this.props; - - const paged = this.getPaged(this.props); + const {title, tab, basePath, pagination, promiseStaticListMyDatasets, promiseStaticListMyDescriptors, promiseListDescriptorLists, + addToEditList, removeFromEditList, deleteAll, publishAll, unpublishAll, isEditMode, setEditMode, editList, paged } = this.props; let promiseListData; + let deleteAllAction; + let publishAllAction; + let unpublishAllAction; switch (tab) { - case 'descriptors': promiseListData = promiseListMyDescriptors; break; - case 'descriptorlists': promiseListData = promiseListDescriptorLists; break; + case 'descriptorlists': + promiseListData = promiseListDescriptorLists; + deleteAllAction = () => deleteAll(deleteDescriptorList); + publishAllAction = () => publishAll(publishDescriptorListAction); + unpublishAllAction = () => unpublishAll(publishDescriptorListAction); + break; + case 'descriptors': + promiseListData = promiseStaticListMyDescriptors; + deleteAllAction = () => deleteAll(deleteDescriptor); + publishAllAction = () => publishAll(publishDescriptorAction); + unpublishAllAction = () => unpublishAll(publishDescriptorAction); + break; case 'datasets': - default: promiseListData = promiseListMyDatasets; break; - } + default: + promiseListData = promiseStaticListMyDatasets; + deleteAllAction = () => deleteAll(deleteDataset); + publishAllAction = () => { publishAll(publishDatasetAction); }; + unpublishAllAction = () => { unpublishAll(publishDatasetAction); }; + break; + } return (
@@ -201,6 +234,14 @@ class DP extends BaseMyDataPage { onPaginationChange={ this.onPaginationChange } pagination={ pagination } promiseListData={ promiseListData } + deleteAllAction={ deleteAllAction } + publishAllAction={ publishAllAction } + unpublishAllAction={ unpublishAllAction } + setEditState={ setEditMode } + editList={ editList } + isEditMode={ isEditMode } + addToEditList={ addToEditList } + removeFromEditList={ removeFromEditList } />
@@ -221,18 +262,25 @@ const mapStateToProps = (state, ownProps) => ({ basePath: ownProps.route.extraProps.basePath, // route-configured preFilter: ownProps.route.extraProps.filter || {}, // route-configured tab: ownProps.match.params.tab || 'datasets', // current tab, or ownProps.location.pathname - datasets: state.datasets.paged, - descriptors: state.descriptors.paged, - descriptorLists: state.descriptorList.paged, + paged: state.dashboard.staticPaged, + editList: state.dashboard.toEditList, + isEditMode: state.dashboard.isEditMode, }); const mapDispatchToProps = (dispatch) => bindActionCreators({ - listDatasets: listMyDatasets, - listDescriptors: listMyDescriptors, - listDescriptorLists: listMyDescriptorLists, - promiseListMyDatasets, - promiseListMyDescriptors, - promiseListDescriptorLists, + listDatasets: staticListMyDatasets, + listDescriptors: staticListMyDescriptors, + listDescriptorLists: staticListMyDescriptorLists, + addToEditList, + removeFromEditList, + onPageChange, + deleteAll, + publishAll, + unpublishAll, + setEditMode, + promiseStaticListMyDatasets, + promiseStaticListMyDescriptors, + promiseStaticListMyDescriptorLists, setPageTitle, filterCodeToUrl, }, dispatch); diff --git a/src/ui/pages/dashboard/c/DashboardActionsArea.tsx b/src/ui/pages/dashboard/c/DashboardActionsArea.tsx new file mode 100644 index 0000000..1069788 --- /dev/null +++ b/src/ui/pages/dashboard/c/DashboardActionsArea.tsx @@ -0,0 +1,49 @@ +import * as React from 'react'; +import Button from '@material-ui/core/Button'; +import withStyles from '@material-ui/core/styles/withStyles'; + +const styles = () => ({ + hidden: { + display: 'none' as 'none', + }, +}); + + +interface IDasboardActionsAreaProps extends React.ClassAttributes { + mainButtonTitle?: string; + cancelButtonTitle?: string; + actions?: Array<{title: string, action: any}>; + onShow: any; + onHide: any; + classes: any; + isEditMode: boolean; +} + + +class DashboardActionsArea extends React.Component { + + private onShowClick = () => { + const { onShow, onHide, isEditMode } = this.props; + + if (onShow && !isEditMode) { + onShow(); + } + if (onHide && isEditMode) { + onHide(); + } + } + + public render() { + const { mainButtonTitle = 'Show', cancelButtonTitle = 'Cancel', actions, classes, isEditMode } = this.props; + return( +
+ + { actions && actions.map((action) => ()) } + +
+ ); + } + +} + +export default withStyles(styles)(DashboardActionsArea); diff --git a/src/ui/pages/dashboard/c/DashboardTableRow.tsx b/src/ui/pages/dashboard/c/DashboardTableRow.tsx index eaa5867..a282c51 100644 --- a/src/ui/pages/dashboard/c/DashboardTableRow.tsx +++ b/src/ui/pages/dashboard/c/DashboardTableRow.tsx @@ -8,6 +8,8 @@ import Markdown from 'ui/catalog/markdown'; import {DatasetLink, DescriptorLink, DescriptorListLink} from 'ui/catalog/Links'; import Button from '@material-ui/core/Button'; +import Checkbox from '@material-ui/core/Checkbox'; +import FormControlLabel from '@material-ui/core/FormControlLabel'; const ResolveLink = ({tab, row, children}) => { switch (tab) { @@ -24,11 +26,29 @@ const ResolveLink = ({tab, row, children}) => { export class DashboardTableRow extends React.Component { + private onCheckboxChange = (e, isChecked) => { + const { row, addToEditAction, removeFromEditAction } = this.props; + + isChecked ? addToEditAction(row) : removeFromEditAction(row); + } + public render() { - const {row, tab, index} = this.props; + const {row, tab, index, isEditMode, inEditList} = this.props; + return ( - { index + 1 } + + + } + label={ { index + 1 } } + /> + { || (Untitled) } diff --git a/src/ui/pages/dashboard/c/MyDataTable.tsx b/src/ui/pages/dashboard/c/MyDataTable.tsx index b1c5672..c819a84 100644 --- a/src/ui/pages/dashboard/c/MyDataTable.tsx +++ b/src/ui/pages/dashboard/c/MyDataTable.tsx @@ -1,7 +1,10 @@ import * as React from 'react'; import { withStyles } from '@material-ui/core/styles'; +import * as _ from 'lodash'; import {Page} from 'model/common.model'; +import {Dataset} from 'model/dataset.model'; +import {Descriptor, DescriptorList} from 'model/descriptor.model'; import Tabs, {Tab} from 'ui/common/Tabs'; import {Table, TableRow, TableCell} from 'ui/common/tables'; @@ -16,6 +19,7 @@ import Paper from '@material-ui/core/Paper'; import Grid from '@material-ui/core/Grid'; import PagedLoader from 'ui/common/PagedLoader'; import Loading from 'ui/common/Loading'; +import DashboardActionsArea from 'ui/pages/dashboard/c/DashboardActionsArea'; const styles = (theme) => ({ filterSection: theme.leftPanel.root, @@ -31,6 +35,14 @@ interface IMyDataTableProps extends React.Props { onFilter: (filter) => void; pagination: any; promiseListData: any; + deleteAllAction: any; + publishAllAction: any; + unpublishAllAction: any; + setEditState: (state: boolean) => void; + editList: Dataset[] | Descriptor[] | DescriptorList[]; + isEditMode: boolean; + addToEditList: (item: Dataset | Descriptor | DescriptorList) => void; + removeFromEditList: (item: Dataset | Descriptor | DescriptorList) => void; } const defaultSortOptions = { @@ -60,8 +72,17 @@ function MyDataTable({ onFilter, pagination, promiseListData, + deleteAllAction, + publishAllAction, + unpublishAllAction, + setEditState, + isEditMode, + editList, + addToEditList, + removeFromEditList, }: IMyDataTableProps) { + const actions = [{title: 'Publish selected', action: publishAllAction }, {title: 'Unpublish selected', action: unpublishAllAction }, {title: 'Delete selected', action: deleteAllAction }]; let sortOptions = defaultSortOptions; switch (tab) { @@ -70,8 +91,18 @@ function MyDataTable({ case 'descriptorlists': sortOptions = descriptorListSortOptions; break; default: break; } - - const renderTableRow = (row, index) => ; + const renderTableRow = (row, index) => ( + + ); const loadNextPage = (page: number, pageSize: number) => { return promiseListData(page, pageSize, pagination.sort, pagination.filter, pagination.dir); @@ -82,7 +113,15 @@ function MyDataTable({ return (
- + {setEditState(true); } } + onHide={ () => {setEditState(false); } } + /> + }> Datasets Descriptors Descriptor lists -- GitLab