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:
Faton Ramadani
2023-01-26 15:51:38 +01:00
committed by GitHub
parent 18f30c8286
commit 3ed16b88a4
9 changed files with 215 additions and 85 deletions
@@ -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'