mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-23 08:00:45 +00:00
feat(frontend): add currency format + add enum autocomplete + fix run… (#2670)
* feat(frontend): add currency format + add enum autocomplete + fix runs page layout * feat(frontend): use ArgEnum for array when using enums * Fix currencies * feat(frontend): fix currency input * feat(frontend): fix multiselect * feat(frontend): fix multiselect
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import AutoComplete from 'simple-svelte-autocomplete'
|
||||
import { Pen } from 'lucide-svelte'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { createEventDispatcher, onMount } from 'svelte'
|
||||
import { twMerge } from 'tailwind-merge'
|
||||
|
||||
export let customValue: boolean
|
||||
@@ -10,41 +11,37 @@
|
||||
export let autofocus: boolean
|
||||
export let defaultValue: string | undefined
|
||||
export let valid: boolean
|
||||
export let disableCustomValue: boolean = false
|
||||
|
||||
let autoCompleteItems = enum_ ?? []
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
onMount(() => {
|
||||
autoCompleteItems = enum_ ?? []
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if !customValue}
|
||||
<select
|
||||
on:focus={(e) => {
|
||||
dispatch('focus')
|
||||
}}
|
||||
{disabled}
|
||||
class="px-6"
|
||||
bind:value
|
||||
>
|
||||
{#each enum_ ?? [] as e}
|
||||
<option>{e}</option>
|
||||
{/each}
|
||||
</select>
|
||||
{:else}
|
||||
<!-- svelte-ignore a11y-autofocus -->
|
||||
<input
|
||||
{autofocus}
|
||||
on:focus
|
||||
type="text"
|
||||
class={twMerge(
|
||||
'bg-surface-secondary',
|
||||
valid
|
||||
? ''
|
||||
: 'border border-red-700 border-opacity-30 focus:border-red-700 focus:border-opacity-30 bg-red-100'
|
||||
)}
|
||||
placeholder={defaultValue ?? ''}
|
||||
bind:value
|
||||
/>
|
||||
{/if}
|
||||
<AutoComplete
|
||||
items={autoCompleteItems}
|
||||
bind:selectedItem={value}
|
||||
inputClassName={twMerge(
|
||||
'bg-surface-secondary flex',
|
||||
valid
|
||||
? ''
|
||||
: 'border border-red-700 border-opacity-30 focus:border-red-700 focus:border-opacity-30 bg-red-100'
|
||||
)}
|
||||
value={value ?? defaultValue}
|
||||
hideArrow={true}
|
||||
dropdownClassName="!text-sm !py-2 !rounded-sm !border-gray-200 !border !shadow-md"
|
||||
className="w-full"
|
||||
onFocus={() => {
|
||||
dispatch('focus')
|
||||
}}
|
||||
{disabled}
|
||||
{autofocus}
|
||||
/>
|
||||
|
||||
{#if !disabled}
|
||||
{#if !disabled && !disableCustomValue}
|
||||
<button
|
||||
class="min-w-min !px-2 items-center text-gray-800 bg-surface-secondary border rounded center-center hover:bg-gray-300 transition-all cursor-pointer"
|
||||
on:click={() => {
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
import ArrayTypeNarrowing from './ArrayTypeNarrowing.svelte'
|
||||
import DateTimeInput from './DateTimeInput.svelte'
|
||||
import S3FilePicker from './S3FilePicker.svelte'
|
||||
import CurrencyInput from './apps/components/inputs/currency/CurrencyInput.svelte'
|
||||
import Label from './Label.svelte'
|
||||
|
||||
export let label: string = ''
|
||||
export let value: any
|
||||
@@ -234,6 +236,9 @@
|
||||
|
||||
{#if type == 'array'}
|
||||
<ArrayTypeNarrowing bind:itemsType />
|
||||
<Label label="Display using multiselect">
|
||||
<Toggle bind:checked={extra.multiselect} />
|
||||
</Label>
|
||||
{:else if (type == 'string' && format != 'date-time') || ['number', 'object'].includes(type ?? '')}
|
||||
<div class="p-2 my-1 text-xs border-solid border border-gray-200 rounded-lg">
|
||||
<div class="w-min">
|
||||
@@ -263,7 +268,12 @@
|
||||
bind:contentEncoding
|
||||
/>
|
||||
{:else if type == 'number'}
|
||||
<NumberTypeNarrowing bind:min={extra['min']} bind:max={extra['max']} />
|
||||
<NumberTypeNarrowing
|
||||
bind:min={extra['min']}
|
||||
bind:max={extra['max']}
|
||||
bind:currency={extra['currency']}
|
||||
bind:currencyLocale={extra['currencyLocale']}
|
||||
/>
|
||||
{:else if type == 'object'}
|
||||
<ObjectTypeNarrowing bind:format />
|
||||
{/if}
|
||||
@@ -293,20 +303,34 @@
|
||||
</div>
|
||||
{:else if extra['seconds'] !== undefined}
|
||||
<SecondsInput bind:seconds={value} on:focus />
|
||||
{:else}
|
||||
<input
|
||||
{autofocus}
|
||||
on:focus
|
||||
{disabled}
|
||||
type="number"
|
||||
class={valid
|
||||
? ''
|
||||
: 'border border-red-700 border-opacity-30 focus:border-red-700 focus:border-opacity-30 bg-red-100'}
|
||||
placeholder={defaultValue ?? ''}
|
||||
{:else if extra?.currency}
|
||||
<CurrencyInput
|
||||
inputClasses={{
|
||||
formatted: twMerge('px-2 w-full py-1.5 text-black'),
|
||||
wrapper: 'w-full windmillapp',
|
||||
formattedZero: twMerge('text-black')
|
||||
}}
|
||||
style="color:black;"
|
||||
bind:value
|
||||
min={extra['min']}
|
||||
max={extra['max']}
|
||||
currency={extra?.currency}
|
||||
locale={extra?.currencyLocale ?? 'en-US'}
|
||||
/>
|
||||
{:else}
|
||||
<div class="relative w-full">
|
||||
<input
|
||||
{autofocus}
|
||||
on:focus
|
||||
{disabled}
|
||||
type="number"
|
||||
class={valid
|
||||
? ''
|
||||
: 'border border-red-700 border-opacity-30 focus:border-red-700 focus:border-opacity-30 bg-red-100'}
|
||||
placeholder={defaultValue ?? ''}
|
||||
bind:value
|
||||
min={extra['min']}
|
||||
max={extra['max']}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
{:else if inputCat == 'boolean'}
|
||||
<Toggle
|
||||
@@ -333,6 +357,15 @@
|
||||
selectedOptionsDraggable={true}
|
||||
/>
|
||||
</div>
|
||||
{:else if extra.multiselect}
|
||||
<div class="items-start">
|
||||
<Multiselect
|
||||
{disabled}
|
||||
bind:selected={value}
|
||||
options={itemsType?.enum ?? []}
|
||||
selectedOptionsDraggable={true}
|
||||
/>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="w-full">
|
||||
{#key redraw}
|
||||
@@ -352,17 +385,19 @@
|
||||
{:else if itemsType?.type == 'object'}
|
||||
<JsonEditor code={JSON.stringify(v, null, 2)} bind:value={v} />
|
||||
{:else if Array.isArray(itemsType?.enum)}
|
||||
<select
|
||||
on:focus={(e) => {
|
||||
<ArgEnum
|
||||
on:focus={() => {
|
||||
dispatch('focus')
|
||||
}}
|
||||
class="px-6"
|
||||
{defaultValue}
|
||||
{valid}
|
||||
{customValue}
|
||||
{disabled}
|
||||
{autofocus}
|
||||
bind:value={v}
|
||||
>
|
||||
{#each itemsType?.enum ?? [] as e}
|
||||
<option>{e}</option>
|
||||
{/each}
|
||||
</select>
|
||||
disableCustomValue={true}
|
||||
enum_={itemsType?.enum ?? []}
|
||||
/>
|
||||
{:else}
|
||||
<input type="text" bind:value={v} id="arg-input-array" />
|
||||
{/if}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
import LightweightResourcePicker from './LightweightResourcePicker.svelte'
|
||||
import LightweightObjectResourceInput from './LightweightObjectResourceInput.svelte'
|
||||
import DateTimeInput from './DateTimeInput.svelte'
|
||||
import CurrencyInput from './apps/components/inputs/currency/CurrencyInput.svelte'
|
||||
|
||||
export let css: ComponentCustomCSS<'schemaformcomponent'> | undefined = undefined
|
||||
export let label: string = ''
|
||||
@@ -181,6 +182,18 @@
|
||||
<span>{extra['max']}</span>
|
||||
<span class="mx-2"><Badge large color="blue">{value}</Badge></span>
|
||||
</div>
|
||||
{:else if extra?.currency}
|
||||
<CurrencyInput
|
||||
inputClasses={{
|
||||
formatted: twMerge('px-2 w-full py-1.5 text-black'),
|
||||
wrapper: 'w-full windmillapp',
|
||||
formattedZero: twMerge('text-black')
|
||||
}}
|
||||
style="color:black;"
|
||||
bind:value
|
||||
currency={extra?.currency}
|
||||
locale={extra?.currencyLocale ?? 'en-US'}
|
||||
/>
|
||||
{:else}
|
||||
<input
|
||||
on:focus={(e) => {
|
||||
@@ -350,6 +363,15 @@
|
||||
placeholder={defaultValue ?? ''}
|
||||
bind:value
|
||||
/>
|
||||
{:else if inputCat == 'currency'}
|
||||
<input
|
||||
type="number"
|
||||
class={valid
|
||||
? ''
|
||||
: 'border border-red-700 border-opacity-30 focus:border-red-700 focus:border-opacity-3'}
|
||||
placeholder={defaultValue ?? ''}
|
||||
bind:value
|
||||
/>
|
||||
{:else if inputCat == 'string'}
|
||||
<div class="flex flex-col w-full">
|
||||
<div class="flex flex-row w-full items-center justify-between">
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
<script lang="ts">
|
||||
import Label from './Label.svelte'
|
||||
import Toggle from './Toggle.svelte'
|
||||
import { selectOptions } from './apps/editor/component'
|
||||
|
||||
export let min: number | undefined
|
||||
export let max: number | undefined
|
||||
export let currency: string | undefined
|
||||
export let currencyLocale: string | undefined
|
||||
|
||||
let minChecked: boolean = min != undefined
|
||||
let maxChecked: boolean = max != undefined
|
||||
@@ -19,4 +23,20 @@
|
||||
<Toggle bind:checked={maxChecked} options={{ right: 'max' }} />
|
||||
<input type="number" bind:value={max} disabled={!maxChecked} />
|
||||
</div>
|
||||
<Label label="Currency">
|
||||
<select class="mt-1" bind:value={currency}>
|
||||
<option value={undefined}> No currency </option>
|
||||
{#each selectOptions.currencyOptions as c}
|
||||
<option value={c}>{c}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</Label>
|
||||
<Label label="Currency locale">
|
||||
<select class="mt-1" bind:value={currencyLocale}>
|
||||
<option value={undefined}> No locale </option>
|
||||
{#each selectOptions.localeOptions as c}
|
||||
<option value={c}>{c}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</Label>
|
||||
</div>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
'date-time'
|
||||
// 'duration',
|
||||
// 'ipv6',
|
||||
// 'jsonpointer'
|
||||
// 'jsonpointer',
|
||||
]
|
||||
|
||||
$: format =
|
||||
@@ -40,6 +40,7 @@
|
||||
pattern = '^[\\w-.]+@([\\w-]+\\.)+[\\w-]{2,4}$'
|
||||
}
|
||||
}
|
||||
|
||||
function add() {
|
||||
enum_ = enum_ ? enum_.concat('') : ['']
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@
|
||||
<div class="whitespace-nowrap text-xs font-semibold">
|
||||
{#if job.script_path}
|
||||
<div class="flex flex-row gap-1 items-center">
|
||||
<a href="/run/{job.id}?workspace={job.workspace_id}" class="truncate w-8/12">
|
||||
<a href="/run/{job.id}?workspace={job.workspace_id}" class="truncate w-30">
|
||||
{job.script_path}
|
||||
</a>
|
||||
<Button
|
||||
|
||||
@@ -337,6 +337,7 @@ export type InputCat =
|
||||
| 'object'
|
||||
| 'sql'
|
||||
| 'yaml'
|
||||
| 'currency'
|
||||
|
||||
export function setInputCat(
|
||||
type: string | undefined,
|
||||
@@ -367,6 +368,8 @@ export function setInputCat(
|
||||
return 'base64'
|
||||
} else if (type == 'string' && format == 'email') {
|
||||
return 'email'
|
||||
} else if (type == 'string' && format == 'currency') {
|
||||
return 'currency'
|
||||
} else {
|
||||
return 'string'
|
||||
}
|
||||
|
||||
@@ -626,7 +626,7 @@
|
||||
your own runs or runs of groups you belong to unless you are an admin.
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div class="hidden xl:block">
|
||||
<div class="hidden 2xl:block">
|
||||
<RunsFilter
|
||||
bind:isSkipped
|
||||
bind:user
|
||||
@@ -644,7 +644,7 @@
|
||||
{paths}
|
||||
/>
|
||||
</div>
|
||||
<div class="xl:hidden">
|
||||
<div class="block 2xl:hidden">
|
||||
<MobileFilters>
|
||||
<svelte:fragment slot="filters">
|
||||
<span class="text-xs font-semibold leading-6">Filters</span>
|
||||
|
||||
Reference in New Issue
Block a user