UNPKG

@craftercms/studio-ui

Version:

Services, components, models & utils to build CrafterCMS authoring extensions.

334 lines (332 loc) 13.1 kB
/* * Copyright (C) 2007-2022 Crafter Software Corporation. All Rights Reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ /* * Copyright (C) 2007-2022 Crafter Software Corporation. All Rights Reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import RecentlyPublishedWidgetUI from './LegacyRecentlyPublishedDashletUI'; import { fetchLegacyDeploymentHistory } from '../../../services/dashboard'; import { FormattedMessage } from 'react-intl'; import Button from '@mui/material/Button'; import MenuItem from '@mui/material/MenuItem'; import { getNumOfMenuOptionsForItem, getSystemTypeFromPath, parseLegacyItemToDetailedItem } from '../../../utils/content'; import LegacyDashletCard from '../LegacyDashletCard'; import useStyles from './styles'; import LegacyRecentlyPublishedDashletUISkeletonTable from './LegacyRecentlyPublishedDashletUISkeletonTable'; import TextField from '@mui/material/TextField'; import { deleteContentEvent, publishEvent } from '../../../state/actions/system'; import { getHostToHostBus } from '../../../utils/subjects'; import { filter } from 'rxjs/operators'; import { useSpreadState } from '../../../hooks/useSpreadState'; import { useLocale } from '../../../hooks/useLocale'; import { getStoredDashboardPreferences, setStoredDashboardPreferences } from '../../../utils/state'; import { useDispatch, useSelector } from 'react-redux'; import { showItemMegaMenu } from '../../../state/actions/dialogs'; import { EmptyState, getEmptyStateStyleSet } from '../../EmptyState'; import { useActiveSite } from '../../../hooks/useActiveSite'; import { asLocalizedDateTime } from '../../../utils/datetime'; import { reversePluckProps } from '../../../utils/object'; import { ApiResponseErrorState } from '../../ApiResponseErrorState'; const dashletInitialPreferences = { filterBy: 'page', numItems: 20, expanded: true }; export function LegacyRecentlyPublishedDashlet() { var _a; const [fetchingHistory, setFetchingHistory] = useState(false); const [errorHistory, setErrorHistory] = useState(); const [parentItems, setParentItems] = useState(); const [itemsLookup, setItemsLookup] = useSpreadState({}); const dashletPreferencesId = 'recentlyPublishedDashlet'; const currentUser = useSelector((state) => state.user.username); const { id: siteId, uuid } = useActiveSite(); const [preferences, setPreferences] = useSpreadState( (_a = getStoredDashboardPreferences(currentUser, uuid, dashletPreferencesId)) !== null && _a !== void 0 ? _a : dashletInitialPreferences ); const [expandedItems, setExpandedItems] = useSpreadState({}); const localeBranch = useLocale(); const { classes } = useStyles(); const dispatch = useDispatch(); const allCollapsed = useMemo( () => Object.keys(expandedItems).every((key) => !Boolean(expandedItems[key])), [expandedItems] ); const toggleCollapseAllItems = useCallback( (documents, expanded) => { documents.forEach((document) => { setExpandedItems({ [document.label]: expanded }); }); }, [setExpandedItems] ); const onFilterChange = (e) => { e.stopPropagation(); setPreferences({ filterBy: e.target.value }); }; const onNumItemsChange = (e) => { e.stopPropagation(); setPreferences({ numItems: e.target.value }); }; useEffect(() => { setStoredDashboardPreferences(preferences, currentUser, uuid, dashletPreferencesId); }, [preferences, currentUser, uuid]); const onCollapseAll = (e) => { e.stopPropagation(); toggleCollapseAllItems(parentItems, allCollapsed); }; const fetchHistory = useCallback( (backgroundRefresh) => { if (!backgroundRefresh) { setFetchingHistory(true); } fetchLegacyDeploymentHistory(siteId, 30, preferences.numItems, preferences.filterBy).subscribe({ next(history) { const parentItems = []; const childrenLookup = {}; history.documents.forEach((document) => { if (document.children.length) { // TODO: The backend is sending an improper date format. let label = document.internalName.replace(/Z$/, ''); try { label = asLocalizedDateTime( label, localeBranch.localeCode, reversePluckProps(localeBranch.dateTimeFormatOptions, 'hour', 'minute', 'second') ); } catch (_a) {} parentItems.push({ label, children: document.children.map((item) => { const key = `${item.uri}:${item.eventDate}`; childrenLookup[key] = parseLegacyItemToDetailedItem(item); // For this dashlet we display the environment where the item was published at the moment, and the API // returns the environment at the current time in the props isLive/isStaging. The prop used for the // environment at the moment of publishing is `endpoint`. So we update `childrenLookup` with the endpoint value. if (item.endpoint === 'live') { childrenLookup[key].stateMap.live = true; childrenLookup[key].stateMap.staging = true; } else { childrenLookup[key].stateMap.live = false; childrenLookup[key].stateMap.staging = true; } // For this dashlet, the property needed is eventDate, since we display the published date at the moment // of the publishing, not the current. childrenLookup[key].live.datePublished = item.eventDate; childrenLookup[key].staging.datePublished = item.eventDate; return key; }) }); } setItemsLookup(childrenLookup); }); setParentItems(parentItems); toggleCollapseAllItems(parentItems, true); if (!backgroundRefresh) { setFetchingHistory(false); } }, error(e) { setErrorHistory(e); if (!backgroundRefresh) { setFetchingHistory(false); } } }); }, [ siteId, preferences.numItems, preferences.filterBy, toggleCollapseAllItems, setItemsLookup, localeBranch.localeCode, localeBranch.dateTimeFormatOptions ] ); useEffect(() => { fetchHistory(); }, [fetchHistory]); // region Item Updates Propagation useEffect(() => { const events = [deleteContentEvent.type, publishEvent.type]; const hostToHost$ = getHostToHostBus(); const subscription = hostToHost$.pipe(filter((e) => events.includes(e.type))).subscribe(({ type, payload }) => { fetchHistory(true); }); return () => { subscription.unsubscribe(); }; }, [fetchHistory, itemsLookup]); // endregion const onItemMenuClick = (event, item) => { const path = item.path; dispatch( showItemMegaMenu({ path, anchorReference: 'anchorPosition', anchorPosition: { top: event.clientY, left: event.clientX }, numOfLoaderItems: getNumOfMenuOptionsForItem({ path: item.path, systemType: getSystemTypeFromPath(item.path) }) }) ); }; return React.createElement( LegacyDashletCard, { title: React.createElement(FormattedMessage, { id: 'recentlyPublishedDashlet.dashletTitle', defaultMessage: 'Recently Published ({total})', values: { total: Object.keys(itemsLookup).length } }), onToggleExpanded: () => setPreferences({ expanded: !preferences.expanded }), expanded: preferences.expanded, refreshDisabled: fetchingHistory, onRefresh: fetchHistory, headerRightSection: React.createElement( React.Fragment, null, React.createElement( Button, { onClick: onCollapseAll, className: classes.rightAction, disabled: fetchingHistory }, !allCollapsed ? React.createElement(FormattedMessage, { id: 'common.collapseAll', defaultMessage: 'Collapse All' }) : React.createElement(FormattedMessage, { id: 'common.expandAll', defaultMessage: 'Expand All' }) ), React.createElement( TextField, { label: React.createElement(FormattedMessage, { id: 'words.show', defaultMessage: 'Show' }), select: true, size: 'small', value: preferences.numItems, disabled: fetchingHistory, onChange: onNumItemsChange, className: classes.rightAction, onClick: (e) => e.stopPropagation() }, React.createElement(MenuItem, { value: 10 }, '10'), React.createElement(MenuItem, { value: 20 }, '20'), React.createElement(MenuItem, { value: 50 }, '50'), Object.keys(itemsLookup).length && React.createElement( MenuItem, { value: Object.keys(itemsLookup).length }, React.createElement(FormattedMessage, { id: 'recentlyPublishedDashlet.showAll', defaultMessage: 'All ({total})', values: { total: Object.keys(itemsLookup).length } }) ) ), React.createElement( TextField, { label: React.createElement(FormattedMessage, { id: 'recentlyPublishedDashlet.filterBy', defaultMessage: 'Filter by' }), select: true, size: 'small', value: preferences.filterBy, disabled: fetchingHistory, onChange: onFilterChange, onClick: (e) => e.stopPropagation() }, React.createElement( MenuItem, { value: 'page' }, React.createElement(FormattedMessage, { id: 'words.pages', defaultMessage: 'Pages' }) ), React.createElement( MenuItem, { value: 'component' }, React.createElement(FormattedMessage, { id: 'words.components', defaultMessage: 'Components' }) ), React.createElement( MenuItem, { value: 'asset' }, React.createElement(FormattedMessage, { id: 'words.assets', defaultMessage: 'Assets' }) ), React.createElement( MenuItem, { value: 'all' }, React.createElement(FormattedMessage, { id: 'words.all', defaultMessage: 'All' }) ) ) ) }, errorHistory ? React.createElement(ApiResponseErrorState, { error: errorHistory }) : fetchingHistory ? React.createElement(LegacyRecentlyPublishedDashletUISkeletonTable, { items: parentItems, expandedLookup: expandedItems }) : parentItems ? parentItems.length ? React.createElement(RecentlyPublishedWidgetUI, { parentItems: parentItems, itemsLookup: itemsLookup, localeBranch: localeBranch, expandedItems: expandedItems, setExpandedItems: setExpandedItems, onItemMenuClick: onItemMenuClick }) : React.createElement(EmptyState, { title: React.createElement(FormattedMessage, { id: 'recentlyPublishedDashlet.emptyMessage', defaultMessage: 'No items published recently' }), styles: Object.assign( Object.assign({}, getEmptyStateStyleSet('horizontal')), getEmptyStateStyleSet('image-sm') ) }) : React.createElement(React.Fragment, null) ); } export default LegacyRecentlyPublishedDashlet;