fix(frontend): Add missing tooltips + multilpe small fix in the app builder (#1590)

* fix(frontend): revert commit that were removed + delete outdated components + add missing tooltips

* fix(frontend): remove outdated select button

* fix(frontend): add missing tooltips

* fix(frontend): add debug tab

* fix(frontend): remove duplicate file

* fix(frontend): add missing props
This commit is contained in:
Faton Ramadani
2023-05-17 17:34:40 +02:00
committed by GitHub
parent f7338c9c9a
commit fff2b5a24a
20 changed files with 187 additions and 212 deletions
+7 -2
View File
@@ -3,6 +3,8 @@
A simple CLI allowing interactions with windmill from the command line.
![](./vhs/output/setup.gif)
You can find more information in [Windmill Docs](https://docs.windmill.dev/docs/advanced/cli)
## Installation
Install the `wmill` CLI tool using
@@ -18,11 +20,14 @@ workspace settings.
## Running Flows & Scripts
Run a script or flow using `wmill flow/script run u/username/path/to/script` and
pass any inputs using `--input/-i <name>=<value>` curl-style syntax using
`-i @-` for stdin or `-i @<filename>` is also supported
pass any inputs using `--data` + Inputs specified as a JSON string or a file using `@ <filename>` or stdin using @-.
Curl-style syntax using `-d @-` for stdin or `-d @<filename>` is also supported.
Flow Steps and Logs will be streamed during execution automatically.
![CLI input example](./vhs/output/cli_inputs_example.png)
## Pushing Resources, Scripts & More
The CLI can push specifications to a windmill instance. See the
Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

-67
View File
@@ -1,67 +0,0 @@
<script lang="ts">
import {
faArchive,
faEdit,
faList,
faPlay,
faShare,
faTrash
} from '@fortawesome/free-solid-svg-icons'
import Icon from 'svelte-awesome'
export let category: 'delete' | 'list' | 'run' | 'add' | 'edit' | 'archive' | 'share'
export let disabled: boolean = false
const colors = {
red: 'bg-transparent hover:bg-red-600 text-red-600 font-normal hover:text-white border-red-600 hover:border-transparent rounded',
blue: 'hover:bg-blue-500 hover:text-white'
}
const getCategoryClasses = () => {
if (category === 'delete' || category === 'archive') return colors.red
else if (category === 'edit' || category === 'list' || category === 'share') return colors.blue
return ''
}
</script>
<button
class="{$$props.class} inline-flex items-center bg-[#5e81ac] hover:bg-blue-700 text-white
font-bold py-1 px-2 border rounded border-blue-500 hover:border-blue-700 w-min min-w-max
text-sm {getCategoryClasses()}"
on:click
{disabled}
>
<div class="inline-flex items-center justify-center">
{#if category === 'delete'}
<Icon data={faTrash} scale={0.5} />
<slot name="text">
<span class="pl-1">Delete</span>
</slot>
{:else if category === 'list'}
<Icon data={faList} scale={0.5} />
<slot name="text">
<span class="pl-1">List</span>
</slot>
{:else if category === 'run'}
<Icon data={faPlay} scale={0.5} />
<slot name="text">
<span class="pl-1">Run</span>
</slot>
{:else if category === 'edit'}
<Icon data={faEdit} scale={0.5} />
<slot name="text-gray-500">
<span class="pl-1">Edit</span>
</slot>
{:else if category === 'archive'}
<Icon data={faArchive} scale={0.5} />
<slot name="text">
<span class="pl-1">Archive</span>
</slot>
{:else if category === 'share'}
<Icon data={faShare} scale={0.5} />
<slot name="text">
<span class="pl-1">Share</span>
</slot>
{/if}
</div>
</button>
@@ -10,7 +10,7 @@
<button
type="submit"
class="mr-6 text-sm underline text-gray-700 inline-flex items-center"
class="mr-6 text-sm underline text-gray-700 inline-flex items-center"
on:click={() => {
viewOptions = !viewOptions
}}
+12 -10
View File
@@ -23,15 +23,17 @@
</span>
<Required {required} class="!ml-0" />
{#if format && format != ''}
<span class="text-sm italic ml-1 text-indigo-800">
({format})
</span>
{:else if displayType}
<span class="text-sm italic ml-1 text-indigo-800">
({type ?? 'any'}{contentEncoding && contentEncoding != ''
? `, encoding: ${contentEncoding}`
: ''})
</span>
{#if displayType}
{#if format && format != ''}
<span class="text-sm italic ml-1 text-indigo-800">
({format})
</span>
{:else}
<span class="text-sm italic ml-1 text-indigo-800">
({type ?? 'any'}{contentEncoding && contentEncoding != ''
? `, encoding: ${contentEncoding}`
: ''})
</span>
{/if}
{/if}
</div>
@@ -69,7 +69,7 @@
</DrawerContent>
</Drawer>
<div class="flex flex-row items-center gap-4 w-full">
<div class="flex flex-row items-center gap-4 w-full">
{#if options.length > 1}
<div class="w-80 mt-1">
<RadioButton {disabled} bind:value={itemKind} {options} />
@@ -54,7 +54,7 @@
<DrawerContent overflow_y={false} title="Superadmin Settings" on:close={closeDrawer}>
<div class="flex flex-col h-full">
<div>
<div class="text-xs pt-1 text-gray-500 ">
<div class="text-xs pt-1 text-gray-500">
Windmill {version}
</div>
@@ -125,7 +125,6 @@
{outputs}
{extraKey}
refreshOnStart={resolvedConfig.triggerOnAppLoad}
triggerable
>
<AlignWrapper {noWFull} {horizontalAlignment} {verticalAlignment}>
{#if errorsMessage}
@@ -75,7 +75,6 @@
runnableClass={css?.container?.class}
runnableStyle={css?.container?.style}
{outputs}
triggerable
>
<AlignWrapper {horizontalAlignment}>
<div
@@ -91,7 +91,6 @@
doOnSuccess={resolvedConfig.onSuccess}
doOnError={resolvedConfig.onError}
{errorHandledByComponent}
triggerable
>
<div class="flex flex-col gap-2 px-4 w-full">
<div>
@@ -12,8 +12,7 @@
export let customCss: ComponentCustomCSS<'htmlcomponent'> | undefined = undefined
export let render: boolean
const { app, worldStore, selectedComponent, mode } =
getContext<AppViewerContext>('AppViewerContext')
const { app, worldStore, mode } = getContext<AppViewerContext>('AppViewerContext')
const outputs = initOutput($worldStore, id, {
result: undefined,
@@ -35,13 +34,6 @@
bind:clientHeight={h}
bind:clientWidth={w}
>
{#if $mode == 'dnd'}
<button
class="absolute bottom-0 left-0 text-xs border px-2 py-0.5 bg-white/80"
on:click={() => ($selectedComponent = [id])}>Select</button
>
{/if}
<RunnableWrapper
{outputs}
{render}
@@ -15,7 +15,7 @@
<button
on:pointerdown|preventDefault|stopPropagation
on:click|preventDefault|stopPropagation={refresh}
class="center-center p-1 rounded border bg-white/60 hover:bg-gray-200 z-10"
class="center-center p-1 rounded border bg-white/60 hover:bg-gray-200 z-10"
>
<RefreshCw class={loading ? 'animate-spin' : ''} size={16} />
</button>
@@ -49,7 +49,6 @@
export let outputs: { result: Output<any>; loading: Output<boolean> }
export let extraKey: string | undefined = undefined
export let refreshOnStart: boolean = false
export let triggerable: boolean = false
export let errorHandledByComponent: boolean = false
const { staticExporter, noBackend, componentControl, runnableComponents } =
@@ -69,7 +68,7 @@
})
// We need to make sure that old apps have correct values. Triggerable (button, form, etc) have both autoRefresh and recomputeOnInputChanged set to false
$: if (triggerable && componentInput?.type === 'runnable' && componentInput.autoRefresh) {
$: if (!autoRefresh && componentInput?.type === 'runnable' && componentInput.autoRefresh) {
componentInput.autoRefresh = false
componentInput.recomputeOnInputChanged = false
}
@@ -124,7 +124,6 @@
forceSchemaDisplay={true}
runnableClass="!block"
{outputs}
triggerable
bind:result
errorHandledByComponent={true}
>
@@ -8,8 +8,8 @@
import type { AppComponent } from './component'
import { twMerge } from 'tailwind-merge'
import { getErrorFromLatestResult } from './appUtils'
import ButtonDropdown from '$lib/components/common/button/ButtonDropdown.svelte'
import { MenuItem } from '@rgossiaux/svelte-headlessui'
import TabsDebug from './TabsDebug.svelte'
export let component: AppComponent
export let selected: boolean
@@ -20,11 +20,9 @@
export let inlineEditorOpened: boolean = false
export let errorHandledByComponent: boolean = false
let isConditionalWrapperManuallySelected: boolean = false
const dispatch = createEventDispatcher()
const { errorByComponent, openDebugRun, jobs, connectingInput, componentControl } =
const { errorByComponent, openDebugRun, jobs, connectingInput } =
getContext<AppViewerContext>('AppViewerContext')
$: error = getErrorFromLatestResult(component.id, $errorByComponent, $jobs)
@@ -74,56 +72,10 @@
</button>
{/if}
{#if component.type === 'conditionalwrapper'}
<button
title="Conditions"
class={classNames(
'text-2xs py-0.5 font-bold w-fit border cursor-pointer rounded-sm',
isConditionalWrapperManuallySelected
? '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="items">
{#each component.conditions ?? [] as { }, index}
<MenuItem
on:click={() => {
$componentControl?.[component.id]?.setTab?.(index)
isConditionalWrapperManuallySelected = true
}}
>
<div
class={classNames(
'!text-gray-600 text-left px-4 py-2 gap-2 cursor-pointer hover:bg-gray-100 !text-xs font-semibold'
)}
>
{#if index === component.conditions.length - 1}
{`Debug default condition`}
{:else}
{`Debug condition ${index + 1}`}
{/if}
</div>
</MenuItem>
{/each}
<MenuItem
on:click={() => {
$componentControl?.[component.id]?.setTab?.(-1)
isConditionalWrapperManuallySelected = false
}}
>
<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>
<TabsDebug id={component.id} tabs={component.conditions ?? []} isConditionalDebugMode />
{/if}
{#if component.type === 'tabscomponent' && component.configuration.tabsKind.type === 'static' && component.configuration.tabsKind.value === 'invisibleOnView'}
<TabsDebug id={component.id} tabs={component.tabs ?? []} />
{/if}
<button
title="Expand"
@@ -0,0 +1,69 @@
<script lang="ts">
import ButtonDropdown from '$lib/components/common/button/ButtonDropdown.svelte'
import { classNames } from '$lib/utils'
import { MenuItem } from '@rgossiaux/svelte-headlessui'
import { createEventDispatcher, getContext } from 'svelte'
import type { AppViewerContext } from '../types'
export let tabs: any[] = []
export let id: string
export let isConditionalDebugMode: boolean = false
const { componentControl } = getContext<AppViewerContext>('AppViewerContext')
let isManuallySelected: boolean = false
const dispatch = createEventDispatcher()
</script>
<button
title={isConditionalDebugMode ? 'Debug conditions' : 'Debug tabs'}
class={classNames(
'text-2xs py-0.5 font-bold w-fit border cursor-pointer rounded-sm',
isManuallySelected
? '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="items">
{#each tabs ?? [] as { }, index}
<MenuItem
on:click={() => {
$componentControl?.[id]?.setTab?.(index)
isManuallySelected = true
}}
>
<div
class={classNames(
'!text-gray-600 text-left px-4 py-2 gap-2 cursor-pointer hover:bg-gray-100 !text-xs font-semibold'
)}
>
{#if index === tabs.length - 1}
{isConditionalDebugMode ? `Debug default condition` : `Debug tab ${index + 1}`}
{:else}
{`Debug ${isConditionalDebugMode ? 'condition' : 'tab'} ${index + 1}`}
{/if}
</div>
</MenuItem>
{/each}
<MenuItem
on:click={() => {
$componentControl?.[id]?.setTab?.(-1)
isManuallySelected = false
}}
>
<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>
@@ -1484,11 +1484,11 @@ Hello \${ctx.username}
type: 'static',
onlyStatic: true,
selectOptions: selectOptions.tabsKindOptions,
value: 'tabs' as string
value: 'tabs' as string,
tooltip: `Tabs can be configured to be either horizontal (tabs), vertical (sidebar), or invisible.`
}
},
componentInput: undefined,
numberOfSubgrids: 2,
tabs: ['First tab', 'Second tab'] as string[]
}
@@ -1530,12 +1530,15 @@ Hello \${ctx.username}
icon: {
type: 'static',
value: 'Smile',
fieldType: 'icon-select'
fieldType: 'icon-select',
tooltip: 'The icons can be found at https://lucide.dev/'
},
color: {
type: 'static',
value: 'currentColor',
fieldType: 'color'
fieldType: 'color',
tooltip:
'The color of the icon can be overridden by the `background-color` property in the styling menu'
},
size: {
type: 'static',
@@ -1810,7 +1813,6 @@ Hello \${ctx.username}
initialData: {
configuration: {},
componentInput: undefined,
panes: [50, 50] as number[],
numberOfSubgrids: 2
}
@@ -1912,7 +1914,6 @@ Hello \${ctx.username}
}
},
componentInput: undefined,
numberOfSubgrids: 1
}
},
@@ -1945,13 +1946,15 @@ Hello \${ctx.username}
fieldType: 'boolean',
type: 'static',
value: false,
onlyStatic: true
onlyStatic: true,
tooltip: 'This will diplay the type and/or the format on the field next to the label.'
},
largeGap: {
fieldType: 'boolean',
type: 'static',
value: false,
onlyStatic: true
onlyStatic: true,
tooltip: 'This will add a large gap between the form elements.'
}
}
}
@@ -1989,7 +1992,9 @@ Hello \${ctx.username}
type: 'static',
value: 'sm',
fieldType: 'select',
selectOptions: selectOptions.buttonSizeOptions
selectOptions: selectOptions.buttonSizeOptions,
tooltip:
'Size of the tabs can be overwritten with custom styles using `font-size` in CSS or using tailwind classes.'
}
}
}
@@ -8,7 +8,8 @@
StylePropertyUnits,
STYLE_STORE_KEY,
type StyleStore,
type StyleStoreValue
type StyleStoreValue,
type StylePropertyValue
} from './quickStyleProperties'
export let prop: StyleStoreValue['style'][number]['prop']
@@ -21,7 +22,19 @@
let unit: (typeof StylePropertyUnits)[number] = StylePropertyUnits[0]
let internalValue: number | string
$: internalValue = value ? +value.replace(unit, '') : ''
function getInteralValue(value: string | undefined, propValue: StylePropertyValue) {
if (!value) {
return ''
}
if (propValue.type === StylePropertyType.number) {
return value
}
if (propValue.type === StylePropertyType.text) {
return +value.replace(unit, '')
}
return ''
}
$: internalValue = getInteralValue(value, prop.value as StylePropertyValue)
$: dispatch('change', value)
function updateValue(next: number) {
@@ -80,7 +80,7 @@
</a>
</div>
<div class="py-1" role="none">
<a
<button
on:click={() => {
if ($workspaceStore === 'admins') {
return
@@ -88,13 +88,12 @@
switchWorkspace('admins')
close()
}}
href=""
class="text-gray-700 block px-4 py-2 text-sm hover:bg-gray-100 hover:text-gray-900"
role="menuitem"
tabindex="-1"
>
<Icon class="pr-0.5" data={faCog} /> Superadmin workspace
</a>
</button>
</div>
{/if}
<div class="py-1" role="none">
@@ -316,9 +316,9 @@
<div>
<div class="mb-1 font-semibold text-gray-700">Schema</div>
<div class="mb-2 w-full flex flex-row-reverse">
<Button on:click={openInferrer} size="sm" color="dark" variant="border"
>Infer schema from a json value</Button
>
<Button on:click={openInferrer} size="sm" color="dark" variant="border">
Infer schema from a json value
</Button>
</div>
<SchemaEditor bind:schema={newResourceType.schema} />
</div>
@@ -340,9 +340,9 @@
documentationLink="https://docs.windmill.dev/docs/core_concepts/resources_and_types"
>
<div class="flex flex-row justify-end gap-4">
<Button variant="border" size="md" startIcon={{ icon: faPlus }} on:click={startNewType}
>Add a resource type</Button
>
<Button variant="border" size="md" startIcon={{ icon: faPlus }} on:click={startNewType}>
Add a resource type
</Button>
<Button size="md" startIcon={{ icon: faChain }} on:click={() => appConnect.open?.()}>
Add a resource/API
</Button>
@@ -357,19 +357,23 @@
</Tab>
<Tab size="md" value="types">
<div class="flex gap-2 items-center my-1">
Resource Types <Tooltip documentationLink="https://docs.windmill.dev/docs/core_concepts/resources_and_types"
>Every resources have Resource Types attached to them which contains its schema and make
it easy in scripts and flows to accept only resources of a specific resource type</Tooltip
Resource Types
<Tooltip
documentationLink="https://docs.windmill.dev/docs/core_concepts/resources_and_types"
>
Every resources have Resource Types attached to them which contains its schema and make it
easy in scripts and flows to accept only resources of a specific resource type
</Tooltip>
</div>
</Tab>
<Tab size="md" value="states">
<div class="flex gap-2 items-center my-1">
States <Tooltip
>States are actually resources (but excluded from the Workspace tab for clarity). States
States
<Tooltip>
States are actually resources (but excluded from the Workspace tab for clarity). States
are used by scripts to keep data persistent between runs of the same script by the same
trigger (schedule or user)</Tooltip
>
trigger (schedule or user)
</Tooltip>
</div>
</Tab>
</Tabs>
@@ -413,8 +417,8 @@
>{#if marked}{@html marked}{:else}{path}{/if}</a
>
</td>
<td class="px-2"
><a
<td class="px-2">
<a
href="#{name}"
on:click={() => {
const linkedRt = resourceTypes?.find((rt) => rt.name === resource_type)
@@ -431,14 +435,16 @@
true
)
}
}}><IconedResourceType name={resource_type} after={true} /></a
></td
>
<td
><span class="text-gray-500 text-xs"
>{removeMarkdown(truncate(description ?? '', 30))}</span
></td
>
}}
>
<IconedResourceType name={resource_type} after={true} />
</a>
</td>
<td>
<span class="text-gray-500 text-xs">
{removeMarkdown(truncate(description ?? '', 30))}
</span>
</td>
<td class="text-center">
<div class="flex flex-row">
<div class="w-10">
@@ -595,8 +601,8 @@
{#if resourceTypes}
{#each resourceTypes as { name, description, schema, canWrite }}
<tr>
<td
><a
<td>
<a
href="#{name}"
on:click={() => {
resourceTypeViewerObj = {
@@ -606,22 +612,25 @@
}
resourceTypeViewer.openDrawer?.()
}}><IconedResourceType after={true} {name} /></a
></td
>
<td
><span class="text-gray-500 text-xs"
>{removeMarkdown(truncate(description ?? '', 200))}</span
></td
>
}}
>
<IconedResourceType after={true} {name} />
</a>
</td>
<td>
<span class="text-gray-500 text-xs">
{removeMarkdown(truncate(description ?? '', 200))}
</span>
</td>
<td>
{#if !canWrite}
<Badge
>Shared globally<Tooltip
>This resource type is from the 'admins' workspace shared with all
workspaces</Tooltip
></Badge
>
<Badge>
Shared globally
<Tooltip>
This resource type is from the 'admins' workspace shared with all
workspaces
</Tooltip>
</Badge>
{:else if $userStore?.is_admin || $userStore?.is_super_admin}
<Button
size="sm"
@@ -633,12 +642,13 @@
Delete
</Button>
{:else}
<Badge
>Non Editable <Tooltip
>Since resource types are shared with the whole workspace, only admins can
edit/delete them</Tooltip
></Badge
>
<Badge>
Non Editable
<Tooltip>
Since resource types are shared with the whole workspace, only admins can
edit/delete them
</Tooltip>
</Badge>
{/if}
</td>
</tr>