Give the digest window a way back
Deleting a message that Slack no longer has counts as done, so the window rewinds instead of staying stuck. A reset button clears the mark by hand, and the empty-run notice now says since the last dispatch instead of seven days.
This commit is contained in:
+6
-3
@@ -287,7 +287,7 @@
|
||||
"typeInUse": "Diese Art wird noch benutzt. Nimm stattdessen den Haken bei Aktiv heraus.",
|
||||
"slackSaved": "Die Slack-Einstellungen sind gespeichert.",
|
||||
"reportSent": "Der Bericht ist raus.",
|
||||
"reportEmpty": "In den letzten sieben Tagen wurde nichts veröffentlicht, deshalb ging keine Nachricht raus.",
|
||||
"reportEmpty": "Seit dem letzten Versand ist nichts dazugekommen, deshalb ging keine Nachricht raus.",
|
||||
"reportNoWebhook": "Es ist kein Webhook hinterlegt.",
|
||||
"reportRejected": "Slack hat die Nachricht abgelehnt.",
|
||||
"messageDeleted": "Die Nachricht ist in Slack gelöscht.",
|
||||
@@ -300,7 +300,8 @@
|
||||
"runNoChannel": "Es ist kein Kanal hinterlegt.",
|
||||
"runNotFound": "Im Kanal war keine passende Nachricht zu finden. Braucht der Bot noch das Recht channels:history?",
|
||||
"channelRequired": "Zum Bot-Token gehört ein Kanal, sonst weiß Logbuch nicht, wohin.",
|
||||
"slackSaid": "Slack sagt: {detail}"
|
||||
"slackSaid": "Slack sagt: {detail}",
|
||||
"windowReset": "Der Zeitraum ist zurückgesetzt. Der nächste Bericht umfasst wieder die letzten sieben Tage."
|
||||
},
|
||||
"errors": {
|
||||
"nameRequired": "Trag einen Namen ein.",
|
||||
@@ -775,7 +776,9 @@
|
||||
"pathNone": "Noch kein Weg hinterlegt, es geht nichts raus.",
|
||||
"messageNoToken": "Ohne Bot-Token nicht löschbar",
|
||||
"lastSent": "Zuletzt verschickt am {when}. Der nächste Bericht beginnt dort und enthält nur, was seitdem erschienen ist.",
|
||||
"neverSent": "Noch nichts verschickt. Der erste Bericht umfasst die letzten sieben Tage."
|
||||
"neverSent": "Noch nichts verschickt. Der erste Bericht umfasst die letzten sieben Tage.",
|
||||
"resetWindow": "Zeitraum zurücksetzen",
|
||||
"resetting": "Wird zurückgesetzt"
|
||||
}
|
||||
},
|
||||
"filter": {
|
||||
|
||||
+6
-3
@@ -287,7 +287,7 @@
|
||||
"typeInUse": "This type is still in use. Clear the active checkbox instead.",
|
||||
"slackSaved": "The Slack settings are saved.",
|
||||
"reportSent": "The digest is out.",
|
||||
"reportEmpty": "Nothing was published in the last seven days, so no message went out.",
|
||||
"reportEmpty": "Nothing appeared since the last digest, so no message went out.",
|
||||
"reportNoWebhook": "No webhook is stored.",
|
||||
"reportRejected": "Slack rejected the message.",
|
||||
"messageDeleted": "The message is deleted in Slack.",
|
||||
@@ -300,7 +300,8 @@
|
||||
"runNoChannel": "No channel is stored.",
|
||||
"runNotFound": "No matching message found in the channel. Does the bot still need channels:history?",
|
||||
"channelRequired": "A bot token needs a channel, otherwise Logbuch has no target.",
|
||||
"slackSaid": "Slack says: {detail}"
|
||||
"slackSaid": "Slack says: {detail}",
|
||||
"windowReset": "The window is reset. The next digest covers the last seven days again."
|
||||
},
|
||||
"errors": {
|
||||
"nameRequired": "Enter a name.",
|
||||
@@ -775,7 +776,9 @@
|
||||
"pathNone": "No route stored yet, nothing goes out.",
|
||||
"messageNoToken": "Not deletable without a bot token",
|
||||
"lastSent": "Last sent on {when}. The next digest starts there and only carries what appeared since.",
|
||||
"neverSent": "Nothing sent yet. The first digest covers the last seven days."
|
||||
"neverSent": "Nothing sent yet. The first digest covers the last seven days.",
|
||||
"resetWindow": "Reset the window",
|
||||
"resetting": "Resetting"
|
||||
}
|
||||
},
|
||||
"filter": {
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
import { useActionState, useState } from 'react'
|
||||
import { useTranslations } from 'next-intl'
|
||||
import { TbSend } from 'react-icons/tb'
|
||||
import { TbRotate2, TbSend } from 'react-icons/tb'
|
||||
import { AdminField } from './AdminField'
|
||||
import { AdminFormMessage } from './AdminFormMessage'
|
||||
import { AdminSubmit } from './AdminSubmit'
|
||||
import { checkboxClass, hintClass, inputClass, labelClass } from './styles'
|
||||
import { saveSlackSettings, sendWeeklyReportNow } from '~/lib/admin-actions'
|
||||
import { resetReportWindow, saveSlackSettings, sendWeeklyReportNow } from '~/lib/admin-actions'
|
||||
import { emptyAdminFormState } from '~/lib/admin-forms'
|
||||
|
||||
export type SlackFormProps = {
|
||||
@@ -22,6 +22,7 @@ export function SlackForm({ hasWebhook, hasBot, channel, fromEnv, includeInterna
|
||||
const t = useTranslations('admin.settings')
|
||||
const [state, formAction] = useActionState(saveSlackSettings, emptyAdminFormState)
|
||||
const [report, reportAction] = useActionState(sendWeeklyReportNow, emptyAdminFormState)
|
||||
const [reset, resetAction] = useActionState(resetReportWindow, emptyAdminFormState)
|
||||
const [replace, setReplace] = useState(!hasWebhook)
|
||||
const [replaceToken, setReplaceToken] = useState(!hasBot)
|
||||
const path = hasBot ? 'pathBot' : hasWebhook ? 'pathWebhook' : 'pathNone'
|
||||
@@ -132,15 +133,23 @@ export function SlackForm({ hasWebhook, hasBot, channel, fromEnv, includeInterna
|
||||
<AdminSubmit pendingLabel={t('saving')}>{t('save')}</AdminSubmit>
|
||||
</form>
|
||||
|
||||
<form action={reportAction} className="flex flex-col gap-3">
|
||||
<div className="flex flex-col gap-3">
|
||||
<AdminFormMessage state={report} />
|
||||
<AdminFormMessage state={reset} />
|
||||
<span className="flex flex-wrap items-center gap-4">
|
||||
<form action={reportAction}>
|
||||
<AdminSubmit quiet pendingLabel={t('sending')} icon={<TbSend className="size-4" />}>
|
||||
{t('sendNow')}
|
||||
</AdminSubmit>
|
||||
</form>
|
||||
<form action={resetAction}>
|
||||
<AdminSubmit quiet pendingLabel={t('resetting')} icon={<TbRotate2 className="size-4" />}>
|
||||
{t('resetWindow')}
|
||||
</AdminSubmit>
|
||||
</form>
|
||||
<span className={hintClass}>{t('hints.sendNow')}</span>
|
||||
</span>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { performSaveBrand } from './admin-brands'
|
||||
import { performCreateClient, performRevokeClient } from './admin-clients'
|
||||
import { performDeletePostType, performSavePostType } from './admin-post-types'
|
||||
import { performSaveProject } from './admin-projects'
|
||||
import { performDeleteMessage, performSaveSlack, performSendReport } from './admin-settings'
|
||||
import { performDeleteMessage, performResetWindow, performSaveSlack, performSendReport } from './admin-settings'
|
||||
import { performGrantRole, performRevokeRole, performSetUserAdmin } from './admin-users'
|
||||
import type { AdminFormState } from './admin-forms'
|
||||
import {
|
||||
@@ -168,3 +168,12 @@ export async function deleteSlackMessage(_state: AdminFormState, formData: FormD
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
export async function resetReportWindow(_state: AdminFormState): Promise<AdminFormState> {
|
||||
const viewer = await requireAdmin()
|
||||
const result = await performResetWindow(viewer)
|
||||
|
||||
revalidatePath(adminSettingsPath)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -187,3 +187,18 @@ export async function performDeleteMessage(viewer: Viewer, formData: FormData):
|
||||
|
||||
return { status: 'ok', message: 'messageDeleted' }
|
||||
}
|
||||
|
||||
export async function performResetWindow(viewer: Viewer): Promise<AdminFormState> {
|
||||
await clearSetting('slack.lastSentAt')
|
||||
|
||||
await recordAudit({
|
||||
actorId: viewer.id,
|
||||
actorLabel: viewer.email,
|
||||
action: 'settings.reportReset',
|
||||
entity: 'setting',
|
||||
entityId: 'slack',
|
||||
data: {},
|
||||
})
|
||||
|
||||
return { status: 'ok', message: 'windowReset' }
|
||||
}
|
||||
|
||||
+3
-1
@@ -69,10 +69,12 @@ export async function sendToSlack(message: SlackMessage, target: SlackTarget): P
|
||||
return { ok: true, sent: { channel: null, messageTs: null } }
|
||||
}
|
||||
|
||||
const goneErrors = ['message_not_found', 'already_deleted']
|
||||
|
||||
export async function deleteFromSlack(token: string, channel: string, messageTs: string): Promise<SlackResult> {
|
||||
const { ok, data } = await callApi(token, 'chat.delete', { channel, ts: messageTs })
|
||||
|
||||
if (!ok) {
|
||||
if (!ok && !goneErrors.includes(String(data.error))) {
|
||||
return { ok: false, detail: String(data.error ?? 'unknown_error') }
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user