Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: delete scheduled published events #10504

Merged
merged 6 commits into from
Jan 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/payload/src/versions/schedule/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ export type SchedulePublishTaskInput = {
}
global?: GlobalSlug
locale?: string
type: string
type?: string
user?: number | string
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import type { ClientConfig } from 'payload'

import { getTranslation, type I18nClient, type TFunction } from '@payloadcms/translations'
import React from 'react'

import type { Column } from '../../Table/index.js'
import type { UpcomingEvent } from './types.js'

import { formatDate } from '../../../utilities/formatDate.js'
import { Button } from '../../Button/index.js'
import { Pill } from '../../Pill/index.js'

type Args = {
dateFormat: string
deleteHandler: (id: number | string) => void
docs: UpcomingEvent[]
i18n: I18nClient
localization: ClientConfig['localization']
Expand All @@ -18,6 +21,7 @@ type Args = {

export const buildUpcomingColumns = ({
dateFormat,
deleteHandler,
docs,
i18n,
localization,
Expand Down Expand Up @@ -81,5 +85,28 @@ export const buildUpcomingColumns = ({
})
}

columns.push({
accessor: 'delete',
active: true,
field: {
name: 'delete',
type: 'text',
},
Heading: <span>{t('general:delete')}</span>,
renderedCells: docs.map((doc) => (
<Button
buttonStyle="icon-label"
className="schedule-publish__delete"
icon="x"
key={doc.id}
onClick={(e) => {
e.preventDefault()
deleteHandler(doc.id)
}}
tooltip={t('general:delete')}
/>
)),
})

return columns
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,9 @@
margin-bottom: var(--base);
}
}

&__delete {
margin: 0;
}
}
}
35 changes: 33 additions & 2 deletions packages/ui/src/elements/PublishButton/ScheduleDrawer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export const ScheduleDrawer: React.FC<Props> = ({ slug }) => {
const modalTitle = t('general:schedulePublishFor', { title })
const [upcoming, setUpcoming] = React.useState<UpcomingEvent[]>()
const [upcomingColumns, setUpcomingColumns] = React.useState<Column[]>()
const deleteHandlerRef = React.useRef<((id: number | string) => Promise<void>) | null>(() => null)

const localeOptions = React.useMemo(() => {
if (localization) {
Expand Down Expand Up @@ -129,9 +130,39 @@ export const ScheduleDrawer: React.FC<Props> = ({ slug }) => {
})
.then((res) => res.json())

setUpcomingColumns(buildUpcomingColumns({ dateFormat, docs, i18n, localization, t }))
setUpcomingColumns(
buildUpcomingColumns({
dateFormat,
// eslint-disable-next-line @typescript-eslint/no-misused-promises
deleteHandler: deleteHandlerRef.current,
docs,
i18n,
localization,
t,
}),
)
setUpcoming(docs)
}, [api, collectionSlug, dateFormat, globalSlug, i18n, id, serverURL, t, localization])
}, [collectionSlug, globalSlug, serverURL, api, dateFormat, id, t, i18n, localization])

const deleteHandler = React.useCallback(
async (id: number | string) => {
try {
await schedulePublish({
deleteID: id,
})
await fetchUpcoming()
toast.success(t('general:deletedSuccessfully'))
} catch (err) {
console.error(err)
toast.error(err.message)
}
},
[fetchUpcoming, schedulePublish, t],
)

React.useEffect(() => {
deleteHandlerRef.current = deleteHandler
}, [deleteHandler])

const handleSave = React.useCallback(async () => {
if (!date) {
Expand Down
26 changes: 22 additions & 4 deletions packages/ui/src/utilities/schedulePublishHandler.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import type { PayloadRequest, SchedulePublishTaskInput } from 'payload'

export type SchedulePublishHandlerArgs = {
date: Date
date?: Date
/**
* The job id to delete to remove a scheduled publish event
*/
deleteID?: number | string
req: PayloadRequest
} & SchedulePublishTaskInput

export const schedulePublishHandler = async ({
type,
date,
deleteID,
doc,
global,
locale,
Expand Down Expand Up @@ -38,6 +43,14 @@ export const schedulePublishHandler = async ({
}

try {
if (deleteID) {
await payload.delete({
collection: 'payload-jobs',
req,
where: { id: { equals: deleteID } },
})
}

await payload.jobs.queue({
input: {
type,
Expand All @@ -50,10 +63,15 @@ export const schedulePublishHandler = async ({
waitUntil: date,
})
} catch (err) {
let error = `Error scheduling ${type} for `
let error

if (doc) {
error += `document with ID ${doc.value} in collection ${doc.relationTo}`
if (deleteID) {
error = `Error deleting scheduled publish event with ID ${deleteID}`
} else {
error = `Error scheduling ${type} for `
if (doc) {
error += `document with ID ${doc.value} in collection ${doc.relationTo}`
}
}

payload.logger.error(error)
Expand Down
113 changes: 108 additions & 5 deletions test/versions/int.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Payload, PayloadRequest } from 'payload'
import { createLocalReq, Payload } from 'payload'
import { schedulePublishHandler } from '@payloadcms/ui/utilities/schedulePublishHandler'

import path from 'path'
import { ValidationError } from 'payload'
Expand Down Expand Up @@ -48,6 +49,7 @@ const formatGraphQLID = (id: number | string) =>
payload.db.defaultIDType === 'number' ? id : `"${id}"`

describe('Versions', () => {
let user
beforeAll(async () => {
process.env.SEED_IN_CONFIG_ONINIT = 'false' // Makes it so the payload config onInit seed is not run. Otherwise, the seed would be run unnecessarily twice for the initial test run - once for beforeEach and once for onInit
;({ payload, restClient } = await initPayloadInt(dirname))
Expand All @@ -69,12 +71,16 @@ describe('Versions', () => {
password: "${devUser.password}"
) {
token
user {
id
}
}
}`
const { data } = await restClient
.GRAPHQL_POST({ body: JSON.stringify({ query: login }) })
.then((res) => res.json())

user = { ...data.loginUser.user, collection: 'users' }
token = data.loginUser.token

// now: initialize
Expand Down Expand Up @@ -1862,10 +1868,6 @@ describe('Versions', () => {

const currentDate = new Date()

const user = (
await payload.find({ collection: 'users', where: { email: { equals: devUser.email } } })
).docs[0]

await payload.jobs.queue({
task: 'schedulePublish',
waitUntil: new Date(currentDate.getTime() + 3000),
Expand Down Expand Up @@ -1998,6 +2000,107 @@ describe('Versions', () => {
expect(retrieved._status).toStrictEqual('draft')
expect(retrieved.title).toStrictEqual('i will be a draft')
})

describe('server functions', () => {
let draftDoc
let event

beforeAll(async () => {
draftDoc = await payload.create({
collection: draftCollectionSlug,
data: {
title: 'my doc',
description: 'hello',
_status: 'draft',
},
})
})

it('should create using schedule-publish', async () => {
const currentDate = new Date()

const req = await createLocalReq({ user }, payload)

// use server action to create the event
await schedulePublishHandler({
req,
type: 'publish',
date: new Date(currentDate.getTime() + 3000),
doc: {
relationTo: draftCollectionSlug,
value: draftDoc.id,
},
user,
locale: 'all',
})

// fetch the job
;[event] = (
await payload.find({
collection: 'payload-jobs',
where: {
'input.doc.value': {
equals: draftDoc.id,
},
},
})
).docs

expect(event).toBeDefined()
})

it('should delete using schedule-publish', async () => {
const currentDate = new Date()

const req = await createLocalReq({ user }, payload)

// use server action to create the event
await schedulePublishHandler({
req,
type: 'publish',
date: new Date(currentDate.getTime() + 3000),
doc: {
relationTo: draftCollectionSlug,
value: draftDoc.id,
},
user,
locale: 'all',
})

// fetch the job
;[event] = (
await payload.find({
collection: 'payload-jobs',
where: {
'input.doc.value': {
equals: draftDoc.id,
},
},
})
).docs

// use server action to delete the event
await schedulePublishHandler({
req,
deleteID: event.id,
user,
})

// fetch the job
;[event] = (
await payload.find({
collection: 'payload-jobs',
where: {
'input.doc.value': {
equals: String(draftDoc.id),
},
},
})
).docs

expect(event).toBeUndefined()
})
})
})

describe('Publish Individual Locale', () => {
Expand Down