feat: duplicate entry (#2956)

This commit is contained in:
Bartholomew 2019-12-11 02:33:02 +01:00 committed by Shawn Erquhart
parent 9ee5d4f66f
commit d180bffb44
11 changed files with 155 additions and 53 deletions

View File

@ -19,8 +19,10 @@ import {
validateNestedObjectFieldsAndExit, validateNestedObjectFieldsAndExit,
validateListFieldsAndExit, validateListFieldsAndExit,
unpublishEntry, unpublishEntry,
publishEntryInEditor,
duplicateEntry,
} from '../utils/steps'; } from '../utils/steps';
import { setting1, setting2, workflowStatus, editorStatus } from '../utils/constants'; import { setting1, setting2, workflowStatus, editorStatus, publishTypes } from '../utils/constants';
const entry1 = { const entry1 = {
title: 'first title', title: 'first title',
@ -135,4 +137,12 @@ describe('Test Backend Editorial Workflow', () => {
// then unpublish it // then unpublish it
unpublishEntry(entry1); unpublishEntry(entry1);
}); });
it('can duplicate an existing entry', () => {
login();
createPost(entry1);
updateWorkflowStatusInEditor(editorStatus.ready);
publishEntryInEditor(publishTypes.publishNow);
duplicateEntry(entry1);
});
}); });

View File

@ -2,6 +2,7 @@ const workflowStatus = { draft: 'Drafts', review: 'In Review', ready: 'Ready' };
const editorStatus = { draft: 'Draft', review: 'In review', ready: 'Ready' }; const editorStatus = { draft: 'Draft', review: 'In review', ready: 'Ready' };
const setting1 = { limit: 10, author: 'John Doe' }; const setting1 = { limit: 10, author: 'John Doe' };
const setting2 = { name: 'Andrew Wommack', description: 'A Gospel Teacher' }; const setting2 = { name: 'Andrew Wommack', description: 'A Gospel Teacher' };
const publishTypes = { publishNow: 'Publish now' };
const notifications = { const notifications = {
saved: 'Entry saved', saved: 'Entry saved',
published: 'Entry published', published: 'Entry published',
@ -25,4 +26,5 @@ module.exports = {
setting1, setting1,
setting2, setting2,
notifications, notifications,
publishTypes,
}; };

View File

@ -1,4 +1,4 @@
const { notifications, workflowStatus } = require('./constants'); const { notifications, workflowStatus, editorStatus, publishTypes } = require('./constants');
function login(user) { function login(user) {
cy.viewport(1200, 1200); cy.viewport(1200, 1200);
@ -147,14 +147,23 @@ function assertWorkflowStatus({ title }, status) {
} }
function updateWorkflowStatusInEditor(newStatus) { function updateWorkflowStatusInEditor(newStatus) {
cy.contains('[role="button"]', 'Set status').as('setStatusButton'); selectDropdownItem('Set status', newStatus);
cy.get('@setStatusButton') assertNotification(notifications.updated);
}
function publishEntryInEditor(publishType) {
selectDropdownItem('Publish', publishType);
assertNotification(notifications.published);
}
function selectDropdownItem(label, item){
cy.contains('[role="button"]', label).as('dropDownButton');
cy.get('@dropDownButton')
.parent() .parent()
.within(() => { .within(() => {
cy.get('@setStatusButton').click(); cy.get('@dropDownButton').click();
cy.contains('[role="menuitem"] span', newStatus).click(); cy.contains('[role="menuitem"] span', item).click();
}); });
assertNotification(notifications.updated);
} }
function populateEntry(entry) { function populateEntry(entry) {
@ -217,18 +226,25 @@ function unpublishEntry(entry) {
cy.contains('h2', entry.title) cy.contains('h2', entry.title)
.parent() .parent()
.click({ force: true }); .click({ force: true });
cy.contains('[role="button"]', 'Published').as('publishedButton'); selectDropdownItem('Published', 'Unpublish');
cy.get('@publishedButton')
.parent()
.within(() => {
cy.get('@publishedButton').click();
cy.contains('[role="menuitem"] span', 'Unpublish').click();
});
assertNotification(notifications.unpublished); assertNotification(notifications.unpublished);
goToWorkflow(); goToWorkflow();
assertWorkflowStatus(entry, workflowStatus.ready); assertWorkflowStatus(entry, workflowStatus.ready);
} }
function duplicateEntry(entry) {
selectDropdownItem('Published', 'Duplicate');
cy.url().should('contain', '/#/collections/posts/new');
cy.contains('button', 'Save').click();
updateWorkflowStatusInEditor(editorStatus.ready);
publishEntryInEditor(publishTypes.publishNow);
exitEditor();
cy.get('a h2').should(($h2s) => {
expect($h2s.eq(0)).to.contain(entry.title);
expect($h2s.eq(1)).to.contain(entry.title);
});
}
function validateObjectFields({ limit, author }) { function validateObjectFields({ limit, author }) {
cy.get('a[href^="#/collections/settings"]').click(); cy.get('a[href^="#/collections/settings"]').click();
cy.get('a[href^="#/collections/settings/entries/general"]').click(); cy.get('a[href^="#/collections/settings/entries/general"]').click();
@ -321,4 +337,6 @@ module.exports = {
validateNestedObjectFieldsAndExit, validateNestedObjectFieldsAndExit,
validateListFieldsAndExit, validateListFieldsAndExit,
unpublishEntry, unpublishEntry,
publishEntryInEditor,
duplicateEntry,
}; };

View File

@ -36,6 +36,7 @@ export const DRAFT_VALIDATION_ERRORS = 'DRAFT_VALIDATION_ERRORS';
export const DRAFT_CLEAR_ERRORS = 'DRAFT_CLEAR_ERRORS'; export const DRAFT_CLEAR_ERRORS = 'DRAFT_CLEAR_ERRORS';
export const DRAFT_LOCAL_BACKUP_RETRIEVED = 'DRAFT_LOCAL_BACKUP_RETRIEVED'; export const DRAFT_LOCAL_BACKUP_RETRIEVED = 'DRAFT_LOCAL_BACKUP_RETRIEVED';
export const DRAFT_CREATE_FROM_LOCAL_BACKUP = 'DRAFT_CREATE_FROM_LOCAL_BACKUP'; export const DRAFT_CREATE_FROM_LOCAL_BACKUP = 'DRAFT_CREATE_FROM_LOCAL_BACKUP';
export const DRAFT_CREATE_DUPLICATE_FROM_ENTRY = 'DRAFT_CREATE_DUPLICATE_FROM_ENTRY';
export const ENTRY_PERSIST_REQUEST = 'ENTRY_PERSIST_REQUEST'; export const ENTRY_PERSIST_REQUEST = 'ENTRY_PERSIST_REQUEST';
export const ENTRY_PERSIST_SUCCESS = 'ENTRY_PERSIST_SUCCESS'; export const ENTRY_PERSIST_SUCCESS = 'ENTRY_PERSIST_SUCCESS';
@ -200,6 +201,13 @@ export function createDraftFromEntry(entry, metadata, mediaFiles) {
}; };
} }
export function createDraftDuplicateFromEntry(entry) {
return {
type: DRAFT_CREATE_DUPLICATE_FROM_ENTRY,
payload: createEntry(entry.get('collection'), '', '', { data: entry.get('data') }),
};
}
export function discardDraft() { export function discardDraft() {
return (dispatch, getState) => { return (dispatch, getState) => {
const state = getState(); const state = getState();

View File

@ -11,6 +11,7 @@ import {
loadEntry, loadEntry,
loadEntries, loadEntries,
createDraftFromEntry, createDraftFromEntry,
createDraftDuplicateFromEntry,
createEmptyDraft, createEmptyDraft,
discardDraft, discardDraft,
changeDraftField, changeDraftField,
@ -49,6 +50,7 @@ export class Editor extends React.Component {
changeDraftFieldValidation: PropTypes.func.isRequired, changeDraftFieldValidation: PropTypes.func.isRequired,
collection: ImmutablePropTypes.map.isRequired, collection: ImmutablePropTypes.map.isRequired,
createDraftFromEntry: PropTypes.func.isRequired, createDraftFromEntry: PropTypes.func.isRequired,
createDraftDuplicateFromEntry: PropTypes.func.isRequired,
createEmptyDraft: PropTypes.func.isRequired, createEmptyDraft: PropTypes.func.isRequired,
discardDraft: PropTypes.func.isRequired, discardDraft: PropTypes.func.isRequired,
entry: ImmutablePropTypes.map, entry: ImmutablePropTypes.map,
@ -255,7 +257,7 @@ export class Editor extends React.Component {
} }
handlePersistEntry = async (opts = {}) => { handlePersistEntry = async (opts = {}) => {
const { createNew = false } = opts; const { createNew = false, duplicate = false } = opts;
const { const {
persistEntry, persistEntry,
collection, collection,
@ -263,7 +265,8 @@ export class Editor extends React.Component {
hasWorkflow, hasWorkflow,
loadEntry, loadEntry,
slug, slug,
createEmptyDraft, createDraftDuplicateFromEntry,
entryDraft,
} = this.props; } = this.props;
await persistEntry(collection); await persistEntry(collection);
@ -272,15 +275,23 @@ export class Editor extends React.Component {
if (createNew) { if (createNew) {
navigateToNewEntry(collection.get('name')); navigateToNewEntry(collection.get('name'));
createEmptyDraft(collection); duplicate && createDraftDuplicateFromEntry(entryDraft.get('entry'));
} else if (slug && hasWorkflow && !currentStatus) { } else if (slug && hasWorkflow && !currentStatus) {
loadEntry(collection, slug); loadEntry(collection, slug);
} }
}; };
handlePublishEntry = async (opts = {}) => { handlePublishEntry = async (opts = {}) => {
const { createNew = false } = opts; const { createNew = false, duplicate = false } = opts;
const { publishUnpublishedEntry, entryDraft, collection, slug, currentStatus, t } = this.props; const {
publishUnpublishedEntry,
createDraftDuplicateFromEntry,
entryDraft,
collection,
slug,
currentStatus,
t,
} = this.props;
if (currentStatus !== status.last()) { if (currentStatus !== status.last()) {
window.alert(t('editor.editor.onPublishingNotReady')); window.alert(t('editor.editor.onPublishingNotReady'));
return; return;
@ -298,6 +309,8 @@ export class Editor extends React.Component {
if (createNew) { if (createNew) {
navigateToNewEntry(collection.get('name')); navigateToNewEntry(collection.get('name'));
} }
duplicate && createDraftDuplicateFromEntry(entryDraft.get('entry'));
}; };
handleUnpublishEntry = async () => { handleUnpublishEntry = async () => {
@ -309,6 +322,13 @@ export class Editor extends React.Component {
return navigateToCollection(collection.get('name')); return navigateToCollection(collection.get('name'));
}; };
handleDuplicateEntry = async () => {
const { createDraftDuplicateFromEntry, collection, entryDraft } = this.props;
await navigateToNewEntry(collection.get('name'));
createDraftDuplicateFromEntry(entryDraft.get('entry'));
};
handleDeleteEntry = () => { handleDeleteEntry = () => {
const { entryDraft, newEntry, collection, deleteEntry, slug, t } = this.props; const { entryDraft, newEntry, collection, deleteEntry, slug, t } = this.props;
if (entryDraft.get('hasChanged')) { if (entryDraft.get('hasChanged')) {
@ -415,6 +435,7 @@ export class Editor extends React.Component {
onChangeStatus={this.handleChangeStatus} onChangeStatus={this.handleChangeStatus}
onPublish={this.handlePublishEntry} onPublish={this.handlePublishEntry}
unPublish={this.handleUnpublishEntry} unPublish={this.handleUnpublishEntry}
onDuplicate={this.handleDuplicateEntry}
showDelete={this.props.showDelete} showDelete={this.props.showDelete}
user={user} user={user}
hasChanged={hasChanged} hasChanged={hasChanged}
@ -486,6 +507,7 @@ export default connect(mapStateToProps, {
persistLocalBackup, persistLocalBackup,
deleteLocalBackup, deleteLocalBackup,
createDraftFromEntry, createDraftFromEntry,
createDraftDuplicateFromEntry,
createEmptyDraft, createEmptyDraft,
discardDraft, discardDraft,
persistEntry, persistEntry,

View File

@ -123,15 +123,15 @@ class EditorInterface extends Component {
}; };
handleOnPersist = (opts = {}) => { handleOnPersist = (opts = {}) => {
const { createNew = false } = opts; const { createNew = false, duplicate = false } = opts;
this.controlPaneRef.validate(); this.controlPaneRef.validate();
this.props.onPersist({ createNew }); this.props.onPersist({ createNew, duplicate });
}; };
handleOnPublish = (opts = {}) => { handleOnPublish = (opts = {}) => {
const { createNew = false } = opts; const { createNew = false, duplicate = false } = opts;
this.controlPaneRef.validate(); this.controlPaneRef.validate();
this.props.onPublish({ createNew }); this.props.onPublish({ createNew, duplicate });
}; };
handleTogglePreview = () => { handleTogglePreview = () => {
@ -161,6 +161,7 @@ class EditorInterface extends Component {
onChangeStatus, onChangeStatus,
onPublish, onPublish,
unPublish, unPublish,
onDuplicate,
onValidate, onValidate,
user, user,
hasChanged, hasChanged,
@ -230,13 +231,16 @@ class EditorInterface extends Component {
isDeleting={entry.get('isDeleting')} isDeleting={entry.get('isDeleting')}
onPersist={this.handleOnPersist} onPersist={this.handleOnPersist}
onPersistAndNew={() => this.handleOnPersist({ createNew: true })} onPersistAndNew={() => this.handleOnPersist({ createNew: true })}
onPersistAndDuplicate={() => this.handleOnPersist({ createNew: true, duplicate: true })}
onDelete={onDelete} onDelete={onDelete}
onDeleteUnpublishedChanges={onDeleteUnpublishedChanges} onDeleteUnpublishedChanges={onDeleteUnpublishedChanges}
onChangeStatus={onChangeStatus} onChangeStatus={onChangeStatus}
showDelete={showDelete} showDelete={showDelete}
onPublish={onPublish} onPublish={onPublish}
unPublish={unPublish} unPublish={unPublish}
onDuplicate={onDuplicate}
onPublishAndNew={() => this.handleOnPublish({ createNew: true })} onPublishAndNew={() => this.handleOnPublish({ createNew: true })}
onPublishAndDuplicate={() => this.handleOnPublish({ createNew: true, duplicate: true })}
user={user} user={user}
hasChanged={hasChanged} hasChanged={hasChanged}
displayUrl={displayUrl} displayUrl={displayUrl}
@ -294,6 +298,7 @@ EditorInterface.propTypes = {
onDeleteUnpublishedChanges: PropTypes.func.isRequired, onDeleteUnpublishedChanges: PropTypes.func.isRequired,
onPublish: PropTypes.func.isRequired, onPublish: PropTypes.func.isRequired,
unPublish: PropTypes.func.isRequired, unPublish: PropTypes.func.isRequired,
onDuplicate: PropTypes.func.isRequired,
onChangeStatus: PropTypes.func.isRequired, onChangeStatus: PropTypes.func.isRequired,
user: ImmutablePropTypes.map.isRequired, user: ImmutablePropTypes.map.isRequired,
hasChanged: PropTypes.bool, hasChanged: PropTypes.bool,

View File

@ -15,7 +15,6 @@ import {
colors, colors,
components, components,
buttons, buttons,
lengths,
} from 'netlify-cms-ui-default'; } from 'netlify-cms-ui-default';
import { status } from 'Constants/publishModes'; import { status } from 'Constants/publishModes';
import SettingsDropdown from 'UI/SettingsDropdown'; import SettingsDropdown from 'UI/SettingsDropdown';
@ -141,24 +140,11 @@ const SaveButton = styled(ToolbarButton)`
${buttons.lightBlue}; ${buttons.lightBlue};
`; `;
const UnpublishButton = styled(StyledDropdownButton)` const PublishedButton = styled(StyledDropdownButton)`
background-color: ${colorsRaw.tealLight}; background-color: ${colorsRaw.tealLight};
color: ${colorsRaw.teal}; color: ${colorsRaw.teal};
`; `;
const StatusPublished = styled.div`
${styles.buttonMargin};
border: 1px solid ${colors.textFieldBorder};
border-radius: ${lengths.borderRadius};
background-color: ${colorsRaw.white};
color: ${colorsRaw.teal};
padding: 0 24px;
line-height: 36px;
cursor: default;
font-size: 14px;
font-weight: 500;
`;
const PublishButton = styled(StyledDropdownButton)` const PublishButton = styled(StyledDropdownButton)`
background-color: ${colorsRaw.teal}; background-color: ${colorsRaw.teal};
`; `;
@ -212,13 +198,16 @@ class EditorToolbar extends React.Component {
isDeleting: PropTypes.bool, isDeleting: PropTypes.bool,
onPersist: PropTypes.func.isRequired, onPersist: PropTypes.func.isRequired,
onPersistAndNew: PropTypes.func.isRequired, onPersistAndNew: PropTypes.func.isRequired,
onPersistAndDuplicate: PropTypes.func.isRequired,
showDelete: PropTypes.bool.isRequired, showDelete: PropTypes.bool.isRequired,
onDelete: PropTypes.func.isRequired, onDelete: PropTypes.func.isRequired,
onDeleteUnpublishedChanges: PropTypes.func.isRequired, onDeleteUnpublishedChanges: PropTypes.func.isRequired,
onChangeStatus: PropTypes.func.isRequired, onChangeStatus: PropTypes.func.isRequired,
onPublish: PropTypes.func.isRequired, onPublish: PropTypes.func.isRequired,
unPublish: PropTypes.func.isRequired, unPublish: PropTypes.func.isRequired,
onDuplicate: PropTypes.func.isRequired,
onPublishAndNew: PropTypes.func.isRequired, onPublishAndNew: PropTypes.func.isRequired,
onPublishAndDuplicate: PropTypes.func.isRequired,
user: ImmutablePropTypes.map.isRequired, user: ImmutablePropTypes.map.isRequired,
hasChanged: PropTypes.bool, hasChanged: PropTypes.bool,
displayUrl: PropTypes.string, displayUrl: PropTypes.string,
@ -293,6 +282,8 @@ class EditorToolbar extends React.Component {
collection, collection,
onPersist, onPersist,
onPersistAndNew, onPersistAndNew,
onPersistAndDuplicate,
onDuplicate,
isPersisting, isPersisting,
hasChanged, hasChanged,
isNewEntry, isNewEntry,
@ -302,7 +293,19 @@ class EditorToolbar extends React.Component {
return ( return (
<> <>
{this.renderDeployPreviewControls(t('editor.editorToolbar.deployButtonLabel'))} {this.renderDeployPreviewControls(t('editor.editorToolbar.deployButtonLabel'))}
<StatusPublished>{t('editor.editorToolbar.published')}</StatusPublished> <ToolbarDropdown
dropdownTopOverlap="40px"
dropdownWidth="150px"
renderButton={() => (
<PublishedButton>{t('editor.editorToolbar.published')}</PublishedButton>
)}
>
<DropdownItem
label={t('editor.editorToolbar.duplicate')}
icon="add"
onClick={onDuplicate}
/>
</ToolbarDropdown>
</> </>
); );
} }
@ -320,17 +323,24 @@ class EditorToolbar extends React.Component {
)} )}
> >
<DropdownItem <DropdownItem
label="Publish now" label={t('editor.editorToolbar.publishNow')}
icon="arrow" icon="arrow"
iconDirection="right" iconDirection="right"
onClick={onPersist} onClick={onPersist}
/> />
{collection.get('create') ? ( {collection.get('create') ? (
<DropdownItem <>
label={t('editor.editorToolbar.publishAndCreateNew')} <DropdownItem
icon="add" label={t('editor.editorToolbar.publishAndCreateNew')}
onClick={onPersistAndNew} icon="add"
/> onClick={onPersistAndNew}
/>
<DropdownItem
label={t('editor.editorToolbar.publishAndDuplicate')}
icon="add"
onClick={onPersistAndDuplicate}
/>
</>
) : null} ) : null}
</ToolbarDropdown> </ToolbarDropdown>
</div> </div>
@ -384,7 +394,9 @@ class EditorToolbar extends React.Component {
onChangeStatus, onChangeStatus,
onPublish, onPublish,
unPublish, unPublish,
onDuplicate,
onPublishAndNew, onPublishAndNew,
onPublishAndDuplicate,
currentStatus, currentStatus,
isNewEntry, isNewEntry,
useOpenAuthoring, useOpenAuthoring,
@ -447,11 +459,18 @@ class EditorToolbar extends React.Component {
onClick={onPublish} onClick={onPublish}
/> />
{collection.get('create') ? ( {collection.get('create') ? (
<DropdownItem <>
label={t('editor.editorToolbar.publishAndCreateNew')} <DropdownItem
icon="add" label={t('editor.editorToolbar.publishAndCreateNew')}
onClick={onPublishAndNew} icon="add"
/> onClick={onPublishAndNew}
/>
<DropdownItem
label={t('editor.editorToolbar.publishAndDuplicate')}
icon="add"
onClick={onPublishAndDuplicate}
/>
</>
) : null} ) : null}
</ToolbarDropdown> </ToolbarDropdown>
)} )}
@ -470,11 +489,11 @@ class EditorToolbar extends React.Component {
dropdownTopOverlap="40px" dropdownTopOverlap="40px"
dropdownWidth="150px" dropdownWidth="150px"
renderButton={() => ( renderButton={() => (
<UnpublishButton> <PublishedButton>
{isPersisting {isPersisting
? t('editor.editorToolbar.unpublishing') ? t('editor.editorToolbar.unpublishing')
: t('editor.editorToolbar.published')} : t('editor.editorToolbar.published')}
</UnpublishButton> </PublishedButton>
)} )}
> >
<DropdownItem <DropdownItem
@ -483,6 +502,11 @@ class EditorToolbar extends React.Component {
iconDirection="right" iconDirection="right"
onClick={unPublish} onClick={unPublish}
/> />
<DropdownItem
label={t('editor.editorToolbar.duplicate')}
icon="add"
onClick={onDuplicate}
/>
</ToolbarDropdown> </ToolbarDropdown>
</> </>
); );

View File

@ -26,6 +26,7 @@ describe('Editor', () => {
changeDraftFieldValidation: jest.fn(), changeDraftFieldValidation: jest.fn(),
collection: fromJS({ name: 'posts' }), collection: fromJS({ name: 'posts' }),
createDraftFromEntry: jest.fn(), createDraftFromEntry: jest.fn(),
createDraftDuplicateFromEntry: jest.fn(),
createEmptyDraft: jest.fn(), createEmptyDraft: jest.fn(),
discardDraft: jest.fn(), discardDraft: jest.fn(),
entry: fromJS({}), entry: fromJS({}),

View File

@ -8,6 +8,7 @@ import {
DRAFT_CLEAR_ERRORS, DRAFT_CLEAR_ERRORS,
DRAFT_LOCAL_BACKUP_RETRIEVED, DRAFT_LOCAL_BACKUP_RETRIEVED,
DRAFT_CREATE_FROM_LOCAL_BACKUP, DRAFT_CREATE_FROM_LOCAL_BACKUP,
DRAFT_CREATE_DUPLICATE_FROM_ENTRY,
ENTRY_PERSIST_REQUEST, ENTRY_PERSIST_REQUEST,
ENTRY_PERSIST_SUCCESS, ENTRY_PERSIST_SUCCESS,
ENTRY_PERSIST_FAILURE, ENTRY_PERSIST_FAILURE,
@ -69,6 +70,16 @@ const entryDraftReducer = (state = Map(), action) => {
state.set('fieldsErrors', Map()); state.set('fieldsErrors', Map());
state.set('hasChanged', true); state.set('hasChanged', true);
}); });
case DRAFT_CREATE_DUPLICATE_FROM_ENTRY:
// Duplicate Entry
return state.withMutations(state => {
state.set('entry', fromJS(action.payload));
state.setIn(['entry', 'newRecord'], true);
state.set('mediaFiles', List());
state.set('fieldsMetaData', Map());
state.set('fieldsErrors', Map());
state.set('hasChanged', true);
});
case DRAFT_DISCARD: case DRAFT_DISCARD:
return initialState; return initialState;
case DRAFT_LOCAL_BACKUP_RETRIEVED: { case DRAFT_LOCAL_BACKUP_RETRIEVED: {

View File

@ -65,8 +65,10 @@ const en = {
publish: 'Publish', publish: 'Publish',
published: 'Published', published: 'Published',
unpublish: 'Unpublish', unpublish: 'Unpublish',
duplicate: 'Duplicate',
unpublishing: 'Unpublishing...', unpublishing: 'Unpublishing...',
publishAndCreateNew: 'Publish and create new', publishAndCreateNew: 'Publish and create new',
publishAndDuplicate: 'Publish and duplicate',
deleteUnpublishedChanges: 'Delete unpublished changes', deleteUnpublishedChanges: 'Delete unpublished changes',
deleteUnpublishedEntry: 'Delete unpublished entry', deleteUnpublishedEntry: 'Delete unpublished entry',
deletePublishedEntry: 'Delete published entry', deletePublishedEntry: 'Delete published entry',

View File

@ -312,7 +312,6 @@ const components = {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
white-space: nowrap;
&:last-of-type { &:last-of-type {
border-bottom: 0; border-bottom: 0;