UNPKG

@craftercms/studio-ui

Version:

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

381 lines (379 loc) 11.6 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 FormGroup from '@mui/material/FormGroup'; import FormControlLabel from '@mui/material/FormControlLabel'; import Checkbox from '@mui/material/Checkbox'; import React, { useRef, useState } from 'react'; import { makeStyles } from 'tss-react/mui'; import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; import SelectButton from '../ConfirmDropdown'; import Typography from '@mui/material/Typography'; import { cancelPackage, fetchPackage } from '../../services/publishing'; import List from '@mui/material/List'; import ListItem from '@mui/material/ListItem'; import CircularProgress from '@mui/material/CircularProgress'; import '../../styles/animations.scss'; import { READY_FOR_LIVE } from './constants'; import { alpha } from '@mui/material/styles'; import palette from '../../styles/palette'; import PrimaryButton from '../PrimaryButton'; const useStyles = makeStyles()((theme) => ({ package: { padding: '20px 8px 20px 0', '& .loading-header': { display: 'flex', alignItems: 'center', height: '42px' }, '& .name': { display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '10px' }, '& .status': { display: 'flex', justifyContent: 'space-between', marginBottom: '10px' }, '& .comment': { display: 'flex', '& p:first-child': { marginRight: '20px', marginBottom: '10px' }, '& span': { color: theme.palette.text.secondary } }, '& .files': { marginTop: '10px' } }, checkbox: { marginRight: 'auto' }, thRow: { background: theme.palette.background.default }, th: { fontWeight: 600 }, list: { '& li': { display: 'flex', justifyContent: 'space-between' } }, spinner: { marginRight: '10px', color: theme.palette.text.secondary }, packageLoading: { WebkitAnimation: 'pulse 3s infinite ease-in-out', animation: 'pulse 3s infinite ease-in-out', pointerEvents: 'none' }, cancelButton: { paddingRight: '10px', color: palette.orange.main, border: `1px solid ${alpha(palette.orange.main, 0.5)}`, '&:hover': { backgroundColor: alpha(palette.orange.main, 0.08) } }, username: { maxWidth: '390px', overflow: 'hidden', textOverflow: 'ellipsis', display: 'inline-block', marginBottom: '-5px' } })); const translations = defineMessages({ cancelText: { id: 'publishingDashboard.cancelItemButtonText', defaultMessage: 'Cancel' }, cancel: { id: 'publishingDashboard.no', defaultMessage: 'No' }, confirm: { id: 'publishingDashboard.yes', defaultMessage: 'Yes' }, confirmHelperText: { id: 'publishingDashboard.confirmHelperText', defaultMessage: 'Set item state to "Cancelled"?' }, fetchPackagesFiles: { id: 'publishingDashboard.fetchPackagesFiles', defaultMessage: 'Fetch Packages Files' }, status: { id: 'publishingDashboard.status', defaultMessage: 'Status is {state} for {environment} target' }, comment: { id: 'publishingDashboard.comment', defaultMessage: 'Comment' }, commentNotProvided: { id: 'publishingDashboard.commentNotProvided', defaultMessage: '(submission comment not provided)' }, filesList: { id: 'publishingDashboard.filesList', defaultMessage: 'files list' }, path: { id: 'words.path', defaultMessage: 'Path' }, type: { id: 'words.type', defaultMessage: 'Type' }, item: { id: 'words.item', defaultMessage: 'Item' }, asset: { id: 'words.asset', defaultMessage: 'Asset' }, script: { id: 'words.script', defaultMessage: 'Script' }, page: { id: 'words.page', defaultMessage: 'Page' }, renderingTemplate: { id: 'words.template', defaultMessage: 'Template' }, component: { id: 'words.component', defaultMessage: 'Component' }, unknown: { id: 'words.unknown', defaultMessage: 'Unknown' } }); export function PublishingPackage(props) { const { classes, cx } = useStyles(); const { formatMessage } = useIntl(); const { id, approver, schedule, state, comment, environment, siteId, selected, setSelected, pending, setPending, getPackages, setApiState, filesPerPackage, setFilesPerPackage, readOnly } = props; const [loading, setLoading] = useState(null); const { current: ref } = useRef({}); ref.cancelComplete = (packageId) => { setPending({ ...pending, [packageId]: false }); getPackages(siteId); }; function onSelect(event, id, checked) { if (checked) { setSelected({ ...selected, [id]: false }); } else { setSelected({ ...selected, [id]: true }); } } function handleCancel(packageId) { setPending({ ...pending, [packageId]: true }); cancelPackage(siteId, [packageId]).subscribe( () => { ref.cancelComplete(packageId); }, ({ response }) => { setApiState({ error: true, errorResponse: response }); } ); } function onFetchPackages(packageId) { setLoading(true); fetchPackage(siteId, packageId).subscribe({ next: (pkg) => { setLoading(false); setFilesPerPackage({ ...filesPerPackage, [packageId]: pkg.items }); }, error: ({ response }) => { setApiState({ error: true, errorResponse: response }); } }); } function renderFiles(files) { return files.map((file, index) => { return React.createElement( ListItem, { key: index, divider: true }, React.createElement(Typography, { variant: 'body2' }, file.path), React.createElement( Typography, { variant: 'body2', color: 'textSecondary' }, file.contentTypeClass in translations ? formatMessage(translations[file.contentTypeClass]) : file.contentTypeClass ) ); }); } const checked = selected[id] ? selected[id] : false; return React.createElement( 'div', { className: cx(classes.package, pending[id] && classes.packageLoading) }, React.createElement( 'section', { className: 'name' }, pending[id] ? React.createElement( 'header', { className: 'loading-header' }, React.createElement(CircularProgress, { size: 15, className: classes.spinner, color: 'inherit' }), React.createElement(Typography, { variant: 'body1' }, React.createElement('strong', null, id)) ) : state === READY_FOR_LIVE ? React.createElement( FormGroup, { className: classes.checkbox }, React.createElement(FormControlLabel, { control: React.createElement(Checkbox, { color: 'primary', checked: checked, onChange: (event) => onSelect(event, id, checked), disabled: readOnly }), label: React.createElement('strong', null, id) }) ) : React.createElement(Typography, { variant: 'body1' }, React.createElement('strong', null, id)), state === READY_FOR_LIVE && React.createElement(SelectButton, { classes: { button: classes.cancelButton }, text: formatMessage(translations.cancelText), cancelText: formatMessage(translations.cancel), confirmText: formatMessage(translations.confirm), confirmHelperText: formatMessage(translations.confirmHelperText), onConfirm: () => handleCancel(id), disabled: readOnly }) ), React.createElement( 'div', { className: 'status' }, React.createElement( Typography, { variant: 'body2' }, React.createElement(FormattedMessage, { id: 'publishingDashboard.scheduled', defaultMessage: 'Scheduled for <b>{schedule, date, medium} {schedule, time, short}</b> by <b>{approver}</b>', values: { schedule: new Date(schedule), approver: approver, b: (content) => React.createElement('strong', { key: content[0], className: classes.username }, content[0]) } }) ), React.createElement( Typography, { variant: 'body2' }, formatMessage(translations.status, { state: React.createElement('strong', { key: state }, state), environment: React.createElement('strong', { key: environment }, environment) }) ) ), React.createElement( 'div', { className: 'comment' }, React.createElement(Typography, { variant: 'body2' }, formatMessage(translations.comment)), React.createElement( Typography, { variant: 'body2' }, comment ? comment : React.createElement('span', null, formatMessage(translations.commentNotProvided)) ) ), React.createElement( 'div', { className: 'files' }, filesPerPackage && filesPerPackage[id] && React.createElement( List, { 'aria-label': formatMessage(translations.filesList), className: classes.list }, React.createElement( ListItem, { className: classes.thRow, divider: true }, React.createElement( Typography, { variant: 'caption', className: classes.th }, formatMessage(translations.item), ' (', formatMessage(translations.path).toLowerCase(), ')' ), React.createElement( Typography, { variant: 'caption', className: classes.th }, formatMessage(translations.type) ) ), renderFiles(filesPerPackage[id]) ), (filesPerPackage === null || !filesPerPackage[id]) && React.createElement( PrimaryButton, { variant: 'outlined', onClick: () => onFetchPackages(id), disabled: !!loading, loading: loading }, formatMessage(translations.fetchPackagesFiles) ) ) ); } export default PublishingPackage;