mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-10-09 08:02:25 +00:00
feat(frontend): Add app inputs configurations (#1142)
* feat(frontend): Add app inputs configurations * feat(frontend): only display resource input by default * feat(frontend): Fix no resource field empty display * feat(frontend): open the drawer when forked from the hub
This commit is contained in:
@@ -1,11 +1,9 @@
|
||||
<script lang="ts">
|
||||
import type { AppInput } from '../../inputType'
|
||||
import AlignWrapper from '../helpers/AlignWrapper.svelte'
|
||||
import RunnableWrapper from '../helpers/RunnableWrapper.svelte'
|
||||
|
||||
export let id: string
|
||||
export let componentInput: AppInput | undefined
|
||||
export let configuration: Record<string, AppInput>
|
||||
|
||||
export const staticOutputs: string[] = ['result', 'loading']
|
||||
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
<script lang="ts">
|
||||
import type { AppComponent } from '../types'
|
||||
import { displayData } from '../utils'
|
||||
import InputsSpecsEditor from './settingsPanel/InputsSpecsEditor.svelte'
|
||||
|
||||
export let component: AppComponent
|
||||
export let resourceOnly = false
|
||||
</script>
|
||||
|
||||
{#if component?.componentInput?.type === 'runnable' && Object.keys(component?.componentInput?.fields ?? {}).length > 0}
|
||||
<div class="mb-8">
|
||||
<div class="flex justify-between mb-4">
|
||||
<span class="text-sm font-bold">{component.id}</span>
|
||||
<span class="text-sm font-bold">{displayData[component.type].name}</span>
|
||||
</div>
|
||||
|
||||
{#if resourceOnly && Object.keys(component.componentInput.fields).filter((fieldKey) => {
|
||||
if (component?.componentInput?.type === 'runnable') {
|
||||
const fields = component.componentInput?.fields
|
||||
const field = fields[fieldKey]
|
||||
return field.fieldType === 'object' && field.format?.startsWith('resource-')
|
||||
}
|
||||
return false
|
||||
}).length === 0}
|
||||
<span class="text-sm">No resource input</span>
|
||||
{:else}
|
||||
<InputsSpecsEditor
|
||||
id={component.id}
|
||||
shouldCapitalize={false}
|
||||
bind:inputSpecs={component.componentInput.fields}
|
||||
userInputEnabled={component.type !== 'buttoncomponent'}
|
||||
{resourceOnly}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
@@ -0,0 +1,18 @@
|
||||
<script lang="ts">
|
||||
import type { ConnectedAppInput, RowAppInput, StaticAppInput, UserAppInput } from '../inputType'
|
||||
import type { AppComponent } from '../types'
|
||||
import InputsSpecsEditor from './settingsPanel/InputsSpecsEditor.svelte'
|
||||
|
||||
export let fields: Record<string, StaticAppInput | ConnectedAppInput | RowAppInput | UserAppInput>
|
||||
export let component: AppComponent
|
||||
export let resourceOnly: boolean = false
|
||||
|
||||
$: fields = resourceOnly ? fields : fields
|
||||
</script>
|
||||
|
||||
<InputsSpecsEditor
|
||||
id={component.id}
|
||||
shouldCapitalize={false}
|
||||
bind:inputSpecs={fields}
|
||||
userInputEnabled={component.type !== 'buttoncomponent'}
|
||||
/>
|
||||
@@ -39,6 +39,7 @@
|
||||
export let initialMode: EditorMode = 'dnd'
|
||||
export let policy: Policy
|
||||
export let summary: string
|
||||
export let fromHub: boolean = false
|
||||
|
||||
const appStore = writable<App>(app)
|
||||
const worldStore = writable<World | undefined>(undefined)
|
||||
@@ -125,7 +126,7 @@
|
||||
{#if !$userStore?.operator}
|
||||
<UnsavedConfirmationModal />
|
||||
{#if initialMode !== 'preview'}
|
||||
<AppEditorHeader {policy} />
|
||||
<AppEditorHeader {policy} {fromHub} />
|
||||
{/if}
|
||||
|
||||
{#if previewing}
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
Loader2,
|
||||
MoreVertical,
|
||||
Pencil,
|
||||
SlidersHorizontal,
|
||||
Smartphone
|
||||
} from 'lucide-svelte'
|
||||
import { getContext } from 'svelte'
|
||||
@@ -51,6 +52,7 @@
|
||||
import type { AppComponent, AppEditorContext } from '../types'
|
||||
import { toStatic } from '../utils'
|
||||
import AppExportButton from './AppExportButton.svelte'
|
||||
import AppInputs from './AppInputs.svelte'
|
||||
import PanelSection from './settingsPanel/common/PanelSection.svelte'
|
||||
|
||||
async function hash(message) {
|
||||
@@ -62,6 +64,7 @@
|
||||
}
|
||||
|
||||
export let policy: Policy
|
||||
export let fromHub: boolean = false
|
||||
|
||||
const {
|
||||
app,
|
||||
@@ -93,12 +96,12 @@
|
||||
|
||||
let newPath: string = ''
|
||||
let pathError: string | undefined = undefined
|
||||
|
||||
let appExport: AppExportButton
|
||||
|
||||
let saveDrawerOpen = false
|
||||
let jobsDrawerOpen = false
|
||||
let publishDrawerOpen = false
|
||||
let inputsDrawerOpen = fromHub
|
||||
|
||||
function closeSaveDrawer() {
|
||||
saveDrawerOpen = false
|
||||
@@ -248,6 +251,12 @@
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
|
||||
<Drawer bind:open={inputsDrawerOpen} size="600px">
|
||||
<DrawerContent title="App inputs configuration" on:close={() => (inputsDrawerOpen = false)}>
|
||||
<AppInputs />
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
|
||||
<Drawer bind:open={jobsDrawerOpen} size="900px">
|
||||
<DrawerContent noPadding title="Debug Runs" on:close={() => (jobsDrawerOpen = false)}>
|
||||
<Splitpanes class="!overflow-visible">
|
||||
@@ -479,6 +488,14 @@
|
||||
>
|
||||
<MoreVertical size={20} />
|
||||
</Dropdown>
|
||||
<span class="hidden md:inline">
|
||||
<Button on:click={() => (inputsDrawerOpen = true)} color="light" size="xs" variant="border">
|
||||
<span class="flex gap-2">
|
||||
<SlidersHorizontal size={14} />
|
||||
App inputs
|
||||
</span>
|
||||
</Button>
|
||||
</span>
|
||||
<span class="hidden md:inline">
|
||||
<Button
|
||||
on:click={() => (jobsDrawerOpen = true)}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
<script lang="ts">
|
||||
import { Alert } from '$lib/components/common'
|
||||
import Toggle from '$lib/components/Toggle.svelte'
|
||||
import { getContext } from 'svelte'
|
||||
import type { AppEditorContext } from '../types'
|
||||
import AppComponentInput from './AppComponentInput.svelte'
|
||||
import InputsSpecsEditor from './settingsPanel/InputsSpecsEditor.svelte'
|
||||
|
||||
const { app, lazyGrid } = getContext<AppEditorContext>('AppEditorContext')
|
||||
|
||||
let resourceOnly: boolean = true
|
||||
</script>
|
||||
|
||||
<Alert type="info" title="Configurations">
|
||||
In order to properly configure the app, you need to fill in the inputs below.
|
||||
</Alert>
|
||||
<Toggle bind:checked={resourceOnly} options={{ right: 'Resource only' }} />
|
||||
{#each $lazyGrid as gridItem (gridItem.data.id)}
|
||||
{#if gridItem.data.type === 'tablecomponent'}
|
||||
<div>
|
||||
<AppComponentInput bind:component={gridItem.data} {resourceOnly} />
|
||||
<div class="ml-4 mt-4">
|
||||
{#each gridItem.data.actionButtons as actionButton (actionButton.id)}
|
||||
<AppComponentInput bind:component={actionButton.data} {resourceOnly} />
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<AppComponentInput bind:component={gridItem.data} {resourceOnly} />
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
{#if $app?.hiddenInlineScripts?.length > 0}
|
||||
<span class="font-bold text-sm">Background script inputs</span>
|
||||
<div class="gap-4 flex flex-col">
|
||||
{#each $app?.hiddenInlineScripts ?? [] as script, index (script.name)}
|
||||
<span class="text-sm">{script.name}</span>
|
||||
|
||||
{#if resourceOnly && Object.keys(script.fields).filter((fieldKey) => {
|
||||
const fields = script.fields
|
||||
const field = fields[fieldKey]
|
||||
return field.fieldType === 'object' && field.format?.startsWith('resource-')
|
||||
}).length === 0}
|
||||
<span class="text-sm">No resource input</span>
|
||||
{:else}
|
||||
<InputsSpecsEditor
|
||||
id={`bg_${index}`}
|
||||
shouldCapitalize={false}
|
||||
bind:inputSpecs={script.fields}
|
||||
userInputEnabled={false}
|
||||
{resourceOnly}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
@@ -14,6 +14,7 @@
|
||||
export let userInputEnabled: boolean = true
|
||||
export let shouldCapitalize: boolean = true
|
||||
export let rowColumns = false
|
||||
export let resourceOnly = false
|
||||
|
||||
const { connectingInput } = getContext<AppEditorContext>('AppEditorContext')
|
||||
</script>
|
||||
@@ -22,97 +23,99 @@
|
||||
<div class="w-full flex flex-col gap-4">
|
||||
{#each Object.keys(inputSpecs) as inputSpecKey (inputSpecKey)}
|
||||
{@const input = inputSpecs[inputSpecKey]}
|
||||
<div class="flex flex-col gap-1">
|
||||
<div class="flex justify-between items-end gap-1">
|
||||
<span class="text-sm font-semibold truncate">
|
||||
{shouldCapitalize
|
||||
? capitalize(addWhitespaceBeforeCapitals(inputSpecKey))
|
||||
: inputSpecKey}
|
||||
{#if input.tooltip}
|
||||
<Tooltip>
|
||||
{input.tooltip}
|
||||
</Tooltip>
|
||||
{/if}
|
||||
</span>
|
||||
{#if !(resourceOnly && (input.fieldType !== 'object' || !input.format?.startsWith('resource-')))}
|
||||
<div class="flex flex-col gap-1">
|
||||
<div class="flex justify-between items-end gap-1">
|
||||
<span class="text-sm font-semibold truncate">
|
||||
{shouldCapitalize
|
||||
? capitalize(addWhitespaceBeforeCapitals(inputSpecKey))
|
||||
: inputSpecKey}
|
||||
{#if input.tooltip}
|
||||
<Tooltip>
|
||||
{input.tooltip}
|
||||
</Tooltip>
|
||||
{/if}
|
||||
</span>
|
||||
|
||||
<div class="flex gap-x-2 gap-y-1 flex-wrap justify-end items-center">
|
||||
<Badge color="blue">
|
||||
{input.fieldType === 'array' && input.subFieldType
|
||||
? `${capitalize(fieldTypeToTsType(input.subFieldType))}[]`
|
||||
: capitalize(fieldTypeToTsType(input.fieldType))}
|
||||
</Badge>
|
||||
<div class="flex gap-x-2 gap-y-1 flex-wrap justify-end items-center">
|
||||
<Badge color="blue">
|
||||
{input.fieldType === 'array' && input.subFieldType
|
||||
? `${capitalize(fieldTypeToTsType(input.subFieldType))}[]`
|
||||
: capitalize(fieldTypeToTsType(input.fieldType))}
|
||||
</Badge>
|
||||
|
||||
{#if !inputSpecs[inputSpecKey].onlyStatic && inputSpecs[inputSpecKey].type != 'eval'}
|
||||
<ToggleButtonGroup
|
||||
bind:selected={inputSpecs[inputSpecKey].type}
|
||||
on:selected={(e) => {
|
||||
if (e.detail == 'connected' && !inputSpecs[inputSpecKey]['connection']) {
|
||||
$connectingInput = {
|
||||
opened: true,
|
||||
input: undefined,
|
||||
hoveredComponent: undefined
|
||||
{#if !inputSpecs[inputSpecKey].onlyStatic && inputSpecs[inputSpecKey].type != 'eval'}
|
||||
<ToggleButtonGroup
|
||||
bind:selected={inputSpecs[inputSpecKey].type}
|
||||
on:selected={(e) => {
|
||||
if (e.detail == 'connected' && !inputSpecs[inputSpecKey]['connection']) {
|
||||
$connectingInput = {
|
||||
opened: true,
|
||||
input: undefined,
|
||||
hoveredComponent: undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Popover placement="bottom" notClickable disapperTimoout={0}>
|
||||
<ToggleButton
|
||||
position="left"
|
||||
value="static"
|
||||
startIcon={{ icon: faPen }}
|
||||
size="xs"
|
||||
iconOnly
|
||||
/>
|
||||
<svelte:fragment slot="text">Static</svelte:fragment>
|
||||
</Popover>
|
||||
{#if rowColumns}
|
||||
}}
|
||||
>
|
||||
<Popover placement="bottom" notClickable disapperTimoout={0}>
|
||||
<ToggleButton
|
||||
position="center"
|
||||
value="row"
|
||||
startIcon={{ icon: faTableCells }}
|
||||
size="xs"
|
||||
>
|
||||
<Tooltip scale={0.6} placement="top-end" wrapperClass="center-center">
|
||||
Use the column name to have the value of the cell be passed to the action
|
||||
</Tooltip>
|
||||
</ToggleButton>
|
||||
<svelte:fragment slot="text">Column</svelte:fragment>
|
||||
</Popover>
|
||||
{/if}
|
||||
{#if userInputEnabled && !input.format?.startsWith('resource-')}
|
||||
<Popover placement="bottom" notClickable disapperTimoout={0}>
|
||||
<ToggleButton
|
||||
position="center"
|
||||
value="user"
|
||||
startIcon={{ icon: faUser }}
|
||||
position="left"
|
||||
value="static"
|
||||
startIcon={{ icon: faPen }}
|
||||
size="xs"
|
||||
iconOnly
|
||||
/>
|
||||
<svelte:fragment slot="text">User Input</svelte:fragment>
|
||||
<svelte:fragment slot="text">Static</svelte:fragment>
|
||||
</Popover>
|
||||
{/if}
|
||||
<Popover placement="bottom" notClickable disapperTimoout={0}>
|
||||
<ToggleButton
|
||||
position="right"
|
||||
value="connected"
|
||||
startIcon={{ icon: faArrowRight }}
|
||||
size="xs"
|
||||
iconOnly
|
||||
/>
|
||||
<svelte:fragment slot="text">Connect</svelte:fragment>
|
||||
</Popover>
|
||||
</ToggleButtonGroup>
|
||||
{/if}
|
||||
{#if rowColumns}
|
||||
<Popover placement="bottom" notClickable disapperTimoout={0}>
|
||||
<ToggleButton
|
||||
position="center"
|
||||
value="row"
|
||||
startIcon={{ icon: faTableCells }}
|
||||
size="xs"
|
||||
>
|
||||
<Tooltip scale={0.6} placement="top-end" wrapperClass="center-center">
|
||||
Use the column name to have the value of the cell be passed to the action
|
||||
</Tooltip>
|
||||
</ToggleButton>
|
||||
<svelte:fragment slot="text">Column</svelte:fragment>
|
||||
</Popover>
|
||||
{/if}
|
||||
{#if userInputEnabled && !input.format?.startsWith('resource-')}
|
||||
<Popover placement="bottom" notClickable disapperTimoout={0}>
|
||||
<ToggleButton
|
||||
position="center"
|
||||
value="user"
|
||||
startIcon={{ icon: faUser }}
|
||||
size="xs"
|
||||
iconOnly
|
||||
/>
|
||||
<svelte:fragment slot="text">User Input</svelte:fragment>
|
||||
</Popover>
|
||||
{/if}
|
||||
<Popover placement="bottom" notClickable disapperTimoout={0}>
|
||||
<ToggleButton
|
||||
position="right"
|
||||
value="connected"
|
||||
startIcon={{ icon: faArrowRight }}
|
||||
size="xs"
|
||||
iconOnly
|
||||
/>
|
||||
<svelte:fragment slot="text">Connect</svelte:fragment>
|
||||
</Popover>
|
||||
</ToggleButtonGroup>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<InputsSpecEditor
|
||||
hasRows={rowColumns}
|
||||
{id}
|
||||
bind:componentInput={inputSpecs[inputSpecKey]}
|
||||
/>
|
||||
</div>
|
||||
<InputsSpecEditor
|
||||
hasRows={rowColumns}
|
||||
{id}
|
||||
bind:componentInput={inputSpecs[inputSpecKey]}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
{:else}
|
||||
|
||||
@@ -74,6 +74,7 @@
|
||||
on_behalf_of_email: $userStore?.email,
|
||||
execution_mode: Policy.execution_mode.PUBLISHER
|
||||
}}
|
||||
fromHub={hubId != null}
|
||||
/>
|
||||
{/key}
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import AppEditor from '$lib/components/apps/editor/AppEditor.svelte'
|
||||
import { AppService, AppWithLastVersion } from '$lib/gen'
|
||||
import { userStore, workspaceStore } from '$lib/stores'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
import { page } from '$app/stores'
|
||||
import { decodeState, sendUserToast } from '$lib/utils'
|
||||
import { goto } from '$app/navigation'
|
||||
|
||||
Reference in New Issue
Block a user