diff --git a/src/components/DatePanel/index.js b/src/components/DatePanel/index.js new file mode 100644 index 0000000..4e67908 --- /dev/null +++ b/src/components/DatePanel/index.js @@ -0,0 +1,25 @@ +/* + * + * Copyright 2018 Odysseus Data Services, inc. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Company: Odysseus Data Services, Inc. + * Product Owner/Architecture: Gregory Klebanov + * Authors: Pavel Grafkin, Alexander Saltykov, Vitaly Koulakov, Anton Gackovka, Alexandr Ryabokon, Mikhail Mironov + * Created: July 25, 2017 + * + */ + +import DatePanel from './presenter'; + +export default DatePanel; diff --git a/src/components/DatePanel/presenter.jsx b/src/components/DatePanel/presenter.jsx new file mode 100644 index 0000000..fe7176b --- /dev/null +++ b/src/components/DatePanel/presenter.jsx @@ -0,0 +1,96 @@ +/* + * + * Copyright 2018 Odysseus Data Services, inc. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Company: Odysseus Data Services, Inc. + * Product Owner/Architecture: Gregory Klebanov + * Authors: Pavel Grafkin, Alexander Saltykov, Vitaly Koulakov, Anton Gackovka, Alexandr Ryabokon, Mikhail Mironov + * Created: July 25, 2017 + * + */ + +import React, { Component, PropTypes } from "react"; +import BEMHelper from "services/BemHelper"; +import { Datepicker } from "arachne-ui-components"; +import * as moment from "moment"; + +require("./style.scss"); + +// NOTE: Datepicker requires DateInput to be a class +/** + * @param {any} options.className + * @param {string} options.value Value passed by datepicker. + * @param {string} options.displayValue Value passed by parent component. + * @param {function} options.onClick Function that should be called to open datepicker. + */ +class DateInput extends Component { + render() { + const classes = new BEMHelper("study-date-input"); + const { className, displayValue, onClick } = this.props; + + // NOTE: + // We should be able to render custom caption (e.g., 'Empty'), + // that's why we cannot use value passed by datepicker which is always a date + return ( + + + date_range + + {displayValue} + + ); + } +} + +DateInput.propTypes = { + className: PropTypes.any, + displayValue: PropTypes.string.isRequired, + onClick: PropTypes.func, +}; + +function DatePanel({ title, selected, dateFormat, minDate, maxDate, isEditable, onChange, className }) { + const classes = new BEMHelper("study-date-panel"); + const displayDate = selected ? moment(selected).format(dateFormat) : "Empty"; + + return ( +
+ {title} + {isEditable ? ( + } + minDate={minDate} + maxDate={maxDate} + onChange={onChange} + /> + ) : ( + + )} +
+ ); +} + +DatePanel.propTypes = { + dateFormat: PropTypes.string.isRequired, + isEditable: PropTypes.bool.isRequired, + maxDate: PropTypes.any, + minDate: PropTypes.any, + onChange: PropTypes.func, + selected: PropTypes.any, + title: PropTypes.string.isRequired, + className: PropTypes.string, +}; + +export default DatePanel; diff --git a/src/components/DatePanel/style.scss b/src/components/DatePanel/style.scss new file mode 100644 index 0000000..1e052cd --- /dev/null +++ b/src/components/DatePanel/style.scss @@ -0,0 +1,58 @@ +/* + * + * Copyright 2018 Odysseus Data Services, inc. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Company: Odysseus Data Services, Inc. + * Product Owner/Architecture: Gregory Klebanov + * Authors: Pavel Grafkin, Alexander Saltykov, Vitaly Koulakov, Anton Gackovka, Alexandr Ryabokon, Mikhail Mironov + * Created: July 25, 2017 + * + */ + +@import "styles/vars-and-mixins.scss"; + +.#{$namespace} { + &study-date-panel { + display: flex; + align-items: center; + background: $white; + border-radius: $block-border-radius; + padding: 12px 20px; + @include block-shadow(); + + &__title { + @include title(); + margin-right: auto; + } + + &__picker { + cursor: pointer; + } + } + + &study-date-input { + &__icon { + @include material-icon(); + margin-right: 1rem; + } + display: flex; + align-items: center; + &__ico { + color: $grey-placeholder; + margin-right: 7px; + } + &__value { + } + } +} diff --git a/src/modules/Admin/actions/licenses.ts b/src/modules/Admin/actions/licenses.ts index 358257f..873b168 100644 --- a/src/modules/Admin/actions/licenses.ts +++ b/src/modules/Admin/actions/licenses.ts @@ -60,10 +60,11 @@ function create(userId: number, vocabularyV4Ids: Array) { }); } -function resolve(id: number, accepted: boolean) { +function resolve(id: number, accepted: boolean, expiredDate: any) { return services.licenseAccept.create({ id, accepted, + expiredDate, }); } diff --git a/src/modules/Admin/components/Licenses/components/ModalEditPermissions/container.ts b/src/modules/Admin/components/Licenses/components/ModalEditPermissions/container.ts index f54c4bc..8c660b5 100644 --- a/src/modules/Admin/components/Licenses/components/ModalEditPermissions/container.ts +++ b/src/modules/Admin/components/Licenses/components/ModalEditPermissions/container.ts @@ -30,11 +30,14 @@ import { get, difference } from 'lodash'; import presenter from './presenter'; import selectors from './selectors'; import { VocabularyOption, User, Vocabulary } from 'modules/Admin/components/Licenses/types'; +import * as moment from 'moment'; +import { commonDateFormat } from 'const/formats'; interface IModalStateProps { vocabularies: Array; initialValues: { vocabularies: Array; + expiredDates: Array; }; user: User; pendingVocabularies: Array; @@ -43,7 +46,7 @@ interface IModalDispatchProps { close: () => (dispatch: Function) => any; remove: (id: string) => (dispatch: Function) => any; loadLicenses: () => (dispatch: Function) => any; - resolveLicense: (id: number, allow: boolean) => (dispatch: Function) => any; + resolveLicense: (id: number, allow: boolean, expiredDate: any) => (dispatch: Function) => any; }; interface IModalProps extends IModalStateProps, IModalDispatchProps { doSubmit: (vocabs: Array) => Promise; @@ -58,6 +61,12 @@ class ModalEditPermissions extends Component { function mapStateToProps(state: any): IModalStateProps { const vocabularies = selectors.getVocabularies(state); const pendingVocabularies = selectors.getPendingVocabularies(state); + const expiredDates = [] + pendingVocabularies.forEach((vocab) => { + expiredDates[vocab.licenseId] = vocab.expiredDate ? moment(vocab.expiredDate).format() : moment(new Date().setFullYear(new Date().getFullYear() + 2)).format() + }) + + const user = get(state, 'modal.editPermission.data.user.name', { id: -1, name: 'Anonymous', @@ -68,6 +77,7 @@ function mapStateToProps(state: any): IModalStateProps { vocabularies, initialValues: { vocabularies: vocabularies.map(v => v.value.toString()), + expiredDates: expiredDates }, user, pendingVocabularies, @@ -90,13 +100,13 @@ function mergeProps( ...stateProps, ...ownProps, ...dispatchProps, - doSubmit: ({ vocabularies = [], pendingVocabs = [] }) => { + doSubmit: ({ vocabularies = [], pendingVocabs = [], expiredDates = [] }) => { const promises = []; difference(stateProps.initialValues.vocabularies, vocabularies).forEach((licenseId) => { promises.push(dispatchProps.remove(licenseId)); }); pendingVocabs.forEach((isAllowed: boolean, licenseId: number) => { - promises.push(dispatchProps.resolveLicense(licenseId, isAllowed)); + promises.push(dispatchProps.resolveLicense(licenseId, isAllowed, expiredDates[licenseId])); }); const promise = Promise.all(promises); promise diff --git a/src/modules/Admin/components/Licenses/components/ModalEditPermissions/presenter.tsx b/src/modules/Admin/components/Licenses/components/ModalEditPermissions/presenter.tsx index 117ab20..a270500 100644 --- a/src/modules/Admin/components/Licenses/components/ModalEditPermissions/presenter.tsx +++ b/src/modules/Admin/components/Licenses/components/ModalEditPermissions/presenter.tsx @@ -20,97 +20,98 @@ * */ -import * as React from 'react'; -import BEMHelper from 'services/BemHelper'; -import { - Modal, - FormCheckboxList, - Form, - TabbedPane, - RadioButton, - Button, -} from 'arachne-ui-components'; -import { Vocabulary } from 'modules/Admin/components/Licenses/types'; -import { Field } from 'redux-form'; +import * as React from "react"; +import BEMHelper from "services/BemHelper"; +import { Modal, FormCheckboxList, TabbedPane, RadioButton, Button } from "arachne-ui-components"; +import { Vocabulary } from "modules/Admin/components/Licenses/types"; +import { Field } from "redux-form"; +import DatePanel from "components/DatePanel"; +import { commonDateFormat } from "const/formats"; -require('./style.scss'); +require("./style.scss"); -function VocRadioButton ({ options, input }) { - const classes = BEMHelper('pending-radio-btn'); +function VocRadioButton({ options, input }) { + const classes = BEMHelper("pending-radio-btn"); - return input.onChange(options.value)} - />; + return ( + input.onChange(options.value)} + /> + ); +} + +function DatepickerControler({ options, input }) { + const classes = BEMHelper("date-picker"); + + return ( + + ); } function ModalEditPermissions(props) { - const { - modal, - vocabularies, - user, - doSubmit, - pendingVocabularies, - handleSubmit, - } = props; - const classes = BEMHelper('edit-permissions'); + const { modal, vocabularies, user, doSubmit, pendingVocabularies, handleSubmit } = props; + const classes = BEMHelper("edit-permissions"); const sections = [ { label: `Granted (${vocabularies.length})`, - content:
- -
, + content: ( +
+ +
+ ), }, { label: `Pending (${pendingVocabularies.length})`, - content:
-
- -
Allow
-
Forbid
-
- {pendingVocabularies.map((voc: Vocabulary) => -
- {voc.name} -
- -
-
- -
+ content: ( +
+
+ +
Expired date
+
Allow
+
Forbid
- )} -
+ {pendingVocabularies.map((voc: Vocabulary) => ( +
+ {voc.name} +
+ +
+
+ +
+
+ +
+
+ ))} +
+ ), }, ]; return (
- -
- {pendingVocabularies.length > 0 - ? - : sections[0].content - } -
- + + + {pendingVocabularies.length > 0 ? : sections[0].content} +
+
-
); +
+ ); } export default ModalEditPermissions; diff --git a/src/modules/Admin/components/Licenses/components/ModalEditPermissions/selectors.ts b/src/modules/Admin/components/Licenses/components/ModalEditPermissions/selectors.ts index ed1e4d7..823b609 100644 --- a/src/modules/Admin/components/Licenses/components/ModalEditPermissions/selectors.ts +++ b/src/modules/Admin/components/Licenses/components/ModalEditPermissions/selectors.ts @@ -23,6 +23,8 @@ import { createSelector } from 'reselect'; import { get } from 'lodash'; import { licenseStatuses } from 'const/vocabulary'; +import moment from 'moment'; +import { commonDateFormat } from 'const/formats'; const getRawVocs = (state: Object) => get(state, 'modal.editPermission.data.vocabularies', []) || []; @@ -31,7 +33,7 @@ const getVocabularies = createSelector( (rawResults: Array) => rawResults .filter(voc => voc.status === licenseStatuses.APPROVED) .map((voc) => ({ - label: voc.code, + label: `${voc.code}${voc.expiredDate ? ' (Expired date: ' + moment(voc.expiredDate).format(commonDateFormat) + ')' : ''}`, value: voc.licenseId, })), ); diff --git a/src/modules/Admin/components/Licenses/components/ModalEditPermissions/style.scss b/src/modules/Admin/components/Licenses/components/ModalEditPermissions/style.scss index 95322e7..034baec 100644 --- a/src/modules/Admin/components/Licenses/components/ModalEditPermissions/style.scss +++ b/src/modules/Admin/components/Licenses/components/ModalEditPermissions/style.scss @@ -20,46 +20,60 @@ * */ -@import 'styles/vars-and-mixins.scss'; +@import "styles/vars-and-mixins.scss"; .#{$namespace} { &edit-permissions { - @include tabbed-modal(); + @include tabbed-modal(); &__tab-content { - padding: 2rem; - min-width: 500px; - min-height: 200px; - } + padding: 2rem; + min-width: 500px; + min-height: 200px; + } &__pending-voc { - display: flex; - padding: 0.5rem 0; + display: flex; + padding: 0.5rem 0; } &__pending-voc-name { - flex-grow: 1; - padding-right: 0.5rem; + //flex-grow: 1; + + display: flex; + align-items: center; + width: 200px; + padding-right: 0.5rem; } &__pending-button { - @include title(); - width: 6rem; + @include title(); + width: 6rem; + display: flex; + align-items: center; + justify-content: center; + } + + &__pending-date-picker { + @include title(); + //width: 6rem; + margin-right: 20px; + width: 200px; } &__submit-button-wrapper { - padding: 1rem 2rem; + padding: 1rem 2rem; } &__submit-button { - width: 100%; + width: 100%; } } &pending-radio-btn { - & .ac-radio-button { - &__box { - margin: auto; - } - } + & .ac-radio-button { + &__box { + margin: auto; + } + } } -} \ No newline at end of file +} diff --git a/src/modules/Admin/components/Statistics/components/Filters/presenter.tsx b/src/modules/Admin/components/Statistics/components/Filters/presenter.tsx index 58f51a3..f0634ad 100644 --- a/src/modules/Admin/components/Statistics/components/Filters/presenter.tsx +++ b/src/modules/Admin/components/Statistics/components/Filters/presenter.tsx @@ -20,136 +20,147 @@ * */ - -import * as React from 'react'; +import * as React from "react"; import BEMHelper from "services/BemHelper"; -import { Button, Form, FormDatepicker, FormInput, FormToggle, LoadingPanel } from 'arachne-ui-components'; +import { Button, Form, FormDatepicker, FormInput, FormToggle, LoadingPanel } from "arachne-ui-components"; import { commonDateFormat } from "const/formats"; import { SortingParams } from "modules/Admin/actions/statistics"; +import DatePanel from "components/DatePanel"; -require('./style.scss'); +require("./style.scss"); interface IStatisticsFilter { - from: any, - to: any, - keywords?: string, - licensedOnly?: boolean, + from: any; + to: any; + keywords?: string; + licensedOnly?: boolean; } -interface IStatisticsFilterProps extends IStatisticsFilterStateProps, IStatisticsFilterDispatchProps, IStatisticsFilterOwnProps { - handleSubmit: Function +interface IStatisticsFilterProps + extends IStatisticsFilterStateProps, + IStatisticsFilterDispatchProps, + IStatisticsFilterOwnProps { + handleSubmit: Function; } interface IStatisticsFilterDispatchProps { - loadStatistics: Function + loadStatistics: Function; } interface IStatisticsFilterStateProps { - isLoading: boolean, - filter: IStatisticsFilter + isLoading: boolean; + filter: IStatisticsFilter; } interface IStatisticsFilterOwnProps { - runSearch: (sorting?: SortingParams) => void, - downloadCSV: Function + runSearch: (sorting?: SortingParams) => void; + downloadCSV: Function; } -function Filters(props: IStatisticsFilterProps) { - const classes = BEMHelper('filters'); +function DatepickerControler({ options, input, titleDisplay }) { + const classes = BEMHelper("date-picker"); + + return ( + + ); +} - const fields = [ - { - name: 'keywords', - InputComponent: { - component: FormInput, - props: { - title: 'Keywords', - placeholder: 'Keywords', - type: 'text', - }, - }, +function Filters(props: IStatisticsFilterProps) { + const classes = BEMHelper("filters"); + + const fields = [ + { + name: "keywords", + InputComponent: { + component: FormInput, + props: { + title: "Keywords", + placeholder: "Keywords", + type: "text", }, - { - name: 'from', - InputComponent: { - component: FormDatepicker, - props: { - title: 'From', - type: 'text', - options: { - selected: props.filter.from, - dateFormat: commonDateFormat, - }, - } - - } + }, + }, + { + name: "from", + InputComponent: { + component: DatepickerControler, + props: { + title: "From", + type: "text", + options: { + titleDisplay: "From", + selected: props.filter.from, + dateFormat: commonDateFormat, + }, }, - { - name: 'to', - InputComponent: { - component: FormDatepicker, - props: { - title: 'To', - type: 'text', - options: { - selected: props.filter.to, - dateFormat: commonDateFormat, - }, - } - - } + }, + }, + { + name: "to", + InputComponent: { + component: DatepickerControler, + props: { + title: "To", + type: "text", + options: { + titleDisplay: "To", + selected: props.filter.to, + dateFormat: commonDateFormat, + }, }, - { - name: 'licensedOnly', - InputComponent: { - component: FormToggle, - props: { - label: 'License Only', - } - - } - } - ]; - const submitBtn = { - label: 'Show', - loadingLabel: 'Loading...', - mods: ['success', 'rounded'], - }; - - return ( -
-
-
-
-
- - -
- - - -
); + }, + }, + { + name: "licensedOnly", + InputComponent: { + component: FormToggle, + props: { + label: "License Only", + }, + }, + }, + ]; + const submitBtn = { + label: "Show", + loadingLabel: "Loading...", + mods: ["success", "rounded"], + }; + + return ( +
+
+ +
+
+ +
+ +
+ ); } export default Filters; export { - IStatisticsFilter, - IStatisticsFilterStateProps, - IStatisticsFilterDispatchProps, - IStatisticsFilterProps, - IStatisticsFilterOwnProps -} \ No newline at end of file + IStatisticsFilter, + IStatisticsFilterStateProps, + IStatisticsFilterDispatchProps, + IStatisticsFilterProps, + IStatisticsFilterOwnProps, +}; diff --git a/src/modules/Admin/components/Statistics/components/Filters/style.scss b/src/modules/Admin/components/Statistics/components/Filters/style.scss index 34dc7f1..99a35aa 100644 --- a/src/modules/Admin/components/Statistics/components/Filters/style.scss +++ b/src/modules/Admin/components/Statistics/components/Filters/style.scss @@ -20,18 +20,17 @@ * */ -@import 'styles/vars-and-mixins.scss'; +@import "styles/vars-and-mixins.scss"; .#{$namespace} { &filters { - & form span { - margin-bottom: 1rem; + // margin-bottom: 1rem; } & form input { box-shadow: 1px -1px 3px 1px rgba(0, 0, 0, 0.1); - + height: 44px; } &__export-button { @@ -44,4 +43,4 @@ color: $white; } } -} \ No newline at end of file +} diff --git a/src/modules/Vocabulary/actions/vocabularies.tsx b/src/modules/Vocabulary/actions/vocabularies.tsx index 73589c3..421c06a 100644 --- a/src/modules/Vocabulary/actions/vocabularies.tsx +++ b/src/modules/Vocabulary/actions/vocabularies.tsx @@ -20,17 +20,18 @@ * */ -import API from 'services/Api'; -import services from '../apiServices'; +import API from "services/Api"; +import services from "../apiServices"; function load() { return services.vocabularies.find(); } -function requestLicense(vocabularyId) { - return services.vocabLicenses.create({ - vocabularyId, - }); +function requestLicense(vocabularyId, expiredDate) { + return services.vocabLicenses.create({ + vocabularyId, + expiredDate, + }); } export default { diff --git a/src/modules/Vocabulary/components/DownloadHistory/container.ts b/src/modules/Vocabulary/components/DownloadHistory/container.ts index e47035b..883c5a2 100644 --- a/src/modules/Vocabulary/components/DownloadHistory/container.ts +++ b/src/modules/Vocabulary/components/DownloadHistory/container.ts @@ -26,7 +26,7 @@ import actions from 'modules/Vocabulary/actions'; import { get } from 'lodash'; import { modal } from 'modules/Vocabulary/const'; import { ModalUtils } from 'arachne-ui-components'; -import presenter, { IDownloadRequest } from './presenter'; +import presenter, { IDownloadRequest, IVocabulary } from './presenter'; import selectors from './selectors'; import { @@ -105,6 +105,7 @@ const mapDispatchToProps = { checkAvailability: actions.download.checkBundleAvailability, showRequestModal: (ids = [], message) => ModalUtils.actions.toggle(modal.licenses, true, { licenses: ids, message }), showShareModal: (bundle) => ModalUtils.actions.toggle(modal.share, true, { bundle }), + openRequestModal: (vocab: IVocabulary) => ModalUtils.actions.toggle(modal.requestLicense, true, vocab), }; function mergeProps( diff --git a/src/modules/Vocabulary/components/DownloadHistory/presenter.tsx b/src/modules/Vocabulary/components/DownloadHistory/presenter.tsx index 43e9c86..a36fa2d 100644 --- a/src/modules/Vocabulary/components/DownloadHistory/presenter.tsx +++ b/src/modules/Vocabulary/components/DownloadHistory/presenter.tsx @@ -20,33 +20,29 @@ * */ -import * as React from 'react'; -import { - Button, - LoadingPanel, - Toolbar, - Table, - TableCellText, -} from 'arachne-ui-components'; -import BEMHelper from 'services/BemHelper'; -import { paths, bundleStatuses } from 'modules/Vocabulary/const'; -import * as moment from 'moment'; -import { - Accordion, - AccordionItem, -} from 'react-sanfona'; -import { fullDateFormat } from 'const/formats'; -import ModalEditNotifications from './components/ModalEditNotifications'; -import ModalRequestLicenses from './components/ModalRequestLicenses'; -import ModalShare from './components/ModalShare'; +import * as React from "react"; +import { Button, LoadingPanel, Toolbar, Table, TableCellText, Link } from "arachne-ui-components"; +import BEMHelper from "services/BemHelper"; +import { paths, bundleStatuses } from "modules/Vocabulary/const"; +import * as moment from "moment"; +import { Accordion, AccordionItem } from "react-sanfona"; +import { fullDateFormat } from "const/formats"; +import ModalEditNotifications from "./components/ModalEditNotifications"; +import ModalRequestLicenses from "./components/ModalRequestLicenses"; +import ModalShare from "./components/ModalShare"; +// import { HISTORY } from "./const"; +import { licenseStatuses } from "const/vocabulary"; +import { Vocabulary } from "../List/components/Results/selectors"; +import ModalRequestLicense from "../List/components/ModalRequestLicense"; -require('./style.scss'); +require("./style.scss"); interface IVocabulary { id: number; code: string; name: string; cdmVersion: string; + expiredDate?: string; } interface IDownloadRequest { @@ -57,19 +53,19 @@ interface IDownloadRequest { cdmVersion: number; name: string; status: string; -}; +} interface IHistoryItem extends IVocabulary { date?: string; link?: string; tableRowClass: string; -}; +} interface IDownloadHistoryStateProps { isLoading: boolean; - history: Array, + history: Array; currentUser: string; -}; +} interface IDownloadHistoryDispatchProps { load: () => (dispatch: Function) => any; @@ -80,88 +76,112 @@ interface IDownloadHistoryDispatchProps { checkAvailability: Function; showRequestModal: Function; showShareModal: Function; -}; + openRequestModal: Function; +} interface IDownloadHistoryProps extends IDownloadHistoryStateProps, IDownloadHistoryDispatchProps { removeBundle: (id: number) => any; restoreBundle: (id: number) => any; download: (bundle: IDownloadRequest) => any; -}; +} interface IDownloadHistoryStatefulProps { toggle: (id: number) => any; expandedBundleId: number; -}; +} + +function CellLicense(props: any) { + const { className, value, openRequestModal, isPending, isCheckable, notAvailable } = props; + const classes = BEMHelper("cell-license"); + if (!value) { + return null; + } + if (isCheckable) { + return {value}; + } else if (isPending) { + return ( + + timer {value} + + ); + } else { + return ( + { + if (notAvailable) { + return false; + } + openRequestModal(); + }} + > + vpn_key + {value} + + ); + } +} function BundleName({ name, date, onClick, isOpened, releaseVersion, downloadShareDTO, currentUser }) { const dateFormat = fullDateFormat; - const classes = BEMHelper('bundle-caption'); + const classes = BEMHelper("bundle-caption"); const isAlreadyShared = downloadShareDTO && downloadShareDTO.ownerUsername === currentUser; - return
- keyboard_arrow_right -
- {name} - {moment(date).format(dateFormat)} - {releaseVersion} - {downloadShareDTO && !isAlreadyShared && Shared by {downloadShareDTO.ownerUsername}} -
-
; + return ( +
+ keyboard_arrow_right +
+ {name} + {moment(date).format(dateFormat)} + {releaseVersion} + {downloadShareDTO && !isAlreadyShared && Shared by {downloadShareDTO.ownerUsername}} +
+
+ ); } function BundleTitle({ bundle, removeBundle, toggle, isExpanded, restore, download, share, showShareModal, currentUser }) { - const classes = BEMHelper('download-history'); + const classes = BEMHelper("download-history"); const isAlreadyShared = bundle.downloadShareDTO && bundle.downloadShareDTO.ownerUsername === currentUser; const isShareable = isAlreadyShared || !bundle.downloadShareDTO; - const shareBtnTitle = isAlreadyShared ? 'Edit share' : !bundle.downloadShareDTO ? 'Share' : ''; - return toggle(bundle.id)} isOpened={isExpanded} currentUser={currentUser} />} - > - {[bundleStatuses.READY].includes(bundle.status) - ?
- {isShareable && ( - + )} {isShareable && ( - )} -
- :
- {bundle.status} - {bundle.status === bundleStatuses.ARCHIVED && isShareable && - - } -
- } -
; +
+ ) : ( +
+ {bundle.status} + {bundle.status === bundleStatuses.ARCHIVED && isShareable && ( + + )} +
+ )} + + ); } function VocabsList(props: IDownloadHistoryProps & IDownloadHistoryStatefulProps) { const { + load, isLoading, history, removeBundle, @@ -169,84 +189,80 @@ function VocabsList(props: IDownloadHistoryProps & IDownloadHistoryStatefulProps expandedBundleId, restoreBundle, share, - load, showNotifications, showShareModal, download, currentUser, + openRequestModal = () => {}, } = props; - const classes = BEMHelper('download-history'); - + const classes = BEMHelper("download-history"); + // const history: IDownloadRequest[] = HISTORY; return (
- - - + + + - {history && history.map((bundle: IDownloadRequest, index: number) => - } - {...classes('bundle-caption')} - key={`caption${index}`} - slug={bundle.id} - > - - ( + - - - +
+ + + + + ({ + className: classes({ + element: "cell", + modifiers: { + selected: vocab.isChecked, + }, + }).className, + isPending: vocab.status === licenseStatuses.PENDING, + openRequestModal: () => openRequestModal(vocab), + isCheckable: vocab.isCheckable, + notAvailable: vocab.required === "Currently not available", + })} /> -
-
- )} + + + + ))}
+
); } export default VocabsList; -export { - IDownloadHistoryStateProps, - IDownloadHistoryDispatchProps, - IDownloadHistoryProps, - IHistoryItem, - IDownloadRequest, - IVocabulary, -}; +export { IDownloadHistoryStateProps, IDownloadHistoryDispatchProps, IDownloadHistoryProps, IHistoryItem, IDownloadRequest, IVocabulary }; diff --git a/src/modules/Vocabulary/components/DownloadHistory/selectors.ts b/src/modules/Vocabulary/components/DownloadHistory/selectors.ts index 10e0789..ec69933 100644 --- a/src/modules/Vocabulary/components/DownloadHistory/selectors.ts +++ b/src/modules/Vocabulary/components/DownloadHistory/selectors.ts @@ -17,28 +17,29 @@ * Product Owner/Architecture: Gregory Klebanov * Authors: Alexandr Saltykov, Pavel Grafkin, Vitaly Koulakov, Anton Gackovka * Created: March 3, 2017 - * + * */ -import { createSelector } from 'reselect'; -import { get } from 'lodash'; -import * as moment from 'moment'; -import BEMHelper from 'services/BemHelper'; -import { IDownloadRequest, IVocabulary, IHistoryItem } from './presenter'; +import { createSelector } from "reselect"; +import { get } from "lodash"; +import * as moment from "moment"; +import BEMHelper from "services/BemHelper"; +import { IDownloadRequest, IVocabulary, IHistoryItem } from "./presenter"; +import { commonDateFormat } from "const/formats"; -const getRawHistory = (state: Object) => get(state, 'vocabulary.history.queryResult') || []; +const getRawHistory = (state: Object) => get(state, "vocabulary.history.queryResult") || []; -const getHistory = createSelector( - getRawHistory, - (rawResults: Array) => rawResults.map((bundle: IDownloadRequest) => ({ - ...bundle, - vocabularies: bundle.vocabularies.map((voc: IVocabulary) => ({ - ...voc, - cdmVersion: `CDM ${bundle.cdmVersion}`, - })) +const getHistory = createSelector(getRawHistory, (rawResults: Array) => + rawResults.map((bundle: IDownloadRequest) => ({ + ...bundle, + vocabularies: bundle.vocabularies.map((voc: IVocabulary) => ({ + ...voc, + cdmVersion: `CDM ${bundle.cdmVersion}`, + expiredDate: voc.expiredDate ? moment(voc.expiredDate).format(commonDateFormat) : "", })), - ); + })) +); export default { getHistory, -}; \ No newline at end of file +}; diff --git a/src/modules/Vocabulary/components/DownloadHistory/style.scss b/src/modules/Vocabulary/components/DownloadHistory/style.scss index e363118..95dd189 100644 --- a/src/modules/Vocabulary/components/DownloadHistory/style.scss +++ b/src/modules/Vocabulary/components/DownloadHistory/style.scss @@ -91,7 +91,11 @@ } &__name-th { - width: 70%; + width: 40%; + } + + &__expiredDate-th { + width: 20%; } &__bundle-caption { diff --git a/src/modules/Vocabulary/components/List/components/ModalRequestLicense/container.ts b/src/modules/Vocabulary/components/List/components/ModalRequestLicense/container.ts index 7a978e7..d4e6133 100644 --- a/src/modules/Vocabulary/components/List/components/ModalRequestLicense/container.ts +++ b/src/modules/Vocabulary/components/List/components/ModalRequestLicense/container.ts @@ -20,34 +20,34 @@ * */ -import { Component } from 'react'; -import { connect } from 'react-redux'; -import actions from 'modules/Vocabulary/actions'; -import { ModalUtils } from 'arachne-ui-components'; -import { modal } from 'modules/Vocabulary/const'; -import { licenseStatuses } from 'const/vocabulary'; -import { get } from 'lodash'; -import presenter from './presenter'; -import { - Vocabulary, -} from '../Results/selectors'; +import { Component } from "react"; +import { connect } from "react-redux"; +import actions from "modules/Vocabulary/actions"; +import { ModalUtils } from "arachne-ui-components"; +import { forms, modal } from "modules/Vocabulary/const"; +import { licenseStatuses } from "const/vocabulary"; +import { get } from "lodash"; +import presenter, { IModalDispatchProps, IModalProps, IModalStateProps } from "./presenter"; +import { Vocabulary } from "../Results/selectors"; +import { reduxForm } from "redux-form"; +import * as moment from "moment"; -interface IModalStateProps { - vocab: Vocabulary; - isLoading: boolean; -}; +// interface IModalStateProps { +// vocab: Vocabulary; +// isLoading: boolean; +// } -interface IModalDispatchProps { - close: () => null; - requestLicense: (id: number) => Promise; - openConfirmModal: Function; - loadList: Function; -}; +// interface IModalDispatchProps { +// close: () => null; +// requestLicense: (id: number, expiredDate: any) => Promise; +// openConfirmModal: Function; +// loadList: Function; +// } -interface IModalProps extends IModalStateProps, IModalDispatchProps { - modal: string; - request: Function; -}; +// interface IModalProps extends IModalStateProps, IModalDispatchProps { +// modal: string; +// request: Function; +// } class ModalRequestLicense extends Component { render() { @@ -55,25 +55,49 @@ class ModalRequestLicense extends Component { } } +const getStatisticExpiredDateValue = (state, expiredDate) => { + let _expiredDate; + if (expiredDate) { + const expiredDateTS = new Date(moment(expiredDate).format()).getTime(); + const currentDateTS = new Date().getTime(); + if (expiredDateTS <= currentDateTS) { + _expiredDate = moment(); + } else { + _expiredDate = moment(expiredDate); + } + } else { + _expiredDate = moment(); + } + return get(state, "form.requestLinsence.values", { + expiredDate: _expiredDate, + }); +}; + function mapStateToProps(state: any): IModalStateProps { - const vocab: Vocabulary = get(state, 'modal.requestLicense.data', { + const vocab: Vocabulary = get(state, "modal.requestLicense.data", { id: -1, - code: '', - name: 'Unnamed vocabulary', + code: "", + name: "Unnamed vocabulary", available: true, - update: '', + update: "", index: 0, isCheckable: false, isChecked: false, - tableRowClass: '', + tableRowClass: "", status: licenseStatuses.APPROVED, clickDefault: false, + expiredDate: "", + typeModal: "", }); - const isLoading = get(state, 'vocabulary.vocabLicenses.isSaving', false); - - return { + const isLoading = get(state, "vocabulary.vocabLicenses.isSaving", false); + const expiredDate = getStatisticExpiredDateValue(state, vocab.expiredDate).expiredDate; + return { vocab, isLoading, + expiredDate, + initialValues: { + expiredDate, + }, }; } @@ -82,32 +106,41 @@ const mapDispatchToProps = { requestLicense: actions.vocabularies.requestLicense, openConfirmModal: () => ModalUtils.actions.toggle(modal.confirmLicense, true), loadList: actions.vocabularies.load, + loadHistory: actions.history.load, }; -function mergeProps( - stateProps: IModalStateProps, - dispatchProps: IModalDispatchProps, - ownProps - ): IModalProps { +function mergeProps(stateProps: IModalStateProps, dispatchProps: IModalDispatchProps, ownProps): IModalProps { return { ...stateProps, ...ownProps, ...dispatchProps, - request: () => { - return dispatchProps.requestLicense(stateProps.vocab.id) + request: (expiredDate, isHistoryScreen) => { + return dispatchProps + .requestLicense(stateProps.vocab.id, expiredDate) .then(() => dispatchProps.close()) .then(() => dispatchProps.openConfirmModal()) - .then(() => dispatchProps.loadList()) + .then(() => { + if (isHistoryScreen) { + dispatchProps.loadHistory(); + } else { + dispatchProps.loadList(); + } + }) .catch(() => {}); }, }; } -const ReduxModalWindow = ModalUtils.connect({ name: modal.requestLicense })(ModalRequestLicense); +const ModalRequestLicenseForm = reduxForm({ + form: forms.requestLinsence, + onSubmit: () => { + console.log("onSubmit"); + }, +})(ModalRequestLicense); +const ReduxModalWindow = ModalUtils.connect({ name: modal.requestLicense })(ModalRequestLicenseForm); export default connect( - mapStateToProps, - mapDispatchToProps, - mergeProps -) -(ReduxModalWindow); + mapStateToProps, + mapDispatchToProps, + mergeProps +)(ReduxModalWindow); diff --git a/src/modules/Vocabulary/components/List/components/ModalRequestLicense/presenter.tsx b/src/modules/Vocabulary/components/List/components/ModalRequestLicense/presenter.tsx index fb782e7..ed3b80f 100644 --- a/src/modules/Vocabulary/components/List/components/ModalRequestLicense/presenter.tsx +++ b/src/modules/Vocabulary/components/List/components/ModalRequestLicense/presenter.tsx @@ -20,45 +20,115 @@ * */ -import * as React from 'react'; -import BEMHelper from 'services/BemHelper'; -import { Modal, Button, LoadingPanel } from 'arachne-ui-components'; -import { - Vocabulary, -} from '../Results/selectors'; +import * as React from "react"; +import BEMHelper from "services/BemHelper"; +import { Modal, Button, LoadingPanel } from "arachne-ui-components"; +import { Vocabulary } from "../Results/selectors"; +import { commonDateFormat } from "const/formats"; +import { Field } from "redux-form"; +import { paths, TYPE_MODAL } from "modules/Vocabulary/const"; +import DatePanel from "components/DatePanel"; -require('./style.scss'); +require("./style.scss"); -interface IModalProps { - modal: string; - request: Function; - vocab: Vocabulary; - isLoading: boolean; -}; +interface IModalStateProps { + vocab: Vocabulary; + isLoading: boolean; + expiredDate: any; + initialValues: { + expiredDate: string; + }; +} + +interface IModalDispatchProps { + close: () => null; + requestLicense: (id: number, expiredDate: any) => Promise; + openConfirmModal: Function; + loadList: Function; + loadHistory: Function; +} + +interface IModalProps extends IModalStateProps, IModalDispatchProps { + modal: string; + request: Function; + vocab: Vocabulary; + isLoading: boolean; +} + +// interface IReduxFieldProps { +// options: any; +// input: any; +// } + +function DatepickerControler({ options, input, titleDisplay }) { + const classes = BEMHelper("date-picker"); + + return ( + + ); +} function ModalConfirmDownload(props: IModalProps) { - const { - modal, - request, - vocab, - isLoading, - } = props; - const classes = BEMHelper('request-license'); + const { modal, request, vocab, isLoading, expiredDate } = props; + const classes = BEMHelper("request-license"); + const getContextModal = () => { + switch (vocab.typeModal) { + case TYPE_MODAL.UPDATE_LICENSE: + return { + modalTitle: "Extend License", + buttonLabel: "Request Extension", + titleDatePicker: "New expiration date", + modalMessage: "", + }; + case TYPE_MODAL.REQUEST_LICENSE: + default: + return { + modalTitle: "Request access", + buttonLabel: "Request", + titleDatePicker: "License Expiration Date", + modalMessage: ( +
+ {`Vocabulary '${vocab.name}' requires a license`}
+
+
+ ), + }; + } + }; + const { modalTitle, buttonLabel, titleDatePicker, modalMessage } = getContextModal(); + + const isHistoryScreen = window.location.pathname === paths.history(); return ( - -
- Vocabulary '{vocab.name}' requires a license
- -
- -
); + +
+ {modalMessage} + {/* Vocabulary '{vocab.name}' requires a license

*/} +
+ } + name="expiredDate" + /> +
+ +
+ +
+ ); } export default ModalConfirmDownload; +export { IModalProps, IModalStateProps, IModalDispatchProps }; diff --git a/src/modules/Vocabulary/components/List/components/ModalRequestLicense/style.scss b/src/modules/Vocabulary/components/List/components/ModalRequestLicense/style.scss index 1e59c9d..bdd623c 100644 --- a/src/modules/Vocabulary/components/List/components/ModalRequestLicense/style.scss +++ b/src/modules/Vocabulary/components/List/components/ModalRequestLicense/style.scss @@ -20,15 +20,23 @@ * */ -@import 'styles/vars-and-mixins.scss'; +@import "styles/vars-and-mixins.scss"; .#{$namespace} { &request-license { min-width: 500px; + &__request-date { + width: 100%; + } + &__request-button { - margin-top: 2rem; + margin-top: 1rem; width: 100%; } } -} \ No newline at end of file + + &date-picker { + display: block; + } +} diff --git a/src/modules/Vocabulary/components/List/components/Results/container.ts b/src/modules/Vocabulary/components/List/components/Results/container.ts index 0ea004c..8a2bf70 100644 --- a/src/modules/Vocabulary/components/List/components/Results/container.ts +++ b/src/modules/Vocabulary/components/List/components/Results/container.ts @@ -20,27 +20,20 @@ * */ -import { connect } from 'react-redux'; -import { Component } from 'react'; -import actions from 'modules/Vocabulary/actions'; -import { apiPaths, forms, modal } from 'modules/Vocabulary/const'; -import { get } from 'lodash'; -import { push as goToPage } from 'react-router-redux'; -import { reduxForm, reset, FormProps, change as reduxFormChange } from 'redux-form'; -import { ModalUtils } from 'arachne-ui-components'; -import { isEmpty } from 'lodash'; -import presenter from './presenter'; -import selectors from './selectors'; +import { connect } from "react-redux"; +import { Component } from "react"; +import actions from "modules/Vocabulary/actions"; +import { apiPaths, forms, modal, TYPE_MODAL } from "modules/Vocabulary/const"; +import { get } from "lodash"; +import { push as goToPage } from "react-router-redux"; +import { reduxForm, reset, FormProps, change as reduxFormChange } from "redux-form"; +import { ModalUtils } from "arachne-ui-components"; +import { isEmpty } from "lodash"; +import presenter from "./presenter"; +import selectors from "./selectors"; -import { - IResultsStateProps, - IResultsDispatchProps, - IResultsProps, - IResultsOwnProps, -} from './presenter'; -import { - Vocabulary, -} from './selectors'; +import { IResultsStateProps, IResultsDispatchProps, IResultsProps, IResultsOwnProps } from "./presenter"; +import { Vocabulary } from "./selectors"; class Results extends Component & IResultsOwnProps, void> { render() { @@ -54,7 +47,7 @@ function mapStateToProps(state: Object, ownProps: any): IResultsStateProps { vocabulary: [], }; // top checkbox is checked - let areAllChecked = get(state, 'vocabulary.download.data.allChecked'); + let areAllChecked = get(state, "vocabulary.download.data.allChecked"); vocabularies.forEach((vocabulary) => { if (areAllChecked === true) { initialValues.vocabulary[`${vocabulary.id}`] = vocabulary.isCheckable; @@ -68,15 +61,15 @@ function mapStateToProps(state: Object, ownProps: any): IResultsStateProps { if (!isEmpty(ownProps.predefinedVocabs)) { initialValues.vocabulary = []; - ownProps.predefinedVocabs.forEach(vocabId => { + ownProps.predefinedVocabs.forEach((vocabId) => { initialValues.vocabulary[vocabId] = true; }); } const values = get(state, `form.${forms.download}.values.vocabulary`, []) || []; - const selection = get(state, `form.${forms.downloadSettings}.values.selection`, 'all'); - if (selection !== 'all') { + const selection = get(state, `form.${forms.downloadSettings}.values.selection`, "all"); + if (selection !== "all") { vocabularies = vocabularies.filter((v: Vocabulary) => get(values, `${v.id}`, false)); } @@ -94,7 +87,7 @@ function mapStateToProps(state: Object, ownProps: any): IResultsStateProps { areAllChecked, areAllRowsChecked, initialValues, - sorting: '', + sorting: "", vocabularies, }; } @@ -103,7 +96,8 @@ const mapDispatchToProps = { unselectAll: () => reset(forms.download), toggleAll: actions.download.toggleAllVocabs, toggle: (id: number, state: boolean) => reduxFormChange(forms.download, `vocabulary[${id}]`, state), - openRequestModal: (vocab: Vocabulary) => ModalUtils.actions.toggle(modal.requestLicense, true, vocab), + openRequestModal: (vocab: Vocabulary, typeModal?: string) => + ModalUtils.actions.toggle(modal.requestLicense, true, { ...vocab, typeModal: typeModal || TYPE_MODAL.REQUEST_LICENSE }), }; function mergeProps( @@ -114,9 +108,7 @@ function mergeProps( return { ...stateProps, ...dispatchProps, - setSorting: () => { - - }, + setSorting: () => {}, toggleAllCheckboxes: () => dispatchProps.toggleAll(!stateProps.areAllChecked), }; } diff --git a/src/modules/Vocabulary/components/List/components/Results/presenter.tsx b/src/modules/Vocabulary/components/List/components/Results/presenter.tsx index 6d9e418..c621b01 100644 --- a/src/modules/Vocabulary/components/List/components/Results/presenter.tsx +++ b/src/modules/Vocabulary/components/List/components/Results/presenter.tsx @@ -20,34 +20,29 @@ * */ -import * as React from 'react'; -import BEMHelper from 'services/BemHelper'; -import { - Button, - Table, - TableCellText as Cell, - Checkbox, - Link, -} from 'arachne-ui-components'; -import { push } from 'react-router-redux'; -import { Field, FormProps } from 'redux-form'; -import { licenseStatuses } from 'const/vocabulary'; -import { Vocabulary } from './selectors'; - -require('./style.scss'); +import * as React from "react"; +import BEMHelper from "services/BemHelper"; +import { Table, TableCellText as Cell, Checkbox, Link } from "arachne-ui-components"; +import { Field, FormProps } from "redux-form"; +import { licenseStatuses } from "const/vocabulary"; +import { Vocabulary } from "./selectors"; +import { TYPE_MODAL } from "modules/Vocabulary/const"; +// import { VOCABULARY } from './const'; + +require("./style.scss"); interface IDownloadCheckboxProps { options: { className: string; }; input: any; -}; +} interface ICellProps { className: string; isCheckable: boolean; name: string; -}; +} interface IResultsStateProps { areAllChecked: boolean | Object; @@ -55,128 +50,148 @@ interface IResultsStateProps { sorting: string; vocabularies: Array; initialValues: Object; -}; +} interface IResultsDispatchProps { toggleAll: (on: boolean) => (dispatch: Function) => any; toggle: (id: number, on: boolean) => (dispatch: Function) => any; openRequestModal: Function; -}; +} interface IResultsProps extends IResultsStateProps, IResultsDispatchProps { toggleAllCheckboxes: () => (dispatch: Function) => any; setSorting: Function; -}; +} interface IResultsOwnProps { predefinedVocabs: Array; -}; +} function DownloadCheckbox(props: IDownloadCheckboxProps) { const { options, /*redux-form*/ input } = props; - return (); + return ; } function CellChecked(props: any) { const { className, isCheckable, name } = props; - - return isCheckable - ? - : null; + const classes = BEMHelper("cell-checked"); + + return isCheckable ? ( + + ) : ( + + License Expired + + ); } function CellLicense(props: any) { const { className, value, openRequestModal, isPending, isCheckable, notAvailable } = props; - const classes = BEMHelper('cell-license'); + const classes = BEMHelper("cell-license"); if (!value) { return null; } if (isCheckable) { return {value}; } else if (isPending) { - return - - timer - {value} - ; + return ( + + timer {value} + + ); } else { - return { - if (notAvailable) { - return false; - } - openRequestModal(); - }}> - - vpn_key - + return ( + { + if (notAvailable) { + return false; + } + openRequestModal(); + }} + > + vpn_key {value} - ; + + ); } +} +function CellLicenseExpiredDate(props: any) { + const { className, value, openRequestModal } = props; + const classes = BEMHelper("cell-license"); + + return ( + { + if (!value) return; + openRequestModal(); + }} + > + {value} + + ); } function Results(props: IResultsProps & FormProps<{}, {}, {}>) { - const { - areAllRowsChecked, - vocabularies, - sorting, - setSorting, - toggleAllCheckboxes, - toggle, - openRequestModal, - } = props; - const classes = BEMHelper('vocabularies'); + const { areAllRowsChecked, vocabularies, sorting, setSorting, toggleAllCheckboxes, toggle, openRequestModal } = props; + // let vocabularies: any = VOCABULARY + const classes = BEMHelper("vocabularies"); const selectAllButton = ; // add modifiers for Table component vocabularies.map((vocabulary) => { if (vocabulary.isChecked) { - vocabulary.tableRowClass = classes('selected-row').className; + vocabulary.tableRowClass = classes("selected-row").className; } }); + console.log("vocabularies", vocabularies); return (
{ - if(vocab.isCheckable) { - toggle(vocab.id, !vocab.isChecked); - } + if (vocab.isCheckable) { + toggle(vocab.id, !vocab.isChecked); } - } + }} > ({ isCheckable: vocab.isCheckable, name: `vocabulary[${vocab.id}]`, className: classes({ - element: 'cell', + element: "cell", modifiers: { unclickable: vocab.isCheckable, }, }).className, })} - /> + /> ({ + {...classes("id")} + header="ID (CDM v4.5)" + field="id" + props={(vocab: Vocabulary) => ({ className: classes({ - element: 'cell', + element: "cell", modifiers: { selected: vocab.isChecked, }, @@ -184,12 +199,12 @@ function Results(props: IResultsProps & FormProps<{}, {}, {}>) { })} /> ({ + {...classes("code")} + header="CODE (CDM v5)" + field="code" + props={(vocab: Vocabulary) => ({ className: classes({ - element: 'cell', + element: "cell", modifiers: { selected: vocab.isChecked, }, @@ -197,12 +212,12 @@ function Results(props: IResultsProps & FormProps<{}, {}, {}>) { })} /> ({ + {...classes("name")} + header="Name" + field="name" + props={(vocab: Vocabulary) => ({ className: classes({ - element: 'cell', + element: "cell", modifiers: { selected: vocab.isChecked, }, @@ -210,29 +225,44 @@ function Results(props: IResultsProps & FormProps<{}, {}, {}>) { })} /> ({ + {...classes("required")} + header="Required" + field="required" + props={(vocab: Vocabulary) => ({ className: classes({ - element: 'cell', + element: "cell", modifiers: { selected: vocab.isChecked, }, }).className, isPending: vocab.status === licenseStatuses.PENDING, - openRequestModal: () => openRequestModal(vocab), + openRequestModal: () => openRequestModal(vocab, TYPE_MODAL.REQUEST_LICENSE), isCheckable: vocab.isCheckable, - notAvailable: vocab.required === 'Currently not available', + notAvailable: vocab.required === "Currently not available", + })} + /> + + ({ + className: classes({ + element: "cell", + modifiers: { + selected: vocab.isChecked, + }, + }).className, + openRequestModal: () => openRequestModal(vocab, TYPE_MODAL.UPDATE_LICENSE), })} /> ({ + {...classes("update")} + header="Latest Update" + field="update" + props={(vocab: Vocabulary) => ({ className: classes({ - element: 'cell', + element: "cell", modifiers: { selected: vocab.isChecked, }, @@ -245,9 +275,4 @@ function Results(props: IResultsProps & FormProps<{}, {}, {}>) { } export default Results; -export { - IResultsStateProps, - IResultsDispatchProps, - IResultsProps, - IResultsOwnProps, -}; +export { IResultsStateProps, IResultsDispatchProps, IResultsProps, IResultsOwnProps }; diff --git a/src/modules/Vocabulary/components/List/components/Results/selectors.ts b/src/modules/Vocabulary/components/List/components/Results/selectors.ts index fd25622..760510e 100644 --- a/src/modules/Vocabulary/components/List/components/Results/selectors.ts +++ b/src/modules/Vocabulary/components/List/components/Results/selectors.ts @@ -38,6 +38,8 @@ interface Vocabulary { status: string; clickDefault: boolean; required?: string; + expiredDate: string; + typeModal: string; }; const getRawVocabs = (state: Object) => get(state, 'vocabulary.vocabularies.queryResult') || []; @@ -47,7 +49,9 @@ const getVocabs = createSelector( (rawResults: Array) => rawResults.map((vocabulary: Vocabulary, index: number) => ({ ...vocabulary, update: vocabulary.update ? moment(vocabulary.update).format(commonDateFormat) : '', + expiredDate: vocabulary.expiredDate ? moment(vocabulary.expiredDate).format(commonDateFormat) : '', isChecked: false, + typeModal: '', isCheckable: vocabulary.available === true, // for redux-form index, diff --git a/src/modules/Vocabulary/components/List/components/Results/style.scss b/src/modules/Vocabulary/components/List/components/Results/style.scss index c905a62..2762646 100644 --- a/src/modules/Vocabulary/components/List/components/Results/style.scss +++ b/src/modules/Vocabulary/components/List/components/Results/style.scss @@ -20,7 +20,7 @@ * */ -@import 'styles/vars-and-mixins.scss'; +@import "styles/vars-and-mixins.scss"; .#{$namespace} { &vocabularies { @@ -43,7 +43,7 @@ width: 10%; } &__name-th { - width: 50%; + width: 37.5%; } &__required-th { width: 12.5%; @@ -51,11 +51,14 @@ &__update-th { width: 12.5%; } + &__expiredDate-th { + width: 12.5%; + } &__selected-row { background: $grey-light; } - + &__cell { &--disabled { @include material-icon(); @@ -94,4 +97,15 @@ margin-right: 1rem; } } + + &cell-checked { + height: 100%; + + &__licenseExpired { + color: #f6f6f6; + opacity: 0; + height: 100%; + display: block; + } + } } diff --git a/src/modules/Vocabulary/const.ts b/src/modules/Vocabulary/const.ts index 9651c0f..0217a8f 100644 --- a/src/modules/Vocabulary/const.ts +++ b/src/modules/Vocabulary/const.ts @@ -20,15 +20,16 @@ * */ -import keyMirror = require('keymirror'); +import keyMirror = require("keymirror"); const forms = keyMirror({ download: null, share: null, - toolbar: null, + toolbar: null, downloadSettings: null, bundle: null, notifications: null, + requestLinsence: null, }); const modal = keyMirror({ @@ -46,29 +47,29 @@ const actionTypes = keyMirror({ }); const paths = { - vocabsList: () => '/vocabulary/list', - history: () => '/vocabulary/download-history', + vocabsList: () => "/vocabulary/list", + history: () => "/vocabulary/download-history", }; const apiPaths = { - availability: id => `/vocabularies/check/${id}`, - share: id => `/vocabularies/downloads/${id}/share`, + availability: (id) => `/vocabularies/check/${id}`, + share: (id) => `/vocabularies/downloads/${id}/share`, }; const resultsPageSize = 15; const cdmVersions = [ { - label: 'CDM VERSION', - value: '', + label: "CDM VERSION", + value: "", }, - { - label: '4.x', - value: '4.5', + { + label: "4.x", + value: "4.5", }, - { - label: '5.x', - value: '5', + { + label: "5.x", + value: "5", }, ]; @@ -79,13 +80,9 @@ const bundleStatuses: { [key: string]: string } = keyMirror({ ARCHIVED: null, }); -export { - actionTypes, - apiPaths, - cdmVersions, - forms, - modal, - paths, - resultsPageSize, - bundleStatuses, +const TYPE_MODAL = { + REQUEST_LICENSE: "REQUEST_LICENSE", + UPDATE_LICENSE: "UPDATE_LICENSE", }; + +export { actionTypes, apiPaths, cdmVersions, forms, modal, paths, resultsPageSize, bundleStatuses, TYPE_MODAL };