UX improvements on runform + creating group from folder

This commit is contained in:
Ruben Fiszel
2023-05-20 11:30:03 +02:00
parent 9e57b942d7
commit 0bd8b88e65
11 changed files with 174 additions and 104 deletions

View File

@@ -15,25 +15,25 @@
<div class="text-sm mt-2 flex">
<!-- svelte-ignore a11y-click-events-have-key-events -->
<span
class="underline mr-4"
<button
class="underline mr-4 text-gray-600"
on:click={() => {
opened = !opened
}}
>
CLI quick setup
<Icon class="ml-2" data={opened ? faChevronUp : faChevronDown} scale={0.7} />
</span>
</button>
</div>
{#if opened}
<div
transition:slide|local
class="bg-gray-100 border-l-4 border-gray-600 text-gray-700 p-4 m-4"
transition:slide|local={{ duration: 100 }}
class="border border-gray-200 rounded-md p-4 mt-2 text-sm text-gray-600"
role="alert"
id="dynamic-input-help-box"
>
<ul class="pl-0 list-decimal list-inside">
<ul class="pl-0 pt-2 list-decimal list-inside">
<li
>Install the latest wmill CLI from deno.land: <InlineCodeCopy
content={'deno install --unstable -A https://deno.land/x/wmill/main.ts'}

View File

@@ -2,14 +2,24 @@
import { faChevronDown, faChevronUp } from '@fortawesome/free-solid-svg-icons'
import Icon from 'svelte-awesome'
import { Button } from './common'
import { slide } from 'svelte/transition'
export let open = false
export let text: string
export let small = false
</script>
<button class="text-gray-600 underline" on:click={() => (open = !open)}>
{text}&nbsp;<Icon data={open ? faChevronUp : faChevronDown} scale={0.5} />
</button>
<div class="flex">
<Button
variant="border"
color="light"
btnClasses="text-gray-600 {small ? 'text-xs' : ''} "
on:click={() => (open = !open)}
>
{text}&nbsp;<Icon data={open ? faChevronUp : faChevronDown} scale={0.5} />
</Button>
</div>
{#if open}
<div><slot /></div>
<div transition:slide|local={{ duration: 100 }}><slot /></div>
{/if}

View File

@@ -9,9 +9,12 @@
} from '$lib/gen'
import AutoComplete from 'simple-svelte-autocomplete'
import TableCustom from './TableCustom.svelte'
import { Alert, Button, ToggleButton, ToggleButtonGroup } from './common'
import { Alert, Button, Drawer, DrawerContent, ToggleButton, ToggleButtonGroup } from './common'
import Skeleton from './common/skeleton/Skeleton.svelte'
import Tooltip from './Tooltip.svelte'
import { Icon } from 'svelte-awesome'
import { faEye, faPlus } from '@fortawesome/free-solid-svg-icons'
import GroupEditor from './GroupEditor.svelte'
export let name: string
let can_write = false
@@ -23,6 +26,9 @@
let groups: string[] = []
let ownerItem: string = ''
let newGroup: Drawer
let viewGroup: Drawer
async function loadUsernames(): Promise<void> {
usernames = await UserService.listUsernames({ workspace: $workspaceStore! })
}
@@ -92,8 +98,50 @@
}
}
let ownerKind: 'user' | 'group' = 'user'
let groupCreated: string | undefined = undefined
let newGroupName: string = ''
async function addGroup() {
await GroupService.createGroup({
workspace: $workspaceStore ?? '',
requestBody: { name: newGroupName }
})
groupCreated = newGroupName
$userStore?.folders?.push(newGroupName)
loadGroups()
ownerItem = newGroupName
}
</script>
<Drawer bind:this={newGroup}>
<DrawerContent
title="New Group"
on:close={() => {
newGroup.closeDrawer()
groupCreated = undefined
}}
>
{#if !groupCreated}
<div class="flex flex-row">
<input class="mr-2" placeholder="New group name" bind:value={newGroupName} />
<Button size="md" startIcon={{ icon: faPlus }} disabled={!newGroupName} on:click={addGroup}>
New&nbsp;group
</Button>
</div>
{:else}
<GroupEditor name={groupCreated} />
{/if}
</DrawerContent>
</Drawer>
<Drawer bind:this={viewGroup}>
<DrawerContent title="Group {ownerItem}" on:close={viewGroup.closeDrawer}>
<GroupEditor name={ownerItem} />
</DrawerContent>
</Drawer>
<div class="flex flex-col gap-6">
<h1>{name}</h1>
<h2>Permissions ({perms?.length ?? 0})</h2>
@@ -108,11 +156,33 @@
<ToggleButton position="right" value="group" size="xs">Group</ToggleButton>
</ToggleButtonGroup>
</div>
{#key ownerKind}
<AutoComplete
items={ownerKind === 'user' ? usernames : groups}
bind:selectedItem={ownerItem}
/>
{#if ownerKind == 'group'}
<Button
title="View Group"
btnClasses="!p-1.5"
variant="border"
size="xs"
disabled={!ownerItem || ownerItem == ''}
on:click={viewGroup.openDrawer}
>
<Icon scale={0.8} data={faEye} /></Button
>
<Button
title="New Group"
btnClasses="!p-1.5"
variant="border"
on:click={newGroup.openDrawer}
size="xs"
>
<Icon scale={0.8} data={faPlus} /></Button
>
{/if}
{/key}
<Button
disabled={ownerItem == ''}

View File

@@ -90,19 +90,21 @@
bind:value={group.summary}
placeholder="Summary of the group"
/>
<Button
disabled={!can_write}
size="xs"
on:click={async () => {
await GroupService.updateGroup({
workspace: $workspaceStore ?? '',
name,
requestBody: { summary: group?.summary }
})
dispatch('update')
sendUserToast('New summary saved')
}}>Save Summary</Button
>
<div class="flex justify-end">
<Button
disabled={!can_write}
size="xs"
on:click={async () => {
await GroupService.updateGroup({
workspace: $workspaceStore ?? '',
name,
requestBody: { summary: group?.summary }
})
dispatch('update')
sendUserToast('New summary saved')
}}>Save Summary</Button
>
</div>
</div>
{:else}
<Skeleton layout={[[4]]} />

View File

@@ -7,7 +7,7 @@
</script>
<!-- svelte-ignore a11y-click-events-have-key-events -->
<pre class="bg-gray-700 text-gray-100 p-2 font-mono text-sm whitespace-pre-wrap"
<pre class="bg-gray-700 text-gray-100 p-2 font-mono text-sm whitespace-pre-wrap my-2"
>{content} <span on:click={() => copyToClipboard(content)} class="cursor-pointer ml-2"
><Icon data={faClipboard} /></span
></pre

View File

@@ -323,6 +323,9 @@
<div class="flex flex-row items-center gap-1 w-full">
<select class="grow w-full" {disabled} bind:value={meta.owner}>
{#if folders?.length == 0}
<option disabled>No folders</option>
{/if}
{#each folders as { name, write }}
<option disabled={!write}>{name}{write ? '' : ' (read-only)'}</option>
{/each}
@@ -332,6 +335,7 @@
btnClasses="!p-1.5"
variant="border"
size="xs"
disabled={!meta.owner || meta.owner == ''}
on:click={viewFolder.openDrawer}
>
<Icon scale={0.8} data={faEye} /></Button

View File

@@ -6,11 +6,9 @@
getToday,
truncateHash
} from '$lib/utils'
import { slide } from 'svelte/transition'
import type { Schema } from '$lib/common'
import { runFormStore, userStore } from '$lib/stores'
import { faChevronDown, faChevronUp } from '@fortawesome/free-solid-svg-icons'
import CliHelpBox from './CliHelpBox.svelte'
import { Badge, Button } from './common'
import InlineCodeCopy from './InlineCodeCopy.svelte'
@@ -64,9 +62,6 @@
export let isValid = true
let viewOptions = false
let viewCliOptions = false
let scheduledForStr: string | undefined
let invisible_to_owner: false
@@ -75,7 +70,7 @@
)}'`
</script>
<div class="max-w-6xl">
<div class="max-w-3xl">
{#if detailed}
{#if runnable}
<div class="flex flex-row flex-wrap justify-between gap-4">
@@ -142,52 +137,53 @@
/>
{/if}
{:else}
<div class="text-xs text-gray-600">No schema</div>
<div class="text-xs text-gray-600">No arguments</div>
{/if}
{#if schedulable}
<div class="mt-10" />
<CollapseLink text="Advanced">
<div class="flex gap-2 items-start flex-wrap justify-between mt-2 md:mt-6 mb-6">
<div class="flex-row-reverse flex grow">
<Button
{loading}
color="dark"
btnClasses="!px-6 !py-1"
disabled={!isValid}
on:click={() => runAction(scheduledForStr, args, invisible_to_owner)}
>
{scheduledForStr ? 'Schedule to run later' : buttonText}
</Button>
</div>
</div>
<CollapseLink small text="Advanced">
<div class="flex flex-col gap-4 mt-2 border p-2">
<div class="flex flex-col gap-2">
{#if SCRIPT_VIEW_SHOW_SCHEDULE_RUN_LATER}
<div class="flex">
<Button
color="light"
size="sm"
endIcon={{ icon: viewOptions ? faChevronUp : faChevronDown }}
on:click={() => (viewOptions = !viewOptions)}
>
Schedule to run later
</Button>
</div>
{/if}
{#if viewOptions}
<div transition:slide|local class="mt-6">
<div class="border rounded-md p-3 pt-4">
<div class="flex flex-row items-end">
<div class="w-max md:w-2/3 mt-2 mb-1">
<label for="run-time" />
<input
class="inline-block"
type="datetime-local"
id="run-time"
name="run-scheduled-time"
bind:value={scheduledForStr}
min={getToday().toISOString().slice(0, 16)}
/>
</div>
<Button
variant="border"
color="blue"
size="sm"
btnClasses="mx-2 mb-1"
on:click={() => {
scheduledForStr = undefined
}}
>
Clear
</Button>
<div class="border rounded-md p-3 pt-4">
<div class="px-2 font-semibold text-sm">Schedule to run later</div>
<div class="flex flex-row items-end">
<div class="w-max md:w-2/3 mt-2 mb-1">
<label for="run-time" />
<input
class="inline-block"
type="datetime-local"
id="run-time"
name="run-scheduled-time"
bind:value={scheduledForStr}
min={getToday().toISOString().slice(0, 16)}
/>
</div>
<Button
variant="border"
color="blue"
size="sm"
btnClasses="mx-2 mb-1"
on:click={() => {
scheduledForStr = undefined
}}
>
Clear
</Button>
</div>
</div>
{/if}
@@ -207,18 +203,6 @@
{/if}
</div>
</CollapseLink>
<div class="flex gap-2 items-start flex-wrap justify-between mt-2 md:mt-6 mb-6">
<div class="flex-row-reverse flex grow">
<Button
{loading}
btnClasses="!px-6 !py-1"
disabled={!isValid}
on:click={() => runAction(scheduledForStr, args, invisible_to_owner)}
>
{scheduledForStr ? 'Schedule to run later' : buttonText}
</Button>
</div>
</div>
{:else if !topButton}
<Button
btnClasses="!px-6 !py-1 w-full"
@@ -231,25 +215,15 @@
{#if viewCliRun}
<div>
<div class="my-20" />
<div class="mt-4" />
{#if SCRIPT_VIEW_SHOW_RUN_FROM_CLI}
<div class="flex">
<Button
color="light"
size="xs"
endIcon={{ icon: viewCliOptions ? faChevronUp : faChevronDown }}
on:click={() => (viewCliOptions = !viewCliOptions)}
>
Run it from the CLI
</Button>
</div>
{/if}
{#if viewCliOptions}
<div transition:slide|local class="mt-2 px-4 pt-2">
<CollapseLink small text="Run it from CLI">
<div class="mt-2" />
<InlineCodeCopy content={cliCommand} />
<CliHelpBox />
</div>
</CollapseLink>
{/if}
<div class="mb-20" />
</div>
{/if}
</div>

View File

@@ -219,7 +219,7 @@
size="xs"
variant="border"
spacingSize="xs2"
btnClasses={'group-hover:block hidden'}
btnClasses={'group-hover:block hidden -my-2'}
on:click={(e) => {
e.stopPropagation()
i.isEditing = !i.isEditing
@@ -236,7 +236,7 @@
size="xs"
spacingSize="xs2"
variant="border"
btnClasses={i.isEditing ? 'block' : 'group-hover:block hidden'}
btnClasses={i.isEditing ? 'block' : 'group-hover:block hidden -my-2'}
on:click={() => deleteInput(i)}
>
<X class="w-4 h-4" />

View File

@@ -11,6 +11,7 @@
import { RawAppService } from '$lib/gen'
import { workspaceStore } from '$lib/stores'
import Path from '../Path.svelte'
import Tooltip from '../Tooltip.svelte'
let drawer: Drawer | undefined = undefined
let rawAppDrawer: Drawer | undefined = undefined
@@ -89,7 +90,16 @@
placeholder="Short summary to be displayed when listed"
/>
<h2 class="border-b pb-1 mt-10 mb-4">IIFE JS code</h2>
<h2 class="border-b pb-1 mt-10 mb-4"
>IIFE JS code <Tooltip
documentationLink="https://docs.windmill.dev/docs/react_vue_svelte_apps/react"
>Bundle that contains an IIFE code that will mount itself to the root element. Any framework
of vanilla JS can be used to create an app and templates are provided for the major
frameworks: React/Vue/Svelte. In those frontend apps, it is possible to inline scripts
directly to be executed by windmill backend which make it a convenient way of building apps
with both frontend and backend all-in-one.</Tooltip
></h2
>
<FileInput
accept={'.js'}
multiple={false}

View File

@@ -169,7 +169,7 @@
size="xs"
variant="border"
disabled={flow == undefined}
color="dark"
color="light"
on:click={() => {
//savedInputPaneSize = savedInputPaneSize == 0 ? 30 : 0
savedInputPaneSize.set($savedInputPaneSize === 0 ? 30 : 0)

View File

@@ -10,12 +10,12 @@
import { inferArgs } from '$lib/infer'
import { userStore, workspaceStore } from '$lib/stores'
import {
canWrite,
canWrite,
defaultIfEmptyString,
displayDaysAgo,
emptySchema,
emptyString,
getModifierKey,
getModifierKey
} from '$lib/utils'
import { faEye, faPen, faPlay } from '@fortawesome/free-solid-svg-icons'
import { Pane, Splitpanes } from 'svelte-splitpanes'
@@ -229,7 +229,7 @@
<Button
variant="border"
size="xs"
color="dark"
color="light"
on:click={() => {
//savedInputPaneSize = savedInputPaneSize == 0 ? 30 : 0
savedInputPaneSize.set($savedInputPaneSize === 0 ? 30 : 0)