mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-24 08:01:38 +00:00
fix: critical alerts modal mute toggles no longer close popover or fail to save (#9969)
* fix: mute toggles in critical alerts modal no longer close popover or fail to save Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: mark popover content root as dropdown-portal so padding clicks don't close modal Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix: derive no-channels warning from mute state so it survives modal reopen Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,4 @@
|
||||
<script lang="ts">
|
||||
import { run } from 'svelte/legacy'
|
||||
|
||||
import { onMount, onDestroy } from 'svelte'
|
||||
import CriticalAlertModalInner from './CriticalAlertModalInner.svelte'
|
||||
import { SettingService, type CriticalAlert } from '$lib/gen'
|
||||
@@ -35,26 +33,11 @@
|
||||
let workspaceContext = $state(false)
|
||||
let childRef: CriticalAlertModalInner | undefined = $state()
|
||||
|
||||
function setupApiFunctions(_ctx?) {
|
||||
getCriticalAlerts = withSuperadminLogic(
|
||||
SettingService.getCriticalAlerts,
|
||||
SettingService.workspaceGetCriticalAlerts
|
||||
)
|
||||
|
||||
acknowledgeCriticalAlert = withSuperadminLogic(
|
||||
SettingService.acknowledgeCriticalAlert,
|
||||
SettingService.workspaceAcknowledgeCriticalAlert
|
||||
)
|
||||
|
||||
acknowledgeAllCriticalAlerts = withSuperadminLogic(
|
||||
SettingService.acknowledgeAllCriticalAlerts,
|
||||
SettingService.workspaceAcknowledgeAllCriticalAlerts
|
||||
)
|
||||
}
|
||||
|
||||
let checkForNewAlertsInterval: ReturnType<typeof setInterval>
|
||||
let checkingForNewAlerts = false
|
||||
|
||||
// The returned closure reads workspaceContext / $workspaceStore / $devopsRole at
|
||||
// call time, so the wrappers are stable and never need recreating.
|
||||
const withSuperadminLogic = (superadminFunction, workspaceFunction) => {
|
||||
return async (params = {}) => {
|
||||
if (!$devopsRole || workspaceContext) {
|
||||
@@ -68,12 +51,21 @@
|
||||
}
|
||||
}
|
||||
|
||||
type AckFn = (params?: {}) => Promise<any>
|
||||
let getCriticalAlerts: AckFn | undefined = $state()
|
||||
let acknowledgeCriticalAlert: AckFn | undefined = $state()
|
||||
let acknowledgeAllCriticalAlerts: AckFn | undefined = $state()
|
||||
|
||||
setupApiFunctions()
|
||||
let getCriticalAlerts = $derived(
|
||||
withSuperadminLogic(SettingService.getCriticalAlerts, SettingService.workspaceGetCriticalAlerts)
|
||||
)
|
||||
let acknowledgeCriticalAlert = $derived(
|
||||
withSuperadminLogic(
|
||||
SettingService.acknowledgeCriticalAlert,
|
||||
SettingService.workspaceAcknowledgeCriticalAlert
|
||||
)
|
||||
)
|
||||
let acknowledgeAllCriticalAlerts = $derived(
|
||||
withSuperadminLogic(
|
||||
SettingService.acknowledgeAllCriticalAlerts,
|
||||
SettingService.workspaceAcknowledgeAllCriticalAlerts
|
||||
)
|
||||
)
|
||||
|
||||
onMount(async () => {
|
||||
await updateHasUnacknowledgedCriticalAlerts(false)
|
||||
@@ -173,13 +165,10 @@
|
||||
await acknowledgeCriticalAlert?.({ id })
|
||||
updateHasUnacknowledgedCriticalAlerts()
|
||||
}
|
||||
run(() => {
|
||||
setupApiFunctions(workspaceContext)
|
||||
})
|
||||
run(() => {
|
||||
$effect(() => {
|
||||
if ($isCriticalAlertsUIOpen) open = $isCriticalAlertsUIOpen
|
||||
})
|
||||
run(() => {
|
||||
$effect(() => {
|
||||
isCriticalAlertsUIOpen.set(open)
|
||||
})
|
||||
</script>
|
||||
@@ -197,10 +186,14 @@
|
||||
{#snippet headerRight()}
|
||||
<List horizontal>
|
||||
{#if $superadmin || $userStore?.is_admin}
|
||||
<!-- Portal to `body` (not the trigger) so toggle clicks don't bubble to the melt
|
||||
trigger and toggle the popover shut. `dropdown-portal` on the content root is a
|
||||
`portalDivs` marker, so the enclosing Modal2's clickOutside treats the whole
|
||||
popover surface — padding included — as inside a portal and stays open. -->
|
||||
<Popover
|
||||
floatingConfig={{ strategy: 'fixed', placement: 'bottom-end' }}
|
||||
portal="#mute-settings-button"
|
||||
contentClasses="p-4"
|
||||
portal="body"
|
||||
contentClasses="p-4 dropdown-portal"
|
||||
>
|
||||
{#snippet trigger()}
|
||||
<div id="mute-settings-button">
|
||||
@@ -246,8 +239,8 @@
|
||||
{#if $superadmin}
|
||||
<Popover
|
||||
floatingConfig={{ strategy: 'fixed', placement: 'bottom-end' }}
|
||||
portal="#settings-button"
|
||||
contentClasses="p-4"
|
||||
portal="body"
|
||||
contentClasses="p-4 dropdown-portal"
|
||||
>
|
||||
{#snippet trigger()}
|
||||
<div id="settings-button">
|
||||
@@ -304,6 +297,7 @@
|
||||
|
||||
<CriticalAlertModalInner
|
||||
bind:workspaceContext
|
||||
{muteSettings}
|
||||
{numUnacknowledgedCriticalAlerts}
|
||||
{updateHasUnacknowledgedCriticalAlerts}
|
||||
{getCriticalAlerts}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
import CriticalAlertTable from './CriticalAlertTable.svelte'
|
||||
import Alert from '$lib/components/common/alert/Alert.svelte'
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
import { untrack } from 'svelte'
|
||||
import { onMount, untrack } from 'svelte'
|
||||
|
||||
let filteredAlerts: CriticalAlert[] = $state([])
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
acknowledgeCriticalAlert: any
|
||||
acknowledgeAllCriticalAlerts: any
|
||||
numUnacknowledgedCriticalAlerts: any
|
||||
muteSettings?: { global?: boolean; workspace?: boolean }
|
||||
workspaceContext?: boolean
|
||||
}
|
||||
|
||||
@@ -39,9 +40,12 @@
|
||||
acknowledgeCriticalAlert,
|
||||
acknowledgeAllCriticalAlerts,
|
||||
numUnacknowledgedCriticalAlerts,
|
||||
muteSettings,
|
||||
workspaceContext = $bindable(false)
|
||||
}: Props = $props()
|
||||
|
||||
let isMuted = $derived(Boolean(muteSettings?.global || muteSettings?.workspace))
|
||||
|
||||
async function acknowledgeAll() {
|
||||
await acknowledgeAllCriticalAlerts()
|
||||
getAlerts(false)
|
||||
@@ -82,6 +86,12 @@
|
||||
hasCriticalAlertChannels = channels && channels.length > 0
|
||||
}
|
||||
|
||||
// Load the channel state on mount so the "no channels" warning doesn't depend on
|
||||
// there being unacknowledged alerts to trigger a refresh (muting auto-acks them).
|
||||
onMount(() => {
|
||||
if ($superadmin) checkCriticalAlertChannels()
|
||||
})
|
||||
|
||||
async function acknowledgeAlert(id: number) {
|
||||
await acknowledgeCriticalAlert({ id })
|
||||
getAlerts(false)
|
||||
@@ -133,7 +143,7 @@
|
||||
</script>
|
||||
|
||||
<List gap="sm">
|
||||
{#if !hasCriticalAlertChannels && $superadmin}
|
||||
{#if $superadmin && isMuted && !hasCriticalAlertChannels}
|
||||
<div class="w-full">
|
||||
<Alert title="No critical alert channels are set up" type="warning" size="xs">
|
||||
Go to the
|
||||
|
||||
Reference in New Issue
Block a user