UX improvements on runform + creating group from folder
This commit is contained in:
@@ -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'}
|
||||
|
||||
@@ -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} <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} <Icon data={open ? faChevronUp : faChevronDown} scale={0.5} />
|
||||
</Button>
|
||||
</div>
|
||||
{#if open}
|
||||
<div><slot /></div>
|
||||
<div transition:slide|local={{ duration: 100 }}><slot /></div>
|
||||
{/if}
|
||||
|
||||
@@ -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 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 == ''}
|
||||
|
||||
@@ -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]]} />
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user