@craftercms/studio-ui
Version:
Services, components, models & utils to build CrafterCMS authoring extensions.
334 lines (332 loc) • 13.1 kB
JavaScript
/*
* 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;