diff --git a/.eslintrc.js b/.eslintrc.js index 2a4c9b03f..f4e3b4196 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,57 +1,60 @@ module.exports = { - "env": { - "browser": true, - "es6": true, - "jasmine": true + 'env': { + 'browser': true, + 'es6': true, + 'jasmine': true }, - "globals": { - "$": true, - "sectionNames": "readonly", + 'globals': { + '$': true, + 'sectionNames': 'readonly', }, - "extends": ["eslint:recommended", "plugin:react/recommended"], - "parserOptions": { - "ecmaFeatures": { - "experimentalObjectRestSpread": true, - "jsx": true + 'extends': ['eslint:recommended', 'plugin:react/recommended', 'plugin:@typescript-eslint/recommended'], + 'parser': '@typescript-eslint/parser', + 'parserOptions': { + 'ecmaFeatures': { + 'experimentalObjectRestSpread': true, + 'jsx': true }, - "sourceType": "module" + 'sourceType': 'module' }, - "plugins": [ - "react" + 'plugins': [ + 'react', + '@typescript-eslint', ], - "rules": { - "indent": [ - "error", + 'rules': { + '@typescript-eslint/no-explicit-any': 0, + 'indent': [ + 'error', 4 ], - "linebreak-style": [ - "error", - "unix" + 'linebreak-style': [ + 'error', + 'unix' ], - "quotes": [ - "error", - "single" + 'quotes': [ + 'error', + 'single' ], - "semi": [ - "error", - "always" + 'semi': [ + 'error', + 'always' ], - "no-console": [ - "error", - {"allow": ["warn", "error"]} + 'no-console': [ + 'error', + {'allow': ['warn', 'error']} ], - "object-curly-spacing": ["error", "never"], - "react/no-deprecated": [ + 'object-curly-spacing': ['error', 'never'], + 'react/no-deprecated': [ 1, ], - "react/jsx-no-target-blank": [ + 'react/jsx-no-target-blank': [ 0, ], - "react/display-name": [0] + 'react/display-name': [0] }, - "settings": { - "react": { - "version": "16.2" + 'settings': { + 'react': { + 'version': '16.2' } } }; diff --git a/assets/actions.js b/assets/actions.ts similarity index 73% rename from assets/actions.js rename to assets/actions.ts index 77e4b2b98..f7179d1a8 100644 --- a/assets/actions.js +++ b/assets/actions.ts @@ -2,7 +2,7 @@ import {get, differenceBy} from 'lodash'; import server from 'server'; export const RENDER_MODAL = 'RENDER_MODAL'; -export function renderModal(modal, data) { +export function renderModal(modal: any, data: any) { return {type: RENDER_MODAL, modal, data}; } @@ -11,19 +11,25 @@ export function closeModal() { return {type: CLOSE_MODAL}; } +export const SET_USER = 'SET_USER'; +export function setUser(user: any) { + return {type: SET_USER, data: user}; +} + + export const SAVED_ITEMS_COUNT = 'SAVED_ITEMS_COUNT'; -export function setSavedItemsCount(count) { +export function setSavedItemsCount(count: any) { return {type: SAVED_ITEMS_COUNT, count: count}; } export const SET_UI_CONFIG = 'SET_UI_CONFIG'; -export function setUiConfig(config) { +export function setUiConfig(config: any) { return {type: SET_UI_CONFIG, config: config}; } export const MODAL_FORM_VALID = 'MODAL_FORM_VALID'; export function modalFormValid() { - return (dispatch, getState) => { + return (dispatch: any, getState: any) => { if (!get(getState(), 'modal.formValid')) { dispatch({type: MODAL_FORM_VALID}); } @@ -34,7 +40,7 @@ export function modalFormValid() { export const MODAL_FORM_INVALID = 'MODAL_FORM_INVALID'; export function modalFormInvalid() { - return (dispatch, getState) => { + return (dispatch: any, getState: any) => { if (get(getState(), 'modal.formValid')) { dispatch({type: MODAL_FORM_INVALID}); } @@ -49,30 +55,30 @@ export function userProfileClosed() { } export const ADD_EDIT_USERS = 'ADD_EDIT_USERS'; -export function getEditUsers(item) { - return function (dispatch, getState) { +export function getEditUsers(item: any) { + return function (dispatch: any, getState: any) { let findUsers = []; const itemUsers = ([ item.original_creator, item.version_creator - ].filter((u) => u)); + ].filter((u: any) => u)); const editUsers = getState().editUsers || []; if (!get(item, 'version_creator') && !get(item, 'original_creator')) { return Promise.resolve(); } - - findUsers = differenceBy(editUsers, itemUsers.map((u) => ({'_id': u})), '_id'); + + findUsers = differenceBy(editUsers, itemUsers.map((u: any) => ({'_id': u})), '_id'); if (editUsers.length === 0 || findUsers.length > 0) { return server.get(`/users/search?ids=${itemUsers.join(',')}`) - .then((data) => { + .then((data: any) => { dispatch({ type: ADD_EDIT_USERS, data }); - }); + }); } return Promise.resolve(); - }; + }; } diff --git a/assets/agenda/actions.js b/assets/agenda/actions.ts similarity index 75% rename from assets/agenda/actions.js rename to assets/agenda/actions.ts index 82b60a2b6..578555693 100644 --- a/assets/agenda/actions.js +++ b/assets/agenda/actions.ts @@ -38,34 +38,34 @@ const WATCH_URL = '/agenda_watch'; const WATCH_COVERAGE_URL = '/agenda_coverage_watch'; export const SET_STATE = 'SET_STATE'; -export function setState(state) { +export function setState(state: any) { return {type: SET_STATE, state}; } export const SET_ITEMS = 'SET_ITEMS'; -export function setItems(items) { +export function setItems(items: any) { return {type: SET_ITEMS, items}; } export const SET_ACTIVE = 'SET_ACTIVE'; -export function setActive(item) { +export function setActive(item: any) { return {type: SET_ACTIVE, item}; } export const PREVIEW_ITEM = 'PREVIEW_ITEM'; -export function preview(item, group, plan) { +export function preview(item: any, group?: any, plan?: any) { return {type: PREVIEW_ITEM, item, group, plan}; } -export function previewAndCopy(item) { - return (dispatch) => { +export function previewAndCopy(item: any) { + return (dispatch: any) => { dispatch(copyPreviewContents(item)); }; } -export function previewItem(item, group, plan) { - return (dispatch, getState) => { +export function previewItem(item?: any, group?: any, plan?: any) { + return (dispatch: any, getState: any) => { dispatch(fetchWireItemsForAgenda(item)); markItemAsRead(item, getState()); dispatch(preview(item, group, plan)); @@ -73,10 +73,10 @@ export function previewItem(item, group, plan) { }; } -export function fetchWireItemsForAgenda(item) { - return (dispatch) => { - let wireIds = []; - (get(item, 'coverages') || []).forEach((c) => { +export function fetchWireItemsForAgenda(item: any) { + return (dispatch: any) => { + const wireIds: Array = []; + (get(item, 'coverages') || []).forEach((c: any) => { if (c.coverage_type === 'text' && c.delivery_id) { wireIds.push(c.delivery_id); } @@ -84,37 +84,39 @@ export function fetchWireItemsForAgenda(item) { if (wireIds.length > 0){ return server.get(`/wire/items/${wireIds.join(',')}`) - .then((items) => { + .then((items: any) => { dispatch(agendaWireItems(items)); return Promise.resolve(items); }) - .catch((error) => errorHandler(error, dispatch)); + .catch((error: any) => errorHandler(error, dispatch)); } }; } export const AGENDA_WIRE_ITEMS = 'AGENDA_WIRE_ITEMS'; -export function agendaWireItems(items) { +export function agendaWireItems(items: any) { return {type: AGENDA_WIRE_ITEMS, items}; } export const OPEN_ITEM = 'OPEN_ITEM'; -export function openItemDetails(item, group, plan) { +export function openItemDetails(item: any, group?: any, plan?: any) { return {type: OPEN_ITEM, item, group, plan}; } -export function requestCoverage(item, message) { +export function requestCoverage(item: any, message: any) { return () => { const url = '/agenda/request_coverage'; - const data = {item: item._id, message}; + const data: any = {item: item._id, message}; return server.post(url, data) .then(() => notify.success(gettext('Your inquiry has been sent successfully'))) - .catch(errorHandler); + .catch((arg: any) => { + errorHandler(arg); + }); }; } -export function openItem(item, group, plan) { - return (dispatch, getState) => { +export function openItem(item: any, group: any, plan: any) { + return (dispatch: any, getState: any) => { const state = getState(); markItemAsRead(item, state); dispatch(fetchWireItemsForAgenda(item)); @@ -137,40 +139,40 @@ export function queryItems() { } export const RECIEVE_ITEMS = 'RECIEVE_ITEMS'; -export function recieveItems(data) { +export function recieveItems(data: any) { return {type: RECIEVE_ITEMS, data}; } export const RECIEVE_ITEM = 'RECIEVE_ITEM'; -export function recieveItem(data) { +export function recieveItem(data: any) { return {type: RECIEVE_ITEM, data}; } export const INIT_DATA = 'INIT_DATA'; -export function initData(agendaData, readData, activeDate, featuredOnly) { +export function initData(agendaData: any, readData: any, activeDate: any, featuredOnly: any) { return {type: INIT_DATA, agendaData, readData, activeDate, featuredOnly}; } export const SELECT_DATE = 'SELECT_DATE'; -export function selectDate(dateString, grouping) { +export function selectDate(dateString: any, grouping: any) { return {type: SELECT_DATE, dateString, grouping}; } export const SET_ERROR = 'SET_ERROR'; -export function setError(errors) { +export function setError(errors: any) { return {type: SET_ERROR, errors}; } export const SET_ERROR_MESSAGE = 'SET_ERROR_MESSAGE'; -export function setErrorMessage(message) { +export function setErrorMessage(message: any) { return { type: SET_ERROR_MESSAGE, message }; } -export function printItem(item) { - return (dispatch, getState) => { +export function printItem(item: any) { + return (dispatch: any, getState: any) => { const map = encodeURIComponent(getMapSource(getLocations(item), 2)); window.open(`/agenda/${item._id}?print&map=${map}`, '_blank'); @@ -187,16 +189,16 @@ export function printItem(item) { * * This is an initial version, should be updated with preview markup changes. */ -export function copyPreviewContents(item) { - return (dispatch, getState) => { +export function copyPreviewContents(item: any) { + return (dispatch: any, getState: any) => { const state = getState(); if (!state.user) { return; } - server.post(`/wire/${item._id}/copy?type=${state.context}`) - .then((response) => { + (server as any).post(`/wire/${item._id}/copy?type=${state.context}`) + .then((response: any) => { dispatch(setCopyItem(item._id)); copyTextToClipboard(response.data, item); }) @@ -211,19 +213,19 @@ export function copyPreviewContents(item) { * @param {bool} next * @return {Promise} */ -function search(state, next) { +function search(state: any, next?: any) { const currentMoment = moment(); - const searchParams = searchParamsSelector(state); - const createdFilter = get(searchParams, 'created') || {}; - const itemTypeFilter = get(state, 'agenda.itemType'); - const eventsOnlyFilter = !state.bookmarks && itemTypeFilter === 'events'; + const searchParams: any = searchParamsSelector(state); + const createdFilter: any = get(searchParams, 'created') || {}; + const itemTypeFilter: any = get(state, 'agenda.itemType'); + const eventsOnlyFilter: any = !state.bookmarks && itemTypeFilter === 'events'; const featuredFilter = noNavigationSelected(searchParams.navigation) && !state.bookmarks && !eventsOnlyFilter && get(state, 'agenda.featuredOnly'); - let fromDateFilter; + let fromDateFilter: any; if (featuredFilter) { fromDateFilter = getMomentDate(get(state, 'agenda.activeDate')).set({ @@ -244,7 +246,7 @@ function search(state, next) { dateTo = createdFilter.from; } - const params = { + const params: any = { q: searchParams.query, id: state.queryId, bookmarks: state.bookmarks && state.user, @@ -261,8 +263,8 @@ function search(state, next) { }; const queryString = Object.keys(params) - .filter((key) => params[key]) - .map((key) => [key, params[key]].join('=')) + .filter((key: any) => params[key]) + .map((key: any) => [key, params[key]].join('=')) .join('&'); return server.get(`/agenda/search?${queryString}&tick=${Date.now().toString()}`); @@ -271,12 +273,12 @@ function search(state, next) { /** * Fetch items for current query */ -export function fetchItems() { - return (dispatch, getState) => { +export function fetchItems(): any { + return (dispatch: any, getState: any) => { const start = Date.now(); dispatch(queryItems()); return search(getState()) - .then((data) => dispatch(recieveItems(data))) + .then((data: any) => dispatch(recieveItems(data))) .then(() => { analytics.timingComplete('search', Date.now() - start); }) @@ -284,29 +286,29 @@ export function fetchItems() { }; } -export function fetchItem(id) { - return (dispatch) => { +export function fetchItem(id: any) { + return (dispatch: any) => { return server.get(`/agenda/${id}?format=json`) - .then((data) => dispatch(recieveItem(data))) + .then((data: any) => dispatch(recieveItem(data))) .catch(errorHandler); }; } export const WATCH_EVENTS = 'WATCH_EVENTS'; -export function watchEvents(ids) { - return (dispatch, getState) => { +export function watchEvents(ids: any) { + return (dispatch: any, getState: any) => { server.post(WATCH_URL, {items: ids}) .then(() => { dispatch({type: WATCH_EVENTS, items: ids}); notify.success(gettext('Started watching items successfully.')); - analytics.multiItemEvent('watch', ids.map((_id) => getState().itemsById[_id])); + analytics.multiItemEvent('watch', ids.map((_id: any) => getState().itemsById[_id])); }); }; } export const STOP_WATCHING_EVENTS = 'STOP_WATCHING_EVENTS'; -export function stopWatchingEvents(items) { - return (dispatch, getState) => { +export function stopWatchingEvents(items: any) { + return (dispatch: any, getState: any) => { server.del(getState().bookmarks ? `${WATCH_URL}?bookmarks=true` : WATCH_URL, {items}) .then(() => { notify.success(gettext('Stopped watching items successfully.')); @@ -328,29 +330,29 @@ export function stopWatchingEvents(items) { * * @return {function} */ -export function shareItems(items) { - return (dispatch, getState) => { +export function shareItems(items: any) { + return (dispatch: any, getState: any) => { const user = getState().user; const company = getState().company; return server.get(`/companies/${company}/users`) - .then((users) => users.filter((u) => u._id !== user)) - .then((users) => dispatch(renderModal('shareItem', {items, users}))) + .then((users: any) => users.filter((u: any) => u._id !== user)) + .then((users: any) => dispatch(renderModal('shareItem', {items, users}))) .catch(errorHandler); }; } export const BOOKMARK_ITEMS = 'BOOKMARK_ITEMS'; -export function setBookmarkItems(items) { +export function setBookmarkItems(items: any) { return {type: BOOKMARK_ITEMS, items}; } export const REMOVE_BOOKMARK = 'REMOVE_BOOKMARK'; -export function removeBookmarkItems(items) { +export function removeBookmarkItems(items: any) { return {type: REMOVE_BOOKMARK, items}; } -export function bookmarkItems(items) { - return (dispatch, getState) => +export function bookmarkItems(items: any) { + return (dispatch: any, getState: any) => server.post('/agenda_bookmark', {items}) .then(() => { if (items.length > 1) { @@ -360,14 +362,14 @@ export function bookmarkItems(items) { } }) .then(() => { - analytics.multiItemEvent('bookmark', items.map((_id) => getState().itemsById[_id])); + analytics.multiItemEvent('bookmark', items.map((_id: any) => getState().itemsById[_id])); }) .then(() => dispatch(setBookmarkItems(items))) .catch(errorHandler); } -export function removeBookmarks(items) { - return (dispatch, getState) => +export function removeBookmarks(items: any) { + return (dispatch: any, getState: any) => server.del('/agenda_bookmark', {items}) .then(() => { if (items.length > 1) { @@ -382,27 +384,27 @@ export function removeBookmarks(items) { } export const TOGGLE_SELECTED = 'TOGGLE_SELECTED'; -export function toggleSelected(item) { +export function toggleSelected(item: any) { return {type: TOGGLE_SELECTED, item}; } export const SHARE_ITEMS = 'SHARE_ITEMS'; -export function setShareItems(items) { +export function setShareItems(items: any) { return {type: SHARE_ITEMS, items}; } export const DOWNLOAD_ITEMS = 'DOWNLOAD_ITEMS'; -export function setDownloadItems(items) { +export function setDownloadItems(items: any) { return {type: DOWNLOAD_ITEMS, items}; } export const COPY_ITEMS = 'COPY_ITEMS'; -export function setCopyItem(item) { +export function setCopyItem(item: any) { return {type: COPY_ITEMS, items: [item]}; } export const PRINT_ITEMS = 'PRINT_ITEMS'; -export function setPrintItem(item) { +export function setPrintItem(item: any) { return {type: PRINT_ITEMS, items: [item]}; } @@ -412,12 +414,21 @@ export function setPrintItem(item) { * * @param {Array} items */ -export function downloadItems(items) { +export function downloadItems(items: any) { return renderModal('downloadItems', {items}); } +/** + * Personalize Home - display modal to personalize home + * + * @param {Array} items + */ +export function personalizeHome(items?: any) { + return renderModal('personalizeHome', {items}); +} + export const REMOVE_NEW_ITEMS = 'REMOVE_NEW_ITEMS'; -export function removeNewItems(data) { +export function removeNewItems(data: any) { return {type: REMOVE_NEW_ITEMS, data}; } @@ -426,8 +437,8 @@ export function removeNewItems(data) { * * @param {Object} data */ -export function pushNotification(push) { - return (dispatch, getState) => { +export function pushNotification(push: any) { + return (dispatch: any, getState: any) => { const user = getState().user; const company = getState().company; @@ -456,11 +467,11 @@ export function pushNotification(push) { }; } -export function reloadMyTopics(reloadTopic = false) { - return function(dispatch) { +export function reloadMyTopics(reloadTopic: any = false) { + return function(dispatch: any) { return loadMyTopics() - .then((data) => { - const agendaTopics = data.filter((topic) => topic.topic_type === 'agenda'); + .then((data: any) => { + const agendaTopics = data.filter((topic: any) => topic.topic_type === 'agenda'); dispatch(setTopics(agendaTopics)); if (reloadTopic) { @@ -475,8 +486,8 @@ export function reloadMyTopics(reloadTopic = false) { } export const SET_NEW_ITEM = 'SET_NEW_ITEM'; -export function setAndUpdateNewItems(data) { - return function(dispatch, getState) { +export function setAndUpdateNewItems(data: any) { + return function(dispatch: any, getState: any) { const item = data.item || {}; if (item.type !== 'agenda') { @@ -493,7 +504,7 @@ export function setAndUpdateNewItems(data) { return Promise.resolve(); } - const coveragesToCheck = (agendaItem.coverages || []).map((c) => c.coverage_id); + const coveragesToCheck = (agendaItem.coverages || []).map((c: any) => c.coverage_id); if (coveragesToCheck.includes(item.coverage_id)) { dispatch(fetchWireItemsForAgenda(agendaItem)); @@ -515,12 +526,12 @@ export function setAndUpdateNewItems(data) { } export const UPDATE_ITEM = 'UPDATE_ITEM'; -export function updateItem(item) { +export function updateItem(item: any) { return {type: UPDATE_ITEM, item: item}; } -export function toggleDropdownFilter(key, val) { - return (dispatch) => { +export function toggleDropdownFilter(key: any, val: any) { + return (dispatch: any) => { dispatch(setActive(null)); dispatch(preview(null)); @@ -536,8 +547,8 @@ export function toggleDropdownFilter(key, val) { }; } -function setLocationFilter(location) { - return (dispatch, getState) => { +function setLocationFilter(location: any) { + return (dispatch: any, getState: any) => { const state = getState(); const currentFilters = cloneDeep(searchFilterSelector(state)); const currentLocation = get(currentFilters, 'location') || {}; @@ -563,13 +574,13 @@ export function startLoading() { } export const RECIEVE_NEXT_ITEMS = 'RECIEVE_NEXT_ITEMS'; -export function recieveNextItems(data) { +export function recieveNextItems(data: any) { return {type: RECIEVE_NEXT_ITEMS, data}; } const MAX_ITEMS = 1000; // server limit export function fetchMoreItems() { - return (dispatch, getState) => { + return (dispatch: any, getState: any) => { const state = getState(); const limit = Math.min(MAX_ITEMS, state.totalItems); @@ -579,7 +590,7 @@ export function fetchMoreItems() { dispatch(startLoading()); return search(getState(), true) - .then((data) => dispatch(recieveNextItems(data))) + .then((data: any) => dispatch(recieveNextItems(data))) .catch(errorHandler); }; } @@ -589,12 +600,12 @@ export function fetchMoreItems() { * * @param {URLSearchParams} params */ -export function initParams(params) { +export function initParams(params: any): any { if (params.get('filter') || params.get('created')) { clearAgendaDropdownFilters(); } - return (dispatch, getState) => { + return (dispatch: any, getState: any) => { const featuredParam = params.get('featured'); if (featuredParam && featuredParam !== get(getState(), 'agenda.featuredOnly', false).toString()) { dispatch(toggleFeaturedFilter(false)); @@ -619,8 +630,8 @@ export function initParams(params) { * @param {String} topicId * @return {Promise} */ -export function loadMyAgendaTopic(topicId) { - return (dispatch, getState) => { +export function loadMyAgendaTopic(topicId: any) { + return (dispatch: any, getState: any) => { // Set featured query option to false when using navigations if (get(getState(), 'agenda.featuredOnly')) { dispatch({type: TOGGLE_FEATURED_FILTER}); @@ -632,8 +643,8 @@ export function loadMyAgendaTopic(topicId) { } export const TOGGLE_FEATURED_FILTER = 'TOGGLE_FEATURED_FILTER'; -export function toggleFeaturedFilter(fetch = true) { - return (dispatch) => { +export function toggleFeaturedFilter(fetch: any = true) { + return (dispatch: any) => { toggleFeaturedOnlyParam(); dispatch({type: TOGGLE_FEATURED_FILTER}); if (!fetch) { @@ -645,13 +656,13 @@ export function toggleFeaturedFilter(fetch = true) { } export const SET_ITEM_TYPE_FILTER = 'SET_ITEM_TYPE_FILTER'; -export function setItemTypeFilter(value) { +export function setItemTypeFilter(value: any) { return {type: SET_ITEM_TYPE_FILTER, value}; } export const WATCH_COVERAGE = 'WATCH_COVERAGE'; -export function watchCoverage(coverage, item) { - return (dispatch) => { +export function watchCoverage(coverage: any, item: any) { + return (dispatch: any) => { server.post(WATCH_COVERAGE_URL, { coverage_id: coverage.coverage_id, item_id: item._id @@ -663,13 +674,13 @@ export function watchCoverage(coverage, item) { item }); notify.success(gettext('Started watching coverage successfully.')); - }, (error) => { errorHandler(error, dispatch);}); + }, (error: any) => { errorHandler(error, dispatch);}); }; } export const STOP_WATCHING_COVERAGE = 'STOP_WATCHING_COVERAGE'; -export function stopWatchingCoverage(coverage, item) { - return (dispatch, getState) => { +export function stopWatchingCoverage(coverage: any, item: any) { + return (dispatch: any, getState: any) => { server.del(WATCH_COVERAGE_URL, { coverage_id: coverage.coverage_id, item_id: item._id @@ -685,18 +696,18 @@ export function stopWatchingCoverage(coverage, item) { if (getState().bookmarks) { dispatch(fetchItems()); // item should get removed from the list in bookmarks view } - }, (error) => { errorHandler(error, dispatch);}); + }, (error: any) => { errorHandler(error, dispatch);}); }; } -export function errorHandler(error, dispatch, setError) { +export function errorHandler(error: any, dispatch?: any, setError?: any) { console.error('error', error); if (setError) { if (error.response && error.response.status === 403) { dispatch(setErrorMessage(gettext( 'There is no product associated with your user. Please reach out to your Company Admin'))); } else { - error.response.json().then(function(data) { + error.response.json().then(function(data: any) { dispatch(setError(data)); }); } diff --git a/assets/agenda/components/AgendaApp.jsx b/assets/agenda/components/AgendaApp.tsx similarity index 93% rename from assets/agenda/components/AgendaApp.jsx rename to assets/agenda/components/AgendaApp.tsx index 4538f155b..bca3f122d 100644 --- a/assets/agenda/components/AgendaApp.jsx +++ b/assets/agenda/components/AgendaApp.tsx @@ -62,7 +62,12 @@ const modals = { }; class AgendaApp extends BaseApp { - constructor(props) { + static propTypes: any; + + modals: any; + tabs: any; + + constructor(props: any) { super(props); this.modals = modals; @@ -70,7 +75,7 @@ class AgendaApp extends BaseApp { } getTabs() { - return this.props.featuredOnly ? this.tabs.filter((t) => t.id !== 'filters') : this.tabs; + return this.props.featuredOnly ? this.tabs.filter((t: any) => t.id !== 'filters') : this.tabs; } @@ -98,14 +103,14 @@ class AgendaApp extends BaseApp { const modal = this.renderModal(this.props.modal); const showDatePicker = isEmpty(this.props.createdFilter.from) && isEmpty(this.props.createdFilter.to) && !this.props.bookmarks; - const panesCount = [this.state.withSidebar, this.props.itemToPreview].filter((x) => x).length; + const panesCount = [this.state.withSidebar, this.props.itemToPreview].filter((x: any) => x).length; const mainClassName = classNames('wire-column__main', { 'wire-articles__one-side-pane': panesCount === 1, 'wire-articles__two-side-panes': panesCount === 2, }); const onDetailClose = this.props.detail ? null : - () => this.props.actions.filter(a => a.id === 'open')[0].action(null, this.props.previewGroup, this.props.previewPlan); + () => this.props.actions.filter((a: any) => a.id === 'open')[0].action(null, this.props.previewGroup, this.props.previewPlan); const eventsOnly = this.props.itemTypeFilter === 'events' || this.props.eventsOnlyAccess; const hideFeaturedToggle = !noNavigationSelected(this.props.activeNavigation) || this.props.bookmarks || @@ -113,12 +118,12 @@ class AgendaApp extends BaseApp { eventsOnly; const numNavigations = get(this.props, 'searchParams.navigation.length', 0); - let showSaveTopic = this.props.showSaveTopic && + const showSaveTopic = this.props.showSaveTopic && !this.props.bookmarks && !this.props.featuredOnly; let showTotalItems = false; let showTotalLabel = false; - let totalItemsLabel; + let totalItemsLabel: any; if (get(this.props, 'activeTopic.label')) { totalItemsLabel = this.props.activeTopic.label; @@ -155,7 +160,7 @@ class AgendaApp extends BaseApp { restrictCoverageInfo={this.props.restrictCoverageInfo} />] : [
-

{gettext('{{agenda}} Content', sectionNames)}

+

{gettext('{{agenda}} Content', window.sectionNames)}

@@ -286,7 +291,7 @@ class AgendaApp extends BaseApp { />
- ]).concat([ + ] as any).concat([ modal, this.renderNavBreadcrumb( this.props.navigations, @@ -353,7 +358,7 @@ AgendaApp.propTypes = { groups: PropTypes.array, }; -const mapStateToProps = (state) => ({ +const mapStateToProps = (state: any) => ({ state: state, isLoading: state.isLoading, totalItems: state.totalItems, @@ -398,26 +403,28 @@ const mapStateToProps = (state) => ({ errorMessage:state.errorMessage }); -const mapDispatchToProps = (dispatch) => ({ +const mapDispatchToProps = (dispatch: any) => ({ fetchItems: () => dispatch(fetchItems()), actions: getAgendaItemActions(dispatch), coverageActions: getCoverageItemActions(dispatch), fetchMoreItems: () => dispatch(fetchMoreItems()), - setView: (view) => dispatch(setView(view)), + setView: (view: any) => dispatch(setView(view)), closePreview: () => dispatch(previewItem(null)), - toggleDropdownFilter: (field, value) => { + toggleDropdownFilter: (field: any, value: any) => { setAgendaDropdownFilter(field, value); dispatch(toggleDropdownFilter(field, value)); }, - selectDate: (dateString, grouping) => { + selectDate: (dateString: any, grouping: any) => { dispatch(selectDate(dateString, grouping)); setActiveDate(dateString); dispatch(fetchItems()); }, - openItemDetails: (item) => dispatch(openItemDetails(item)), - requestCoverage: (item, message) => dispatch(requestCoverage(item, message)), - toggleFeaturedFilter: (fetch) => dispatch(toggleFeaturedFilter(fetch)), - setQuery: (query) => dispatch(setQuery(query)), + openItemDetails: (item: any) => dispatch(openItemDetails(item)), + requestCoverage: (item: any, message: any) => dispatch(requestCoverage(item, message)), + toggleFeaturedFilter: (fetch: any) => dispatch(toggleFeaturedFilter(fetch)), + setQuery: (query: any) => dispatch(setQuery(query)), }); -export default connect(mapStateToProps, mapDispatchToProps)(AgendaApp); +const component: React.ComponentType = connect(mapStateToProps, mapDispatchToProps)(AgendaApp); + +export default component; diff --git a/assets/agenda/components/AgendaAttachments.jsx b/assets/agenda/components/AgendaAttachments.tsx similarity index 92% rename from assets/agenda/components/AgendaAttachments.jsx rename to assets/agenda/components/AgendaAttachments.tsx index 48d4888f1..0f2044c54 100644 --- a/assets/agenda/components/AgendaAttachments.jsx +++ b/assets/agenda/components/AgendaAttachments.tsx @@ -8,7 +8,7 @@ import {bem} from 'ui/utils'; const kB = 1024; const MB = kB * kB; -function filesize (size) { +function filesize (size: any) { if (size > MB) { return (size / MB).toFixed(1) + ' MB'; } else if (size > kB) { @@ -18,12 +18,12 @@ function filesize (size) { } } -export default function AgendaAttachments({item}) { +export default function AgendaAttachments({item}: any) { if (!hasAttachments(item)) { return null; } - return getAttachments(item).map((attachment) => ( + return getAttachments(item).map((attachment: any) => (
diff --git a/assets/agenda/components/AgendaCalendarAgendaFilter.jsx b/assets/agenda/components/AgendaCalendarAgendaFilter.tsx similarity index 95% rename from assets/agenda/components/AgendaCalendarAgendaFilter.jsx rename to assets/agenda/components/AgendaCalendarAgendaFilter.tsx index 2cb602d94..bda053cdf 100644 --- a/assets/agenda/components/AgendaCalendarAgendaFilter.jsx +++ b/assets/agenda/components/AgendaCalendarAgendaFilter.tsx @@ -7,7 +7,8 @@ import {processBuckets} from '../../components/DropdownFilter'; import {getDropdownItems} from './AgendaFilters'; -export class AgendaCalendarAgendaFilter extends React.PureComponent { +export class AgendaCalendarAgendaFilter extends React.PureComponent { + static propTypes: any; render() { const isActive = !!(this.props.activeFilter.calendar) || !!(this.props.activeFilter.agendas); const calendarFilter = { @@ -15,7 +16,7 @@ export class AgendaCalendarAgendaFilter extends React.PureComponent { field: 'calendar', icon: 'icon-small--calendar', itemTypes: ['events', 'combined'], - isItemActive: (key) => ( + isItemActive: (key: any) => ( (this.props.activeFilter.calendar || []).includes(key) ), }; @@ -25,7 +26,7 @@ export class AgendaCalendarAgendaFilter extends React.PureComponent { nestedField: 'agenda', icon: 'icon-small--calendar', itemTypes: ['planning', 'combined'], - isItemActive: (key) => ( + isItemActive: (key: any) => ( (this.props.activeFilter.agendas || []).includes(key) ), }; diff --git a/assets/agenda/components/AgendaCoverageExistsFilter.jsx b/assets/agenda/components/AgendaCoverageExistsFilter.tsx similarity index 95% rename from assets/agenda/components/AgendaCoverageExistsFilter.jsx rename to assets/agenda/components/AgendaCoverageExistsFilter.tsx index 41ef97de9..4179d965b 100644 --- a/assets/agenda/components/AgendaCoverageExistsFilter.jsx +++ b/assets/agenda/components/AgendaCoverageExistsFilter.tsx @@ -17,7 +17,7 @@ const FILTER_VALUES = { NOT_PLANNED: 'not planned', }; -function getActiveFilterLabel(filter, activeFilter) { +function getActiveFilterLabel(filter: any, activeFilter: any) { const filterValue = get(activeFilter, `${filter.field}[0]`); switch (filterValue) { @@ -30,7 +30,7 @@ function getActiveFilterLabel(filter, activeFilter) { return filter.label; } -function AgendaCoverageExistsFilter ({toggleFilter, activeFilter}) { +function AgendaCoverageExistsFilter ({toggleFilter, activeFilter}: any) { return ( { + static propTypes: any; + constructor(props: any, context: any) { super(props, context); this.onRequestClick = this.onRequestClick.bind(this); this.reset = this.reset.bind(this); @@ -23,7 +24,7 @@ class AgendaCoverageRequest extends React.Component { this.setState({opened: false, message: '', status: null}); } - onMessageChange(event) { + onMessageChange(event: any) { this.setState({message: event.target.value, status: null}); } diff --git a/assets/agenda/components/AgendaCoverages.jsx b/assets/agenda/components/AgendaCoverages.tsx similarity index 85% rename from assets/agenda/components/AgendaCoverages.jsx rename to assets/agenda/components/AgendaCoverages.tsx index 3bae66124..add31bdaa 100644 --- a/assets/agenda/components/AgendaCoverages.jsx +++ b/assets/agenda/components/AgendaCoverages.tsx @@ -18,25 +18,25 @@ import { import {agendaContentLinkTarget} from 'ui/selectors'; -function AgendaCoveragesComponent({item, coverages, wireItems, actions, user, onClick, hideViewContentItems, contentLinkTarget}) { +function AgendaCoveragesComponent({item, coverages, wireItems, actions, user, onClick, hideViewContentItems, contentLinkTarget}: any) { if (isEmpty(coverages)) { return null; } - const getSlugline = (coverage) => { + const getSlugline = (coverage: any) => { const slugline = coverage.item_slugline || coverage.slugline; return slugline ? ` | ${slugline}` : ''; }; - const coveragesWithoutState = coverages.filter((coverage) => WORKFLOW_COLORS[coverage.workflow_status] == null); - const coveragesWithState = coverages.filter((coverage) => WORKFLOW_COLORS[coverage.workflow_status] != null); + const coveragesWithoutState = coverages.filter((coverage: any) => WORKFLOW_COLORS[coverage.workflow_status] == null); + const coveragesWithState = coverages.filter((coverage: any) => WORKFLOW_COLORS[coverage.workflow_status] != null); return ( {!coveragesWithoutState.length ? null : (
- {coveragesWithoutState.map((coverage) => ( + {coveragesWithoutState.map((coverage: any) => ( )} - {coveragesWithState.map((coverage) => ( + {coveragesWithState.map((coverage: any) => (
({ +const mapStateToProps = (state: any) => ({ contentLinkTarget: agendaContentLinkTarget(state), }); -const AgendaCoverages = connect(mapStateToProps)(AgendaCoveragesComponent); +const AgendaCoverages: React.ComponentType = connect(mapStateToProps)(AgendaCoveragesComponent); export default AgendaCoverages; diff --git a/assets/agenda/components/AgendaDateButtons.jsx b/assets/agenda/components/AgendaDateButtons.tsx similarity index 95% rename from assets/agenda/components/AgendaDateButtons.jsx rename to assets/agenda/components/AgendaDateButtons.tsx index 61fda9d4f..277f62760 100644 --- a/assets/agenda/components/AgendaDateButtons.jsx +++ b/assets/agenda/components/AgendaDateButtons.tsx @@ -5,7 +5,7 @@ import {gettext} from 'utils'; import {formatNavigationDate, getNext, getPrevious} from '../utils'; -function AgendaDateButtons({selectDate, activeDate, activeGrouping}) { +function AgendaDateButtons({selectDate, activeDate, activeGrouping}: any): any { return ([{formatNavigationDate(activeDate, activeGrouping)}, ); -function AgendaDropdownFilter({aggregations, filter, toggleFilter, activeFilter, getDropdownItems}) { +function AgendaDropdownFilter({aggregations, filter, toggleFilter, activeFilter, getDropdownItems}: any) { return ( { + const fixText = (text: any) => { // remove prefixes added by planning // which are always in english const POSTPONED = 0; const RESCHEDULED = 1; const CANCELLED = 2; - const prefixes = []; + const prefixes: Array = []; prefixes[POSTPONED] = 'Event Postponed: '; prefixes[RESCHEDULED] = 'Event Rescheduled: '; prefixes[CANCELLED] = 'Event Cancelled: '; - const index = prefixes.findIndex((_prefix) => text.startsWith(_prefix)); + const index = prefixes.findIndex((_prefix: any) => text.startsWith(_prefix)); if (index !== -1) { const reason = text.substr(prefixes[index].length); @@ -58,7 +58,7 @@ export default function AgendaEdNote({item, plan, secondaryNoteField, noMargin}) return text; }; - const getMultiLineNote = (note) => (note && fixText(note).split('\n').map((t, key) => + const getMultiLineNote = (note: any) => (note && fixText(note).split('\n').map((t: any, key: any) => {t}
) ); diff --git a/assets/agenda/components/AgendaEventInfo.jsx b/assets/agenda/components/AgendaEventInfo.tsx similarity index 92% rename from assets/agenda/components/AgendaEventInfo.jsx rename to assets/agenda/components/AgendaEventInfo.tsx index c7a607b29..ed7f1eb42 100644 --- a/assets/agenda/components/AgendaEventInfo.jsx +++ b/assets/agenda/components/AgendaEventInfo.tsx @@ -8,7 +8,7 @@ import AgendaName from './AgendaName'; import AgendaMetaTime from './AgendaMetaTime'; import AgendaLocation from'./AgendaLocation'; -const AgendaEventInfo = ({item, onClick}) => { +const AgendaEventInfo = ({item, onClick}: any) => { const event = get(item, 'event'); if (isEmpty(event)) { return null; @@ -16,7 +16,7 @@ const AgendaEventInfo = ({item, onClick}) => { return (
+ title={onClick ? gettext('Open {{agenda}} in new tab', window.sectionNames) : onClick}>
diff --git a/assets/agenda/components/AgendaFeaturedStoriesToogle.jsx b/assets/agenda/components/AgendaFeaturedStoriesToogle.tsx similarity index 99% rename from assets/agenda/components/AgendaFeaturedStoriesToogle.jsx rename to assets/agenda/components/AgendaFeaturedStoriesToogle.tsx index 9551cf981..f8e2f7025 100644 --- a/assets/agenda/components/AgendaFeaturedStoriesToogle.jsx +++ b/assets/agenda/components/AgendaFeaturedStoriesToogle.tsx @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import Toggle from 'react-toggle'; import {gettext} from 'utils'; -function AgendaFeaturedStoriesToogle ({featuredFilter, onChange}) { +function AgendaFeaturedStoriesToogle ({featuredFilter, onChange}: any) { return (
diff --git a/assets/agenda/components/AgendaFilterButton.jsx b/assets/agenda/components/AgendaFilterButton.tsx similarity index 91% rename from assets/agenda/components/AgendaFilterButton.jsx rename to assets/agenda/components/AgendaFilterButton.tsx index 2b7afeb29..0b2829c7e 100644 --- a/assets/agenda/components/AgendaFilterButton.jsx +++ b/assets/agenda/components/AgendaFilterButton.tsx @@ -3,11 +3,11 @@ import PropTypes from 'prop-types'; import {gettext} from 'utils'; import classNames from 'classnames'; -const getActiveFilterLabel = (filter, activeFilter, isActive) => { +const getActiveFilterLabel = (filter: any, activeFilter: any, isActive: any) => { return isActive ? gettext(activeFilter[filter.field][0]) : gettext(filter.label); }; -function AgendaFilterButton({filter, activeFilter, autoToggle, onClick, getFilterLabel}) { +function AgendaFilterButton({filter, activeFilter, autoToggle, onClick, getFilterLabel}: any) { const filterLabel = getFilterLabel ? getFilterLabel : getActiveFilterLabel; const isActive = activeFilter[filter.field]; return (
@@ -145,10 +144,10 @@ AgendaFiltersComponent.propTypes = { filtersConfig: PropTypes.arrayOf(PropTypes.string), }; -const mapStateToProps = (state) => ({ +const mapStateToProps = (state: any) => ({ filtersConfig: agendaFiltersConfigSelector(state), }); -const AgendaFilters = connect(mapStateToProps)(AgendaFiltersComponent); +const AgendaFilters: React.ComponentType = connect(mapStateToProps)(AgendaFiltersComponent); export default AgendaFilters; diff --git a/assets/agenda/components/AgendaInternalNote.jsx b/assets/agenda/components/AgendaInternalNote.tsx similarity index 95% rename from assets/agenda/components/AgendaInternalNote.jsx rename to assets/agenda/components/AgendaInternalNote.tsx index abcb90690..92a0592c5 100644 --- a/assets/agenda/components/AgendaInternalNote.jsx +++ b/assets/agenda/components/AgendaInternalNote.tsx @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import classNames from 'classnames'; import {gettext, getPlainTextMemoized} from 'utils'; -export default function AgendaInternalNote({internalNote, onlyIcon, noMargin, mt2, alignCenter, marginRightAuto, borderRight, noPaddingRight}) { +export default function AgendaInternalNote({internalNote, onlyIcon, noMargin, mt2, alignCenter, marginRightAuto, borderRight, noPaddingRight}: any) { const note = getPlainTextMemoized(internalNote); if (!note) { @@ -39,7 +39,7 @@ export default function AgendaInternalNote({internalNote, onlyIcon, noMargin, mt {internalNote[0] !== '<' ? - internalNote.split('\n').map((item, key) => ( + internalNote.split('\n').map((item: any, key: any) => (

{item}

)) : ( diff --git a/assets/agenda/components/AgendaItemDetails.jsx b/assets/agenda/components/AgendaItemDetails.tsx similarity index 95% rename from assets/agenda/components/AgendaItemDetails.jsx rename to assets/agenda/components/AgendaItemDetails.tsx index 5c697e1d3..685c1c1f3 100644 --- a/assets/agenda/components/AgendaItemDetails.jsx +++ b/assets/agenda/components/AgendaItemDetails.tsx @@ -30,8 +30,7 @@ import {AgendaPreviewPlanning} from './AgendaPreviewPlanning'; import {AgendaPreviewEvent} from './AgendaPreviewEvent'; import {AgendaRegistrationInvitationDetails} from './AgendaRegistrationInvitationDetails'; - -export default function AgendaItemDetails( +function AgendaItemDetails( { item, user, @@ -45,7 +44,7 @@ export default function AgendaItemDetails( coverageActions, detailsConfig, restrictCoverageInfo, - }) + }: any) { const locations = getLocations(item); let map = null; @@ -60,7 +59,7 @@ export default function AgendaItemDetails( map = ; } - const plan = (get(item, 'planning_items') || []).find((p) => p.guid === planningId); + const plan = (get(item, 'planning_items') || []).find((p: any) => p.guid === planningId); const internalNotes = getInternalNote(item, plan); return ( @@ -133,3 +132,7 @@ AgendaItemDetails.propTypes = { detailsConfig: PropTypes.object, restrictCoverageInfo: PropTypes.bool, }; + +const component: React.ComponentType = AgendaItemDetails; + +export default component; \ No newline at end of file diff --git a/assets/agenda/components/AgendaItemTimeUpdater.jsx b/assets/agenda/components/AgendaItemTimeUpdater.tsx similarity index 89% rename from assets/agenda/components/AgendaItemTimeUpdater.jsx rename to assets/agenda/components/AgendaItemTimeUpdater.tsx index ac63bcb9b..1fd28007a 100644 --- a/assets/agenda/components/AgendaItemTimeUpdater.jsx +++ b/assets/agenda/components/AgendaItemTimeUpdater.tsx @@ -7,8 +7,14 @@ import classNames from 'classnames'; import {bem} from 'ui/utils'; import {gettext} from 'utils'; -class AgendaItemTimeUpdater extends React.Component { - constructor(props) { +class AgendaItemTimeUpdater extends React.Component { + static propTypes: any; + static defaultProps: any; + + interval: number; + timerIntervalId: any; + + constructor(props: any) { super(props); this.state = {timeText: ''}; @@ -22,7 +28,7 @@ class AgendaItemTimeUpdater extends React.Component { this.activateTimer(this.props.item); } - componentWillReceiveProps(nextProps) { + componentWillReceiveProps(nextProps: any) { if (get(this.props, 'item._created') !== get(nextProps, 'item._created') || get(this.props, 'item._updated') !== get(nextProps, 'item._updated')) { this.activateTimer(nextProps.item); @@ -33,7 +39,7 @@ class AgendaItemTimeUpdater extends React.Component { this.deactivateTimer(); } - activateTimer(item) { + activateTimer(item: any) { // Deactivate if a timer already exits this.deactivateTimer(); @@ -55,13 +61,13 @@ class AgendaItemTimeUpdater extends React.Component { } } - isItemPastTime(item) { + isItemPastTime(item: any) { // Check if the updated (and created) time is past the interval duration return item && (moment().diff(moment(item._created), 'minutes') >= this.interval && moment().diff(moment(item._updated), 'minutes') >= this.interval); } - updateState(item, checkPastTime = true) { + updateState(item: any, checkPastTime: any = true) { if (checkPastTime && this.isItemPastTime(item)) { this.deactivateTimer(); return; diff --git a/assets/agenda/components/AgendaItemTypeFilter.jsx b/assets/agenda/components/AgendaItemTypeFilter.tsx similarity index 95% rename from assets/agenda/components/AgendaItemTypeFilter.jsx rename to assets/agenda/components/AgendaItemTypeFilter.tsx index dbf81646c..88b93f700 100644 --- a/assets/agenda/components/AgendaItemTypeFilter.jsx +++ b/assets/agenda/components/AgendaItemTypeFilter.tsx @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import {gettext} from 'utils'; import {AgendaDropdown} from './AgendaDropdown'; -function AgendaItemTypeFilter ({toggleFilter, itemTypeFilter, eventsOnlyAccess, restrictCoverageInfo}) { +function AgendaItemTypeFilter ({toggleFilter, itemTypeFilter, eventsOnlyAccess, restrictCoverageInfo}: any) { if (eventsOnlyAccess) { return null; } @@ -16,7 +16,7 @@ function AgendaItemTypeFilter ({toggleFilter, itemTypeFilter, eventsOnlyAccess, ] }; - const filter = { + const filter: any = { label: gettext('Events & Coverages'), field: 'itemType', icon: 'icon-small--coverage-infographics' diff --git a/assets/agenda/components/AgendaList.jsx b/assets/agenda/components/AgendaList.tsx similarity index 89% rename from assets/agenda/components/AgendaList.jsx rename to assets/agenda/components/AgendaList.tsx index 74fafbbe3..007bf3556 100644 --- a/assets/agenda/components/AgendaList.jsx +++ b/assets/agenda/components/AgendaList.tsx @@ -19,21 +19,21 @@ import {AGENDA_DATE_FORMAT_LONG, AGENDA_DATE_FORMAT_SHORT} from '../../utils'; const PREVIEW_TIMEOUT = 500; // time to preview an item after selecting using kb const CLICK_TIMEOUT = 200; // time when we wait for double click after click -const EMPTY_ARRAY = []; +const EMPTY_ARRAY: Array = []; const EMPTY_OBJECT = {}; -const itemIdsSelector = (state) => state.items || EMPTY_ARRAY; -const itemsByIdSelector = (state) => state.itemsById || EMPTY_OBJECT; +const itemIdsSelector = (state: any) => state.items || EMPTY_ARRAY; +const itemsByIdSelector = (state: any) => state.itemsById || EMPTY_OBJECT; const itemsSelector = createSelector( [itemIdsSelector, itemsByIdSelector], - (ids, items) => ids.map((itemId) => items[itemId]) + (ids, items) => ids.map((itemId: any) => items[itemId]) ); -const activeDateSelector = (state) => get(state, 'agenda.activeDate'); -const activeGroupingSelector = (state) => get(state, 'agenda.activeGrouping'); -const featuredOnlySelector = (state) => get(state, 'agenda.featuredOnly', false); +const activeDateSelector = (state: any) => get(state, 'agenda.activeDate'); +const activeGroupingSelector = (state: any) => get(state, 'agenda.activeGrouping'); +const featuredOnlySelector = (state: any) => get(state, 'agenda.featuredOnly', false); const groupedItemsSelector = createSelector( [itemsSelector, activeDateSelector, activeGroupingSelector, featuredOnlySelector], @@ -50,8 +50,13 @@ const listItemsSelector = createSelector( ); -class AgendaList extends React.Component { - constructor(props) { +class AgendaList extends React.Component { + static propTypes: any; + previewTimeout: any; + clickTimeout: any; + elem: any; + + constructor(props: any) { super(props); this.state = {actioningItem: null}; @@ -65,7 +70,7 @@ class AgendaList extends React.Component { this.isActiveItem = this.isActiveItem.bind(this); } - onKeyDown(event) { + onKeyDown(event: any) { let diff = 0; switch (event.key) { case 'ArrowDown': @@ -91,7 +96,7 @@ class AgendaList extends React.Component { event.preventDefault(); const activeItem = this.props.activeItem; - const activeIndex = this.props.activeItem ? this.props.listItems.findIndex((item) => { + const activeIndex = this.props.activeItem ? this.props.listItems.findIndex((item: any) => { return item._id === activeItem._id && item.group === activeItem.group && get(item, 'plan._id') === get(activeItem, 'plan._id'); @@ -133,7 +138,7 @@ class AgendaList extends React.Component { } } - onItemClick(item, group, plan) { + onItemClick(item: any, group: any, plan: any) { const itemId = item._id; this.setState({actioningItem: null}); this.cancelPreviewTimeout(); @@ -156,13 +161,13 @@ class AgendaList extends React.Component { this.setState({actioningItem: null}); } - onItemDoubleClick(item, group, plan) { + onItemDoubleClick(item: any, group: any, plan: any) { this.cancelClickTimeout(); this.props.dispatch(setActive({_id: item._id, group: group, plan: plan})); this.props.dispatch(openItem(item, group, plan)); } - onActionList(event, item, group, plan) { + onActionList(event: any, item: any, group: any, plan: any) { event.stopPropagation(); if (this.state.actioningItem && this.state.actioningItem._id === item._id && (!this.state.activePlan || (this.state.activePlan && this.state.activePlan.guid === get(plan, 'guid')))) { @@ -172,12 +177,12 @@ class AgendaList extends React.Component { } } - filterActions(item, config) { - return this.props.actions.filter((action) => (!config || isDisplayed(action.id, config)) && + filterActions(item: any, config: any) { + return this.props.actions.filter((action: any) => (!config || isDisplayed(action.id, config)) && (!action.when || action.when(this.props, item))); } - isActiveItem(_id, group, plan) { + isActiveItem(_id: any, group: any, plan: any) { const {activeItem} = this.props; if (!activeItem || (!_id && !group && !plan)) { @@ -195,7 +200,7 @@ class AgendaList extends React.Component { return _id === activeItem._id; } - componentDidUpdate(nextProps) { + componentDidUpdate(nextProps: any) { if (!isEqual(nextProps.activeDate, this.props.activeDate) || !isEqual(nextProps.activeNavigation, this.props.activeNavigation) || (!nextProps.searchInitiated && this.props.searchInitiated)) { @@ -203,7 +208,7 @@ class AgendaList extends React.Component { } } - getListGroupDate(group) { + getListGroupDate(group: any) { if (get(group, 'date')) { const groupDate = moment(group.date, DATE_FORMAT); const today = moment(); @@ -233,21 +238,21 @@ class AgendaList extends React.Component { } = this.props; const isExtended = activeView === EXTENDED_VIEW; const showShortcutActionIcons = shouldShowListShortcutActionIcons(this.props.listConfig, isExtended); - const articleGroups = groupedItems.map((group) => + const articleGroups = groupedItems.map((group: any) => [
{this.getListGroupDate(group)}
,
- {group.items.map((_id, groupIndex) => { + {group.items.map((_id: any, groupIndex: any) => { const plans = getPlanningItemsByGroup(itemsById[_id], group.date); if (plans.length > 0) { return ( { - plans.map((plan) => + plans.map((plan: any) => { + ref={(elem: any) => { if (elem) { refNode(elem); this.elem = elem; @@ -359,7 +364,7 @@ AgendaList.propTypes = { listConfig: PropTypes.object, }; -const mapStateToProps = (state) => ({ +const mapStateToProps = (state: any) => ({ itemsById: state.itemsById, activeItem: state.activeItem, previewItem: state.previewItem, @@ -382,4 +387,6 @@ const mapStateToProps = (state) => ({ listConfig: listConfigSelector(state), }); -export default connect(mapStateToProps)(AgendaList); +const component: React.ComponentType = connect(mapStateToProps)(AgendaList); + +export default component; diff --git a/assets/agenda/components/AgendaListCoverageItem.jsx b/assets/agenda/components/AgendaListCoverageItem.tsx similarity index 88% rename from assets/agenda/components/AgendaListCoverageItem.jsx rename to assets/agenda/components/AgendaListCoverageItem.tsx index 528000e90..a44adb354 100644 --- a/assets/agenda/components/AgendaListCoverageItem.jsx +++ b/assets/agenda/components/AgendaListCoverageItem.tsx @@ -13,32 +13,33 @@ import { WORKFLOW_COLORS, } from '../utils'; -class AgendaListCoverageItem extends React.Component { - constructor(props) { +class AgendaListCoverageItem extends React.Component { + static propTypes: any; + constructor(props: any) { super(props); this.state = this.getUpdatedState(props); } - shouldComponentUpdate(nextProps) { + shouldComponentUpdate(nextProps: any) { return !isEqual(this.props.coverage, nextProps.coverage); } - componentWillReceiveProps(nextProps) { + componentWillReceiveProps(nextProps: any) { if (!isEqual(this.props.coverage, nextProps.coverage)) { this.setState(this.getUpdatedState(nextProps)); } } - getUpdatedState(props) { + getUpdatedState(props: any) { const watched = isWatched(props.coverage, props.user); - const state = { + const state: any = { coverageClass: `icon--coverage-${getCoverageIcon(props.coverage.coverage_type)}`, beingUpdated: isCoverageBeingUpdated(props.coverage), isWatched: watched, watchText: watched ? gettext('(Watching)') : '', - isCoverageForExtraDay: isCoverageForExtraDay(props.coverage, props.group), + isCoverageForExtraDay: isCoverageForExtraDay(props.coverage), }; state.tooltip = `${state.watchText} ${getCoverageTooltip(props.coverage, state.beingUpdated)}`; diff --git a/assets/agenda/components/AgendaListItem.jsx b/assets/agenda/components/AgendaListItem.tsx similarity index 94% rename from assets/agenda/components/AgendaListItem.jsx rename to assets/agenda/components/AgendaListItem.tsx index 36bc5a02b..4fd628780 100644 --- a/assets/agenda/components/AgendaListItem.jsx +++ b/assets/agenda/components/AgendaListItem.tsx @@ -25,8 +25,14 @@ import { import ActionMenu from '../../components/ActionMenu'; import {LIST_ANIMATIONS, isMobilePhone} from 'utils'; -class AgendaListItem extends React.Component { - constructor(props) { +class AgendaListItem extends React.Component { + static propTypes: any; + + slugline: any; + state: any; + dom: any; + + constructor(props: any) { super(props); this.slugline = props.item.slugline && props.item.slugline.trim(); @@ -40,7 +46,7 @@ class AgendaListItem extends React.Component { this.onMouseEnter = this.onMouseEnter.bind(this); } - onKeyDown(event) { + onKeyDown(event: any) { switch (event.key) { case ' ': // on space toggle selected item this.props.toggleSelected(); @@ -53,7 +59,7 @@ class AgendaListItem extends React.Component { event.preventDefault(); } - setArticleRef(elem) { + setArticleRef(elem: any) { this.dom.article = elem; } @@ -71,7 +77,7 @@ class AgendaListItem extends React.Component { } } - shouldComponentUpdate(nextProps, nextState) { + shouldComponentUpdate(nextProps: any, nextState: any) { const {props, state} = this; return props.item._etag !== nextProps.item._etag || state.hover !== nextState.hover || @@ -92,11 +98,11 @@ class AgendaListItem extends React.Component { } } - stopPropagation(event) { + stopPropagation(event: any) { event.stopPropagation(); } - getClassNames(isExtended) { + getClassNames(isExtended: any) { return { card: classNames('wire-articles__item-wrap col-12 agenda-item'), wrap: classNames('wire-articles__item wire-articles__item--list', { @@ -120,10 +126,10 @@ class AgendaListItem extends React.Component { }; } - renderListItem(isMobile, children) { + renderListItem(isMobile: any, children: any) { const {item, isExtended, group, planningId, listConfig} = this.props; const classes = this.getClassNames(isExtended); - const planningItem = (get(item, 'planning_items') || []).find((p) => p.guid === planningId) || {}; + const planningItem = (get(item, 'planning_items') || []).find((p: any) => p.guid === planningId) || {}; const description =item.es_highlight ? getHighlightedDescription(item, planningItem) : getDescription(item,planningItem); // Show headline for adhoc planning items const showHeadline = !item.event && get(item, 'headline.length', 0) > 0; @@ -137,7 +143,7 @@ class AgendaListItem extends React.Component { onMouseEnter={this.onMouseEnter} onKeyDown={this.onKeyDown} > -
+

@@ -191,7 +197,7 @@ class AgendaListItem extends React.Component { renderNonMobile() { const {item, planningId} = this.props; - const planningItem = (get(item, 'planning_items') || []).find((p) => p.guid === planningId) || {}; + const planningItem = (get(item, 'planning_items') || []).find((p: any) => p.guid === planningId) || {}; return this.renderListItem(false, !this.props.actions.length ? null : (
@@ -206,7 +212,7 @@ class AgendaListItem extends React.Component { showShortcutActions={!this.props.showShortcutActionIcons} /> - {!this.props.showShortcutActionIcons ? null : this.props.actions.map((action) => action.shortcut && ( + {!this.props.showShortcutActionIcons ? null : this.props.actions.map((action: any) => action.shortcut && ( p.guid === planningId) || {}; + const planningItem = (get(item, 'planning_items') || []).find((p: any) => p.guid === planningId) || {}; const internalNote = getInternalNote(item, planningItem); return this.renderListItem(true, ( @@ -236,7 +242,7 @@ class AgendaListItem extends React.Component { noPaddingRight={true} /> - {!this.props.showShortcutActionIcons ? null : this.props.actions.map((action) => action.shortcut && ( + {!this.props.showShortcutActionIcons ? null : this.props.actions.map((action: any) => action.shortcut && ( ( + (props.item.coverages || []).filter((c: any) => ( !props.group || c.scheduled == null || - (isCoverageForExtraDay(c, props.group) && c.planning_id === get(props, 'planningItem.guid')) + (isCoverageForExtraDay(c) && c.planning_id === get(props, 'planningItem.guid')) )), attachments: (getAttachments(props.item)).length, isRecurring: isRecurring(props.item), }; } - getItemByschema(props) { + getItemByschema(props: any) { const {listConfig} = props; if ((listConfig || {}).subject != null) { const scheme = listConfig.subject.scheme; const subjects = getSubjects(props.item); return scheme == null ? subjects : - subjects.filter((item) => Array.isArray(scheme) ? + subjects.filter((item: any) => Array.isArray(scheme) ? scheme.includes(item.scheme) : item.scheme === scheme ); @@ -73,8 +76,8 @@ class AgendaListItemIcons extends React.Component { } render() { - const props = this.props; - const state = this.state; + const props: any = this.props; + const state: any = this.state; const className = bem('wire-articles', 'item__meta', {row: props.row}); const subject = this.getItemByschema(props); @@ -90,7 +93,7 @@ class AgendaListItemIcons extends React.Component { {state.coveragesToDisplay.length > 0 && (
- {state.coveragesToDisplay.map((coverage, index) => ( + {state.coveragesToDisplay.map((coverage: any, index: any) => ( - {subject.map((item) => ( + {subject.map((item: any) => ( { - let labelText; - let labelColor; + let labelText: any; + let labelColor: any; if (isPostponed(item)) { labelText = gettext('postponed'); labelColor = 'label--blue'; @@ -35,7 +35,7 @@ function AgendaListItemLabels({item, right}) { } return (
{labelText}
); - + }; return getLabel(); diff --git a/assets/agenda/components/AgendaListViewControls.jsx b/assets/agenda/components/AgendaListViewControls.tsx similarity index 97% rename from assets/agenda/components/AgendaListViewControls.jsx rename to assets/agenda/components/AgendaListViewControls.tsx index 9e9f5c8ab..309d7c8ba 100644 --- a/assets/agenda/components/AgendaListViewControls.jsx +++ b/assets/agenda/components/AgendaListViewControls.tsx @@ -1,11 +1,11 @@ import React from 'react'; import PropTypes from 'prop-types'; -import AgendaFeaturedStoriesToogle from './AgendaFeaturedStoriesToogle.jsx'; +import AgendaFeaturedStoriesToogle from './AgendaFeaturedStoriesToogle'; import {DISPLAY_AGENDA_FEATURED_STORIES_ONLY} from 'utils'; import ListViewOptions from 'components/ListViewOptions'; -function AgendaListViewControls({activeView, setView, hideFeaturedToggle, toggleFeaturedFilter, featuredFilter, hasAgendaFeaturedItems}) { +function AgendaListViewControls({activeView, setView, hideFeaturedToggle, toggleFeaturedFilter, featuredFilter, hasAgendaFeaturedItems}: any) { return (
diff --git a/assets/agenda/components/AgendaLocation.jsx b/assets/agenda/components/AgendaLocation.tsx similarity index 98% rename from assets/agenda/components/AgendaLocation.jsx rename to assets/agenda/components/AgendaLocation.tsx index 4999af12f..a7fb45754 100644 --- a/assets/agenda/components/AgendaLocation.jsx +++ b/assets/agenda/components/AgendaLocation.tsx @@ -4,7 +4,7 @@ import classNames from 'classnames'; import {hasLocation, getLocationString} from '../utils'; -export default function AgendaLocation({item, isMobilePhone, border}) { +export default function AgendaLocation({item, isMobilePhone, border}: any) { if (!hasLocation(item)) { return null; } diff --git a/assets/agenda/components/AgendaLongDescription.jsx b/assets/agenda/components/AgendaLongDescription.tsx similarity index 90% rename from assets/agenda/components/AgendaLongDescription.jsx rename to assets/agenda/components/AgendaLongDescription.tsx index bc0efe907..73f8e98ae 100644 --- a/assets/agenda/components/AgendaLongDescription.jsx +++ b/assets/agenda/components/AgendaLongDescription.tsx @@ -4,7 +4,7 @@ import {get} from 'lodash'; import {getHighlightedDescription} from '../utils'; -export default function AgendaLongDescription({item, plan}) { +export default function AgendaLongDescription({item, plan}: {item: any, plan: any}) { const description = item.es_highlight ? getHighlightedDescription(item, plan) : get( plan, 'description_text') || item.definition_long || item.definition_short; diff --git a/assets/agenda/components/AgendaMap.jsx b/assets/agenda/components/AgendaMap.tsx similarity index 77% rename from assets/agenda/components/AgendaMap.jsx rename to assets/agenda/components/AgendaMap.tsx index 2d81b1611..dca2ca366 100644 --- a/assets/agenda/components/AgendaMap.jsx +++ b/assets/agenda/components/AgendaMap.tsx @@ -2,12 +2,12 @@ import React from 'react'; import PropTypes from 'prop-types'; -export default function AgendaMap({image}) { +export default function AgendaMap({image}: any) { return ( image &&
{image} - +
); } diff --git a/assets/agenda/components/AgendaMeta.jsx b/assets/agenda/components/AgendaMeta.tsx similarity index 86% rename from assets/agenda/components/AgendaMeta.jsx rename to assets/agenda/components/AgendaMeta.tsx index a17351f3e..6af7d33c4 100644 --- a/assets/agenda/components/AgendaMeta.jsx +++ b/assets/agenda/components/AgendaMeta.tsx @@ -3,9 +3,9 @@ import PropTypes from 'prop-types'; import {hasLocation, hasLocationNotes, getEventLinks, getLocationString, getPublicContacts, getCalendars} from 'agenda/utils'; -const url = (link) => link.startsWith('http') ? link : 'https://' + link; +const url = (link: any) => link.startsWith('http') ? link : 'https://' + link; -function AgendaPreviewMeta({item}) { +function AgendaPreviewMeta({item}: any) { return (
@@ -19,7 +19,7 @@ function AgendaPreviewMeta({item}) { {item.location[0].details[0]}
)} - {getPublicContacts(item).map((contact, index) =>
@@ -27,7 +27,7 @@ function AgendaPreviewMeta({item}) { {contact.email && {contact.email}}
)} - {getEventLinks(item).map((link) =>
+ {getEventLinks(item).map((link: any) => )} diff --git a/assets/agenda/components/AgendaMetaTime.jsx b/assets/agenda/components/AgendaMetaTime.tsx similarity index 90% rename from assets/agenda/components/AgendaMetaTime.jsx rename to assets/agenda/components/AgendaMetaTime.tsx index 79be696b7..8b5e2fafb 100644 --- a/assets/agenda/components/AgendaMetaTime.jsx +++ b/assets/agenda/components/AgendaMetaTime.tsx @@ -3,12 +3,11 @@ import PropTypes from 'prop-types'; import classNames from 'classnames'; import {bem} from 'ui/utils'; -import {hasCoverages} from '../utils'; -import {formatAgendaDate} from 'utils'; +import {formatAgendaDate, hasCoverages} from '../utils'; import AgendaItemTimeUpdater from './AgendaItemTimeUpdater'; -function format(item, group, onlyDates) { +function format(item: any, group: any, onlyDates: any) { return ( {formatAgendaDate(item, group, {onlyDates})} @@ -16,7 +15,7 @@ function format(item, group, onlyDates) { ); } -function getCalendarClass(item) { +function getCalendarClass(item: any) { if (item.state === 'rescheduled') { return 'icon--orange'; } @@ -32,7 +31,7 @@ function getCalendarClass(item) { } } -export default function AgendaMetaTime({item, borderRight, isRecurring, group, isMobilePhone, onlyDates}) { +export default function AgendaMetaTime({item, borderRight, isRecurring, group, isMobilePhone, onlyDates}: any): any { const times = (
{ + static propTypes: any; + static defaultProps: any; + preview: any; + constructor(props: any) { super(props); } - componentDidUpdate(nextProps) { + componentDidUpdate(nextProps: any) { if (!isEqualItem(nextProps.item, this.props.item) && this.props.item) { this.preview.scrollTop = 0; // reset scroll on change } @@ -66,7 +69,7 @@ class AgendaPreview extends React.PureComponent { 'wire-column__preview--watched': isWatching, }); - const plan = (get(item, 'planning_items') || []).find((p) => p.guid === previewPlan); + const plan = (get(item, 'planning_items') || []).find((p: any) => p.guid === previewPlan); const previewInnerElement = (); return ( @@ -78,14 +81,14 @@ class AgendaPreview extends React.PureComponent {
this.preview = preview} + ref={(preview: any) => this.preview = preview} className={classNames( 'wire-column__preview__content pt-0', {noselect: this.props.previewConfig.disable_text_selection} )} > - + diff --git a/assets/agenda/components/AgendaPreviewAttachments.jsx b/assets/agenda/components/AgendaPreviewAttachments.tsx similarity index 89% rename from assets/agenda/components/AgendaPreviewAttachments.jsx rename to assets/agenda/components/AgendaPreviewAttachments.tsx index d2373f620..85925ccb9 100644 --- a/assets/agenda/components/AgendaPreviewAttachments.jsx +++ b/assets/agenda/components/AgendaPreviewAttachments.tsx @@ -7,7 +7,7 @@ import {hasAttachments} from '../utils'; import PreviewBox from 'ui/components/PreviewBox'; import AgendaAttachments from './AgendaAttachments'; -export default function AgendaPreviewAttachments({item}) { +export default function AgendaPreviewAttachments({item}: any) { if (!hasAttachments(item)) { return null; } diff --git a/assets/agenda/components/AgendaPreviewCoverages.jsx b/assets/agenda/components/AgendaPreviewCoverages.tsx similarity index 96% rename from assets/agenda/components/AgendaPreviewCoverages.jsx rename to assets/agenda/components/AgendaPreviewCoverages.tsx index 2da2a83e2..d044a3b65 100644 --- a/assets/agenda/components/AgendaPreviewCoverages.jsx +++ b/assets/agenda/components/AgendaPreviewCoverages.tsx @@ -11,8 +11,9 @@ import AgendaCoverages from './AgendaCoverages'; import AgendaInternalNote from './AgendaInternalNote'; import AgendaEdNote from './AgendaEdNote'; -class AgendaPreviewCoverages extends React.Component { - constructor(props) { +class AgendaPreviewCoverages extends React.Component { + static propTypes: any; + constructor(props: any) { super(props); this.state = {expanded: true}; @@ -20,7 +21,7 @@ class AgendaPreviewCoverages extends React.Component { } toggleExpanded() { - this.setState((prevState) => ({expanded: !prevState.expanded})); + this.setState((prevState: any) => ({expanded: !prevState.expanded})); } render() { @@ -78,7 +79,7 @@ class AgendaPreviewCoverages extends React.Component {

{!this.state.expanded ? (
- {displayCoverages.current.concat(displayCoverages.previous).map((coverage) => ( + {displayCoverages.current.concat(displayCoverages.previous).map((coverage: any) => ( { + static propTypes: any; + constructor(props: any) { super(props); this.state = { @@ -32,7 +33,7 @@ class AgendaPreviewEventComponent extends React.Component { this.reloadEvent(); } - componentDidUpdate(prevProps) { + componentDidUpdate(prevProps: any) { if (get(prevProps.item, 'event_id') !== get(this.props.item, 'event_id')) { this.reloadEvent(); } @@ -49,7 +50,7 @@ class AgendaPreviewEventComponent extends React.Component { } toggleExpanded() { - this.setState((prevState) => ({expanded: !prevState.expanded})); + this.setState((prevState: any) => ({expanded: !prevState.expanded})); } render() { @@ -121,12 +122,12 @@ AgendaPreviewEventComponent.propTypes = { fetchEvent: PropTypes.func, }; -const mapStateToProps = (state, ownProps) => ({ +const mapStateToProps = (state: any, ownProps: any) => ({ event: state.itemsById[ownProps.item.event_id], }); -const mapDispatchToProps = (dispatch) => ({ - fetchEvent: (eventId) => dispatch(fetchItem(eventId)), +const mapDispatchToProps = (dispatch: any) => ({ + fetchEvent: (eventId: any) => dispatch(fetchItem(eventId)), }); export const AgendaPreviewEvent = connect(mapStateToProps, mapDispatchToProps)(AgendaPreviewEventComponent); diff --git a/assets/agenda/components/AgendaPreviewImage.jsx b/assets/agenda/components/AgendaPreviewImage.tsx similarity index 93% rename from assets/agenda/components/AgendaPreviewImage.jsx rename to assets/agenda/components/AgendaPreviewImage.tsx index 9f35b4114..9df3a33fd 100644 --- a/assets/agenda/components/AgendaPreviewImage.jsx +++ b/assets/agenda/components/AgendaPreviewImage.tsx @@ -11,7 +11,7 @@ import {get} from 'lodash'; * @param {Object} item * @param {function} onClick */ -export default function AgendaPreviewImage({item, onClick}) { +export default function AgendaPreviewImage({item, onClick}: any) { if (!shouldRenderLocation(item)) { return null; } diff --git a/assets/agenda/components/AgendaPreviewPlanning.jsx b/assets/agenda/components/AgendaPreviewPlanning.tsx similarity index 92% rename from assets/agenda/components/AgendaPreviewPlanning.jsx rename to assets/agenda/components/AgendaPreviewPlanning.tsx index 0db68fa50..2c424b971 100644 --- a/assets/agenda/components/AgendaPreviewPlanning.jsx +++ b/assets/agenda/components/AgendaPreviewPlanning.tsx @@ -6,7 +6,8 @@ import {gettext} from 'utils'; import {isPlanningItem} from '../utils'; import AgendaPreviewCoverages from './AgendaPreviewCoverages'; -export class AgendaPreviewPlanning extends React.Component { +export class AgendaPreviewPlanning extends React.Component { + static propTypes: any; render() { const { item, @@ -19,8 +20,8 @@ export class AgendaPreviewPlanning extends React.Component { } = this.props; const planningItems = get(item, 'planning_items') || []; - const plan = planningItems.find((p) => p.guid === planningId); - const otherPlanningItems = planningItems.filter((p) => p.guid !== planningId); + const plan = planningItems.find((p: any) => p.guid === planningId); + const otherPlanningItems = planningItems.filter((p: any) => p.guid !== planningId); if (isPlanningItem(item) || restrictCoverageInfo) { return ( @@ -63,7 +64,7 @@ export class AgendaPreviewPlanning extends React.Component { {gettext('Other Planning Items')} - {otherPlanningItems.map((planningItem) => ( + {otherPlanningItems.map((planningItem: any) => ( ({ +const mapStateToProps = (state: any) => ({ filterGroupLabels: filterGroupsToLabelMap(state), }); -const AgendaTags = connect(mapStateToProps)(AgendaTagsComponent); +const AgendaTags: React.ComponentType = connect(mapStateToProps)(AgendaTagsComponent); export default AgendaTags; diff --git a/assets/agenda/components/AgendaTime.jsx b/assets/agenda/components/AgendaTime.tsx similarity index 89% rename from assets/agenda/components/AgendaTime.jsx rename to assets/agenda/components/AgendaTime.tsx index fdaac55d5..870c3398e 100644 --- a/assets/agenda/components/AgendaTime.jsx +++ b/assets/agenda/components/AgendaTime.tsx @@ -5,20 +5,19 @@ import {get} from 'lodash'; import classNames from 'classnames'; import {bem} from 'ui/utils'; -import {isItemTBC} from '../utils'; -import {formatAgendaDate} from 'utils'; +import {formatAgendaDate, isItemTBC} from '../utils'; import {shouldRenderLocation} from'maps/utils'; -export default function AgendaTime({item, children}) { +export default function AgendaTime({item, children}: any) { const tbcItem = isItemTBC(item); - const getClassNames = (modifier = 'event') => { + const getClassNames = (modifier: any = 'event') => { return bem('wire-column__preview', 'date', modifier); }; const startDateInRemoteTZ = moment.tz(moment(item.dates.start).utc(), item.dates.tz); const isRemoteTimezone = get(item, 'dates.tz') && moment.tz(moment.tz.guess()).format('Z') !== startDateInRemoteTZ.format('Z'); - const getDates = (remoteTz = false) => { + const getDates = (remoteTz: any = false) => { if (remoteTz) { if (!isRemoteTimezone) { return null; @@ -47,7 +46,7 @@ export default function AgendaTime({item, children}) { return (
- {formatAgendaDate(item, null)} + {formatAgendaDate(item, null, {})}
); }; diff --git a/assets/agenda/components/CoverageItemStatus.jsx b/assets/agenda/components/CoverageItemStatus.tsx similarity index 91% rename from assets/agenda/components/CoverageItemStatus.jsx rename to assets/agenda/components/CoverageItemStatus.tsx index 9a425c270..98f219003 100644 --- a/assets/agenda/components/CoverageItemStatus.jsx +++ b/assets/agenda/components/CoverageItemStatus.tsx @@ -13,23 +13,25 @@ import AgendaInternalNote from './AgendaInternalNote'; import AgendaEdNote from './AgendaEdNote'; import ActionButton from 'components/ActionButton'; -function getDeliveryHref(coverage) { +function getDeliveryHref(coverage: any) { return get(coverage, 'delivery_href'); } -function getDeliveryId(coverage) { +function getDeliveryId(coverage: any) { return get(coverage, 'delivery_id'); } -export default class CoverageItemStatus extends React.Component { - constructor(props) { +export default class CoverageItemStatus extends React.Component { + static propTypes: any; + static defaultProps: any; + constructor(props: any) { super(props); this.state = {wireItem: null}; this.filterActions = this.filterActions.bind(this); this.onAnchorClick = this.onAnchorClick.bind(this); } - onAnchorClick(e) { + onAnchorClick(e: any) { e.stopPropagation(); } @@ -37,14 +39,14 @@ export default class CoverageItemStatus extends React.Component { this.setWireItem(this.props); } - componentWillReceiveProps(nextProps) { + componentWillReceiveProps(nextProps: any) { this.setWireItem(nextProps); } - setWireItem(props) { + setWireItem(props: any) { const wireId = getDeliveryId(props.coverage); if (wireId && get(props, 'wireItems.length', 0) > 0) { - this.setState({wireItem: props.wireItems.find((w) => w._id === wireId)}); + this.setState({wireItem: props.wireItems.find((w: any) => w._id === wireId)}); } } @@ -58,10 +60,10 @@ export default class CoverageItemStatus extends React.Component { return ''; } - getStatusContent(coverage) { + getStatusContent(coverage: any) { const actionsToShow = this.filterActions(); const parentWatched = isWatched(this.props.item, this.props.user); - const actions = actionsToShow.map((action) => + const actions = actionsToShow.map((action: any) => ); - let content = [ + const content = [ ( {gettext('Status')}: {getCoverageStatusText(coverage)} @@ -122,7 +124,7 @@ export default class CoverageItemStatus extends React.Component { } filterActions() { - return this.props.actions.filter((action) => !action.when || + return this.props.actions.filter((action: any) => !action.when || action.when(this.props.coverage, this.props.user, this.props.item)); } @@ -147,7 +149,7 @@ export default class CoverageItemStatus extends React.Component { render() { const coverage = this.props.coverage; - const wireText = this.getItemText(coverage); + const wireText = this.getItemText(); const internalNote = get(this.props, 'coverageData.internal_note', {})[coverage.coverage_id]; const edNote = this.state.wireItem ? this.state.wireItem.ednote : get(this.props, 'coverageData.ednote', {})[coverage.coverage_id]; diff --git a/assets/agenda/components/LocationFilter.jsx b/assets/agenda/components/LocationFilter.tsx similarity index 93% rename from assets/agenda/components/LocationFilter.jsx rename to assets/agenda/components/LocationFilter.tsx index 50f6642ed..668accfde 100644 --- a/assets/agenda/components/LocationFilter.jsx +++ b/assets/agenda/components/LocationFilter.tsx @@ -16,8 +16,13 @@ const LOCATION_TYPE = { PLACE: 'location', }; -export class LocationFilter extends React.Component { - constructor(props) { +export class LocationFilter extends React.Component { + static propTypes: any; + + dom: any; + handleSearch: any; + + constructor(props: any) { super(props); this.state = { @@ -101,7 +106,7 @@ export class LocationFilter extends React.Component { * * @param {MouseEvent} event */ - handleClickOutside(event) { + handleClickOutside(event: any) { if (!this.dom.container || this.dom.container.contains(event.target) || !document.contains(event.target) || @@ -120,7 +125,7 @@ export class LocationFilter extends React.Component { * * @param {KeyboardEvent} event */ - handleKeydown(event) { + handleKeydown(event: any) { if (event.code === KEYS.ESCAPE) { event.preventDefault(); event.stopPropagation(); @@ -130,8 +135,8 @@ export class LocationFilter extends React.Component { const activeElement = document.activeElement; const numResults = this.state.results.places.length + this.state.results.regions.length; - const activeIndex = activeElement.getAttribute('data-item-index') != null ? - parseInt(activeElement.getAttribute('data-item-index'), 10) : + const activeIndex = activeElement?.getAttribute('data-item-index') != null ? + parseInt(activeElement.getAttribute('data-item-index') as any, 10) : null; if (event.code === KEYS.UP) { @@ -151,7 +156,7 @@ export class LocationFilter extends React.Component { * @param {Number} numResults - The number of search results (both regions & places) * @param {Number | null} activeIndex - The `data-item-index` attribute of current focused element */ - handleKeyUpArrow(event, activeElement, numResults, activeIndex) { + handleKeyUpArrow(event: any, activeElement: any, numResults: any, activeIndex: any) { if (activeElement === this.dom.searchInput) { // Place focus on the last item in the search results event.preventDefault(); @@ -188,7 +193,7 @@ export class LocationFilter extends React.Component { * @param {Number} numResults - The number of search results (both regions & places) * @param {Number | null} activeIndex - The `data-item-index` attribute of current focused element */ - handleKeyDownArrow(event, activeElement, numResults, activeIndex) { + handleKeyDownArrow(event: any, activeElement: any, numResults: any, activeIndex: any) { if (activeElement === this.dom.searchInput) { // Place focus on either the 'Any Location' button or first item in search results event.preventDefault(); @@ -230,7 +235,7 @@ export class LocationFilter extends React.Component { * @param {Number} numResults - The number of search results (both regions & places) * @param {Number | null} activeIndex - The `data-item-index` attribute of current focused element */ - handleKeyEnter(event, activeElement, numResults, activeIndex) { + handleKeyEnter(event: any, activeElement: any, numResults: any, activeIndex: any) { if (activeElement === this.dom.searchInput && numResults === 1) { // If there is only 1 result, then select that one event.preventDefault(); @@ -241,7 +246,7 @@ export class LocationFilter extends React.Component { } else if (activeIndex != null) { // If a search result item is currently focused, select that event.preventDefault(); - document.querySelector(`[data-item-index="${activeIndex}"]`).click(); + (document.querySelector(`[data-item-index="${activeIndex}"]`) as HTMLElement).click(); } } @@ -250,9 +255,9 @@ export class LocationFilter extends React.Component { * * @param {Number} index */ - focusItem(index) { + focusItem(index: any) { this.setState({selectedIndex: index}); - const item = document.querySelector(`[data-item-index="${index}"]`); + const item: HTMLElement = document.querySelector(`[data-item-index="${index}"]`) as HTMLElement; if (item != null) { item.focus(); @@ -264,7 +269,7 @@ export class LocationFilter extends React.Component { * * @param {Object} selected */ - onChange(selected) { + onChange(selected?: any) { this.toggleDropdown(); this.props.toggleFilter('location', selected); } @@ -274,7 +279,7 @@ export class LocationFilter extends React.Component { * * @param {Event} e */ - onSearchInputChange(e) { + onSearchInputChange(e: any) { this.handleSearch(e.target.value); } @@ -284,7 +289,7 @@ export class LocationFilter extends React.Component { * @param {String | undefined} query * @private */ - _handleSearch(query) { + _handleSearch(query?: any) { this.setState({isSearchLoading: true}); let searchURL = '/agenda/search_locations'; @@ -293,7 +298,7 @@ export class LocationFilter extends React.Component { } server.get(searchURL) - .then((results) => { + .then((results: any) => { this.setState({ isSearchLoading: false, results: results, @@ -308,7 +313,7 @@ export class LocationFilter extends React.Component { * @param {Number} index - The index in the results list * @returns {JSX.Element} */ - renderRegionSearchResult(item, index) { + renderRegionSearchResult(item: any, index: any) { const {selectedIndex} = this.state; if (item.type === LOCATION_TYPE.CITY) { @@ -417,7 +422,7 @@ export class LocationFilter extends React.Component {
this.dom.container = ref} + ref={(ref: any) => this.dom.container = ref} > this.dom.searchInput = ref} + ref={(ref: any) => this.dom.searchInput = ref} />
this.onChange()} - ref={(ref) => this.dom.clearButton = ref} + ref={(ref: any) => this.dom.clearButton = ref} > {gettext('Any location')} diff --git a/assets/agenda/index.js b/assets/agenda/index.ts similarity index 84% rename from assets/agenda/index.js rename to assets/agenda/index.ts index b5286e4b9..96d27682b 100644 --- a/assets/agenda/index.js +++ b/assets/agenda/index.ts @@ -1,4 +1,5 @@ -import {createStore, render, initWebSocket, getInitData, isMobilePhone, closeItemOnMobile} from 'utils'; +import {createStore, render, getInitData, isMobilePhone, closeItemOnMobile} from 'utils'; +import {initWebSocket} from 'websocket'; import agendaReducer from './reducers'; import {getActiveDate, getReadItems, getFeaturedOnlyParam} from 'local-store'; @@ -11,7 +12,6 @@ const store = createStore(agendaReducer, 'Agenda'); // init data store.dispatch(initData(getInitData(window.agendaData), getReadItems(), getActiveDate(), getFeaturedOnlyParam())); - // init query const params = new URLSearchParams(window.location.search); store.dispatch(initParams(params)); @@ -22,12 +22,12 @@ if (localStorage.getItem('view')) { } // handle history -window.onpopstate = function(event) { +window.onpopstate = function(event: any) { if (event.state) { closeItemOnMobile(store.dispatch, event.state, openItemDetails, previewItem); if (!isMobilePhone()) { store.dispatch(setState(event.state)); - store.dispatch(fetchItems(false)); + store.dispatch(fetchItems()); } } }; diff --git a/assets/agenda/item-actions.js b/assets/agenda/item-actions.js deleted file mode 100644 index e165fd760..000000000 --- a/assets/agenda/item-actions.js +++ /dev/null @@ -1,55 +0,0 @@ -import {get} from 'lodash'; -import {getItemActions} from '../item-actions'; -import * as agendaActions from './actions'; -import {gettext} from '../utils'; -import {isWatched} from './utils'; - -const canWatchAgendaItem = (state, item, includeCoverages) => { - let result = state.user && !isWatched(item, state.user); - if (!state.bookmarks || includeCoverages) { - return result; - } - - const coveragesWatched = (get(item, 'coverages') || []).filter((c) => isWatched(c, state.user)); - return coveragesWatched.length > 0 ? false : result; -}; - -export const getAgendaItemActions = (dispatch) => { - const {watchEvents, stopWatchingEvents} = agendaActions; - return getItemActions(dispatch, {...agendaActions}).concat([ - { - name: gettext('Watch'), - icon: 'watch', - multi: true, - when: (state, item, includeCoverages) => canWatchAgendaItem(state, item, includeCoverages), - action: (items) => dispatch(watchEvents(items)), - }, - { - name: gettext('Stop watching'), - icon: 'unwatch', - multi: true, - when: (state, item, includeCoverages) => !canWatchAgendaItem(state, item, includeCoverages), - action: (items) => dispatch(stopWatchingEvents(items)), - }, - ]); -}; - -export const getCoverageItemActions = (dispatch) => { - const {watchCoverage, stopWatchingCoverage} = agendaActions; - return [ - { - name: gettext('Watch'), - icon: 'watch', - when: (cov, user) => user && !isWatched(cov, user), - action: (coverage, group, item) => dispatch(watchCoverage(coverage, item)), - tooltip: gettext('Watch this coverage'), - }, - { - name: gettext('Stop watching'), - icon: 'unwatch', - when: (cov, user) => user && isWatched(cov, user), - action: (coverage, group, item) => dispatch(stopWatchingCoverage(coverage, item)), - }, - ]; -}; - diff --git a/assets/agenda/item-actions.ts b/assets/agenda/item-actions.ts new file mode 100644 index 000000000..3863899e7 --- /dev/null +++ b/assets/agenda/item-actions.ts @@ -0,0 +1,55 @@ +import {get} from 'lodash'; +import {getItemActions} from '../item-actions'; +import * as agendaActions from './actions'; +import {gettext} from '../utils'; +import {isWatched} from './utils'; + +const canWatchAgendaItem = (state: any, item: any, includeCoverages: any) => { + const result = state.user && !isWatched(item, state.user); + if (!state.bookmarks || includeCoverages) { + return result; + } + + const coveragesWatched = (get(item, 'coverages') || []).filter((c: any) => isWatched(c, state.user)); + return coveragesWatched.length > 0 ? false : result; +}; + +export const getAgendaItemActions = (dispatch: any) => { + const {watchEvents, stopWatchingEvents} = agendaActions; + return (getItemActions(dispatch, {...agendaActions}) as any).concat([ + { + name: gettext('Watch'), + icon: 'watch', + multi: true, + when: (state: any, item: any, includeCoverages: any) => canWatchAgendaItem(state, item, includeCoverages), + action: (items: any) => dispatch(watchEvents(items)), + }, + { + name: gettext('Stop watching'), + icon: 'unwatch', + multi: true, + when: (state: any, item: any, includeCoverages: any) => !canWatchAgendaItem(state, item, includeCoverages), + action: (items: any) => dispatch(stopWatchingEvents(items)), + }, + ]); +}; + +export const getCoverageItemActions = (dispatch: any) => { + const {watchCoverage, stopWatchingCoverage} = agendaActions; + return [ + { + name: gettext('Watch'), + icon: 'watch', + when: (cov: any, user: any) => user && !isWatched(cov, user), + action: (coverage: any, group: any, item: any) => dispatch(watchCoverage(coverage, item)), + tooltip: gettext('Watch this coverage'), + }, + { + name: gettext('Stop watching'), + icon: 'unwatch', + when: (cov: any, user: any) => user && isWatched(cov, user), + action: (coverage: any, group: any, item: any) => dispatch(stopWatchingCoverage(coverage, item)), + }, + ]; +}; + diff --git a/assets/agenda/reducers.js b/assets/agenda/reducers.js index be70c9e8f..4a05f8404 100644 --- a/assets/agenda/reducers.js +++ b/assets/agenda/reducers.js @@ -1,5 +1,4 @@ import { - SET_EVENT_QUERY, RECIEVE_ITEMS, INIT_DATA, SELECT_DATE, @@ -106,9 +105,6 @@ export default function agendaReducer(state = initialState, action) { case RECIEVE_ITEMS: return recieveItems(state, action.data); - case SET_EVENT_QUERY: - return {...state, query: action.query, activeItem: null}; - case WATCH_EVENTS: { const itemsById = Object.assign({}, state.itemsById); action.items.forEach((_id) => { diff --git a/assets/agenda/reducers.ts b/assets/agenda/reducers.ts new file mode 100644 index 000000000..287128feb --- /dev/null +++ b/assets/agenda/reducers.ts @@ -0,0 +1,249 @@ +import { + RECIEVE_ITEMS, + INIT_DATA, + SELECT_DATE, + WATCH_EVENTS, + STOP_WATCHING_EVENTS, + UPDATE_ITEM, + TOGGLE_FEATURED_FILTER, + SET_ITEM_TYPE_FILTER, + AGENDA_WIRE_ITEMS, + WATCH_COVERAGE, + STOP_WATCHING_COVERAGE, +} from './actions'; + +import {get, uniq} from 'lodash'; +import {EXTENDED_VIEW} from 'wire/defaults'; +import {searchReducer} from 'search/reducers'; +import {defaultReducer} from '../reducers'; +import {EARLIEST_DATE} from './utils'; +import {ITopic} from 'interfaces/topic'; + +const initialState = { + items: [], + itemsById: {}, + aggregations: null, + activeItem: null, + previewItem: null, + previewGroup: null, + previewPlan: null, + openItem: null, + isLoading: false, + resultsFiltered: false, + totalItems: null, + activeQuery: null, + user: null, + company: null, + topics: [], + selectedItems: [], + bookmarks: false, + context: 'agenda', + formats: [], + newItems: [], + newItemsByTopic: {}, + readItems: {}, + agenda: { + activeView: EXTENDED_VIEW, + activeDate: Date.now(), + activeGrouping: 'day', + eventsOnlyAccess: false, + itemType: null, + featuredOnly: false, + agendaWireItems: [], + }, + search: searchReducer(undefined, undefined, 'agenda'), + detail: false, + userSections: {}, + searchInitiated: false, + uiConfig: {}, + groups: [], + hasAgendaFeaturedItems: false, +}; + +export interface IAgendaState { + topics: Array; +} + +function recieveItems(state: any, data: any) { + const itemsById = Object.assign({}, state.itemsById); + const items = data._items.map((item: any) => { + itemsById[item._id] = item; + return item._id; + }); + + return { + ...state, + items, + itemsById, + isLoading: false, + totalItems: data._meta.total, + aggregations: data._aggregations || null, + newItems: [], + searchInitiated: false, + }; +} + +function _agendaReducer(state: any, action: any) { + switch (action.type) { + + case SELECT_DATE: + return { + ...state, + selectedItems: [], + activeDate: action.dateString, + activeGrouping: action.grouping || 'day', + }; + + default: + return state; + } +} + +export default function agendaReducer(state: any = initialState, action: any): IAgendaState { + switch (action.type) { + + case RECIEVE_ITEMS: + return recieveItems(state, action.data); + + case WATCH_EVENTS: { + const itemsById = Object.assign({}, state.itemsById); + action.items.forEach((_id: any) => { + const watches = get(itemsById[_id], 'watches', []).concat(state.user); + itemsById[_id] = Object.assign({}, itemsById[_id], {watches}); + (get(itemsById[_id], 'coverages') || []).forEach((c: any) => { + if (get(c, 'watches.length', 0) > 0) { + c.watches = []; + } + }); + }); + + return {...state, itemsById}; + } + + case WATCH_COVERAGE: { + const itemsById = Object.assign({}, state.itemsById); + const item = itemsById[get(action, 'item._id')]; + const coverage = (get(item, 'coverages') || []).find((c: any) => c.coverage_id === action.coverage.coverage_id); + if (coverage) { + coverage['watches'] = uniq([ + ...(get(coverage, 'watches') || []), + state.user + ]); + } + + return {...state, itemsById}; + } + + case STOP_WATCHING_COVERAGE: { + const itemsById = Object.assign({}, state.itemsById); + const item = itemsById[get(action, 'item._id')]; + const coverage = (get(item, 'coverages') || []).find((c: any) => c.coverage_id === action.coverage.coverage_id); + if (coverage) { + coverage['watches'] = (get(coverage, 'watches') || []).filter((u: any) => u !== state.user); + } + + return {...state, itemsById}; + } + + case STOP_WATCHING_EVENTS: { + const itemsById = Object.assign({}, state.itemsById); + action.items.forEach((_id: any) => { + const watches = get(itemsById[_id], 'watches', []).filter((userId: any) => userId !== state.user); + itemsById[_id] = Object.assign({}, itemsById[_id], {watches}); + }); + + return {...state, itemsById}; + } + + case UPDATE_ITEM: { + // Update existing items, remove killed items + const itemsById = Object.assign({}, state.itemsById); + let updatedItems = [ ...state.items ]; + const item = action.item; + if(itemsById[item._id]) { + if (get(item, 'state') === 'killed') { + delete itemsById[item._id]; + updatedItems = updatedItems.filter((i: any) => i !== item._id); + } else { + itemsById[item._id] = item; + } + } + + return { + ...state, + itemsById: itemsById, + items: updatedItems, + }; + } + + case INIT_DATA: { + const navigations = get(action, 'agendaData.navigations', []); + const openItem = get(action, 'agendaData.item', null); + const agenda: any = { + ...state.agenda, + activeDate: action.agendaData.bookmarks ? EARLIEST_DATE : action.activeDate || state.agenda.activeDate, + eventsOnlyAccess: action.agendaData.events_only, + restrictCoverageInfo: action.agendaData.restrict_coverage_info, + featuredOnly: action.featuredOnly, + }; + + return { + ...state, + readItems: action.readData || {}, + user: (action.agendaData.user || {})._id || null, + topics: action.agendaData.topics || [], + company: action.agendaData.company || null, + bookmarks: action.agendaData.bookmarks || false, + formats: action.agendaData.formats || [], + search: Object.assign({}, state.search, {navigations}), + context: 'agenda', + openItem: openItem, + detail: !!openItem, + agenda, + savedItemsCount: action.agendaData.saved_items || null, + userSections: action.agendaData.userSections || {}, + locators: action.agendaData.locators || null, + uiConfig: action.agendaData.ui_config || {}, + groups: action.agendaData.groups || [], + hasAgendaFeaturedItems: action.agendaData.has_agenda_featured_items || false, + }; + } + + case SELECT_DATE: + return { + ...state, + activeItem: null, + previewItem: null, + agenda: _agendaReducer(state.agenda, action) + }; + + case TOGGLE_FEATURED_FILTER: + return { + ...state, + agenda: { + ...state.agenda, + featuredOnly: !state.agenda.featuredOnly, + } + }; + case SET_ITEM_TYPE_FILTER: + return { + ...state, + agenda: { + ...state.agenda, + itemType: action.value, + }, + }; + + case AGENDA_WIRE_ITEMS: + return { + ...state, + agenda: { + ...state.agenda, + agendaWireItems: action.items + } + }; + + default: + return defaultReducer(state || initialState, action); + } +} diff --git a/assets/agenda/selectors.js b/assets/agenda/selectors.ts similarity index 62% rename from assets/agenda/selectors.js rename to assets/agenda/selectors.ts index f3d01440b..7b2d6b96f 100644 --- a/assets/agenda/selectors.js +++ b/assets/agenda/selectors.ts @@ -1,7 +1,7 @@ import {get} from 'lodash'; import {uiConfigSelector} from 'ui/selectors'; -export const agendaFiltersConfigSelector = (state) => get(uiConfigSelector(state), 'subnav.filters') || [ +export const agendaFiltersConfigSelector = (state: any) => get(uiConfigSelector(state), 'subnav.filters') || [ 'item_type', 'calendar', 'location', diff --git a/assets/agenda/tests/CoverageItemStatus.spec.js b/assets/agenda/tests/CoverageItemStatus.spec.tsx similarity index 98% rename from assets/agenda/tests/CoverageItemStatus.spec.js rename to assets/agenda/tests/CoverageItemStatus.spec.tsx index d2550e27b..b0a524d06 100644 --- a/assets/agenda/tests/CoverageItemStatus.spec.js +++ b/assets/agenda/tests/CoverageItemStatus.spec.tsx @@ -5,7 +5,7 @@ import CoverageItemStatus from '../components/CoverageItemStatus'; import 'tests/setup'; -function setup(coverage) { +function setup(coverage: any) { return shallow(); } diff --git a/assets/agenda/tests/utils.spec.js b/assets/agenda/tests/utils.spec.ts similarity index 94% rename from assets/agenda/tests/utils.spec.js rename to assets/agenda/tests/utils.spec.ts index 07ec7bb62..4d2e8489f 100644 --- a/assets/agenda/tests/utils.spec.js +++ b/assets/agenda/tests/utils.spec.ts @@ -1,3 +1,4 @@ +/* eslint-disable no-prototype-builtins */ import {keyBy} from 'lodash'; import * as utils from '../utils'; @@ -18,7 +19,7 @@ describe('utils', () => { }, ]; - const groupedItems = keyBy(utils.groupItems(items, '2018-10-13', 'day'), 'date'); + const groupedItems: any = keyBy(utils.groupItems(items, '2018-10-13', 'day'), 'date'); expect(groupedItems.hasOwnProperty('13-10-2018')).toBe(false); expect(groupedItems.hasOwnProperty('14-10-2018')).toBe(false); @@ -42,7 +43,7 @@ describe('utils', () => { }, ]; - const groupedItems = keyBy(utils.groupItems(items, '2018-10-21', 'day'), 'date'); + const groupedItems: any = keyBy(utils.groupItems(items, '2018-10-21', 'day'), 'date'); expect(groupedItems['15-10-2018']['items']).toEqual(['foo']); expect(groupedItems['16-10-2018']['items']).toEqual(['foo']); @@ -60,7 +61,7 @@ describe('utils', () => { event: {_id: 'foo'} }]; - const groupedItems = keyBy(utils.groupItems(items, '2018-10-11', 'day'), 'date'); + const groupedItems: any = keyBy(utils.groupItems(items, '2018-10-11', 'day'), 'date'); expect(groupedItems.hasOwnProperty('11-10-2018')).toBe(false); expect(groupedItems.hasOwnProperty('12-10-2018')).toBe(false); @@ -84,7 +85,7 @@ describe('utils', () => { event: null }]; - const groupedItems = keyBy(utils.groupItems(items, '2018-10-15', 'day'), 'date'); + const groupedItems: any = keyBy(utils.groupItems(items, '2018-10-15', 'day'), 'date'); expect(groupedItems.hasOwnProperty('15-10-2018')).toBe(false); expect(groupedItems.hasOwnProperty('17-10-2018')).toBe(false); @@ -103,7 +104,7 @@ describe('utils', () => { event: null }]; - const groupedItems = keyBy(utils.groupItems(items, '2018-10-15', 'day'), 'date'); + const groupedItems: any = keyBy(utils.groupItems(items, '2018-10-15', 'day'), 'date'); expect(groupedItems.hasOwnProperty('15-10-2018')).toBe(false); expect(groupedItems.hasOwnProperty('16-10-2018')).toBe(false); diff --git a/assets/agenda/utils.js b/assets/agenda/utils.ts similarity index 68% rename from assets/agenda/utils.js rename to assets/agenda/utils.ts index 8e0ce6a71..cef30fbaf 100644 --- a/assets/agenda/utils.js +++ b/assets/agenda/utils.ts @@ -12,6 +12,7 @@ import { parseDate, AGENDA_DATE_FORMAT_SHORT, formatTime, + DAY_IN_MINUTES, } from '../utils'; export const STATUS_KILLED = 'killed'; @@ -19,32 +20,32 @@ export const STATUS_CANCELED = 'cancelled'; export const STATUS_POSTPONED = 'postponed'; export const STATUS_RESCHEDULED = 'rescheduled'; -const navigationFunctions = { +const navigationFunctions: any = { 'day': { 'next': getNextDay, 'previous': getPreviousDay, - 'format': (dateString) => moment(dateString).format(AGENDA_DATE_FORMAT_SHORT), + 'format': (dateString: any) => moment(dateString).format(AGENDA_DATE_FORMAT_SHORT), }, 'week': { 'next': getNextWeek, 'previous': getPreviousWeek, - 'format': (dateString) => `${moment(dateString).format('D MMMM')} - + 'format': (dateString: any) => `${moment(dateString).format('D MMMM')} - ${moment(dateString).add(6, 'days').format('D MMMM')}`, }, 'month': { 'next': getNextMonth, 'previous': getPreviousMonth, - 'format': (dateString) => moment(dateString).format('MMMM, YYYY'), + 'format': (dateString: any) => moment(dateString).format('MMMM, YYYY'), } }; -const Groupers = { +const Groupers: any = { 'day': formatDate, 'week': formatWeek, 'month': formatMonth, }; -export function getCoverageStatusText(coverage) { +export function getCoverageStatusText(coverage: any) { if (coverage.workflow_status === WORKFLOW_STATUS.DRAFT) { return get(DRAFT_STATUS_TEXTS, coverage.coverage_status, ''); } @@ -115,7 +116,7 @@ export const EARLIEST_DATE = moment('20170101').valueOf(); * @param {Object} item * @return {Boolean} */ -export function isCanceled(item) { +export function isCanceled(item: any) { return item && (item.state === STATUS_CANCELED || item.state === STATUS_KILLED); } @@ -125,7 +126,7 @@ export function isCanceled(item) { * @param {Object} item * @return {Boolean} */ -export function isPostponed(item) { +export function isPostponed(item: any) { return item && item.state === STATUS_POSTPONED; } @@ -135,7 +136,7 @@ export function isPostponed(item) { * @param {Object} item * @return {Boolean} */ -export function isRescheduled(item) { +export function isRescheduled(item: any) { return item && item.state === STATUS_RESCHEDULED; } @@ -145,7 +146,7 @@ export function isRescheduled(item) { * @param {Object} item * @return {Boolean} */ -export function hasCoverages(item) { +export function hasCoverages(item: any) { return !isEmpty(get(item, 'coverages')); } @@ -155,7 +156,7 @@ export function hasCoverages(item) { * @param coverageType * @returns {*} */ -export function getCoverageIcon(coverageType) { +export function getCoverageIcon(coverageType: any) { const coverageTypes = getConfig('coverage_types', {}); return get(coverageTypes, `${coverageType}.icon`, 'unrecognized'); } @@ -166,7 +167,7 @@ export function getCoverageIcon(coverageType) { * @param coverageType * @returns {*} */ -export function getCoverageDisplayName(coverageType) { +export function getCoverageDisplayName(coverageType: any) { const coverageTypes = getConfig('coverage_types', {}); const locale = (window.locale || 'en').toLowerCase(); @@ -181,7 +182,7 @@ export function getCoverageDisplayName(coverageType) { * @param {String} userId * @return {Boolean} */ -export function isWatched(item, userId) { +export function isWatched(item: any, userId: any) { return userId && includes(get(item, 'watches', []), userId); } @@ -192,7 +193,7 @@ export function isWatched(item, userId) { * @param {String} dateString * @return {Boolean} */ -export function isCoverageForExtraDay(coverage) { +export function isCoverageForExtraDay(coverage: any) { return coverage.scheduled != null; } @@ -202,7 +203,7 @@ export function isCoverageForExtraDay(coverage) { * @param {Object} item * @return {Boolean} */ -export function isRecurring(item) { +export function isRecurring(item: any) { return item && !!item.recurrence_id; } @@ -212,7 +213,7 @@ export function isRecurring(item) { * @param {Object} item * @return {String} */ -export function getGeoLocation(item) { +export function getGeoLocation(item: any) { return get(item, 'location.location', null); } @@ -222,7 +223,7 @@ export function getGeoLocation(item) { * @param {Object} item * @return {String} */ -export function getLocationString(item) { +export function getLocationString(item: any) { return [ get(item, 'location.0.name', get(item, 'location.0.address.title')), get(item, 'location.0.address.line.0'), @@ -230,7 +231,7 @@ export function getLocationString(item) { get(item, 'location.0.address.state') || get(item, 'location.0.address.locality'), get(item, 'location.0.address.postal_code'), get(item, 'location.0.address.country'), - ].filter((d) => d).join(', '); + ].filter((d: any) => d).join(', '); } /** @@ -239,11 +240,11 @@ export function getLocationString(item) { * @param {Object} item * @return {Boolean} */ -export function hasLocation(item) { +export function hasLocation(item: any) { return !!getLocationString(item); } -export function hasLocationNotes(item) { +export function hasLocationNotes(item: any) { return get(item, 'location[0].details[0].length', 0) > 0; } @@ -253,14 +254,14 @@ export function hasLocationNotes(item) { * @param {Object} item * @return {String} */ -export function getPublicContacts(item) { +export function getPublicContacts(item: any) { const contacts = get(item, 'event.event_contact_info', []); - return contacts.filter(c => c.public).map(c => ({ - name: [c.first_name, c.last_name].filter((x) => !!x).join(' '), + return contacts.filter((c: any) => c.public).map((c: any) => ({ + name: [c.first_name, c.last_name].filter((x: any) => !!x).join(' '), organisation: c.organisation || '', email: (c.contact_email || []).join(', '), - phone: (c.contact_phone || []).filter(m => m.public).map(m => m.number).join(', '), - mobile: (c.mobile || []).filter(m => m.public).map(m => m.number).join(', '), + phone: (c.contact_phone || []).filter((m: any) => m.public).map((m: any) => m.number).join(', '), + mobile: (c.mobile || []).filter((m: any) => m.public).map((m: any) => m.number).join(', '), })); } @@ -270,24 +271,24 @@ export function getPublicContacts(item) { * @param {Object} item * @return {String} */ -export function getCalendars(item) { - return (get(item, 'calendars') || []).map(cal => cal.name).join(', '); +export function getCalendars(item: any) { + return (get(item, 'calendars') || []).map((cal: any) => cal.name).join(', '); } -export function getAgendaNames(item) { +export function getAgendaNames(item: any) { return (get(item, 'agendas') || []) - .map((agenda) => agenda.name) + .map((agenda: any) => agenda.name) .join(', '); } -export function isPlanningItem(item) { +export function isPlanningItem(item: any) { return item.item_type === 'planning' || ( item.item_type == null && item.event_id == null ); } -export function planHasEvent(item) { +export function planHasEvent(item: any) { return isPlanningItem(item) && item.event_id != null; } @@ -297,7 +298,7 @@ export function planHasEvent(item) { * @param {Object} item * @return {String} */ -export function getEventLinks(item) { +export function getEventLinks(item: any) { return get(item, 'event.links') || []; } @@ -308,7 +309,7 @@ export function getEventLinks(item) { * @param {String} dateString * @return {String} */ -export function formatNavigationDate(dateString, grouping) { +export function formatNavigationDate(dateString: any, grouping: any) { return navigationFunctions[grouping].format(dateString); } @@ -319,7 +320,7 @@ export function formatNavigationDate(dateString, grouping) { * @param {String} dateString * @return {String} */ -export function getDateInputDate(dateString) { +export function getDateInputDate(dateString: any) { if (dateString) { const parsed = moment(parseInt(dateString)); return parsed.format('YYYY-MM-DD'); @@ -334,7 +335,7 @@ export function getDateInputDate(dateString) { * @param {String} dateString * @return {String} */ -export function getMomentDate(dateString) { +export function getMomentDate(dateString: any): any { if (dateString) { return moment(parseInt(dateString)); } @@ -348,7 +349,7 @@ export function getMomentDate(dateString) { * @param {String} dateString * @return {String} number of milliseconds since the Unix Epoch */ -function getNextDay(dateString) { +function getNextDay(dateString: any) { return moment(dateString).add(1, 'days').valueOf(); } @@ -359,7 +360,7 @@ function getNextDay(dateString) { * @param {String} dateString * @return {String} number of milliseconds since the Unix Epoch */ -function getPreviousDay(dateString) { +function getPreviousDay(dateString: any) { return moment(dateString).add(-1, 'days').valueOf(); } @@ -369,7 +370,7 @@ function getPreviousDay(dateString) { * @param {String} dateString * @return {String} number of milliseconds since the Unix Epoch */ -function getNextWeek(dateString) { +function getNextWeek(dateString: any) { return moment(dateString).add(7, 'days').isoWeekday(1).valueOf(); } @@ -380,7 +381,7 @@ function getNextWeek(dateString) { * @param {String} dateString * @return {String} number of milliseconds since the Unix Epoch */ -function getPreviousWeek(dateString) { +function getPreviousWeek(dateString: any) { return moment(dateString).add(-7, 'days').isoWeekday(1).valueOf(); } @@ -390,7 +391,7 @@ function getPreviousWeek(dateString) { * @param {String} dateString * @return {String} number of milliseconds since the Unix Epoch */ -function getNextMonth(dateString) { +function getNextMonth(dateString: any) { return moment(dateString).add(1, 'months').startOf('month').valueOf(); } @@ -401,7 +402,7 @@ function getNextMonth(dateString) { * @param {String} dateString * @return {String} number of milliseconds since the Unix Epoch */ -export function getPreviousMonth(dateString) { +export function getPreviousMonth(dateString: any) { return moment(dateString).add(-1, 'months').startOf('month').valueOf(); } @@ -412,7 +413,7 @@ export function getPreviousMonth(dateString) { * @param {String} grouping: day, week or month * @return {String} number of milliseconds since the Unix Epoch */ -export function getNext(dateString, grouping) { +export function getNext(dateString: any, grouping: any) { return navigationFunctions[grouping].next(dateString); } @@ -423,7 +424,7 @@ export function getNext(dateString, grouping) { * @param {String} grouping: day, week or month * @return {String} number of milliseconds since the Unix Epoch */ -export function getPrevious(dateString, grouping) { +export function getPrevious(dateString: any, grouping: any) { return navigationFunctions[grouping].previous(dateString); } @@ -433,7 +434,7 @@ export function getPrevious(dateString, grouping) { * @param {Object} item * @return {Array} */ -export function getAttachments(item) { +export function getAttachments(item: any) { return get(item, 'event.files', []); } @@ -443,7 +444,7 @@ export function getAttachments(item) { * @param {Object} item * @return {Array} */ -export function getInternalNote(item, plan) { +export function getInternalNote(item: any, plan: any) { return get(plan, 'internal_note') || get(item, 'event.internal_note'); } @@ -453,18 +454,18 @@ export function getInternalNote(item, plan) { * @param {Object} item * @return {Object} */ -export function getDataFromCoverages(item) { +export function getDataFromCoverages(item: any) { const planningItems = get(item, 'planning_items', []); - let data = { + const data: any = { 'internal_note': {}, 'ednote': {}, 'workflow_status_reason': {}, 'scheduled_update_status': {}, }; - planningItems.forEach(p => { - (get(p, 'coverages') || []).forEach((c) => { - ['internal_note', 'ednote', 'workflow_status_reason'].forEach((field) => { + planningItems.forEach((p: any) => { + (get(p, 'coverages') || []).forEach((c: any) => { + ['internal_note', 'ednote', 'workflow_status_reason'].forEach((field: any) => { // Don't populate if the value is same as the field in upper level planning_item // Don't populate workflow_status_reason is planning_item is cancelled to avoid UI duplication @@ -490,7 +491,7 @@ export function getDataFromCoverages(item) { return data; } -const getNextPendingScheduledUpdate = (coverage) => { +const getNextPendingScheduledUpdate = (coverage: any) => { if (coverage.scheduled == null) { // Not privileged to see coverage details return null; @@ -505,13 +506,13 @@ const getNextPendingScheduledUpdate = (coverage) => { return coverage.scheduled_updates[0]; } - const lastScheduledDelivery = (coverage.deliveries.reverse()).find((d) => d.scheduled_update_id); + const lastScheduledDelivery = (coverage.deliveries.reverse()).find((d: any) => d.scheduled_update_id); // More deliveries, but no scheduled_update was published if (!lastScheduledDelivery) { return coverage.scheduled_updates[0]; } - const lastPublishedShceduledUpdateIndex = coverage.scheduled_updates.findIndex((s) => + const lastPublishedShceduledUpdateIndex = coverage.scheduled_updates.findIndex((s: any) => s.scheduled_update_id === lastScheduledDelivery.scheduled_update_id); if (lastPublishedShceduledUpdateIndex === coverage.scheduled_updates.length - 1) { @@ -531,7 +532,7 @@ const getNextPendingScheduledUpdate = (coverage) => { * @param {Object} item * @return {Array} */ -export function getSubjects(item) { +export function getSubjects(item: any) { return get(item, 'subject') || []; } @@ -541,7 +542,7 @@ export function getSubjects(item) { * @param {Object} item * @return {Boolean} */ -export function hasAttachments(item) { +export function hasAttachments(item: any) { return !isEmpty(getAttachments(item)); } @@ -551,11 +552,11 @@ export function hasAttachments(item) { * @param {Object} item * @return {String} */ -export function getName(item) { +export function getName(item: any) { return item.name || item.slugline || item.headline; } -export function getHighlightedName(item) { +export function getHighlightedName(item: any) { if (item.es_highlight.name){ return item.es_highlight.name[0]; } @@ -577,8 +578,8 @@ export function getHighlightedName(item) { * @param {Object} plan * @return {String} */ -export function getDescription(item, plan) { - return plan.description_text || item.definition_short; +export function getDescription(item: any, plan: any) { + return plan ? plan.description_text : (item ? item.definition_short : ''); } /** @@ -588,7 +589,7 @@ export function getDescription(item, plan) { * @param {Object} plan * @return {String} */ -export function getHighlightedDescription(item, plan) { +export function getHighlightedDescription(item: any, plan: any) { if (item.es_highlight.description_text) { return item.es_highlight.description_text[0]; @@ -611,8 +612,8 @@ export function getHighlightedDescription(item, plan) { * @param {Object} item * @return {Array} list of dates */ -export function getExtraDates(item) { - return getDisplayDates(item).map((ed) => moment(ed.date)); +export function getExtraDates(item: any) { + return getDisplayDates(item).map((ed: any) => moment(ed.date)); } /** @@ -620,34 +621,34 @@ export function getExtraDates(item) { * @param item: Event or Planning item * @returns {Array.<{date: moment.Moment}>} */ -export function getDisplayDates(item) { +export function getDisplayDates(item: any) { const matchedPlanning = get(item, '_hits.matched_planning_items'); if (matchedPlanning == null) { return get(item, 'display_dates') || []; } - const dates = []; + const dates: Array = []; const planningItems = get(item, 'planning_items') || []; const planningIds = item._hits.matched_planning_items; const coverageIds = get(item, '_hits.matched_coverages') != null ? item._hits.matched_coverages : - (get(item, 'coverages') || []).map((coverage) => coverage.coverage_id); + (get(item, 'coverages') || []).map((coverage: any) => coverage.coverage_id); planningItems - .forEach((plan) => { + .forEach((plan: any) => { if (!planningIds.includes(plan._id)) { return; } - const coverages = (get(plan, 'coverages') || []).filter((coverage) => coverage.scheduled); + const coverages = (get(plan, 'coverages') || []).filter((coverage: any) => coverage.scheduled); if (!coverages.length) { dates.push({date: plan.planning_date}); return; } - coverages.forEach((coverage) => { + coverages.forEach((coverage: any) => { if (!coverageIds.includes(coverage.coverage_id)) { return; } @@ -666,24 +667,24 @@ export function getDisplayDates(item) { * @param {Date} date to check (moment) * @return {Boolean} */ -export function containsExtraDate(item, dateToCheck) { - return getDisplayDates(item).map(ed => moment(ed.date).format('YYYY-MM-DD')).includes(dateToCheck.format('YYYY-MM-DD')); +export function containsExtraDate(item: any, dateToCheck: any) { + return getDisplayDates(item).map((ed: any) => moment(ed.date).format('YYYY-MM-DD')).includes(dateToCheck.format('YYYY-MM-DD')); } // get start date in utc mode if there is no time info -const getStartDate = (item) => item.dates.all_day ? moment.utc(item.dates.start) : moment(item.dates.start); +const getStartDate = (item: any) => item.dates.all_day ? moment.utc(item.dates.start) : moment(item.dates.start); // get end date in utc mode if there is no end time info -const getEndDate = (item) => item.dates.no_end_time || item.dates.all_day ? +const getEndDate = (item: any) => item.dates.no_end_time || item.dates.all_day ? moment.utc(item.dates.end || item.dates.start) : moment(item.dates.end || item.dates.start); // compare days without being affected by timezone -const isBetweenDay = (day, start, end) => { +const isBetweenDay = (day: any, start: any, end: any) => { // it will be converted to local time // if passed as string which we need // for all day events which are in utc mode - const startDate = start.format('YYYY-MM-DD'); + const startDate = start.format('YYYY-MM-DD'); const endDate = end.format('YYYY-MM-DD'); return day.isBetween(startDate, endDate, 'day', '[]'); @@ -695,19 +696,19 @@ const isBetweenDay = (day, start, end) => { * @param activeDate: date that the grouping will start from * @param activeGrouping: type of grouping i.e. day, week, month */ -export function groupItems(items, activeDate, activeGrouping, featuredOnly) { +export function groupItems(items: any, activeDate: any, activeGrouping: any, featuredOnly?: any) { const maxStart = moment(activeDate).set({'h': 0, 'm': 0, 's': 0}); - const groupedItems = {}; + const groupedItems: any = {}; const grouper = Groupers[activeGrouping]; items // Filter out items that didn't match any Planning items - .filter((item) => ( + .filter((item: any) => ( get(item, 'planning_items.length', 0) === 0 || get(item, '_hits.matched_planning_items') == null || get(item, '_hits.matched_planning_items.length', 0) > 0) ) - .forEach((item) => { + .forEach((item: any) => { const itemExtraDates = getExtraDates(item); const itemStartDate = getStartDate(item); const start = item._display_from ? moment(item._display_from) : @@ -748,26 +749,26 @@ export function groupItems(items, activeDate, activeGrouping, featuredOnly) { } }); - Object.keys(groupedItems).forEach((k) => { + Object.keys(groupedItems).forEach((k: any) => { if (featuredOnly) { - groupedItems[k] = groupedItems[k].map((i) => i._id); + groupedItems[k] = groupedItems[k].map((i: any) => i._id); } else { const tbcPartitioned = partition(groupedItems[k], (i) => isItemTBC(i)); groupedItems[k] = [ ...tbcPartitioned[0], ...tbcPartitioned[1], - ].map((i) => i._id); + ].map((i: any) => i._id); } }); return sortBy( - Object.keys(groupedItems).map((k) => ( + Object.keys(groupedItems).map((k: any) => ( { date: k, items: groupedItems[k], _sortDate: moment(k, DATE_FORMAT) })), - (g) => g._sortDate); + (g: any) => g._sortDate); } /** @@ -775,7 +776,7 @@ export function groupItems(items, activeDate, activeGrouping, featuredOnly) { * @param item: Agenda item * @param group: Group Date */ -export function getPlanningItemsByGroup(item, group) { +export function getPlanningItemsByGroup(item: any, group: any) { const planningItems = get(item, 'planning_items') || []; if (planningItems.length === 0) { @@ -783,16 +784,16 @@ export function getPlanningItemsByGroup(item, group) { } // Planning item without coverages - const plansWithoutCoverages = planningItems.filter((p) => + const plansWithoutCoverages = planningItems.filter((p: any) => formatDate(p.planning_date) === group && get(p, 'coverages.length', 0) === 0); const allPlans = keyBy(planningItems, '_id'); - const processed = {}; + const processed: any = {}; // get unique plans for that group based on the coverage. const plansWithCoverages = (item.coverages || []) - .map((coverage) => { - if (isCoverageForExtraDay(coverage, group)) { + .map((coverage: any) => { + if (isCoverageForExtraDay(coverage)) { if (!processed[coverage.planning_id]) { processed[coverage.planning_id] = 1; return allPlans[coverage.planning_id]; @@ -801,12 +802,12 @@ export function getPlanningItemsByGroup(item, group) { } return null; }) - .filter((p) => p); + .filter((p: any) => p); return [...plansWithCoverages, ...plansWithoutCoverages]; } -export function isCoverageOnPreviousDay(coverage, group) { +export function isCoverageOnPreviousDay(coverage: any, group: any) { return ( coverage.scheduled != null && moment(coverage.scheduled).isBefore(moment(group, DATE_FORMAT), 'day') @@ -814,14 +815,14 @@ export function isCoverageOnPreviousDay(coverage, group) { } -export function getCoveragesForDisplay(item, plan, group) { - const currentCoverage = []; - const previousCoverage = []; +export function getCoveragesForDisplay(item: any, plan: any, group: any) { + const currentCoverage: any = []; + const previousCoverage: any = []; // get current and preview coverages (get(item, 'coverages') || []) - .forEach((coverage) => { + .forEach((coverage: any) => { if (!get(plan, 'guid') || coverage.planning_id === get(plan, 'guid')) { - if (isCoverageForExtraDay(coverage, group)) { + if (isCoverageForExtraDay(coverage)) { currentCoverage.push(coverage); } else if (isCoverageOnPreviousDay(coverage, group)) { previousCoverage.push(coverage); @@ -834,14 +835,14 @@ export function getCoveragesForDisplay(item, plan, group) { return {current: currentCoverage, previous: previousCoverage}; } -export function getListItems(groups, itemsById) { - const listItems = []; +export function getListItems(groups: any, itemsById: any) { + const listItems: any = []; - groups.forEach((group) => { - group.items.forEach((_id) => { + groups.forEach((group: any) => { + group.items.forEach((_id: any) => { const plans = getPlanningItemsByGroup(itemsById[_id], group.date); if (plans.length > 0) { - plans.forEach((plan) => { + plans.forEach((plan: any) => { listItems.push({_id, group: group.date, plan}); }); } else { @@ -852,16 +853,16 @@ export function getListItems(groups, itemsById) { return listItems; } -export function isCoverageBeingUpdated(coverage) { +export function isCoverageBeingUpdated(coverage: any) { return get(coverage, 'deliveries[0].delivery_state', null) && !['published', 'corrected'].includes(coverage.deliveries[0].delivery_state); } -export const groupRegions = (filter, aggregations, props) => { +export const groupRegions = (filter: any, aggregations: any, props: any) => { if (props.locators && Object.keys(props.locators).length > 0) { - let regions = sortBy(props.locators.filter((l) => l.state).map((l) => ({...l, 'key': l.name, 'label': l.state})), 'label'); - const others = props.locators.filter((l) => !l.state).map((l) => ({...l, 'key': l.name, 'label': l.country || l.world_region})); - const separator = {'key': 'divider'}; + let regions = sortBy(props.locators.filter((l: any) => l.state).map((l: any) => ({...l, 'key': l.name, 'label': l.state})), 'label'); + const others = props.locators.filter((l: any) => !l.state).map((l: any) => ({...l, 'key': l.name, 'label': l.country || l.world_region})); + const separator: any = {'key': 'divider'}; if (others.length > 0) { if (regions.length > 0) { @@ -876,9 +877,9 @@ export const groupRegions = (filter, aggregations, props) => { return aggregations[filter.field].buckets; }; -export const getRegionName = (key, locator) => locator.label || key; +export const getRegionName = (key: any, locator: any) => locator.label || key; -export const isItemTBC = (item) => ( +export const isItemTBC = (item: any) => ( !get(item, 'event') ? get(item, `planning_items[0].${TO_BE_CONFIRMED_FIELD}`) : get(item, `event.${TO_BE_CONFIRMED_FIELD}`) ); @@ -889,13 +890,13 @@ export const isItemTBC = (item) => ( * @param {String} dateString * @return {String} */ -export function formatCoverageDate(coverage) { +export function formatCoverageDate(coverage: any) { return get(coverage, TO_BE_CONFIRMED_FIELD) ? `${parseDate(coverage.scheduled).format(COVERAGE_DATE_FORMAT)} ${TO_BE_CONFIRMED_TEXT}` : parseDate(coverage.scheduled).format(COVERAGE_DATE_TIME_FORMAT); } -export const getCoverageTooltip = (coverage, beingUpdated) => { +export const getCoverageTooltip = (coverage: any, beingUpdated?: any) => { const slugline = coverage.item_slugline || coverage.slugline; const coverageType = getCoverageDisplayName(coverage.coverage_type); const coverageScheduled = moment(coverage.scheduled); @@ -926,7 +927,7 @@ export const getCoverageTooltip = (coverage, beingUpdated) => { slugline: slugline, }); } else if (coverage.workflow_status === WORKFLOW_STATUS.COMPLETED) { - let deliveryState; + let deliveryState: any; if (get(coverage, 'deliveries.length', 0) > 1) { deliveryState = beingUpdated ? gettext('(update to come)') : gettext('(updated)'); } @@ -940,3 +941,153 @@ export const getCoverageTooltip = (coverage, beingUpdated) => { return gettext('{{ type }} coverage', {type: coverageType}); }; + +function getScheduleType(item: any) { + const start = moment(item.dates.start); + const end = moment(item.dates.end); + const duration = end.diff(start, 'minutes'); + + if (item.dates.all_day) { + return duration === 0 ? SCHEDULE_TYPE.ALL_DAY : SCHEDULE_TYPE.MULTI_DAY; + } + + if (item.dates.no_end_time) { + return SCHEDULE_TYPE.NO_DURATION; + } + + if (duration > DAY_IN_MINUTES || !start.isSame(end, 'day')) { + return SCHEDULE_TYPE.MULTI_DAY; + } + + if (duration === DAY_IN_MINUTES && start.isSame(end, 'day')) { + return SCHEDULE_TYPE.ALL_DAY; + } + + if (duration === 0) { + return SCHEDULE_TYPE.NO_DURATION; + } + + return SCHEDULE_TYPE.REGULAR; +} + +/** + * Format agenda item start and end dates + * + * @param {String} dateString + * @param {String} group: date of the selected event group + * @param {Object} options + * @return {Array} [time string, date string] + */ +export function formatAgendaDate(item: any, group: any, {localTimeZone = true, onlyDates = false}) { + const getFormattedTimezone = (date: any) => { + const tzStr = date.format('z'); + if (tzStr.indexOf('+0') >= 0) { + return tzStr.replace('+0', 'GMT+'); + } + + if (tzStr.indexOf('+') >= 0) { + return tzStr.replace('+', 'GMT+'); + } + + return tzStr; + }; + + const isTBCItem = isItemTBC(item); + let start = parseDate(item.dates.start, item.dates.all_day); + const end = parseDate(item.dates.end, item.dates.all_day || item.dates.no_end_time); + const dateGroup = group ? moment(group, DATE_FORMAT) : null; + + const isGroupBetweenEventDates = dateGroup ? + start.isSameOrBefore(dateGroup, 'day') && end.isSameOrAfter(dateGroup, 'day') : true; + + if (!isGroupBetweenEventDates && hasCoverages(item)) { + // we rendering for extra days + const scheduleDates = item.coverages + .map((coverage: any) => { + if (isCoverageForExtraDay(coverage)) { + return coverage.scheduled; + } + return null; + }) + .filter((d: any) => d) + .sort((a: any, b: any) => { + if (a < b) return -1; + if (a > b) return 1; + return 0; + }); + if (scheduleDates.length > 0) { + start = moment(scheduleDates[0]); + } + } + + const scheduleType = getScheduleType(item); + const startDate = formatDate(start); + const startTime = formatTime(start); + const endDate = formatDate(end); + const endTime = formatTime(end); + const timezone = localTimeZone ? '' : getFormattedTimezone(start); + + switch (true) { + case isTBCItem && startDate !== endDate: + return gettext('{{startDate}} to {{endDate}} (Time to be confirmed)', { + startDate, + endDate, + }); + + case isTBCItem: + return gettext('{{startDate}} (Time to be confirmed)', { + startDate, + }); + + case startDate !== endDate && (item.dates.all_day || onlyDates || (startTime === '00:00' && endTime === '23:59')): + return gettext('{{startDate}} to {{endDate}}', { + startDate, + endDate, + }); + + case startDate === endDate && (item.dates.all_day || onlyDates || scheduleType === SCHEDULE_TYPE.ALL_DAY): + return startDate; + + case item.dates.no_end_time && startDate !== endDate: + return gettext('{{startTime}} {{startDate}} - {{endDate}} {{timezone}}', { + startTime, + startDate, + endDate, + timezone, + }); + + case item.dates.no_end_time || scheduleType === SCHEDULE_TYPE.NO_DURATION: + return gettext('{{startTime}} {{startDate}} {{timezone}}', { + startTime, + startDate, + timezone, + }); + + case scheduleType === SCHEDULE_TYPE.REGULAR: + return gettext('{{startTime}} - {{endTime}} {{startDate}} {{timezone}}', { + startTime, + startDate, + endTime, + timezone, + }); + + case scheduleType === SCHEDULE_TYPE.MULTI_DAY: + return gettext('{{startTime}} {{startDate}} to {{endTime}} {{endDate}} {{timezone}}', { + startTime, + startDate, + endTime, + endDate, + timezone, + }); + + default: + console.warn('not sure about the datetime format', item, scheduleType); + return gettext('{{startTime}} {{startDate}} to {{endTime}} {{endDate}} {{timezone}}', { + startTime, + startDate, + endTime, + endDate, + timezone + }); + } +} diff --git a/assets/am-news/components/AmNewsApp.jsx b/assets/am-news/components/AmNewsApp.tsx similarity index 93% rename from assets/am-news/components/AmNewsApp.jsx rename to assets/am-news/components/AmNewsApp.tsx index 4492e8311..ea189637d 100644 --- a/assets/am-news/components/AmNewsApp.jsx +++ b/assets/am-news/components/AmNewsApp.tsx @@ -43,21 +43,26 @@ import { } from 'search/selectors'; -const modals = { +const modals: any = { shareItem: ShareItemModal, downloadItems: DownloadItemsModal, }; class AmNewsApp extends BaseApp { - constructor(props) { + static propTypes: any; + + modals: any; + tabs: any; + + constructor(props: any) { super(props); this.modals = modals; this.state = {isMobile: false}; // to cater for responsive behaviour during widnow resize this.setIsMobile = this.setIsMobile.bind(this); - this.tabs = this.tabs.filter((t) => get(this.props.advancedSearchTabConfig, t.id, true)); + this.tabs = this.tabs.filter((t: any) => get(this.props.advancedSearchTabConfig, t.id, true)); } - getSnapshotBeforeUpdate(prevProps) { + getSnapshotBeforeUpdate(prevProps: any) { if (prevProps.itemToOpen && !this.props.itemToOpen && noNavigationSelected(this.props.activeNavigation)) { // enable first navigation this.props.toggleNavigation(get(this.props, 'navigations[0]')); @@ -87,13 +92,13 @@ class AmNewsApp extends BaseApp { user={this.props.user} actions={this.filterActions(this.props.itemToOpen)} detailsConfig={this.props.detailsConfig} - onClose={() => this.props.actions.filter(a => a.id === 'open')[0].action(null)} /> + onClose={() => this.props.actions.filter((a: any) => a.id === 'open')[0].action(null)} /> ]); } renderListAndPreview() { let mainClassName = ''; - const panesCount = [this.state.withSidebar, this.props.itemToPreview].filter((x) => x).length; + const panesCount = [this.state.withSidebar, this.props.itemToPreview].filter((x: any) => x).length; if (this.state.isMobile) { mainClassName = classNames('wire-column__main', { 'wire-articles__one-side-pane': panesCount === 1, @@ -203,7 +208,7 @@ class AmNewsApp extends BaseApp { const modal = this.renderModal(this.props.modal); return ( - (this.props.itemToOpen ? this.renderItemDetails() : this.renderListAndPreview()) + (this.props.itemToOpen ? this.renderItemDetails() : this.renderListAndPreview() as any) .concat([ modal, this.renderNavBreadcrumb( @@ -246,7 +251,7 @@ AmNewsApp.propTypes = { context: PropTypes.string, }; -const mapStateToProps = (state) => ({ +const mapStateToProps = (state: any) => ({ state: state, isLoading: state.isLoading, totalItems: state.totalItems, @@ -270,13 +275,15 @@ const mapStateToProps = (state) => ({ context: state.context, }); -const mapDispatchToProps = (dispatch) => ({ +const mapDispatchToProps = (dispatch: any) => ({ fetchItems: () => dispatch(fetchItems()), - setQuery: (query) => dispatch(setQuery(query)), + setQuery: (query: any) => dispatch(setQuery(query)), actions: getItemActions(dispatch), fetchMoreItems: () => dispatch(fetchMoreItems()), closePreview: () => dispatch(previewItem(null)), - toggleNavigation: (navigation) => dispatch(toggleNavigation(navigation)), + toggleNavigation: (navigation: any) => dispatch(toggleNavigation(navigation)), }); -export default connect(mapStateToProps, mapDispatchToProps)(AmNewsApp); +const component: React.ComponentType = connect(mapStateToProps, mapDispatchToProps)(AmNewsApp); + +export default component; diff --git a/assets/am-news/components/AmNewsIcon.jsx b/assets/am-news/components/AmNewsIcon.tsx similarity index 91% rename from assets/am-news/components/AmNewsIcon.jsx rename to assets/am-news/components/AmNewsIcon.tsx index a45a4ca07..bae2f38ac 100644 --- a/assets/am-news/components/AmNewsIcon.jsx +++ b/assets/am-news/components/AmNewsIcon.tsx @@ -4,7 +4,7 @@ import classNames from 'classnames'; import {bem} from 'ui/utils'; -const AMNewsIcon = ({iconType, borderRight, toolTip}) => { +const AMNewsIcon = ({iconType, borderRight, toolTip}: any) => { const css = classNames( 'wire-articles__item__am-icons', bem('wire-articles__item', 'meta-time', {'border-right': borderRight}) diff --git a/assets/am-news/components/AmNewsList.jsx b/assets/am-news/components/AmNewsList.tsx similarity index 88% rename from assets/am-news/components/AmNewsList.jsx rename to assets/am-news/components/AmNewsList.tsx index b532a4499..415fd4040 100644 --- a/assets/am-news/components/AmNewsList.jsx +++ b/assets/am-news/components/AmNewsList.tsx @@ -14,11 +14,11 @@ import {getContextName} from 'selectors'; const PREVIEW_TIMEOUT = 500; // time to preview an item after selecting using kb const CLICK_TIMEOUT = 200; // time when we wait for double click after click -const itemsSelector = (state) => state.items.map((_id) => state.itemsById[_id]); +const itemsSelector = (state: any) => state.items.map((_id: any) => state.itemsById[_id]); const groupedItemsSelector = createSelector( [itemsSelector], - (items) => { - const groupByDate = (item) => { + (items: any) => { + const groupByDate = (item: any) => { const date = moment(item.versioncreated).set({'h': 0, 'm': 0, 's': 0}); return formatDate(date); }; @@ -30,8 +30,12 @@ const groupedItemsSelector = createSelector( ); -class AmNewsList extends React.Component { - constructor(props) { +class AmNewsList extends React.Component { + static propTypes: any; + previewTimeout: any; + clickTimeout: any; + + constructor(props: any) { super(props); this.state = {actioningItem: null}; @@ -43,7 +47,7 @@ class AmNewsList extends React.Component { this.filterActions = this.filterActions.bind(this); } - onKeyDown(event) { + onKeyDown(event: any) { let diff = 0; switch (event.key) { case 'ArrowDown': @@ -103,7 +107,7 @@ class AmNewsList extends React.Component { } } - onItemClick(item) { + onItemClick(item: any) { const itemId = item._id; this.setState({actioningItem: null}); this.cancelPreviewTimeout(); @@ -120,13 +124,13 @@ class AmNewsList extends React.Component { }, CLICK_TIMEOUT); } - onItemDoubleClick(item) { + onItemDoubleClick(item: any) { this.cancelClickTimeout(); this.props.dispatch(setActive(item._id)); this.props.dispatch(openItem(item)); } - onActionList(event, item) { + onActionList(event: any, item: any) { event.stopPropagation(); if (this.state.actioningItem && this.state.actioningItem._id === item._id) { this.setState({actioningItem: null}); @@ -135,8 +139,8 @@ class AmNewsList extends React.Component { } } - filterActions(item) { - return this.props.actions.filter((action) => !action.when || action.when(this.props, item)); + filterActions(item: any) { + return this.props.actions.filter((action: any) => !action.when || action.when(this.props, item)); } render() { @@ -144,8 +148,8 @@ class AmNewsList extends React.Component { const todayMoment = moment(); const today = formatDate(todayMoment); - const groups = Object.keys(groupedItems).map((keyDate) => { - const groupItem = []; + const groups = Object.keys(groupedItems).map((keyDate: any) => { + const groupItem: Array = []; if(today !== keyDate) { const keyDateMoment = moment(keyDate, DATE_FORMAT); @@ -159,7 +163,7 @@ class AmNewsList extends React.Component { groupItem.push(
{ - groupedItems[keyDate].map((item) => + groupedItems[keyDate].map((item: any) => ({ +const mapStateToProps = (state: any) => ({ items: state.items, itemsById: state.itemsById, activeItem: state.activeItem, @@ -234,4 +238,6 @@ const mapStateToProps = (state) => ({ contextName: getContextName(state), }); -export default connect(mapStateToProps)(AmNewsList); +const component: React.ComponentType = connect(mapStateToProps)(AmNewsList); + +export default component; diff --git a/assets/am-news/components/AmNewsListItem.jsx b/assets/am-news/components/AmNewsListItem.tsx similarity index 93% rename from assets/am-news/components/AmNewsListItem.jsx rename to assets/am-news/components/AmNewsListItem.tsx index 7a052aa89..17e7584aa 100644 --- a/assets/am-news/components/AmNewsListItem.jsx +++ b/assets/am-news/components/AmNewsListItem.tsx @@ -22,8 +22,12 @@ import { } from '../utils'; -class AmNewsListItem extends React.Component { - constructor(props) { +class AmNewsListItem extends React.Component { + static propTypes: any; + wordCount: any; + articleElem: any; + + constructor(props: any) { super(props); this.wordCount = wordCount(props.item); this.state = {previousVersions: false}; @@ -31,7 +35,7 @@ class AmNewsListItem extends React.Component { this.togglePreviousVersions = this.togglePreviousVersions.bind(this); } - onKeyDown(event) { + onKeyDown(event: any) { switch (event.key) { case ' ': // on space toggle selected item this.props.toggleSelected(); @@ -44,7 +48,7 @@ class AmNewsListItem extends React.Component { event.preventDefault(); } - togglePreviousVersions(event) { + togglePreviousVersions(event: any) { event.stopPropagation(); this.setState({previousVersions: !this.state.previousVersions}); } @@ -55,7 +59,7 @@ class AmNewsListItem extends React.Component { } } - stopPropagation(event) { + stopPropagation(event: any) { event.stopPropagation(); } @@ -93,12 +97,12 @@ class AmNewsListItem extends React.Component { return (
this.articleElem = elem} + ref={(elem: any) => this.articleElem = elem} onClick={() => onClick(item)} onDoubleClick={() => onDoubleClick(item)} onKeyDown={this.onKeyDown} > -
+

@@ -138,7 +142,7 @@ class AmNewsListItem extends React.Component { showActions={this.props.showActions} /> - {this.props.actions.map((action) => + {this.props.actions.map((action: any) => action.shortcut && { +const Navigations: React.ComponentType = ({navigations=[], activeNavigation, toggleNavigation, fetchItems}) => { - const tabs = navigations.map((navigation) => ( + const tabs = navigations.map((navigation: any) => (
  • { + onClick={(event: any) => { event.preventDefault(); toggleNavigation(navigation); fetchItems(); diff --git a/assets/am-news/index.js b/assets/am-news/index.ts similarity index 90% rename from assets/am-news/index.js rename to assets/am-news/index.ts index 08a769090..55b529fc4 100644 --- a/assets/am-news/index.js +++ b/assets/am-news/index.ts @@ -1,6 +1,7 @@ import {get, startsWith} from 'lodash'; -import {createStore, render, initWebSocket, getInitData, closeItemOnMobile, isMobilePhone} from '../utils'; +import {createStore, render, getInitData, closeItemOnMobile, isMobilePhone} from '../utils'; +import {initWebSocket} from 'websocket'; import {getReadItems} from 'local-store'; import AmNewsApp from './components/AmNewsApp'; @@ -41,7 +42,7 @@ if (navigationId) { } // handle history -window.onpopstate = function(event) { +window.onpopstate = function(event: any) { if (event.state) { closeItemOnMobile(store.dispatch, event.state, openItemDetails, previewItem); if (!isMobilePhone()) { diff --git a/assets/am-news/style.js b/assets/am-news/style.ts similarity index 100% rename from assets/am-news/style.js rename to assets/am-news/style.ts diff --git a/assets/am-news/utils.js b/assets/am-news/utils.ts similarity index 56% rename from assets/am-news/utils.js rename to assets/am-news/utils.ts index 6db63c227..8be7b41bd 100644 --- a/assets/am-news/utils.js +++ b/assets/am-news/utils.ts @@ -2,16 +2,16 @@ import {get} from 'lodash'; import {gettext} from '../utils'; -export const isFuel = (item) => (get(item, 'slugline') || '').match(/fuel/gi); -export const isWeather = (item) => (get(item, 'slugline') || '').match(/weather|observation|forecast/gi); -export const isTraffic = (item) => (get(item, 'slugline') || '').match(/traffic/gi); -export const isPublicTransport = (item) => (get(item, 'slugline') || '').match(/public transport/gi); -export const isAlert = (item) => !isFuel(item) && (get(item, 'slugline') || '').match(/^alert /gi); -export const isQuote = (item) => !isFuel(item) && (get(item, 'slugline') || '').match(/^quote /gi); -export const isHeadlines = (item) => !isFuel(item) && (get(item, 'slugline') || '').match(/headlines/gi); -export const isDataItem = (item) => (isFuel(item) || isWeather(item)); - -export const getAMNewsIcon = (item) => { +export const isFuel = (item: any) => (get(item, 'slugline') || '').match(/fuel/gi); +export const isWeather = (item: any) => (get(item, 'slugline') || '').match(/weather|observation|forecast/gi); +export const isTraffic = (item: any) => (get(item, 'slugline') || '').match(/traffic/gi); +export const isPublicTransport = (item: any) => (get(item, 'slugline') || '').match(/public transport/gi); +export const isAlert = (item: any) => !isFuel(item) && (get(item, 'slugline') || '').match(/^alert /gi); +export const isQuote = (item: any) => !isFuel(item) && (get(item, 'slugline') || '').match(/^quote /gi); +export const isHeadlines = (item: any) => !isFuel(item) && (get(item, 'slugline') || '').match(/headlines/gi); +export const isDataItem = (item: any) => (isFuel(item) || isWeather(item)); + +export const getAMNewsIcon = (item: any) => { let iconType = 'text'; if (isDataItem(item)) { @@ -33,7 +33,7 @@ export const getAMNewsIcon = (item) => { return iconType; }; -export const getAMNewsToolTip = (item) => { +export const getAMNewsToolTip = (item: any) => { let iconType = gettext('Text'); if (isDataItem(item)) { diff --git a/assets/analytics.js b/assets/analytics.ts similarity index 70% rename from assets/analytics.js rename to assets/analytics.ts index 2c91966f2..8ab34a06a 100644 --- a/assets/analytics.js +++ b/assets/analytics.ts @@ -1,11 +1,11 @@ import {get} from 'lodash'; class Analytics { - _event(name, params) { + _event(name: any, params?: any) { if (window.gtag) { const company = get(window, 'profileData.companyName', 'none'); const user = get(window, 'profileData.user.first_name', 'unknown'); - const userParams = { + const userParams: any = { event_category: company, company: company, user: user, @@ -15,28 +15,28 @@ class Analytics { } } - event(name, label, params) { + event(name: any, label: any, params: any) { this._event(name, Object.assign({ event_label: label, }, params)); } - itemEvent(name, item, params) { + itemEvent(name: any, item: any, params?: any) { this.event(name, item.headline || item.name || item.slugline, params); } - timingComplete(name, value) { + timingComplete(name: any, value: any) { this._event('timing_complete', {name, value}); } - pageview(title, path) { + pageview(title?: any, path?: any) { this._event('page_view', { page_title: title, page_path: path, }); } - itemView(item) { + itemView(item: any) { if (item) { this.pageview(item.headline || item.slugline, `/${item._type}/${item._id}`); } else { @@ -44,14 +44,14 @@ class Analytics { } } - sendEvents(events) { - events.forEach((event) => { + sendEvents(events: any) { + events.forEach((event: any) => { this._event(event); }); } - multiItemEvent(event, items) { - items.forEach((item) => item && this.itemEvent(event, item)); + multiItemEvent(event: any, items: any) { + items.forEach((item: any) => item && this.itemEvent(event, item)); } } diff --git a/assets/cards/actions.js b/assets/cards/actions.ts similarity index 75% rename from assets/cards/actions.js rename to assets/cards/actions.ts index 7cdbbbbb8..e2403d49d 100644 --- a/assets/cards/actions.js +++ b/assets/cards/actions.ts @@ -5,12 +5,12 @@ import {searchQuerySelector} from 'search/selectors'; export const SELECT_CARD = 'SELECT_CARD'; -export function selectCard(id) { +export function selectCard(id: any) { return {type: SELECT_CARD, id}; } export const EDIT_CARD = 'EDIT_CARD'; -export function editCard(event) { +export function editCard(event: any) { return {type: EDIT_CARD, event}; } @@ -20,7 +20,7 @@ export function newCard() { } export const CANCEL_EDIT = 'CANCEL_EDIT'; -export function cancelEdit(event) { +export function cancelEdit(event: any) { return {type: CANCEL_EDIT, event}; } @@ -30,22 +30,22 @@ export function queryCards() { } export const GET_CARDS = 'GET_CARDS'; -export function getCards(data) { +export function getCards(data: any) { return {type: GET_CARDS, data}; } export const GET_PRODUCTS = 'GET_PRODUCTS'; -export function getProducts(data) { +export function getProducts(data: any) { return {type: GET_PRODUCTS, data}; } export const SET_ERROR = 'SET_ERROR'; -export function setError(errors) { +export function setError(errors: any) { return {type: SET_ERROR, errors}; } export const GET_NAVIGATIONS = 'GET_NAVIGATIONS'; -export function getNavigations(data) { +export function getNavigations(data: any) { return {type: GET_NAVIGATIONS, data}; } @@ -54,13 +54,13 @@ export function getNavigations(data) { * */ export function fetchCards() { - return function (dispatch, getState) { + return function (dispatch: any, getState: any) { dispatch(queryCards()); const query = searchQuerySelector(getState()) || ''; return server.get(`/cards/search?q=${query}`) - .then((data) => dispatch(getCards(data))) - .catch((error) => errorHandler(error, dispatch, setError)); + .then((data: any) => dispatch(getCards(data))) + .catch((error: any) => errorHandler(error, dispatch, setError)); }; } @@ -70,21 +70,21 @@ export function fetchCards() { * */ export function postCard() { - return function (dispatch, getState) { + return function (dispatch: any, getState: any) { const card = getState().cardToEdit; - let data = new FormData(); + const data = new FormData(); data.append('card', JSON.stringify(card)); if (card.type === '4-photo-gallery') { - const errors = { + const errors: any = { config: { sources: [{}, {}, {}, {}] } }; let errorFound = false; let mediaCount = 0; - (card.config.sources || []).forEach((source, index) => { + (card.config.sources || []).forEach((source: any, index: any) => { if (!source.url && parseInt(source.count, 10) > 0) { errors.config.sources[index].url = gettext('Invalid Url.'); errorFound = true; @@ -116,8 +116,8 @@ export function postCard() { } if (card.type === '2x2-events') { - [...Array(4)].forEach((_, i) => { - const input = document.getElementById(`config.events[${i}].file`); + [...Array(4)].forEach((_: any, i: any) => { + const input: any = document.getElementById(`config.events[${i}].file`); if (input && input.files.length > 0) { data.append(`file${i}`, input.files[0]); } @@ -135,7 +135,7 @@ export function postCard() { } dispatch(fetchCards()); }) - .catch((error) => errorHandler(error, dispatch, setError)); + .catch((error: any) => errorHandler(error, dispatch, setError)); }; } @@ -146,17 +146,17 @@ export function postCard() { * */ export function deleteCard() { - return function (dispatch, getState) { + return function (dispatch: any, getState: any) { const card = getState().cardToEdit; const url = `/cards/${card._id}`; - return server.del(url) + return (server as any).del(url) .then(() => { notify.success(gettext('Card deleted successfully')); dispatch(fetchCards()); }) - .catch((error) => errorHandler(error, dispatch, setError)); + .catch((error: any) => errorHandler(error, dispatch, setError)); }; } @@ -166,18 +166,18 @@ export function deleteCard() { * */ export function fetchProducts() { - return function (dispatch) { + return function (dispatch: any) { return server.get('/products/search') - .then((data) => { + .then((data: any) => { dispatch(getProducts(data)); }) - .catch((error) => errorHandler(error, dispatch, setError)); + .catch((error: any) => errorHandler(error, dispatch, setError)); }; } -export function initViewData(data) { - return function (dispatch) { +export function initViewData(data: any): any { + return function (dispatch: any) { dispatch(getCards(data.cards)); dispatch(getProducts(data.products)); dispatch(getNavigations(data.navigations)); diff --git a/assets/cards/components/CardList.jsx b/assets/cards/components/CardList.tsx similarity index 91% rename from assets/cards/components/CardList.jsx rename to assets/cards/components/CardList.tsx index 93325883d..853ed62b5 100644 --- a/assets/cards/components/CardList.jsx +++ b/assets/cards/components/CardList.tsx @@ -4,8 +4,8 @@ import CardListItem from './CardListItem'; import {gettext} from 'utils'; -function CardList({cards, products, onClick, activeCardId}) { - const list = cards.map((card) => +function CardList({cards, products, onClick, activeCardId}: any) { + const list = cards.map((card: any) => product._id == id); +function getProductName(products: any, id: any) { + const product = products.find((product: any) => product._id == id); return product && product.name; } -function CardListItem({card, products, isActive, onClick}) { +function CardListItem({card, products, isActive, onClick}: any) { return ( onClick(card._id)}> {card.label} {gettext(card.type)} diff --git a/assets/cards/components/Cards.jsx b/assets/cards/components/Cards.tsx similarity index 83% rename from assets/cards/components/Cards.jsx rename to assets/cards/components/Cards.tsx index dc8f2b1c5..bb3a9a6ad 100644 --- a/assets/cards/components/Cards.jsx +++ b/assets/cards/components/Cards.tsx @@ -20,8 +20,9 @@ import EditCard from './EditCard'; import CardList from './CardList'; import SearchResults from 'search/components/SearchResults'; -class Cards extends React.Component { - constructor(props, context) { +class Cards extends React.Component { + static propTypes: any; + constructor(props: any, context: any) { super(props, context); this.isFormValid = this.isFormValid.bind(this); @@ -31,7 +32,7 @@ class Cards extends React.Component { isFormValid() { let valid = true; - let errors = {}; + const errors: any = {}; if (!this.props.cardToEdit.label) { errors.label = [gettext('Please provide card label')]; @@ -48,7 +49,7 @@ class Cards extends React.Component { return valid; } - save(event) { + save(event: any) { event.preventDefault(); if (!this.isFormValid()) { @@ -58,7 +59,7 @@ class Cards extends React.Component { this.props.saveCard(); } - deleteCard(event) { + deleteCard(event: any) { event.preventDefault(); if (confirm(gettext('Would you like to delete card: {{label}}', {label: this.props.cardToEdit.label}))) { @@ -67,8 +68,8 @@ class Cards extends React.Component { } render() { - const progressStyle = {width: '25%'}; - const cardFilter = (card) => !this.props.activeDashboard || + const progressStyle: any = {width: '25%'}; + const cardFilter = (card: any) => !this.props.activeDashboard || get(card, 'dashboard', 'newsroom') === this.props.activeDashboard; return ( @@ -133,8 +134,8 @@ Cards.propTypes = { dashboards: PropTypes.arrayOf(PropTypes.object), }; -const mapStateToProps = (state) => ({ - cards: state.cards.map((id) => state.cardsById[id]), +const mapStateToProps = (state: any) => ({ + cards: state.cards.map((id: any) => state.cardsById[id]), cardToEdit: state.cardToEdit, activeCardId: state.activeCardId, isLoading: state.isLoading, @@ -147,14 +148,16 @@ const mapStateToProps = (state) => ({ dashboards: state.dashboards.list, }); -const mapDispatchToProps = (dispatch) => ({ - selectCard: (_id) => dispatch(selectCard(_id)), - editCard: (event) => dispatch(editCard(event)), - saveCard: (type) => dispatch(postCard(type)), - deleteCard: (type) => dispatch(deleteCard(type)), +const mapDispatchToProps = (dispatch: any) => ({ + selectCard: (_id: any) => dispatch(selectCard(_id)), + editCard: (event: any) => dispatch(editCard(event)), + saveCard: (type: any) => dispatch(postCard()), + deleteCard: (type: any) => dispatch(deleteCard()), newCard: () => dispatch(newCard()), - cancelEdit: (event) => dispatch(cancelEdit(event)), + cancelEdit: (event: any) => dispatch(cancelEdit(event)), dispatch: dispatch, }); -export default connect(mapStateToProps, mapDispatchToProps)(Cards); +const component: React.ComponentType = connect(mapStateToProps, mapDispatchToProps)(Cards); + +export default component; diff --git a/assets/cards/components/CardsApp.jsx b/assets/cards/components/CardsApp.tsx similarity index 87% rename from assets/cards/components/CardsApp.jsx rename to assets/cards/components/CardsApp.tsx index 8676875b6..b0ee84cd0 100644 --- a/assets/cards/components/CardsApp.jsx +++ b/assets/cards/components/CardsApp.tsx @@ -13,8 +13,9 @@ import ListBar from 'components/ListBar'; import DashboardSwitch from 'features/dashboard/DashboardSwitch'; -class CardsApp extends React.Component { - constructor(props, context) { +class CardsApp extends React.Component { + static propTypes: any; + constructor(props: any, context: any) { super(props, context); } @@ -45,12 +46,12 @@ CardsApp.propTypes = { activeDashboard: PropTypes.string, }; -const mapStateToProps = (state) => ({ +const mapStateToProps = (state: any) => ({ dashboards: state.dashboards.list, activeDashboard: state.dashboards.active, }); -const mapDispatchToProps = { +const mapDispatchToProps: any = { newCard, fetchCards, setQuery: setSearchQuery, diff --git a/assets/cards/components/EditCard.jsx b/assets/cards/components/EditCard.tsx similarity index 91% rename from assets/cards/components/EditCard.jsx rename to assets/cards/components/EditCard.tsx index 4e0c8f244..2422f509f 100644 --- a/assets/cards/components/EditCard.jsx +++ b/assets/cards/components/EditCard.tsx @@ -13,22 +13,23 @@ import { } from 'components/cards/utils'; -class EditCard extends React.Component { - constructor(props) { +class EditCard extends React.Component { + static propTypes: any; + constructor(props: any) { super(props); } render() { - const dashboard = (get(this.props, 'dashboards') || []).find((d) => d._id === this.props.card.dashboard); + const dashboard = (get(this.props, 'dashboards') || []).find((d: any) => d._id === this.props.card.dashboard); const cardType = this.props.card.type || ''; const CardComponent = getCardEditComponent(cardType); - const cardTypes = CARD_TYPES.filter( - (card) => dashboard.cards.includes(card._id) - ).map((c) => ({value: c._id, text: c.text})); + const cardTypes: any = CARD_TYPES.filter( + (card: any) => dashboard.cards.includes(card._id) + ).map((c: any) => ({value: c._id, text: c.text})); cardTypes.unshift({value: '', text: '', component: getCardEditComponent('')}); - const cardProps = { + const cardProps: any = { card: this.props.card, onChange: this.props.onChange, errors: this.props.errors diff --git a/assets/cards/index.js b/assets/cards/index.ts similarity index 100% rename from assets/cards/index.js rename to assets/cards/index.ts diff --git a/assets/cards/reducers.js b/assets/cards/reducers.ts similarity index 92% rename from assets/cards/reducers.js rename to assets/cards/reducers.ts index 8d23e0aac..5cdca88a0 100644 --- a/assets/cards/reducers.js +++ b/assets/cards/reducers.ts @@ -18,7 +18,7 @@ import {searchReducer} from 'search/reducers'; import {getCard} from 'components/cards/utils'; -const initialState = { +const initialState: any = { query: null, cards: [], cardsById: {}, @@ -32,11 +32,11 @@ const initialState = { search: searchReducer(undefined, undefined, 'settings'), }; -export default function cardReducer(state = initialState, action) { +export default function cardReducer(state: any = initialState, action: any) { switch (action.type) { case SELECT_CARD: { - const defaultCard = { + const defaultCard: any = { label: '', type: '', config: {}, @@ -53,8 +53,8 @@ export default function cardReducer(state = initialState, action) { case EDIT_CARD: { const target = action.event.target; const field = target.name; - let card = {...state.cardToEdit}; - let size = get(getCard(card.type), 'size', 0); + const card: any = {...state.cardToEdit}; + const size = get(getCard(card.type), 'size', 0); if (!card.dashboard) { card.dashboard = 'newsroom'; @@ -93,7 +93,7 @@ export default function cardReducer(state = initialState, action) { } case NEW_CARD: { - const cardToEdit = { + const cardToEdit: any = { label: '', type: '', config: {}, @@ -119,7 +119,7 @@ export default function cardReducer(state = initialState, action) { case GET_CARDS: { const cardsById = Object.assign({}, state.cardsById); - const cards = action.data.map((card) => { + const cards = action.data.map((card: any) => { cardsById[card._id] = card; return card._id; }); diff --git a/assets/common.js b/assets/common.ts similarity index 95% rename from assets/common.js rename to assets/common.ts index b5f56f471..e057d7785 100644 --- a/assets/common.js +++ b/assets/common.ts @@ -1,5 +1,4 @@ import 'alertifyjs'; -import 'babel-polyfill'; import 'bootstrap'; import 'classnames'; diff --git a/assets/companies/actions.js b/assets/companies/actions.ts similarity index 70% rename from assets/companies/actions.js rename to assets/companies/actions.ts index 037dcd605..819ff0fa1 100644 --- a/assets/companies/actions.js +++ b/assets/companies/actions.ts @@ -4,29 +4,29 @@ import {searchQuerySelector} from 'search/selectors'; export const SELECT_COMPANY = 'SELECT_COMPANY'; -export function selectCompany(id) { - return function (dispatch) { +export function selectCompany(id: any) { + return function (dispatch: any) { dispatch(select(id)); dispatch(fetchCompanyUsers(id)); }; } -function select(id) { +function select(id: any) { return {type: SELECT_COMPANY, id}; } export const EDIT_COMPANY = 'EDIT_COMPANY'; -export function editCompany(event) { +export function editCompany(event: any) { return {type: EDIT_COMPANY, event}; } export const TOGGLE_COMPANY_SECTION = 'TOGGLE_COMPANY_SECTION'; -export function toggleCompanySection(sectionId) { +export function toggleCompanySection(sectionId: any) { return {type: TOGGLE_COMPANY_SECTION, sectionId: sectionId}; } export const TOGGLE_COMPANY_PRODUCT = 'TOGGLE_COMPANY_PRODUCT'; -export function toggleCompanyProduct(productId, sectionId, enable) { +export function toggleCompanyProduct(productId: any, sectionId: any, enable: any) { return {type: TOGGLE_COMPANY_PRODUCT, payload: { productId: productId, sectionId: sectionId, @@ -35,7 +35,7 @@ export function toggleCompanyProduct(productId, sectionId, enable) { } export const UPDATE_COMPANY_SEATS = 'UPDATE_COMPANY_SEATS'; -export function updateCompanySeats(productId, seats) { +export function updateCompanySeats(productId: any, seats: any) { return {type: UPDATE_COMPANY_SEATS, payload: { productId: productId, seats: seats, @@ -43,12 +43,12 @@ export function updateCompanySeats(productId, seats) { } export const NEW_COMPANY = 'NEW_COMPANY'; -export function newCompany(data) { +export function newCompany(data: any) { return {type: NEW_COMPANY, data}; } export const CANCEL_EDIT = 'CANCEL_EDIT'; -export function cancelEdit(event) { +export function cancelEdit(event?: any) { return {type: CANCEL_EDIT, event}; } @@ -58,22 +58,22 @@ export function queryCompanies() { } export const GET_COMPANIES = 'GET_COMPANIES'; -export function getCompanies(data) { +export function getCompanies(data: any) { return {type: GET_COMPANIES, data}; } export const GET_COMPANY_USERS = 'GET_COMPANY_USERS'; -export function getCompanyUsers(data) { +export function getCompanyUsers(data: any) { return {type: GET_COMPANY_USERS, data}; } export const GET_PRODUCTS = 'GET_PRODUCTS'; -export function getProducts(data) { +export function getProducts(data: any) { return {type: GET_PRODUCTS, data}; } export const SET_ERROR = 'SET_ERROR'; -export function setError(errors) { +export function setError(errors: any) { return {type: SET_ERROR, errors}; } @@ -83,15 +83,15 @@ export function setError(errors) { * */ export function fetchCompanies() { - return function (dispatch, getState) { + return function (dispatch: any, getState: any) { dispatch(queryCompanies()); const query = searchQuerySelector(getState()) || ''; return server.get(`/companies/search?q=${query}`) - .then((data) => { + .then((data: any) => { dispatch(getCompanies(data)); }) - .catch((error) => errorHandler(error, dispatch, setError)); + .catch((error: any) => errorHandler(error, dispatch, setError)); }; } @@ -100,17 +100,17 @@ export function fetchCompanies() { * * @param {String} companyId */ -export function fetchCompanyUsers(companyId, force = false) { - return function (dispatch, getState) { +export function fetchCompanyUsers(companyId: any, force: any = false) { + return function (dispatch: any, getState: any) { if (!force && !getState().companiesById[companyId].name) { return; } return server.get(`/companies/${companyId}/users`) - .then((data) => { + .then((data: any) => { return dispatch(getCompanyUsers(data)); }) - .catch((error) => errorHandler(error, dispatch, setError)); + .catch((error: any) => errorHandler(error, dispatch, setError)); }; } @@ -120,7 +120,7 @@ export function fetchCompanyUsers(companyId, force = false) { * */ export function postCompany() { - return function (dispatch, getState) { + return function (dispatch: any, getState: any) { const company = getState().companyToEdit; const url = `/companies/${company._id ? company._id : 'new'}`; @@ -135,7 +135,7 @@ export function postCompany() { dispatch(fetchCompanies()); dispatch(cancelEdit()); }) - .catch((error) => errorHandler(error, dispatch, setError)); + .catch((error: any) => errorHandler(error, dispatch, setError)); }; } @@ -145,12 +145,12 @@ export function postCompany() { * */ export function fetchProducts() { - return function (dispatch) { + return function (dispatch: any) { return server.get('/products/search') - .then((data) => { + .then((data: any) => { dispatch(getProducts(data)); }) - .catch((error) => errorHandler(error, dispatch, setError)); + .catch((error: any) => errorHandler(error, dispatch, setError)); }; } @@ -160,19 +160,19 @@ export function fetchProducts() { * */ export function deleteCompany() { - return function (dispatch, getState) { + return function (dispatch: any, getState: any) { const company = getState().companyToEdit; const url = `/companies/${company._id}`; - return server.del(url) + return (server as any).del(url) .then(() => { notify.success(gettext('Company deleted successfully')); dispatch(fetchCompanies()); }) - .catch((error) => { + .catch((error: any) => { if (error.response.status == 403) { - error.response.json().then(function(data) { + error.response.json().then(function(data: any) { notify.error(data['error']); }); } @@ -182,6 +182,6 @@ export function deleteCompany() { } export const INIT_VIEW_DATA = 'INIT_VIEW_DATA'; -export function initViewData(data) { +export function initViewData(data: any) { return {type: INIT_VIEW_DATA, data}; } diff --git a/assets/companies/components/CompaniesApp.jsx b/assets/companies/components/CompaniesApp.tsx similarity index 89% rename from assets/companies/components/CompaniesApp.jsx rename to assets/companies/components/CompaniesApp.tsx index 76fa91c22..942cdf7ed 100644 --- a/assets/companies/components/CompaniesApp.jsx +++ b/assets/companies/components/CompaniesApp.tsx @@ -20,8 +20,8 @@ function CompaniesApp({ activeQuery, totalCompanies, companyToEdit, - countries -}) { + countries, +}: any) { return ( )} -
  • )} {!companyToEdit ? null : ( - + )}

    @@ -69,7 +69,7 @@ CompaniesApp.propTypes = { countries: PropTypes.array, }; -const mapStateToProps = (state) => ({ +const mapStateToProps = (state: any) => ({ isLoading: state.isLoading, totalCompanies: state.totalCompanies, activeQuery: searchQuerySelector(state), @@ -77,10 +77,12 @@ const mapStateToProps = (state) => ({ countries: state.countries, }); -const mapDispatchToProps = { +const mapDispatchToProps: any = { newCompany: newCompany, fetchCompanies: fetchCompanies, setQuery: setSearchQuery, }; -export default connect(mapStateToProps, mapDispatchToProps)(CompaniesApp); +const component: React.ComponentType = connect(mapStateToProps, mapDispatchToProps)(CompaniesApp); + +export default component; \ No newline at end of file diff --git a/assets/companies/components/CompanyList.jsx b/assets/companies/components/CompanyList.tsx similarity index 81% rename from assets/companies/components/CompanyList.jsx rename to assets/companies/components/CompanyList.tsx index d978ff502..fd11f48c1 100644 --- a/assets/companies/components/CompanyList.jsx +++ b/assets/companies/components/CompanyList.tsx @@ -9,14 +9,14 @@ import {companiesSubscriberIdEnabled} from 'ui/selectors'; import CompanyListItem from './CompanyListItem'; -function CompanyList({companies, selectCompany, activeCompanyId, companyTypes, showSubscriberId, countries}) { - const list = companies.map((company) => +function CompanyList({companies, selectCompany, activeCompanyId, companyTypes, showSubscriberId, countries}: any) { + const list = companies.map((company: any) => ctype.id === company.company_type)} + type={companyTypes.find((ctype: any) => ctype.id === company.company_type)} showSubscriberId={showSubscriberId} countries = {countries} /> @@ -27,7 +27,7 @@ function CompanyList({companies, selectCompany, activeCompanyId, companyTypes, s
    @@ -60,15 +60,17 @@ CompanyList.propTypes = { countries: PropTypes.array }; -const mapStateToProps = (state) => ({ - companies: state.companies.map((id) => state.companiesById[id]), +const mapStateToProps = (state: any) => ({ + companies: state.companies.map((id: any) => state.companiesById[id]), activeCompanyId: state.activeCompanyId, companyTypes: state.companyTypes, showSubscriberId: companiesSubscriberIdEnabled(state), }); -const mapDispatchToProps = { +const mapDispatchToProps: any = { selectCompany: selectCompany, }; -export default connect(mapStateToProps, mapDispatchToProps)(CompanyList); +const component: React.ComponentType = connect(mapStateToProps, mapDispatchToProps)(CompanyList); + +export default component; diff --git a/assets/companies/components/CompanyListItem.jsx b/assets/companies/components/CompanyListItem.tsx similarity index 95% rename from assets/companies/components/CompanyListItem.jsx rename to assets/companies/components/CompanyListItem.tsx index 6ee7ba65f..bb5e7d99c 100644 --- a/assets/companies/components/CompanyListItem.jsx +++ b/assets/companies/components/CompanyListItem.tsx @@ -4,19 +4,19 @@ import classNames from 'classnames'; import {gettext, shortDate, isInPast} from 'utils'; import {getCountryLabel} from '../utils'; -function CompanyListItem({company, type, isActive, onClick, showSubscriberId, countries}) { +function CompanyListItem({company, type, isActive, onClick, showSubscriberId, countries}: any) { return ( onClick(company._id)} - tabIndex="0" + tabIndex={0} data-test-id={`company-list-item--${company._id}`} > {showSubscriberId && } - diff --git a/assets/companies/components/CompanyPermissions.jsx b/assets/companies/components/CompanyPermissions.tsx similarity index 94% rename from assets/companies/components/CompanyPermissions.jsx rename to assets/companies/components/CompanyPermissions.tsx index b04c4b78d..db902a702 100644 --- a/assets/companies/components/CompanyPermissions.jsx +++ b/assets/companies/components/CompanyPermissions.tsx @@ -13,9 +13,9 @@ function CompanyPermissions({ toggleCompanySection, toggleCompanyProduct, updateCompanySeats, -}) { - const productsEnabled = (company.products || []).map((product) => product._id); - const seats = (company.products || []).reduce((productSeats, product) => { +}: any) { + const productsEnabled = (company.products || []).map((product: any) => product._id); + const seats = (company.products || []).reduce((productSeats: any, product: any) => { productSeats[product._id] = product.seats; return productSeats; @@ -39,7 +39,7 @@ function CompanyPermissions({
  • @@ -75,7 +75,7 @@ function CompanyPermissions({
      - {sections.map((item) => ( + {sections.map((item: any) => (
    • - {sections.filter((section) => (company.sections || {})[section._id] === true).map((section) => ( + {sections.filter((section: any) => (company.sections || {})[section._id] === true).map((section: any) => ( [
      @@ -106,8 +106,8 @@ function CompanyPermissions({ {gettext('Number of seats')}
      ,
        - {products.filter((p) => (p.product_type || 'wire').toLowerCase() === section._id.toLowerCase()) - .map((product) => ( + {products.filter((p: any) => (p.product_type || 'wire').toLowerCase() === section._id.toLowerCase()) + .map((product: any) => (
      • @@ -140,7 +140,7 @@ function CompanyPermissions({ className="form-control form-control--small form-control--right form-control--compact" style={{maxWidth: '60px'}} min="0" - tabIndex="0" + tabIndex={0} value={(seats[product._id] || 0).toString()} onChange={(event) => { updateCompanySeats( diff --git a/assets/companies/components/EditCompany.jsx b/assets/companies/components/EditCompany.tsx similarity index 82% rename from assets/companies/components/EditCompany.jsx rename to assets/companies/components/EditCompany.tsx index a61b73310..a826b0d2d 100644 --- a/assets/companies/components/EditCompany.jsx +++ b/assets/companies/components/EditCompany.tsx @@ -22,8 +22,11 @@ import EditCompanyAPI from './EditCompanyAPI'; import AuditInformation from 'components/AuditInformation'; import {EditCompanyDetails} from './EditCompanyDetails'; -class EditCompany extends React.Component { - constructor(props) { +class EditCompany extends React.Component { + static propTypes: any; + tabs: Array<{label: any; name: string}>; + + constructor(props: any) { super(props); this.handleTabClick = this.handleTabClick.bind(this); this.getUsers = this.getUsers.bind(this); @@ -44,9 +47,10 @@ class EditCompany extends React.Component { } } - handleTabClick(event) { - this.setState({activeTab: event.target.name}); - if (event.target.name === 'users' && this.props.company._id) { + handleTabClick(name: string) { + this.setState({activeTab: name}); + + if (name === 'users' && this.props.company._id) { this.props.fetchCompanyUsers(this.props.company._id); } } @@ -55,12 +59,12 @@ class EditCompany extends React.Component { if (isEmpty(this.props.users)) { return (
  • - + ); } - return this.props.users.map((user) => ( + return this.props.users.map((user: any) => ( @@ -70,7 +74,7 @@ class EditCompany extends React.Component { isFormValid() { let valid = true; - let errors = {}; + const errors: any = {}; if (!this.props.company.name) { errors.name = [gettext('Please provide company name')]; @@ -81,7 +85,7 @@ class EditCompany extends React.Component { return valid; } - save(externalEvent) { + save(externalEvent: any) { if (externalEvent) { externalEvent.preventDefault(); @@ -94,7 +98,7 @@ class EditCompany extends React.Component { this.props.saveCompany(); } - deleteCompany(event) { + deleteCompany(event: any) { event.preventDefault(); if (confirm(gettext('Would you like to delete company: {{name}}', {name: this.props.company.name}))) { @@ -120,7 +124,6 @@ class EditCompany extends React.Component { id='hide-sidebar' type='button' className='icon-button' - data-bs-dismiss='modal' aria-label={gettext('Close')} onClick={this.props.cancelEdit}>
    {company.name} {type ? gettext(type.name) : ''}{company.sd_subscriber_id}{company.account_manager} + {(company.is_enabled ? gettext('Enabled') : gettext('Disabled'))} {company.contact_name}
    {gettext('There are no users in the company.')}{gettext('There are no users in the company.')}
    {user.first_name} {user.last_name} {shortDate(user._created)}
    {section.name}
    @@ -62,7 +62,7 @@ export function CompanyUserListItem({user, onClick, selected, sections}) {
    {gettext('Status')} {gettext('{{ section }} Products', {section: section.name})}
    {company}{get(results[company], headerName, 0)}
    {item.name} {item.is_enabled ? gettext('Active') : gettext('Disabled')} {formatDate(get(item, 'company._created'))} {get(item, 'company.expiry_date') ? formatDate(item.company.expiry_date) : gettext('Unspecified')}
    + {getContactDetails(item.company)}
    +
    {gettext('Account Manager')}: {item.account_manager}
    @@ -72,12 +72,12 @@ function Company({results, print}) {
    + {getUsers(item.users)}
    + {getProductDetails(item.products)}
    {item.name} {item.is_enabled.toString()} {item.products.length}
    + {getProductDetails(item.products)}
    {item.name} {item.is_enabled.toString()}
    {item.versioncreated} {item.headline} {item.anpa_take_key}{item.place.map((place) => ( + {item.place.map((place: any) => ( {place}
    ))}
    {item.service.map((service) => ( + {item.service.map((service: any) => ( {service}
    ))}
    {item.subject.map((subject) => ( + {item.subject.map((subject: any) => ( {subject}
    ))}
    {item.companies.map((company) => ( + {item.companies.map((company: any) => ( {company}
    @@ -269,7 +270,7 @@ class ContentActivity extends React.Component { /> - {filters.map((filterName) => { + {filters.map((filterName: any) => { const filter = this.state[filterName]; return ( @@ -315,7 +316,7 @@ ContentActivity.propTypes = { apiEnabled: PropTypes.bool, }; -const mapStateToProps = (state) => ({ +const mapStateToProps = (state: any) => ({ companies: state.companies, reportParams: state.reportParams, isLoading: state.isLoading, @@ -323,7 +324,7 @@ const mapStateToProps = (state) => ({ aggregations: state.reportAggregations, }); -const mapDispatchToProps = { +const mapDispatchToProps: any = { toggleFilter, fetchReport, runReport, diff --git a/assets/company-reports/components/ExpiredCompanies.jsx b/assets/company-reports/components/ExpiredCompanies.tsx similarity index 88% rename from assets/company-reports/components/ExpiredCompanies.jsx rename to assets/company-reports/components/ExpiredCompanies.tsx index 23463ef4b..44b476349 100644 --- a/assets/company-reports/components/ExpiredCompanies.jsx +++ b/assets/company-reports/components/ExpiredCompanies.tsx @@ -5,8 +5,8 @@ import {get} from 'lodash'; import ReportsTable from './ReportsTable'; -function ExpiredCompanies({results, print}) { - const list = results && results.map((item) => +function ExpiredCompanies({results, print}: any) { + const list = results && results.map((item: any) =>
    {get(item, 'name')} {item.is_enabled ? gettext('Active') : gettext('Disabled')}
    {item.product}{item.enabled_companies.map((company) => ( + {item.enabled_companies.map((company: any) => ( {company}
    ))}
    {item.disabled_companies.map((company) => ( + {item.disabled_companies.map((company: any) => ( {company}
    @@ -71,7 +75,7 @@ class ProductCompanies extends React.Component {
    {item.name} {item.is_enabled.toString()}
    {h}{h}
    {item.company} {item.section}
    {item.name} {item.is_enabled.toString()}
    - -1} - onChange={() => this.toggleUser(user._id)} /> - - -
    - this.toggleAllUsers()} - checked={this.state.users.length === this.props.data.users.length} - /> - - -