Compare commits
8 Commits
glm/improv
...
glm/rework
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
868fe2af5d | ||
|
|
3cef291327 | ||
|
|
cf73f67079 | ||
|
|
f07b630f82 | ||
|
|
e50428a3aa | ||
|
|
a9e67c601f | ||
|
|
ea6ccae19e | ||
|
|
13b74fa923 |
@@ -13,7 +13,7 @@
|
||||
containerClasses="border rounded-lg shadow-lg p-4 bg-surface"
|
||||
>
|
||||
<svelte:fragment slot="button">
|
||||
<Button color="dark" size="xs" nonCaptureEvent={true}>
|
||||
<Button color="nord" size="xs" nonCaptureEvent={true}>
|
||||
<div class="flex flex-row gap-1 items-center"
|
||||
><Pen size={14} /> Custom Tags <Tooltip light
|
||||
>Tags are assigned to scripts and flows. Workers only accept jobs that correspond to their
|
||||
|
||||
@@ -24,8 +24,6 @@
|
||||
import ToggleButtonGroup from './common/toggleButton-v2/ToggleButtonGroup.svelte'
|
||||
import FolderEditor from './FolderEditor.svelte'
|
||||
import { random_adj } from './random_positive_adjetive'
|
||||
import Required from './Required.svelte'
|
||||
import Tooltip from './Tooltip.svelte'
|
||||
import { Eye, Folder, Plus, SearchCode, User } from 'lucide-svelte'
|
||||
|
||||
type PathKind =
|
||||
@@ -292,6 +290,7 @@
|
||||
)
|
||||
</script>
|
||||
|
||||
<!-- Drawers -->
|
||||
<Drawer bind:this={newFolder}>
|
||||
<DrawerContent
|
||||
title="New Folder"
|
||||
@@ -319,121 +318,150 @@
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
|
||||
<div>
|
||||
<div class="flex flex-col sm:flex-row sm:items-center gap-2 sm:gap-4 pb-0 mb-1">
|
||||
<div class="flex flex-col gap-2">
|
||||
<!-- full path -->
|
||||
<div class="flex grow flex-col w-full">
|
||||
<div class="flex justify-start w-full grow">
|
||||
<Badge
|
||||
color="gray"
|
||||
class="center-center !bg-surface-secondary !text-tertiary !font-normal !w-[70px] !h-[24px] rounded-r-none border border-r-0"
|
||||
>
|
||||
Full path
|
||||
</Badge>
|
||||
<input
|
||||
type="text"
|
||||
readonly
|
||||
value={path}
|
||||
size={path?.length || 50}
|
||||
class="font-mono !text-xs max-w-[calc(100%-70px)] !text-tertiary !w-auto !h-[24px] !py-0 !border-l-0 !rounded-l-none grow"
|
||||
on:focus={({ currentTarget }) => {
|
||||
currentTarget.select()
|
||||
}}
|
||||
/>
|
||||
<!-- <span class="font-mono text-sm break-all">{path}</span> -->
|
||||
</div>
|
||||
<div class="text-red-600 dark:text-red-400 text-2xs">{error}</div>
|
||||
</div>
|
||||
|
||||
<!-- path selector-->
|
||||
<div class="flex flex-row items-center gap-4">
|
||||
<!-- new folder picker -->
|
||||
{#if meta != undefined}
|
||||
<div class="flex gap-x-4 shrink">
|
||||
<!-- svelte-ignore a11y-label-has-associated-control -->
|
||||
{#if !hideUser}
|
||||
<div class="block">
|
||||
<span class="text-secondary text-sm whitespace-nowrap"> </span>
|
||||
|
||||
<ToggleButtonGroup
|
||||
class="mt-0.5"
|
||||
bind:selected={meta.ownerKind}
|
||||
on:selected={(e) => {
|
||||
setDirty()
|
||||
const kind = e.detail
|
||||
if (meta) {
|
||||
if (kind === 'folder') {
|
||||
meta.owner = folders?.[0]?.name ?? ''
|
||||
} else if (kind === 'group') {
|
||||
meta.owner = 'all'
|
||||
} else {
|
||||
meta.owner = $userStore?.username?.split('@')[0] ?? ''
|
||||
}
|
||||
<!-- svelte-ignore a11y-label-has-associated-control -->
|
||||
{#if !hideUser}
|
||||
<!-- Toggle User/Folder -->
|
||||
<div class="block">
|
||||
<ToggleButtonGroup
|
||||
class="items-center"
|
||||
bind:selected={meta.ownerKind}
|
||||
on:selected={(e) => {
|
||||
setDirty()
|
||||
const kind = e.detail
|
||||
if (meta) {
|
||||
if (kind === 'folder') {
|
||||
meta.owner = folders?.[0]?.name ?? ''
|
||||
} else if (kind === 'group') {
|
||||
meta.owner = 'all'
|
||||
} else {
|
||||
meta.owner = $userStore?.username?.split('@')[0] ?? ''
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ToggleButton
|
||||
icon={User}
|
||||
{disabled}
|
||||
light
|
||||
size="xs"
|
||||
value="user"
|
||||
position="left"
|
||||
label="User"
|
||||
/>
|
||||
<!-- <ToggleButton light size="xs" value="group" position="center">Group</ToggleButton> -->
|
||||
<ToggleButton
|
||||
icon={Folder}
|
||||
{disabled}
|
||||
light
|
||||
size="xs"
|
||||
value="folder"
|
||||
position="right"
|
||||
label="Folder"
|
||||
/>
|
||||
</ToggleButtonGroup>
|
||||
</div>
|
||||
{/if}
|
||||
{#if meta.ownerKind === 'user'}
|
||||
<label class="block shrink min-w-0">
|
||||
<span class="text-secondary text-sm">User</span>
|
||||
<input
|
||||
class="!w-36"
|
||||
type="text"
|
||||
bind:value={meta.owner}
|
||||
placeholder={$userStore?.username ?? ''}
|
||||
disabled={disabled || !($superadmin || ($userStore?.is_admin ?? false))}
|
||||
on:keydown={setDirty}
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ToggleButton
|
||||
icon={User}
|
||||
{disabled}
|
||||
light
|
||||
size="xs"
|
||||
value="user"
|
||||
position="left"
|
||||
label="User"
|
||||
/>
|
||||
</label>
|
||||
{:else if meta.ownerKind === 'folder'}
|
||||
<label class="block grow w-48">
|
||||
<span class="text-secondary text-sm">
|
||||
Folder
|
||||
<Tooltip
|
||||
documentationLink="https://www.windmill.dev/docs/core_concepts/groups_and_folders"
|
||||
>
|
||||
Read and write permissions are given to groups and users at the folder level and
|
||||
shared by all items inside the folder.
|
||||
</Tooltip>
|
||||
</span>
|
||||
|
||||
<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}
|
||||
</select>
|
||||
<Button
|
||||
title="View folder"
|
||||
btnClasses="!p-1.5"
|
||||
variant="border"
|
||||
color="light"
|
||||
size="xs"
|
||||
disabled={!meta.owner || meta.owner == ''}
|
||||
on:click={viewFolder.openDrawer}
|
||||
iconOnly
|
||||
startIcon={{ icon: Eye }}
|
||||
/>
|
||||
<Button
|
||||
title="New folder"
|
||||
btnClasses="!p-1.5"
|
||||
variant="border"
|
||||
color="light"
|
||||
size="xs"
|
||||
{disabled}
|
||||
on:click={newFolder.openDrawer}
|
||||
iconOnly
|
||||
startIcon={{ icon: Plus }}
|
||||
/>
|
||||
</div>
|
||||
</label>
|
||||
{/if}
|
||||
</div>
|
||||
<label class="block grow w-full max-w-md">
|
||||
<div class="text-secondary text-sm flex items-center gap-1 w-full justify-between">
|
||||
<div>
|
||||
Name
|
||||
<Required required={true} />
|
||||
</div>
|
||||
<div class="text-2xs text-tertiary"> '/' for subfolders </div>
|
||||
<!-- <ToggleButton light size="xs" value="group" position="center">Group</ToggleButton> -->
|
||||
<ToggleButton
|
||||
icon={Folder}
|
||||
{disabled}
|
||||
light
|
||||
size="xs"
|
||||
value="folder"
|
||||
position="right"
|
||||
label="Folder"
|
||||
/>
|
||||
</ToggleButtonGroup>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="text-secondary text-sm">/</div>
|
||||
|
||||
{#if meta.ownerKind === 'user'}
|
||||
<label class="block grow shrink min-w-36">
|
||||
<input
|
||||
class="w-full"
|
||||
type="text"
|
||||
bind:value={meta.owner}
|
||||
placeholder={$userStore?.username ?? ''}
|
||||
disabled={disabled || !($superadmin || ($userStore?.is_admin ?? false))}
|
||||
on:keydown={setDirty}
|
||||
/>
|
||||
</label>
|
||||
{:else if meta.ownerKind === 'folder'}
|
||||
<label class="block grow min-w-48">
|
||||
<!-- <span class="text-secondary text-sm">
|
||||
Folder
|
||||
<Tooltip
|
||||
documentationLink="https://www.windmill.dev/docs/core_concepts/groups_and_folders"
|
||||
>
|
||||
Read and write permissions are given to groups and users at the folder level and
|
||||
shared by all items inside the folder.
|
||||
</Tooltip>
|
||||
</span> -->
|
||||
|
||||
<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}
|
||||
</select>
|
||||
<Button
|
||||
title="View folder"
|
||||
btnClasses="!p-1.5"
|
||||
variant="border"
|
||||
color="light"
|
||||
size="xs"
|
||||
disabled={!meta.owner || meta.owner == ''}
|
||||
on:click={viewFolder.openDrawer}
|
||||
iconOnly
|
||||
startIcon={{ icon: Eye }}
|
||||
/>
|
||||
<Button
|
||||
title="New folder"
|
||||
btnClasses="!p-1.5"
|
||||
variant="border"
|
||||
color="light"
|
||||
size="xs"
|
||||
{disabled}
|
||||
on:click={newFolder.openDrawer}
|
||||
iconOnly
|
||||
startIcon={{ icon: Plus }}
|
||||
/>
|
||||
</div>
|
||||
</label>
|
||||
{/if}
|
||||
|
||||
<div class="text-secondary text-sm">/</div>
|
||||
|
||||
<!-- The name -->
|
||||
<label class="block grow w-full max-w-md">
|
||||
<!-- <div class="text-secondary text-sm flex items-center gap-1 w-full justify-between">
|
||||
<div>
|
||||
Name
|
||||
<Required required={true} />
|
||||
</div>
|
||||
<div class="text-2xs text-tertiary"> '/' for subfolders </div>
|
||||
</div> -->
|
||||
<!-- svelte-ignore a11y-autofocus -->
|
||||
<input
|
||||
{disabled}
|
||||
@@ -453,29 +481,6 @@
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col w-full mt-4">
|
||||
<div class="flex justify-start w-full">
|
||||
<Badge
|
||||
color="gray"
|
||||
class="center-center !bg-surface-secondary !text-tertiary !w-[70px] !h-[24px] rounded-r-none border"
|
||||
>
|
||||
Full path
|
||||
</Badge>
|
||||
<input
|
||||
type="text"
|
||||
readonly
|
||||
value={path}
|
||||
size={path?.length || 50}
|
||||
class="font-mono !text-xs max-w-[calc(100%-70px)] !w-auto !h-[24px] !py-0 !border-l-0 !rounded-l-none"
|
||||
on:focus={({ currentTarget }) => {
|
||||
currentTarget.select()
|
||||
}}
|
||||
/>
|
||||
<!-- <span class="font-mono text-sm break-all">{path}</span> -->
|
||||
</div>
|
||||
<div class="text-red-600 dark:text-red-400 text-2xs">{error}</div>
|
||||
</div>
|
||||
|
||||
{#if kind != 'app' && kind != 'schedule' && kind != 'http_trigger' && initialPath != '' && initialPath != undefined && initialPath != path}
|
||||
<Alert type="warning" class="mt-4" title="Moving may break other items relying on it">
|
||||
You are renaming an item that may be depended upon by other items. This may break apps, flows
|
||||
|
||||
@@ -9,18 +9,23 @@
|
||||
export let eeOnly = false
|
||||
export let small: boolean = false
|
||||
|
||||
export let subsection: boolean = false
|
||||
|
||||
export let collapsable: boolean = false
|
||||
let collapsed: boolean = true
|
||||
</script>
|
||||
|
||||
<div class="w-full">
|
||||
<div class="flex flex-row justify-between items-center mb-2">
|
||||
<div class="flex flex-row justify-between items-start mb-2">
|
||||
<h2
|
||||
class={twMerge(
|
||||
'font-semibold flex flex-row items-center gap-1',
|
||||
subsection ? 'font-normal' : 'font-semibold',
|
||||
subsection ? 'text-secondary' : 'text-primary',
|
||||
'flex flex-row items-center gap-1',
|
||||
small ? 'text-sm' : 'text-base'
|
||||
)}
|
||||
>
|
||||
{label}
|
||||
{#if collapsable}
|
||||
<button class="flex items-center gap-1" on:click={() => (collapsed = !collapsed)}>
|
||||
{#if collapsed}
|
||||
@@ -28,10 +33,7 @@
|
||||
{:else}
|
||||
<ChevronDown size={16} />
|
||||
{/if}
|
||||
{label}
|
||||
</button>
|
||||
{:else}
|
||||
{label}
|
||||
{/if}
|
||||
|
||||
<slot name="header" />
|
||||
@@ -48,7 +50,11 @@
|
||||
{/if}
|
||||
</h2>
|
||||
<slot name="action" />
|
||||
{#if collapsable && collapsed}
|
||||
<slot name="badge" />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class={collapsable && collapsed ? `hidden ${$$props.class}` : `${$$props.class}`}>
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
@@ -14,14 +14,18 @@
|
||||
export let disabled = false
|
||||
export let textClass = ''
|
||||
export let textStyle = ''
|
||||
export let color: 'blue' | 'red' = 'blue'
|
||||
export let color: 'blue' | 'red' | 'nord' = 'blue'
|
||||
export let id = (Math.random() + 1).toString(36).substring(10)
|
||||
export let lightMode: boolean = false
|
||||
|
||||
export let size: 'sm' | 'xs' = 'sm'
|
||||
|
||||
export let lightToogle = false
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
const bothOptions = Boolean(options.left) && Boolean(options.right)
|
||||
|
||||
export let textDisabled = false
|
||||
</script>
|
||||
|
||||
<label
|
||||
@@ -34,7 +38,7 @@
|
||||
<span
|
||||
class={twMerge(
|
||||
'mr-2 font-medium duration-50 select-none',
|
||||
bothOptions ? (checked ? 'text-disabled' : 'text-primary') : 'text-primary',
|
||||
bothOptions || textDisabled ? (checked ? 'text-disabled' : 'text-primary') : 'text-primary',
|
||||
size === 'xs' ? 'text-xs' : 'text-sm',
|
||||
textClass
|
||||
)}
|
||||
@@ -67,8 +71,12 @@
|
||||
"transition-all bg-surface-selected rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:bg-surface after:border-white after:border after:rounded-full after:transition-all ",
|
||||
color == 'red'
|
||||
? 'peer-checked:bg-red-600'
|
||||
: 'peer-checked:bg-blue-600 dark:peer-checked:bg-blue-500',
|
||||
size === 'sm'
|
||||
: color == 'blue'
|
||||
? 'peer-checked:bg-blue-600 dark:peer-checked:bg-blue-500'
|
||||
: 'peer-checked:bg-nord-300 dark:peer-checked:bg-nord-500',
|
||||
lightToogle
|
||||
? 'w-9 h-5 after:top-0.5 after:left-[2px] after:h-4 after:w-4'
|
||||
: size === 'sm'
|
||||
? 'w-11 h-6 after:top-0.5 after:left-[2px] after:h-5 after:w-5'
|
||||
: 'w-7 h-4 after:top-0.5 after:left-[2px] after:h-3 after:w-3'
|
||||
)}
|
||||
@@ -78,7 +86,7 @@
|
||||
<span
|
||||
class={twMerge(
|
||||
'ml-2 font-medium duration-50 select-none',
|
||||
bothOptions ? (checked ? 'text-primary' : 'text-disabled') : 'text-primary',
|
||||
bothOptions || textDisabled ? (checked ? 'text-primary' : 'text-disabled') : 'text-primary',
|
||||
size === 'xs' ? 'text-xs' : 'text-sm',
|
||||
textClass
|
||||
)}
|
||||
@@ -86,7 +94,7 @@
|
||||
>
|
||||
{options?.right}
|
||||
{#if options?.rightTooltip}
|
||||
<Tooltip light={lightMode}>{options?.rightTooltip}</Tooltip>
|
||||
<Tooltip light={lightMode} wrapperClass="text-disabled">{options?.rightTooltip}</Tooltip>
|
||||
{/if}
|
||||
</span>
|
||||
{/if}
|
||||
|
||||
@@ -114,6 +114,13 @@
|
||||
contained:
|
||||
'bg-surface border-transparent hover:bg-surface-hover focus-visible:bg-surface-hover text-primary focus-visible:ring-surface-selected',
|
||||
divider: 'divide-x divide-gray-200 dark:divide-gray-700'
|
||||
},
|
||||
nord: {
|
||||
border:
|
||||
'border-nord-200 bg-surface hover:bg-surface-hover focus-visible:bg-surface-hover text-primary hover:text-secondary focus-visible:text-secondary focus-visible:ring-surface-selected-inverse dark:border-nord-200',
|
||||
contained:
|
||||
'bg-nord-300 hover:bg-nord-0 focus-visible:bg-surface-hover-inverse text-primary-inverse focus-visible:ring-surface-selected-inverse dark:bg-nord-400 dark:hover:bg-nord-600 dark:text-primary-inverse',
|
||||
divider: 'divide-x divide-gray-800 dark:divide-gray-200'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
<script lang="ts">
|
||||
import Toggle from '$lib/components/Toggle.svelte'
|
||||
|
||||
import { FlowService, ScriptService } from '$lib/gen'
|
||||
import { sendUserToast } from '$lib/toast'
|
||||
import { workspaceStore } from '$lib/stores'
|
||||
|
||||
export let kind: 'script' | 'flow'
|
||||
export let scriptOrFlowPath: string
|
||||
export let errorHandlerMuted: boolean | undefined
|
||||
export let textDisabled = false
|
||||
let toggleState = errorHandlerMuted
|
||||
|
||||
export let color: 'nord' | 'red' | 'blue' = 'nord'
|
||||
|
||||
async function toggleErrorHandler(): Promise<void> {
|
||||
toggleState = !toggleState
|
||||
if ($workspaceStore !== undefined) {
|
||||
try {
|
||||
if (kind === 'flow') {
|
||||
await FlowService.toggleWorkspaceErrorHandlerForFlow({
|
||||
workspace: $workspaceStore,
|
||||
path: scriptOrFlowPath,
|
||||
requestBody: {
|
||||
muted: !errorHandlerMuted
|
||||
}
|
||||
})
|
||||
} else {
|
||||
await ScriptService.toggleWorkspaceErrorHandlerForScript({
|
||||
workspace: $workspaceStore,
|
||||
path: scriptOrFlowPath,
|
||||
requestBody: {
|
||||
muted: !errorHandlerMuted
|
||||
}
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
sendUserToast(
|
||||
`Error while toggling Workspace Error Handler: ${error.body || error.message}`,
|
||||
true
|
||||
)
|
||||
toggleState = false
|
||||
return
|
||||
}
|
||||
errorHandlerMuted = !errorHandlerMuted
|
||||
toggleState = errorHandlerMuted
|
||||
sendUserToast(
|
||||
errorHandlerMuted ? 'Workspace error handler muted' : 'Workspace error handler active',
|
||||
false
|
||||
)
|
||||
}
|
||||
toggleState = false
|
||||
}
|
||||
|
||||
$: console.log('toggleState', toggleState)
|
||||
</script>
|
||||
|
||||
<Toggle
|
||||
{color}
|
||||
lightToogle={true}
|
||||
size="sm"
|
||||
checked={toggleState}
|
||||
on:change={toggleErrorHandler}
|
||||
options={{
|
||||
right: 'Mute',
|
||||
rightTooltip: 'Disable workspace error handler, EE only'
|
||||
}}
|
||||
{textDisabled}
|
||||
/>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -384,6 +384,20 @@ const config = {
|
||||
400: '#394A6D',
|
||||
500: '#323F5B'
|
||||
},
|
||||
nord: {
|
||||
0: '#2E3440',
|
||||
100: '#3B4252',
|
||||
200: '#434C5E',
|
||||
300: '#4C566A',
|
||||
400: '#D8DEE9',
|
||||
500: '#E5E9F0',
|
||||
600: '#ECEFF4',
|
||||
700: '#8FBCBB',
|
||||
800: '#88C0D0',
|
||||
900: '#81A1C1',
|
||||
950: '#5E81AC'
|
||||
},
|
||||
|
||||
surface: 'rgb(var(--color-surface) / <alpha-value>)',
|
||||
'surface-secondary': 'rgb(var(--color-surface-secondary) / <alpha-value>)',
|
||||
'surface-hover': 'rgb(var(--color-surface-hover) / <alpha-value>)',
|
||||
|
||||
Reference in New Issue
Block a user