mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-12 08:05:44 +00:00
fix(forks): replace browser confirm() with ConfirmationModal for fork conflict
The fork-conflict warning previously used the browser's native confirm() which doesn't match Windmill's design system. Switches to a singleton ConfirmationModal mounted at the (logged) layout root, driven by a new forkConflictModal store. The withForkConflictRetry helper now sets the store and awaits the user's choice via a Promise, instead of blocking on window.confirm. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
7247c8741f
commit
41ec0ddc92
@@ -0,0 +1,30 @@
|
||||
<script lang="ts">
|
||||
import ConfirmationModal from '$lib/components/common/confirmationModal/ConfirmationModal.svelte'
|
||||
import { forkConflictModal } from '$lib/stores'
|
||||
|
||||
const state = $derived(forkConflictModal.val)
|
||||
|
||||
function close(confirmed: boolean) {
|
||||
const resolve = state?.resolve
|
||||
forkConflictModal.val = undefined
|
||||
resolve?.(confirmed)
|
||||
}
|
||||
</script>
|
||||
|
||||
<ConfirmationModal
|
||||
open={!!state}
|
||||
title="Enable in fork conflicts with parent"
|
||||
confirmationText="Enable anyway"
|
||||
onConfirmed={() => close(true)}
|
||||
onCanceled={() => close(false)}
|
||||
>
|
||||
{#if state}
|
||||
<p>
|
||||
This {state.kindLabel} is also enabled in the parent workspace (<span class="font-mono"
|
||||
>{state.parentWorkspaceId}</span
|
||||
>). Enabling it here means both will run at the same time and may compete for the same
|
||||
upstream events or duplicate side effects.
|
||||
</p>
|
||||
<p class="mt-2">Enable in this fork anyway?</p>
|
||||
{/if}
|
||||
</ConfirmationModal>
|
||||
@@ -132,12 +132,25 @@ export const codeCompletionSessionEnabled = writable<boolean>(
|
||||
export const usedTriggerKinds = writable<string[]>([])
|
||||
|
||||
export let globalDbManagerDrawer: StateStore<DbManagerUriState | undefined> = { val: undefined }
|
||||
export let globalForkModal: StateStore<GlobalForkModalState | undefined> = createState({ val: undefined })
|
||||
export let globalForkModal: StateStore<GlobalForkModalState | undefined> = createState({
|
||||
val: undefined
|
||||
})
|
||||
|
||||
export type GlobalForkModalState = {
|
||||
opened: true
|
||||
}
|
||||
|
||||
export type ForkConflictModalState = {
|
||||
kind: string
|
||||
kindLabel: string
|
||||
parentWorkspaceId: string
|
||||
resolve: (proceed: boolean) => void
|
||||
}
|
||||
|
||||
export let forkConflictModal: StateStore<ForkConflictModalState | undefined> = createState({
|
||||
val: undefined
|
||||
})
|
||||
|
||||
type SQLBaseSchema = {
|
||||
[schemaKey: string]: {
|
||||
[tableKey: string]: {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { forkConflictModal } from '$lib/stores'
|
||||
|
||||
/**
|
||||
* The backend rejects "enable" requests on triggers/schedules in a fork when
|
||||
* the parent workspace has the same path enabled. The error body is shaped as
|
||||
@@ -22,8 +24,20 @@ export function detectForkConflict(e: unknown): ForkConflict | null {
|
||||
}
|
||||
|
||||
/**
|
||||
* Catches a fork-conflict error from `fn(false)`, asks the user to confirm,
|
||||
* and retries with `fn(true)` when accepted. Re-throws every other error.
|
||||
* Opens the global ForkConflictModal and awaits the user's choice. Resolves
|
||||
* to true when the user clicks "Enable anyway", false when they cancel or
|
||||
* dismiss.
|
||||
*/
|
||||
function askForkConflictConfirm(kind: string, kindLabel: string, parentWorkspaceId: string) {
|
||||
return new Promise<boolean>((resolve) => {
|
||||
forkConflictModal.val = { kind, kindLabel, parentWorkspaceId, resolve }
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Catches a fork-conflict error from `fn(false)`, shows the confirmation
|
||||
* dialog, and retries with `fn(true)` when the user accepts. Re-throws every
|
||||
* other error.
|
||||
*
|
||||
* `kindLabel` is shown to the user — pass a friendly name like "kafka trigger"
|
||||
* or "schedule" so the dialog reads naturally.
|
||||
@@ -37,11 +51,10 @@ export async function withForkConflictRetry<T>(
|
||||
} catch (e) {
|
||||
const conflict = detectForkConflict(e)
|
||||
if (!conflict) throw e
|
||||
const proceed = window.confirm(
|
||||
`This ${kindLabel} is also enabled in the parent workspace ` +
|
||||
`(${conflict.parentWorkspaceId}). Both will run at the same time and may ` +
|
||||
`compete for the same upstream events or duplicate side effects.\n\n` +
|
||||
`Enable in this fork anyway?`
|
||||
const proceed = await askForkConflictConfirm(
|
||||
conflict.kind,
|
||||
kindLabel,
|
||||
conflict.parentWorkspaceId
|
||||
)
|
||||
if (!proceed) {
|
||||
throw new Error(
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
import WorkspaceMenu from '$lib/components/sidebar/WorkspaceMenu.svelte'
|
||||
import SidebarContent from '$lib/components/sidebar/SidebarContent.svelte'
|
||||
import CriticalAlertModal from '$lib/components/sidebar/CriticalAlertModal.svelte'
|
||||
import ForkConflictModal from '$lib/components/ForkConflictModal.svelte'
|
||||
import {
|
||||
enterpriseLicense,
|
||||
isPremiumStore,
|
||||
@@ -837,6 +838,8 @@
|
||||
<DBManagerDrawer uriState={globalDbManagerDrawer.val} />
|
||||
{/if}
|
||||
|
||||
<ForkConflictModal />
|
||||
|
||||
<Modal2
|
||||
title="Forking {$workspaceStore}"
|
||||
target="#content"
|
||||
|
||||
Reference in New Issue
Block a user