UNPKG

@craftercms/studio-ui

Version:

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

344 lines (342 loc) 13.5 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, useRef, useState } from 'react'; import useStyles from './styles'; import { FormattedMessage, useIntl } from 'react-intl'; import { fetchLegacyGetGoLiveItems } from '../../../services/dashboard'; import AwaitingApprovalDashletGridUI from '../LegacyAwaitingApprovalDashletGrid'; import { getNumOfMenuOptionsForItem, getSystemTypeFromPath, parseLegacyItemToDetailedItem } from '../../../utils/content'; import LegacyDashletCard from '../LegacyDashletCard'; import AwaitingApprovalDashletSkeletonTable from '../LegacyAwaitingApprovalDashletGrid/AwaitingApprovalDashletSkeletonTable'; import Button from '@mui/material/Button'; import { deleteContentEvent, publishEvent, workflowEvent } from '../../../state/actions/system'; import { getHostToHostBus } from '../../../utils/subjects'; import { filter } from 'rxjs/operators'; import { useSpreadState } from '../../../hooks/useSpreadState'; import { useDispatch, useSelector } from 'react-redux'; import { getStoredDashboardPreferences, setStoredDashboardPreferences } from '../../../utils/state'; import { showItemMegaMenu } from '../../../state/actions/dialogs'; import ActionsBar from '../../ActionsBar'; import translations from './translations'; import { createPresenceTable } from '../../../utils/array'; import { itemActionDispatcher } from '../../../utils/itemActions'; import { useEnv } from '../../../hooks/useEnv'; import EmptyState, { getEmptyStateStyleSet } from '../../EmptyState'; import { useActiveSite } from '../../../hooks/useActiveSite'; import useItemsByPath from '../../../hooks/useItemsByPath'; import useFetchSandboxItems from '../../../hooks/useFetchSandboxItems'; import { ApiResponseErrorState } from '../../ApiResponseErrorState'; const dashletInitialPreferences = { expanded: true, showUnpublished: false }; export function AwaitingApprovalDashlet() { var _a; const { id: siteId, uuid } = useActiveSite(); const { classes } = useStyles(); const [state, setState] = useState({ publishingTargetLookup: {}, itemsLookup: {}, parentItems: null, total: null }); const [selectedLookup, setSelectedLookup] = useState({}); const selectedLookupRef = useRef({}); selectedLookupRef.current = selectedLookup; const [expandedLookup, setExpandedLookup] = useSpreadState({}); const [error, setError] = useState(); const currentUser = useSelector((state) => state.user.username); const dashletPreferencesId = 'awaitingApprovalDashlet'; const [preferences, setPreferences] = useSpreadState( (_a = getStoredDashboardPreferences(currentUser, uuid, dashletPreferencesId)) !== null && _a !== void 0 ? _a : dashletInitialPreferences ); const [isFetching, setIsFetching] = useState(false); const dispatch = useDispatch(); const { formatMessage } = useIntl(); const { authoringBase } = useEnv(); const showExpanded = useMemo(() => Object.values(expandedLookup).some((value) => !value), [expandedLookup]); const isAllChecked = useMemo( () => Boolean(Object.keys(selectedLookup).length) && !Object.keys(state.itemsLookup).some((path) => !selectedLookup[path]), [selectedLookup, state.itemsLookup] ); const selectedItemsLength = useMemo(() => Object.values(selectedLookup).filter(Boolean).length, [selectedLookup]); const isIndeterminate = useMemo( () => Object.keys(state.itemsLookup).some((path) => selectedLookup[path]) && !isAllChecked, [isAllChecked, selectedLookup, state.itemsLookup] ); const itemsByPath = useItemsByPath(); useFetchSandboxItems(Object.keys(selectedLookup)); const refresh = useCallback( (backgroundRefresh) => { if (!backgroundRefresh) { setIsFetching(true); } fetchLegacyGetGoLiveItems(siteId, 'eventDate', null, preferences.showUnpublished, null).subscribe( (response) => { const parentItems = []; const itemsLookup = {}; const publishingTargetLookup = {}; const expandedLookup = {}; function getSubmittedItems(items, children) { items.forEach((item) => { if (item.contentType === 'folder') { getSubmittedItems(item.children, children); } else { children.push(item); } }); } response.documents.forEach((item) => { if (item.children.length) { expandedLookup[item.uri] = true; const children = []; getSubmittedItems(item.children, children); parentItems.push({ label: item.name, path: item.uri, children: children.map((item) => { publishingTargetLookup[item.uri] = item.submittedToEnvironment; itemsLookup[item.uri] = parseLegacyItemToDetailedItem(item); return item.uri; }) }); } }); setExpandedLookup(expandedLookup); setState({ publishingTargetLookup, itemsLookup, parentItems, total: response.total }); if (!backgroundRefresh) { setIsFetching(false); } // Update selected lookup const selectedKeys = Object.keys(selectedLookupRef.current).filter((selected) => Boolean(itemsLookup[selected]) ); setSelectedLookup(createPresenceTable(selectedKeys, true)); }, ({ response }) => { setError(response); } ); }, [setExpandedLookup, siteId, preferences.showUnpublished] ); useEffect(() => { refresh(); }, [refresh]); // region Item Updates Propagation useEffect(() => { const events = [deleteContentEvent.type, workflowEvent.type, publishEvent.type]; const hostToHost$ = getHostToHostBus(); const subscription = hostToHost$.pipe(filter((e) => events.includes(e.type))).subscribe(({ type, payload }) => { switch (type) { case deleteContentEvent.type: case workflowEvent.type: case publishEvent.type: { refresh(true); break; } } }); return () => { subscription.unsubscribe(); }; }, [refresh, selectedLookup, state.itemsLookup]); // endregion useEffect(() => { setStoredDashboardPreferences(preferences, currentUser, uuid, dashletPreferencesId); }, [preferences, currentUser, uuid]); const onToggleCollapse = (e) => { e.stopPropagation(); const lookup = {}; Object.keys(expandedLookup).forEach((path) => { lookup[path] = showExpanded; }); setExpandedLookup(lookup); }; const onShowInProgress = (e) => { e.stopPropagation(); setPreferences({ showUnpublished: !preferences.showUnpublished }); }; const onExpandedRow = (path, value) => { setExpandedLookup({ [path]: value }); }; const onToggleCheckedAll = () => { if (isAllChecked) { setSelectedLookup({}); } else { setSelectedLookup( Object.assign(Object.assign({}, selectedLookup), createPresenceTable(Object.keys(state.itemsLookup), true)) ); } }; const handleItemChecked = (path) => { setSelectedLookup(Object.assign(Object.assign({}, selectedLookup), { [path]: !selectedLookup[path] })); }; 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) }) }) ); }; const onActionBarOptionClicked = (option) => { if (option === 'clear') { setSelectedLookup({}); } else { const selected = Object.keys(selectedLookup) .filter((path) => selectedLookup[path]) .flatMap((path) => { var _a; return (_a = itemsByPath[path]) !== null && _a !== void 0 ? _a : []; }); itemActionDispatcher({ site: siteId, item: selected.length > 1 ? selected : selected[0], option: option, authoringBase, dispatch, formatMessage }); } }; return React.createElement( LegacyDashletCard, { title: React.createElement(FormattedMessage, { id: 'awaitingApprovalDashlet.dashletTitle', defaultMessage: 'Items Waiting For Approval ({count})', values: { count: state.total } }), expanded: preferences.expanded, onToggleExpanded: () => setPreferences({ expanded: !preferences.expanded }), refreshDisabled: isFetching, onRefresh: refresh, headerRightSection: React.createElement( React.Fragment, null, React.createElement( Button, { disabled: isFetching, onClick: onToggleCollapse, className: classes.collapseAll }, showExpanded ? React.createElement(FormattedMessage, { id: 'common.expandAll', defaultMessage: 'Expand All' }) : React.createElement(FormattedMessage, { id: 'common.collapseAll', defaultMessage: 'Collapse All' }) ), React.createElement( Button, { disabled: isFetching, onClick: onShowInProgress }, preferences.showUnpublished ? React.createElement(FormattedMessage, { id: 'awaitingApprovalDashlet.hideUnpublished', defaultMessage: 'Hide Unpublished' }) : React.createElement(FormattedMessage, { id: 'awaitingApprovalDashlet.showUnpublished', defaultMessage: 'Show Unpublished' }) ) ) }, error ? React.createElement(ApiResponseErrorState, { error: error }) : isFetching ? React.createElement(AwaitingApprovalDashletSkeletonTable, { items: state.parentItems, expandedLookup: expandedLookup }) : state.parentItems ? state.parentItems.length ? React.createElement( React.Fragment, null, (isIndeterminate || isAllChecked) && React.createElement(ActionsBar, { classes: { root: classes.actionsBarRoot, checkbox: classes.actionsBarCheckbox }, options: [ { id: 'approvePublish', label: formatMessage(translations.publish) }, { id: 'rejectPublish', label: formatMessage(translations.reject) }, { id: 'clear', label: formatMessage(translations.clear, { count: selectedItemsLength }) } ], isIndeterminate: isIndeterminate, isChecked: isAllChecked, onOptionClicked: onActionBarOptionClicked, onCheckboxChange: onToggleCheckedAll }), React.createElement(AwaitingApprovalDashletGridUI, { items: state.parentItems, expandedLookup: expandedLookup, publishingTargetLookup: state.publishingTargetLookup, itemsLookup: state.itemsLookup, selectedLookup: selectedLookup, onToggleCheckedAll: onToggleCheckedAll, isAllChecked: isAllChecked, isIndeterminate: isIndeterminate, onExpandedRow: onExpandedRow, onItemMenuClick: onItemMenuClick, onItemChecked: handleItemChecked }) ) : React.createElement(EmptyState, { title: React.createElement(FormattedMessage, { id: 'awaitingApprovalDashlet.emptyMessage', defaultMessage: 'No items are awaiting approval' }), styles: Object.assign( Object.assign({}, getEmptyStateStyleSet('horizontal')), getEmptyStateStyleSet('image-sm') ) }) : React.createElement(React.Fragment, null) ); } export default AwaitingApprovalDashlet;