mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-05 08:02:18 +00:00
fix(frontend): fix Decision Tree + fix Infinite list default ts code … (#3993)
* fix(frontend): fix Decision Tree + fix Infinite list default ts code indentation + add missing reset column defs button * fix(frontend): improves how columnDefs are computed when we change the table * fix(frontend): improved code quality * fix(frontend): fix current node id * fix(frontend): wip * fix(frontend): wip * fix(frontend): done * fix(frontend): done * fix(frontend): fix node deletion * fix(frontend): fix node deletion * fix(frontend): simplify node deletion
This commit is contained in:
@@ -64,6 +64,10 @@
|
||||
lastTable = undefined
|
||||
}
|
||||
|
||||
clearColumnDefs()
|
||||
}
|
||||
|
||||
function clearColumnDefs() {
|
||||
const gridItem = findGridItem($app, id)
|
||||
|
||||
if (!gridItem) {
|
||||
@@ -445,6 +449,11 @@
|
||||
|
||||
state = undefined
|
||||
|
||||
// If in the mean time the table has changed, we don't want to update the columnDefs
|
||||
if (lastTable !== table) {
|
||||
return
|
||||
}
|
||||
|
||||
//@ts-ignore
|
||||
gridItem.data.configuration.columnDefs = { value: ncols, type: 'static', loading: false }
|
||||
gridItem.data = gridItem.data
|
||||
@@ -669,6 +678,11 @@
|
||||
on:update={onUpdate}
|
||||
on:delete={onDelete}
|
||||
allowColumnDefsActions={false}
|
||||
on:recompute={() => {
|
||||
lastTable = undefined
|
||||
clearColumnDefs()
|
||||
listColumnsIfAvailable()
|
||||
}}
|
||||
{actions}
|
||||
/>
|
||||
{/key}
|
||||
|
||||
+3
@@ -301,6 +301,9 @@
|
||||
agGrid: { api: e.api, columnApi: e.columnApi },
|
||||
setSelectedIndex: (index) => {
|
||||
e.api.getRowNode(index.toString())?.setSelected(true)
|
||||
},
|
||||
recompute: () => {
|
||||
dispatch('recompute')
|
||||
}
|
||||
}
|
||||
api = e.api
|
||||
|
||||
@@ -17,95 +17,127 @@
|
||||
|
||||
let currentNodeId: string = $worldStore.outputsById[id]?.currentNodeId?.peak() ?? 'a'
|
||||
|
||||
$worldStore.outputsById[id]?.currentNodeId?.subscribe(
|
||||
{
|
||||
id: id,
|
||||
next: (value) => {
|
||||
currentNodeId = value
|
||||
}
|
||||
},
|
||||
currentNodeId
|
||||
)
|
||||
|
||||
$: if (nodes[$debuggingComponents[id] ?? 0]?.id === undefined) {
|
||||
currentNodeId = ''
|
||||
$componentControl?.[id]?.setTab?.(0)
|
||||
|
||||
$debuggingComponents = Object.fromEntries(
|
||||
Object.entries($debuggingComponents).filter(([key]) => key !== id)
|
||||
function subscribeToCurrentNode(id: string) {
|
||||
return $worldStore.outputsById[id]?.currentNodeId?.subscribe(
|
||||
{
|
||||
id: `id-${id}-${currentNodeId}`,
|
||||
next: (value) => {
|
||||
currentNodeId = value
|
||||
}
|
||||
},
|
||||
currentNodeId
|
||||
)
|
||||
}
|
||||
|
||||
let subscription = subscribeToCurrentNode(id)
|
||||
|
||||
function onDebugNode(debuggedNodeIndex: number | undefined) {
|
||||
if (debuggedNodeIndex === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
if (debuggedNodeIndex !== undefined && nodes[debuggedNodeIndex]?.id === undefined) {
|
||||
currentNodeId = nodes[0]?.id ?? ''
|
||||
$componentControl?.[id]?.setTab?.(0)
|
||||
|
||||
$debuggingComponents = Object.fromEntries(
|
||||
Object.entries($debuggingComponents).filter(([key]) => key !== id)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
$: onDebugNode($debuggingComponents[id])
|
||||
|
||||
let renderCount: number = 0
|
||||
let lastNodes: DecisionTreeNode[] = nodes
|
||||
|
||||
function onNodesChange(newNodes: DecisionTreeNode[]) {
|
||||
if (JSON.stringify(newNodes) !== JSON.stringify(lastNodes)) {
|
||||
lastNodes = newNodes
|
||||
|
||||
if (subscription) {
|
||||
subscription?.()
|
||||
}
|
||||
subscription = subscribeToCurrentNode(id)
|
||||
|
||||
renderCount++
|
||||
}
|
||||
}
|
||||
|
||||
$: onNodesChange(nodes)
|
||||
</script>
|
||||
|
||||
<button
|
||||
title={'Debug tabs'}
|
||||
class={classNames(
|
||||
'text-2xs py-0.5 font-bold w-fit border cursor-pointer rounded-sm',
|
||||
isDebugging($debuggingComponents, id)
|
||||
? 'bg-red-100 text-red-600 border-red-500 hover:bg-red-200 hover:text-red-800'
|
||||
: 'bg-indigo-100 text-indigo-600 border-indigo-500 hover:bg-indigo-200 hover:text-indigo-800'
|
||||
)}
|
||||
on:click={() => dispatch('triggerInlineEditor')}
|
||||
on:pointerdown|stopPropagation
|
||||
>
|
||||
<ButtonDropdown hasPadding={false}>
|
||||
<svelte:fragment slot="buttonReplacement">
|
||||
<div class="px-1">
|
||||
{#if isDebugging($debuggingComponents, id)}
|
||||
<div class="flex flex-row items-center gap-2">
|
||||
{`Debugging node ${nodes[$debuggingComponents[id] ?? 0]?.id}`}
|
||||
<button
|
||||
on:click={() => {
|
||||
$componentControl?.[id]?.setTab?.(0)
|
||||
{#key renderCount}
|
||||
<button
|
||||
title={'Debug tabs'}
|
||||
class={classNames(
|
||||
'text-2xs py-0.5 font-bold w-fit border cursor-pointer rounded-sm',
|
||||
isDebugging($debuggingComponents, id)
|
||||
? 'bg-red-100 text-red-600 border-red-500 hover:bg-red-200 hover:text-red-800'
|
||||
: 'bg-indigo-100 text-indigo-600 border-indigo-500 hover:bg-indigo-200 hover:text-indigo-800'
|
||||
)}
|
||||
on:click={() => dispatch('triggerInlineEditor')}
|
||||
on:pointerdown|stopPropagation
|
||||
>
|
||||
<ButtonDropdown hasPadding={false}>
|
||||
<svelte:fragment slot="buttonReplacement">
|
||||
<div class="px-1">
|
||||
{#if isDebugging($debuggingComponents, id)}
|
||||
<div class="flex flex-row items-center gap-2">
|
||||
{`Debugging node ${nodes[$debuggingComponents[id] ?? 0]?.id}`}
|
||||
<button
|
||||
on:click={() => {
|
||||
$componentControl?.[id]?.setTab?.(0)
|
||||
|
||||
$debuggingComponents = Object.fromEntries(
|
||||
Object.entries($debuggingComponents).filter(([key]) => key !== id)
|
||||
)
|
||||
}}
|
||||
$debuggingComponents = Object.fromEntries(
|
||||
Object.entries($debuggingComponents).filter(([key]) => key !== id)
|
||||
)
|
||||
}}
|
||||
>
|
||||
<X size={14} />
|
||||
</button>
|
||||
</div>
|
||||
{:else}
|
||||
{`Debug nodes (current node: ${currentNodeId})`}
|
||||
{/if}
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="items">
|
||||
{#each nodes ?? [] as node, index}
|
||||
<MenuItem
|
||||
on:click={() => {
|
||||
$componentControl?.[id]?.setTab?.(index)
|
||||
|
||||
$debuggingComponents[id] = index
|
||||
}}
|
||||
>
|
||||
<div
|
||||
class={classNames(
|
||||
'!text-tertiary text-left px-4 py-2 gap-2 cursor-pointer hover:bg-gray-100 !text-xs font-semibold'
|
||||
)}
|
||||
>
|
||||
<X size={14} />
|
||||
</button>
|
||||
</div>
|
||||
{:else}
|
||||
{`Debug nodes (current node: ${currentNodeId})`}
|
||||
{/if}
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="items">
|
||||
{#each nodes ?? [] as node, index}
|
||||
{`Debug node ${node.label}`}
|
||||
</div>
|
||||
</MenuItem>
|
||||
{/each}
|
||||
<MenuItem
|
||||
on:click={() => {
|
||||
$componentControl?.[id]?.setTab?.(index)
|
||||
$componentControl?.[id]?.setTab?.(0)
|
||||
|
||||
$debuggingComponents[id] = index
|
||||
$debuggingComponents = Object.fromEntries(
|
||||
Object.entries($debuggingComponents).filter(([key]) => key !== id)
|
||||
)
|
||||
}}
|
||||
>
|
||||
<div
|
||||
class={classNames(
|
||||
'!text-tertiary text-left px-4 py-2 gap-2 cursor-pointer hover:bg-gray-100 !text-xs font-semibold'
|
||||
'!text-red-600 text-left px-4 py-2 gap-2 cursor-pointer hover:bg-gray-100 !text-xs font-semibold'
|
||||
)}
|
||||
>
|
||||
{`Debug node ${node.label}`}
|
||||
{`Reset debug mode`}
|
||||
</div>
|
||||
</MenuItem>
|
||||
{/each}
|
||||
<MenuItem
|
||||
on:click={() => {
|
||||
$componentControl?.[id]?.setTab?.(0)
|
||||
|
||||
$debuggingComponents = Object.fromEntries(
|
||||
Object.entries($debuggingComponents).filter(([key]) => key !== id)
|
||||
)
|
||||
}}
|
||||
>
|
||||
<div
|
||||
class={classNames(
|
||||
'!text-red-600 text-left px-4 py-2 gap-2 cursor-pointer hover:bg-gray-100 !text-xs font-semibold'
|
||||
)}
|
||||
>
|
||||
{`Reset debug mode`}
|
||||
</div>
|
||||
</MenuItem>
|
||||
</svelte:fragment>
|
||||
</ButtonDropdown>
|
||||
</button>
|
||||
</svelte:fragment>
|
||||
</ButtonDropdown>
|
||||
</button>
|
||||
{/key}
|
||||
|
||||
@@ -115,9 +115,9 @@ export async function main(db: Postgresql) {
|
||||
email: string;
|
||||
created_at: string;
|
||||
};
|
||||
const GLOBAL_COUNT = 25000;
|
||||
const GLOBAL_COUNT = 25000;
|
||||
|
||||
function getUsers(limit: number, offset: number): User[] {
|
||||
function getUsers(limit: number, offset: number): User[] {
|
||||
|
||||
if (offset > GLOBAL_COUNT) {
|
||||
return []
|
||||
@@ -130,28 +130,28 @@ export async function main(db: Postgresql) {
|
||||
email: \`user\${offset + index}@example.com\`,
|
||||
created_at: new Date().toISOString(),
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
export async function main(offset: number, limit: number, orderBy: string, isDesc: boolean, search: string): Promise<User[]> {
|
||||
export async function main(offset: number, limit: number, orderBy: string, isDesc: boolean, search: string): Promise<User[]> {
|
||||
let users = getUsers(limit, offset);
|
||||
|
||||
if (orderBy && Object.keys(users[0]).includes(orderBy)) {
|
||||
users.sort((a, b) => {
|
||||
const aValue = a[orderBy as keyof User];
|
||||
const bValue = b[orderBy as keyof User];
|
||||
const aValue = a[orderBy as keyof User];
|
||||
const bValue = b[orderBy as keyof User];
|
||||
|
||||
if (typeof aValue === 'string' && typeof bValue === 'string') {
|
||||
return isDesc ? bValue.localeCompare(aValue) : aValue.localeCompare(bValue);
|
||||
}
|
||||
return 0;
|
||||
if (typeof aValue === 'string' && typeof bValue === 'string') {
|
||||
return isDesc ? bValue.localeCompare(aValue) : aValue.localeCompare(bValue);
|
||||
}
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
if (search) {
|
||||
return users.filter(user =>
|
||||
Object.values(user).some(value =>
|
||||
typeof value === 'string' && value.toLowerCase().includes(search.toLowerCase())
|
||||
)
|
||||
Object.values(user).some(value =>
|
||||
typeof value === 'string' && value.toLowerCase().includes(search.toLowerCase())
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+24
-8
@@ -8,6 +8,7 @@
|
||||
import { generateRandomString, pluralize } from '$lib/utils'
|
||||
import Toggle from '$lib/components/Toggle.svelte'
|
||||
import QuickAddColumn from './QuickAddColumn.svelte'
|
||||
import RefreshDatabaseStudioTable from './RefreshDatabaseStudioTable.svelte'
|
||||
|
||||
export let componentInput: StaticInput<any[]> & { loading?: boolean }
|
||||
export let subFieldType: InputType | undefined = undefined
|
||||
@@ -192,11 +193,23 @@
|
||||
componentInput.value = reorderedValues
|
||||
}
|
||||
|
||||
let items = (Array.isArray(componentInput.value) ? componentInput.value : [])
|
||||
.filter((x) => x != undefined)
|
||||
.map((item, index) => {
|
||||
return { value: item, id: generateRandomString() }
|
||||
})
|
||||
let items = getItems(componentInput)
|
||||
|
||||
function getItems(componentInput: StaticInput<any[]> & { loading?: boolean }) {
|
||||
return (Array.isArray(componentInput.value) ? componentInput.value : [])
|
||||
.filter((x) => x != undefined)
|
||||
.map((item) => {
|
||||
return { value: item, id: generateRandomString() }
|
||||
})
|
||||
}
|
||||
|
||||
function clearTableOnComponentReset(value: any[] | undefined) {
|
||||
if (Array.isArray(value) && value.length === 0 && items.length > 0) {
|
||||
items = []
|
||||
}
|
||||
}
|
||||
|
||||
$: subFieldType === 'db-explorer' && clearTableOnComponentReset(componentInput?.value)
|
||||
|
||||
$: items != undefined && handleItemsChange()
|
||||
|
||||
@@ -287,11 +300,14 @@
|
||||
{/if}
|
||||
{#if subFieldType === 'db-explorer'}
|
||||
{#if componentInput.loading}
|
||||
<div class="flex flex-row gap-2 w-full items-center text-xs">
|
||||
<Loader2 class="animate-spin" size={14} />
|
||||
Loading columns defintions...
|
||||
<div class="flex flex-row gap-2 w-full items-center">
|
||||
<div class="flex flex-row gap-2 w-full items-center text-xs">
|
||||
<Loader2 class="animate-spin" size={14} />
|
||||
Loading columns defintions...
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<RefreshDatabaseStudioTable {id} />
|
||||
{/if}
|
||||
{#if subFieldType !== 'db-explorer'}
|
||||
<Button size="xs" color="light" startIcon={{ icon: Plus }} on:click={() => addElementByType()}>
|
||||
|
||||
+9
-1
@@ -3,7 +3,7 @@
|
||||
import { Network, Plus, Trash } from 'lucide-svelte'
|
||||
import type { AppComponent, DecisionTreeNode } from '../component'
|
||||
import { Pane, Splitpanes } from 'svelte-splitpanes'
|
||||
import { setContext } from 'svelte'
|
||||
import { getContext, setContext } from 'svelte'
|
||||
import InputsSpecEditor from './InputsSpecEditor.svelte'
|
||||
import Section from '$lib/components/Section.svelte'
|
||||
import { writable } from 'svelte/store'
|
||||
@@ -11,6 +11,7 @@
|
||||
import { addNewBranch, removeNode } from './decisionTree/utils'
|
||||
import Label from '$lib/components/Label.svelte'
|
||||
import { debounce } from '$lib/utils'
|
||||
import type { AppViewerContext } from '../../types'
|
||||
|
||||
export let component: AppComponent
|
||||
export let nodes: DecisionTreeNode[]
|
||||
@@ -21,6 +22,8 @@
|
||||
let paneHeight = 0
|
||||
let renderCount = 0
|
||||
|
||||
const { debuggingComponents } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
const selectedNodeId = writable<string | undefined>(undefined)
|
||||
|
||||
$: selectedNode = nodes?.find((node) => node.id == $selectedNodeId)
|
||||
@@ -64,6 +67,11 @@
|
||||
variant="border"
|
||||
on:click={() => {
|
||||
nodes = removeNode(nodes, selectedNode)
|
||||
|
||||
$debuggingComponents = Object.fromEntries(
|
||||
Object.entries($debuggingComponents).filter(([key]) => key !== component.id)
|
||||
)
|
||||
|
||||
renderCount++
|
||||
}}
|
||||
disabled={selectedNode?.next?.length > 1 || nodes.length == 1}
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
<script lang="ts">
|
||||
import { Button } from '$lib/components/common'
|
||||
import { RotateCcw } from 'lucide-svelte'
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppViewerContext } from '../../types'
|
||||
|
||||
export let id: string | undefined = undefined
|
||||
|
||||
const { componentControl } = getContext<AppViewerContext>('AppViewerContext')
|
||||
|
||||
function onRefresh() {
|
||||
if (!id) return
|
||||
$componentControl[id]?.recompute?.()
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if id}
|
||||
<Button size="xs2" color="light" startIcon={{ icon: RotateCcw }} on:click={() => onRefresh()}>
|
||||
Force refresh
|
||||
</Button>
|
||||
{/if}
|
||||
+8
-3
@@ -222,13 +222,18 @@
|
||||
}
|
||||
|
||||
case 'delete': {
|
||||
const graphhNodeIndex = nodes.findIndex((node) => node.id == graphNode?.id)
|
||||
const graphNodeIndex = nodes.findIndex((node) => node.id == graphNode?.id)
|
||||
|
||||
if (graphhNodeIndex > -1) {
|
||||
deleteSubgrid(graphhNodeIndex)
|
||||
if (graphNodeIndex > -1) {
|
||||
deleteSubgrid(graphNodeIndex)
|
||||
}
|
||||
|
||||
nodes = removeNode(nodes, graphNode)
|
||||
|
||||
$debuggingComponents = Object.fromEntries(
|
||||
Object.entries($debuggingComponents).filter(([key]) => key !== component.id)
|
||||
)
|
||||
|
||||
break
|
||||
}
|
||||
case 'addBranch': {
|
||||
|
||||
@@ -259,6 +259,7 @@ export type AppViewerContext = {
|
||||
validateAll?: () => void
|
||||
clearFiles?: () => void
|
||||
showToast?: (message: string, error?: boolean) => void
|
||||
recompute?: () => void
|
||||
}
|
||||
>
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user