UI
This commit is contained in:
@@ -60,6 +60,7 @@
|
||||
import { base } from '$lib/base'
|
||||
import ToggleButtonGroup from './common/toggleButton-v2/ToggleButtonGroup.svelte'
|
||||
import ToggleButton from './common/toggleButton-v2/ToggleButton.svelte'
|
||||
import DatatableSchemaDiff from './DatatableSchemaDiff.svelte'
|
||||
|
||||
interface Props {
|
||||
currentWorkspaceId: string
|
||||
@@ -709,382 +710,409 @@
|
||||
{@const selectedConflicts = conflictingDiffs.filter((e) =>
|
||||
selectedItems.includes(getItemKey(e))
|
||||
).length}
|
||||
|
||||
<WorkspaceDeployLayout
|
||||
items={deployableItems}
|
||||
{selectedItems}
|
||||
{deploymentStatus}
|
||||
selectablePredicate={(item) => selectableDiffs.some((d) => getItemKey(d) === item.key)}
|
||||
{allSelected}
|
||||
onToggleItem={(item) => {
|
||||
const diff = comparison?.diffs.find((d) => getItemKey(d) === item.key)
|
||||
if (diff) toggleItem(diff)
|
||||
}}
|
||||
onSelectAll={selectAll}
|
||||
onDeselectAll={deselectAll}
|
||||
emptyMessage="No comparison data available"
|
||||
>
|
||||
{#snippet header()}
|
||||
<div class="flex items-center justify-between">
|
||||
<div
|
||||
class="flex flex-col gap-2 border bg-surface-tertiary w-full p-4 border-radius-5 rounded"
|
||||
>
|
||||
<div class="flex flex-row gap-1 items-center">
|
||||
<ToggleButtonGroup
|
||||
disabled={deploying}
|
||||
selected="deploy_to"
|
||||
onSelected={toggleDeploymentDirection}
|
||||
noWFull
|
||||
>
|
||||
{#snippet children({ item })}
|
||||
<ToggleButton
|
||||
value="deploy_to"
|
||||
label="Deploy to {parentWorkspaceId}"
|
||||
icon={ArrowUp}
|
||||
{item}
|
||||
/>
|
||||
<ToggleButton value="update" label="Update current" icon={ArrowDown} {item} />
|
||||
{/snippet}
|
||||
</ToggleButtonGroup>
|
||||
{#if currentWorkspaceInfo && parentWorkspaceInfo}
|
||||
<Badge
|
||||
color="transparent"
|
||||
class="ml-5 font-semibold"
|
||||
title={mergeIntoParent ? currentWorkspaceInfo.name : parentWorkspaceInfo.name}
|
||||
>
|
||||
<span class="text-secondary">merge:</span>
|
||||
{#if mergeIntoParent}
|
||||
<GitFork size={14} />
|
||||
<span class="text-emphasis">{currentWorkspaceInfo.id}</span>
|
||||
{:else}
|
||||
<Building size={14} />
|
||||
<span class="text-emphasis">{parentWorkspaceInfo.id}</span>
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="bg-surface-tertiary p-4 rounded-md border">
|
||||
<WorkspaceDeployLayout
|
||||
items={deployableItems}
|
||||
{selectedItems}
|
||||
{deploymentStatus}
|
||||
selectablePredicate={(item) => selectableDiffs.some((d) => getItemKey(d) === item.key)}
|
||||
{allSelected}
|
||||
onToggleItem={(item) => {
|
||||
const diff = comparison?.diffs.find((d) => getItemKey(d) === item.key)
|
||||
if (diff) toggleItem(diff)
|
||||
}}
|
||||
onSelectAll={selectAll}
|
||||
onDeselectAll={deselectAll}
|
||||
emptyMessage="No comparison data available"
|
||||
>
|
||||
{#snippet header()}
|
||||
<div class="flex items-center justify-between bg-surface-tertiary">
|
||||
<div class="flex flex-col gap-2 w-full pb-4 border-b">
|
||||
<div class="flex flex-wrap gap-1 items-center">
|
||||
<ToggleButtonGroup
|
||||
disabled={deploying}
|
||||
selected="deploy_to"
|
||||
onSelected={toggleDeploymentDirection}
|
||||
noWFull
|
||||
>
|
||||
{#snippet children({ item })}
|
||||
<ToggleButton
|
||||
value="deploy_to"
|
||||
label="Deploy to {parentWorkspaceId}"
|
||||
icon={ArrowUp}
|
||||
{item}
|
||||
/>
|
||||
<ToggleButton value="update" label="Update current" icon={ArrowDown} {item} />
|
||||
{/snippet}
|
||||
</ToggleButtonGroup>
|
||||
{#if currentWorkspaceInfo && parentWorkspaceInfo}
|
||||
<div class="flex-1 flex gap-1 items-center">
|
||||
<Badge
|
||||
color="transparent"
|
||||
class="ml-5 font-semibold"
|
||||
title={mergeIntoParent ? currentWorkspaceInfo.name : parentWorkspaceInfo.name}
|
||||
>
|
||||
<span class="text-secondary">merge:</span>
|
||||
{#if mergeIntoParent}
|
||||
<GitFork size={14} />
|
||||
<span class="text-emphasis">{currentWorkspaceInfo.id}</span>
|
||||
{:else}
|
||||
<Building size={14} />
|
||||
<span class="text-emphasis">{parentWorkspaceInfo.id}</span>
|
||||
{/if}
|
||||
</Badge>
|
||||
<ArrowRight size={16} />
|
||||
<Badge
|
||||
color="transparent"
|
||||
class="font-semibold"
|
||||
title={!mergeIntoParent
|
||||
? currentWorkspaceInfo.name
|
||||
: parentWorkspaceInfo.name}
|
||||
>
|
||||
<span class="text-secondary">into:</span>
|
||||
{#if !mergeIntoParent}
|
||||
<GitFork size={14} />
|
||||
<span class="text-emphasis">{currentWorkspaceInfo.id}</span>
|
||||
{:else}
|
||||
<Building size={14} />
|
||||
<span class="text-emphasis">{parentWorkspaceInfo.id}</span>
|
||||
{/if}
|
||||
</Badge>
|
||||
</div>
|
||||
{/if}
|
||||
</Badge>
|
||||
<ArrowRight size={16} />
|
||||
<Badge
|
||||
color="transparent"
|
||||
class="font-semibold"
|
||||
title={!mergeIntoParent ? currentWorkspaceInfo.name : parentWorkspaceInfo.name}
|
||||
>
|
||||
<span class="text-secondary">into:</span>
|
||||
{#if !mergeIntoParent}
|
||||
<GitFork size={14} />
|
||||
<span class="text-emphasis">{currentWorkspaceInfo.id}</span>
|
||||
{:else}
|
||||
<Building size={14} />
|
||||
<span class="text-emphasis">{parentWorkspaceInfo.id}</span>
|
||||
{/if}
|
||||
</Badge>
|
||||
{/if}
|
||||
<div class="flex items-center gap-2 text-sm">
|
||||
<Badge color="transparent">
|
||||
{comparison.summary.total_diffs} total items
|
||||
</Badge>
|
||||
<Badge color="transparent">
|
||||
{selectableDiffs.length}
|
||||
{mergeIntoParent ? 'deployable' : 'updateable'}
|
||||
</Badge>
|
||||
{#if conflictingDiffs.length > 0}
|
||||
<Badge color="orange">
|
||||
<AlertTriangle class="w-3 h-3 inline mr-1" />
|
||||
{conflictingDiffs.length} conflicts
|
||||
</Badge>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 text-sm">
|
||||
<Badge color="transparent">
|
||||
{comparison.summary.total_diffs} total items
|
||||
</Badge>
|
||||
<Badge color="transparent">
|
||||
{selectableDiffs.length}
|
||||
{mergeIntoParent ? 'deployable' : 'updateable'}
|
||||
</Badge>
|
||||
{#if conflictingDiffs.length > 0}
|
||||
<Badge color="orange">
|
||||
<AlertTriangle class="w-3 h-3 inline mr-1" />
|
||||
{conflictingDiffs.length} conflicts
|
||||
</Badge>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/snippet}
|
||||
{/snippet}
|
||||
|
||||
{#snippet alerts()}
|
||||
{#if mergeIntoParent}
|
||||
<ParentWorkspaceProtectionAlert
|
||||
{parentWorkspaceId}
|
||||
onUpdateCanDeploy={(canDeploy) => {
|
||||
canDeployToParent = canDeploy
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
{#if conflictingDiffs.length > 0}
|
||||
<Alert title="Conflicting changes detected" type="warning" class="mt-2">
|
||||
<span>
|
||||
{conflictingDiffs.length} item{conflictingDiffs.length !== 1 ? 's have' : ' has'} conflicting
|
||||
changes, it was modified on the original workspace while changes were made on this fork.
|
||||
Make sure to resolve these before merging.
|
||||
</span>
|
||||
</Alert>
|
||||
{/if}
|
||||
{#if hasBehindChanges && hasAheadChanges && !(mergeIntoParent && !canDeployToParent)}
|
||||
<Alert
|
||||
title="This fork is behind {parentWorkspaceId} and needs to be up to date before deploying"
|
||||
type="warning"
|
||||
class="my-2"
|
||||
>
|
||||
You have items behind '{parentWorkspaceId}'. You need to update and test your changes
|
||||
before being able to deploy.
|
||||
<span class="font-medium flex flex-row gap-1 text-red-500">
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={allowBehindChangesOverride}
|
||||
class="rounded max-w-4"
|
||||
{#snippet alerts()}
|
||||
{#if mergeIntoParent}
|
||||
<ParentWorkspaceProtectionAlert
|
||||
{parentWorkspaceId}
|
||||
onUpdateCanDeploy={(canDeploy) => {
|
||||
canDeployToParent = canDeploy
|
||||
}}
|
||||
/>
|
||||
Override: Deploy despite {itemsWithBehindChanges.length} outdated item{itemsWithBehindChanges.length !==
|
||||
1
|
||||
? 's'
|
||||
: ''}
|
||||
</span>
|
||||
</Alert>
|
||||
{/if}
|
||||
{#if !comparison.all_ahead_items_visible || !comparison.all_behind_items_visible}
|
||||
<Alert title="This fork has changes not visible to your user" type="warning" class="my-2">
|
||||
{#if !comparison.all_ahead_items_visible && !comparison.all_behind_items_visible}
|
||||
This fork is ahead and behind its parent
|
||||
{:else if !comparison.all_behind_items_visible}
|
||||
This fork is behind of its parent
|
||||
{:else if !comparison.all_ahead_items_visible}
|
||||
This fork is ahead of its parent
|
||||
{/if}
|
||||
and some of the changes are not visible by you. Only a user with access to the whole context
|
||||
may deploy or update this fork. You can share the link to this page to someone with proper
|
||||
permissions to get it deployed.
|
||||
</Alert>
|
||||
{/if}
|
||||
{/snippet}
|
||||
|
||||
{#snippet itemSummary(item)}
|
||||
{@const diff = item.diff as WorkspaceItemDiff}
|
||||
{@const key = item.key}
|
||||
{@const isSelectable = selectableDiffs.includes(diff)}
|
||||
{@const oldSummary = mergeIntoParent ? summaryCache[key]?.parent : summaryCache[key]?.current}
|
||||
{@const newSummary = mergeIntoParent ? summaryCache[key]?.current : summaryCache[key]?.parent}
|
||||
{@const existsInBothWorkspaces = !(
|
||||
(diff.exists_in_fork && !diff.exists_in_source) ||
|
||||
(!diff.exists_in_fork && diff.exists_in_source)
|
||||
)}
|
||||
{#if oldSummary != newSummary && isSelectable && existsInBothWorkspaces}
|
||||
<span class="line-through text-secondary">{oldSummary || diff.path}</span>
|
||||
{newSummary || diff.path}
|
||||
{:else if !existsInBothWorkspaces}
|
||||
{newSummary || oldSummary || diff.path}
|
||||
{:else}
|
||||
{newSummary || diff.path}
|
||||
{/if}
|
||||
{/snippet}
|
||||
|
||||
{#snippet itemActions(item)}
|
||||
{@const diff = item.diff as WorkspaceItemDiff}
|
||||
{@const key = item.key}
|
||||
{@const targetOnBehalfOf = getTargetOnBehalfOf(key)}
|
||||
{@const isConflict = diff.ahead > 0 && diff.behind > 0}
|
||||
{@const existsInBothWorkspaces = !(
|
||||
(diff.exists_in_fork && !diff.exists_in_source) ||
|
||||
(!diff.exists_in_fork && diff.exists_in_source)
|
||||
)}
|
||||
<!-- On-behalf-of selector -->
|
||||
{#if itemNeedsOnBehalfOfSelection(key, diff.kind)}
|
||||
<OnBehalfOfSelector
|
||||
targetWorkspace={deployTargetWorkspace}
|
||||
targetValue={targetOnBehalfOf}
|
||||
selected={onBehalfOfChoice[key]}
|
||||
onSelect={(choice, details) => {
|
||||
onBehalfOfChoice[key] = choice
|
||||
if (details) customOnBehalfOf[key] = details
|
||||
}}
|
||||
kind={diff.kind}
|
||||
canPreserve={canPreserveOnBehalfOf}
|
||||
customValue={customOnBehalfOf[key]?.permissionedAs}
|
||||
/>
|
||||
{/if}
|
||||
{#if diff.kind === 'raw_app'}
|
||||
<Badge small icon={{ icon: FileJson }}>Raw</Badge>
|
||||
{/if}
|
||||
<!-- Status badges -->
|
||||
{#if !diff.exists_in_fork && diff.exists_in_source && diff.ahead == 0 && diff.behind > 0}
|
||||
<Badge
|
||||
title="This item was newly created in the parent workspace '{parentWorkspaceId}'"
|
||||
color="indigo"
|
||||
size="xs">New</Badge
|
||||
>
|
||||
{/if}
|
||||
{#if !diff.exists_in_fork && diff.exists_in_source && diff.ahead > 0}
|
||||
<Badge title="This item was deleted in '{currentWorkspaceId}'" color="red" size="xs"
|
||||
>Deleted</Badge
|
||||
>
|
||||
{/if}
|
||||
{#if diff.exists_in_fork && !diff.exists_in_source && diff.behind > 0}
|
||||
<Badge
|
||||
title="This item was deleted in the parent workspace '{parentWorkspaceId}'"
|
||||
color="red"
|
||||
size="xs">Deleted</Badge
|
||||
>
|
||||
{/if}
|
||||
{#if diff.exists_in_fork && !diff.exists_in_source && diff.ahead > 0 && diff.behind == 0}
|
||||
<Badge
|
||||
title="This item was newly created in '{currentWorkspaceId}'"
|
||||
color="indigo"
|
||||
size="xs">New</Badge
|
||||
>
|
||||
{/if}
|
||||
{#if !deploymentStatus[key] || deploymentStatus[key].status != 'deployed'}
|
||||
<div class="flex items-center gap-2">
|
||||
{#if isConflict || existsInBothWorkspaces}
|
||||
{#if diff.ahead > 0}
|
||||
<Badge color="green" size="xs">
|
||||
<ArrowUpRight class="w-3 h-3 inline" />
|
||||
{diff.ahead} ahead
|
||||
</Badge>
|
||||
{/if}
|
||||
{#if diff.behind > 0}
|
||||
<Badge color="blue" size="xs">
|
||||
<ArrowDownRight class="w-3 h-3 inline" />
|
||||
{diff.behind} behind
|
||||
</Badge>
|
||||
{/if}
|
||||
{#if isConflict}
|
||||
<Badge color="orange" size="xs">
|
||||
<AlertTriangle class="w-3 h-3 inline" />
|
||||
Conflict
|
||||
</Badge>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
<div class:invisible={!existsInBothWorkspaces}>
|
||||
<Button size="xs" variant="subtle" onclick={() => showDiff(diff.kind as Kind, diff.path)}>
|
||||
<DiffIcon class="w-3 h-3" />
|
||||
Show diff
|
||||
</Button>
|
||||
</div>
|
||||
{/if}
|
||||
{/snippet}
|
||||
|
||||
{#snippet footer()}
|
||||
<div class="flex items-center justify-between">
|
||||
<div></div>
|
||||
|
||||
<div class="flex flex-col items-end gap-2">
|
||||
{#if comparison.all_behind_items_visible && comparison.all_ahead_items_visible}
|
||||
{#if !(mergeIntoParent && !canDeployToParent)}
|
||||
<Button
|
||||
color="blue"
|
||||
disabled={selectedItems.length === 0 ||
|
||||
deploying ||
|
||||
(hasBehindChanges && !allowBehindChangesOverride) ||
|
||||
(mergeIntoParent && !canDeployToParent) ||
|
||||
hasUnselectedOnBehalfOf}
|
||||
loading={deploying}
|
||||
on:click={deployChanges}
|
||||
>
|
||||
{mergeIntoParent ? 'Deploy' : 'Update'}
|
||||
{selectedItems.length} Item{selectedItems.length !== 1 ? 's' : ''}
|
||||
{#if selectedConflicts != 0}
|
||||
({selectedConflicts} conflicts)
|
||||
{/if}
|
||||
</Button>
|
||||
{#if hasUnselectedOnBehalfOf}
|
||||
<span class="text-xs text-yellow-600">
|
||||
You must set the "on behalf of" user for all items before deploying
|
||||
<Tooltip class="text-yellow-600">
|
||||
The "run on behalf of" field defines which user's permissions will be applied
|
||||
during execution. Make sure this is set to an appropriate user before deploying.
|
||||
</Tooltip>
|
||||
</span>
|
||||
{/if}
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
{#if deploymentErrorMessage != ''}
|
||||
<Alert
|
||||
title="Cannot {mergeIntoParent ? 'deploy these changes' : 'update these items'}"
|
||||
type="error"
|
||||
class="my-2 max-w-80"
|
||||
>
|
||||
{#if conflictingDiffs.length > 0}
|
||||
<Alert title="Conflicting changes detected" type="warning" class="mt-2">
|
||||
<span>
|
||||
{deploymentErrorMessage}
|
||||
{conflictingDiffs.length} item{conflictingDiffs.length !== 1 ? 's have' : ' has'} conflicting
|
||||
changes, it was modified on the original workspace while changes were made on this fork.
|
||||
Make sure to resolve these before merging.
|
||||
</span>
|
||||
</Alert>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/snippet}
|
||||
</WorkspaceDeployLayout>
|
||||
|
||||
<!-- Fork Triggers Section -->
|
||||
<div class="mt-6">
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<h3 class="text-sm font-semibold">Triggers created in this fork</h3>
|
||||
{#if !loadingTriggers}
|
||||
<Badge color="indigo" size="xs"
|
||||
>{forkTriggers.length} trigger{forkTriggers.length !== 1 ? 's' : ''}</Badge
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<Alert title="Deploy and/or delete these triggers" type="info" class="mb-2">
|
||||
When forking a workspace, triggers are not forked to avoid unnecessary executions or
|
||||
collisions. If you created this triggers with the intention of deploying them to the parent
|
||||
workspace, you can do so here. Otherwise it is recommended to delete them or disable them.
|
||||
</Alert>
|
||||
|
||||
{#if loadingTriggers}
|
||||
<div class="flex items-center gap-2 text-secondary text-sm p-4">
|
||||
<Loader2 class="animate-spin w-4 h-4" />
|
||||
Loading triggers...
|
||||
</div>
|
||||
{:else if forkTriggers.length === 0}
|
||||
<div class="text-secondary text-sm p-4 border rounded-md bg-surface-tertiary">
|
||||
No triggers in this fork workspace.
|
||||
</div>
|
||||
{:else}
|
||||
<div class="border rounded-md bg-surface-tertiary">
|
||||
{#each forkTriggers as trigger (trigger.triggerKind + ':' + trigger.path)}
|
||||
<Row
|
||||
kind="trigger"
|
||||
triggerKind={trigger.triggerKind}
|
||||
path={trigger.path}
|
||||
href={getTriggerHref(trigger.triggerKind)}
|
||||
marked={undefined}
|
||||
isSelectable={false}
|
||||
canFavorite={false}
|
||||
workspaceId={currentWorkspaceId}
|
||||
>
|
||||
{#snippet customSummary()}
|
||||
<span>{getTriggerDisplayName(trigger.triggerKind)}</span>
|
||||
<span class="text-secondary mx-1">→</span>
|
||||
<span class="text-secondary">{trigger.scriptPath}</span>
|
||||
{#if trigger.isFlow}
|
||||
<Badge color="blue" size="xs">flow</Badge>
|
||||
{#if hasBehindChanges && hasAheadChanges && !(mergeIntoParent && !canDeployToParent)}
|
||||
<Alert
|
||||
title="This fork is behind {parentWorkspaceId} and needs to be up to date before deploying"
|
||||
type="warning"
|
||||
class="my-2"
|
||||
>
|
||||
You have items behind '{parentWorkspaceId}'. You need to update and test your changes
|
||||
before being able to deploy.
|
||||
<span class="font-medium flex flex-row gap-1 text-red-500">
|
||||
<input
|
||||
type="checkbox"
|
||||
bind:checked={allowBehindChangesOverride}
|
||||
class="rounded max-w-4"
|
||||
/>
|
||||
Override: Deploy despite {itemsWithBehindChanges.length} outdated item{itemsWithBehindChanges.length !==
|
||||
1
|
||||
? 's'
|
||||
: ''}
|
||||
</span>
|
||||
</Alert>
|
||||
{/if}
|
||||
{#if !comparison.all_ahead_items_visible || !comparison.all_behind_items_visible}
|
||||
<Alert
|
||||
title="This fork has changes not visible to your user"
|
||||
type="warning"
|
||||
class="my-2"
|
||||
>
|
||||
{#if !comparison.all_ahead_items_visible && !comparison.all_behind_items_visible}
|
||||
This fork is ahead and behind its parent
|
||||
{:else if !comparison.all_behind_items_visible}
|
||||
This fork is behind of its parent
|
||||
{:else if !comparison.all_ahead_items_visible}
|
||||
This fork is ahead of its parent
|
||||
{/if}
|
||||
{#if trigger.extraLabel}
|
||||
<span class="text-tertiary text-xs">({trigger.extraLabel})</span>
|
||||
{/if}
|
||||
{/snippet}
|
||||
{#snippet actions()}
|
||||
{#if trigger.enabled != null}
|
||||
<Badge color={trigger.enabled ? 'green' : 'gray'} size="xs">
|
||||
{trigger.enabled ? 'Enabled' : 'Disabled'}
|
||||
</Badge>
|
||||
and some of the changes are not visible by you. Only a user with access to the whole context
|
||||
may deploy or update this fork. You can share the link to this page to someone with proper
|
||||
permissions to get it deployed.
|
||||
</Alert>
|
||||
{/if}
|
||||
{/snippet}
|
||||
|
||||
{#snippet itemSummary(item)}
|
||||
{@const diff = item.diff as WorkspaceItemDiff}
|
||||
{@const key = item.key}
|
||||
{@const isSelectable = selectableDiffs.includes(diff)}
|
||||
{@const oldSummary = mergeIntoParent
|
||||
? summaryCache[key]?.parent
|
||||
: summaryCache[key]?.current}
|
||||
{@const newSummary = mergeIntoParent
|
||||
? summaryCache[key]?.current
|
||||
: summaryCache[key]?.parent}
|
||||
{@const existsInBothWorkspaces = !(
|
||||
(diff.exists_in_fork && !diff.exists_in_source) ||
|
||||
(!diff.exists_in_fork && diff.exists_in_source)
|
||||
)}
|
||||
{#if oldSummary != newSummary && isSelectable && existsInBothWorkspaces}
|
||||
<span class="line-through text-secondary">{oldSummary || diff.path}</span>
|
||||
{newSummary || diff.path}
|
||||
{:else if !existsInBothWorkspaces}
|
||||
{newSummary || oldSummary || diff.path}
|
||||
{:else}
|
||||
{newSummary || diff.path}
|
||||
{/if}
|
||||
{/snippet}
|
||||
|
||||
{#snippet itemActions(item)}
|
||||
{@const diff = item.diff as WorkspaceItemDiff}
|
||||
{@const key = item.key}
|
||||
{@const targetOnBehalfOf = getTargetOnBehalfOf(key)}
|
||||
{@const isConflict = diff.ahead > 0 && diff.behind > 0}
|
||||
{@const existsInBothWorkspaces = !(
|
||||
(diff.exists_in_fork && !diff.exists_in_source) ||
|
||||
(!diff.exists_in_fork && diff.exists_in_source)
|
||||
)}
|
||||
<!-- On-behalf-of selector -->
|
||||
{#if itemNeedsOnBehalfOfSelection(key, diff.kind)}
|
||||
<OnBehalfOfSelector
|
||||
targetWorkspace={deployTargetWorkspace}
|
||||
targetValue={targetOnBehalfOf}
|
||||
selected={onBehalfOfChoice[key]}
|
||||
onSelect={(choice, details) => {
|
||||
onBehalfOfChoice[key] = choice
|
||||
if (details) customOnBehalfOf[key] = details
|
||||
}}
|
||||
kind={diff.kind}
|
||||
canPreserve={canPreserveOnBehalfOf}
|
||||
customValue={customOnBehalfOf[key]?.permissionedAs}
|
||||
/>
|
||||
{/if}
|
||||
{#if diff.kind === 'raw_app'}
|
||||
<Badge small icon={{ icon: FileJson }}>Raw</Badge>
|
||||
{/if}
|
||||
<!-- Status badges -->
|
||||
{#if !diff.exists_in_fork && diff.exists_in_source && diff.ahead == 0 && diff.behind > 0}
|
||||
<Badge
|
||||
title="This item was newly created in the parent workspace '{parentWorkspaceId}'"
|
||||
color="indigo"
|
||||
size="xs">New</Badge
|
||||
>
|
||||
{/if}
|
||||
{#if !diff.exists_in_fork && diff.exists_in_source && diff.ahead > 0}
|
||||
<Badge title="This item was deleted in '{currentWorkspaceId}'" color="red" size="xs"
|
||||
>Deleted</Badge
|
||||
>
|
||||
{/if}
|
||||
{#if diff.exists_in_fork && !diff.exists_in_source && diff.behind > 0}
|
||||
<Badge
|
||||
title="This item was deleted in the parent workspace '{parentWorkspaceId}'"
|
||||
color="red"
|
||||
size="xs">Deleted</Badge
|
||||
>
|
||||
{/if}
|
||||
{#if diff.exists_in_fork && !diff.exists_in_source && diff.ahead > 0 && diff.behind == 0}
|
||||
<Badge
|
||||
title="This item was newly created in '{currentWorkspaceId}'"
|
||||
color="indigo"
|
||||
size="xs">New</Badge
|
||||
>
|
||||
{/if}
|
||||
{#if !deploymentStatus[key] || deploymentStatus[key].status != 'deployed'}
|
||||
<div class="flex items-center gap-2">
|
||||
{#if isConflict || existsInBothWorkspaces}
|
||||
{#if diff.ahead > 0}
|
||||
<Badge color="green" size="xs">
|
||||
<ArrowUpRight class="w-3 h-3 inline" />
|
||||
{diff.ahead} ahead
|
||||
</Badge>
|
||||
{/if}
|
||||
{#if diff.behind > 0}
|
||||
<Badge color="blue" size="xs">
|
||||
<ArrowDownRight class="w-3 h-3 inline" />
|
||||
{diff.behind} behind
|
||||
</Badge>
|
||||
{/if}
|
||||
{#if isConflict}
|
||||
<Badge color="orange" size="xs">
|
||||
<AlertTriangle class="w-3 h-3 inline" />
|
||||
Conflict
|
||||
</Badge>
|
||||
{/if}
|
||||
{/if}
|
||||
</div>
|
||||
<div class:invisible={!existsInBothWorkspaces}>
|
||||
<Button
|
||||
size="xs"
|
||||
variant="subtle"
|
||||
onclick={() => {
|
||||
deploymentDrawer?.openDrawer(trigger.path, 'trigger', {
|
||||
triggers: { kind: trigger.triggerKind }
|
||||
})
|
||||
}}
|
||||
onclick={() => showDiff(diff.kind as Kind, diff.path)}
|
||||
>
|
||||
<Upload size={12} />
|
||||
Deploy
|
||||
<DiffIcon class="w-3 h-3" />
|
||||
Show diff
|
||||
</Button>
|
||||
<Button size="xs" variant="subtle" color="red" onclick={() => deleteTrigger(trigger)}>
|
||||
<Trash2 size={12} />
|
||||
</Button>
|
||||
{/snippet}
|
||||
</Row>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
{/snippet}
|
||||
|
||||
{#snippet footer()}
|
||||
<div class="flex items-center justify-between">
|
||||
<div></div>
|
||||
|
||||
<div class="flex flex-col items-end gap-2">
|
||||
{#if comparison.all_behind_items_visible && comparison.all_ahead_items_visible}
|
||||
{#if !(mergeIntoParent && !canDeployToParent)}
|
||||
<Button
|
||||
color="blue"
|
||||
disabled={selectedItems.length === 0 ||
|
||||
deploying ||
|
||||
(hasBehindChanges && !allowBehindChangesOverride) ||
|
||||
(mergeIntoParent && !canDeployToParent) ||
|
||||
hasUnselectedOnBehalfOf}
|
||||
loading={deploying}
|
||||
on:click={deployChanges}
|
||||
>
|
||||
{mergeIntoParent ? 'Deploy' : 'Update'}
|
||||
{selectedItems.length} Item{selectedItems.length !== 1 ? 's' : ''}
|
||||
{#if selectedConflicts != 0}
|
||||
({selectedConflicts} conflicts)
|
||||
{/if}
|
||||
</Button>
|
||||
{#if hasUnselectedOnBehalfOf}
|
||||
<span class="text-xs text-yellow-600">
|
||||
You must set the "on behalf of" user for all items before deploying
|
||||
<Tooltip class="text-yellow-600">
|
||||
The "run on behalf of" field defines which user's permissions will be
|
||||
applied during execution. Make sure this is set to an appropriate user
|
||||
before deploying.
|
||||
</Tooltip>
|
||||
</span>
|
||||
{/if}
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
{#if deploymentErrorMessage != ''}
|
||||
<Alert
|
||||
title="Cannot {mergeIntoParent ? 'deploy these changes' : 'update these items'}"
|
||||
type="error"
|
||||
class="my-2 max-w-80"
|
||||
>
|
||||
<span>
|
||||
{deploymentErrorMessage}
|
||||
</span>
|
||||
</Alert>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/snippet}
|
||||
</WorkspaceDeployLayout>
|
||||
</div>
|
||||
|
||||
<!-- Fork Triggers Section -->
|
||||
<div class="bg-surface-tertiary p-4 rounded-md border">
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<h3 class="text-sm font-semibold">Triggers created in this fork</h3>
|
||||
{#if !loadingTriggers}
|
||||
<Badge color="indigo" size="xs"
|
||||
>{forkTriggers.length} trigger{forkTriggers.length !== 1 ? 's' : ''}</Badge
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<Alert title="Deploy and/or delete these triggers" type="info" class="mb-2">
|
||||
When forking a workspace, triggers are not forked to avoid unnecessary executions or
|
||||
collisions. If you created this triggers with the intention of deploying them to the parent
|
||||
workspace, you can do so here. Otherwise it is recommended to delete them or disable them.
|
||||
</Alert>
|
||||
|
||||
{#if loadingTriggers}
|
||||
<div class="flex items-center gap-2 text-secondary text-sm p-4">
|
||||
<Loader2 class="animate-spin w-4 h-4" />
|
||||
Loading triggers...
|
||||
</div>
|
||||
{:else if forkTriggers.length === 0}
|
||||
<div class="text-secondary text-sm p-4 border rounded-md bg-surface-tertiary">
|
||||
No triggers in this fork workspace.
|
||||
</div>
|
||||
{:else}
|
||||
<div class="border rounded-md bg-surface-tertiary">
|
||||
{#each forkTriggers as trigger (trigger.triggerKind + ':' + trigger.path)}
|
||||
<Row
|
||||
kind="trigger"
|
||||
triggerKind={trigger.triggerKind}
|
||||
path={trigger.path}
|
||||
href={getTriggerHref(trigger.triggerKind)}
|
||||
marked={undefined}
|
||||
isSelectable={false}
|
||||
canFavorite={false}
|
||||
workspaceId={currentWorkspaceId}
|
||||
>
|
||||
{#snippet customSummary()}
|
||||
<span>{getTriggerDisplayName(trigger.triggerKind)}</span>
|
||||
<span class="text-secondary mx-1">→</span>
|
||||
<span class="text-secondary">{trigger.scriptPath}</span>
|
||||
{#if trigger.isFlow}
|
||||
<Badge color="blue" size="xs">flow</Badge>
|
||||
{/if}
|
||||
{#if trigger.extraLabel}
|
||||
<span class="text-tertiary text-xs">({trigger.extraLabel})</span>
|
||||
{/if}
|
||||
{/snippet}
|
||||
{#snippet actions()}
|
||||
{#if trigger.enabled != null}
|
||||
<Badge color={trigger.enabled ? 'green' : 'gray'} size="xs">
|
||||
{trigger.enabled ? 'Enabled' : 'Disabled'}
|
||||
</Badge>
|
||||
{/if}
|
||||
<Button
|
||||
size="xs"
|
||||
variant="subtle"
|
||||
onclick={() => {
|
||||
deploymentDrawer?.openDrawer(trigger.path, 'trigger', {
|
||||
triggers: { kind: trigger.triggerKind }
|
||||
})
|
||||
}}
|
||||
>
|
||||
<Upload size={12} />
|
||||
Deploy
|
||||
</Button>
|
||||
<Button
|
||||
size="xs"
|
||||
variant="subtle"
|
||||
color="red"
|
||||
onclick={() => deleteTrigger(trigger)}
|
||||
>
|
||||
<Trash2 size={12} />
|
||||
</Button>
|
||||
{/snippet}
|
||||
</Row>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="bg-surface-tertiary p-4 rounded-md border">
|
||||
<DatatableSchemaDiff {currentWorkspaceId} {parentWorkspaceId} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DeployWorkspaceDrawer bind:this={deploymentDrawer} />
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
<!-- Select all row -->
|
||||
<div class="px-4 py-2 flex items-center justify-between">
|
||||
<div
|
||||
class="flex items-center gap-2 text-secondary text-sm"
|
||||
class="flex items-center gap-2 text-secondary text-xs"
|
||||
class:opacity-50={!hasSelectableItems}
|
||||
>
|
||||
<input
|
||||
@@ -149,7 +149,7 @@
|
||||
|
||||
<!-- Footer -->
|
||||
{#if footer}
|
||||
<div class="p-4 bg-surface">
|
||||
<div class="p-4">
|
||||
{@render footer()}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<script lang="ts">
|
||||
import CompareWorkspaces from '$lib/components/CompareWorkspaces.svelte'
|
||||
import DatatableSchemaDiff from '$lib/components/DatatableSchemaDiff.svelte'
|
||||
import { WorkspaceService, type WorkspaceComparison } from '$lib/gen'
|
||||
import { page } from '$app/state'
|
||||
import { userWorkspaces } from '$lib/stores'
|
||||
@@ -63,7 +62,6 @@
|
||||
<!-- }} -->
|
||||
<!-- /> -->
|
||||
<CompareWorkspaces {currentWorkspaceId} {parentWorkspaceId} {comparison} />
|
||||
<DatatableSchemaDiff {currentWorkspaceId} {parentWorkspaceId} />
|
||||
{/if}
|
||||
{#if !currentWorkspaceId}
|
||||
No workspace selected
|
||||
|
||||
Reference in New Issue
Block a user