From b8e9338d722fe0ec166df3f3b7c895f2ed8ea7ac Mon Sep 17 00:00:00 2001 From: Faton Ramadani Date: Thu, 3 Aug 2023 12:24:38 +0200 Subject: [PATCH] feat(frontend): Settings rework (#1983) * feat(frontend): rework settings wup * feat(frontend): rework settings wop * feat(frontend): rework settings wip * feat(frontend): rework workers + folders * feat(frontend): modify the nord theme * feat(frontend): folder columns size * feat(frontend): Fix build --- frontend/src/lib/components/AddUser.svelte | 101 ++-- .../src/lib/components/EditorTheme.svelte | 2 +- .../src/lib/components/FolderEditor.svelte | 49 +- frontend/src/lib/components/FolderInfo.svelte | 18 +- .../src/lib/components/FolderUsageInfo.svelte | 5 +- .../src/lib/components/GroupEditor.svelte | 31 +- frontend/src/lib/components/InviteUser.svelte | 88 +-- frontend/src/lib/components/PageHeader.svelte | 16 +- frontend/src/lib/components/ShareModal.svelte | 13 +- frontend/src/lib/components/common/index.ts | 2 - .../src/lib/components/common/tabs/Tab.svelte | 2 +- .../common/toggleButton/ToggleButton.svelte | 28 - .../toggleButton/ToggleButtonGroup.svelte | 36 -- .../settings/WorkspaceUserSettings.svelte | 372 +++++++++++++ frontend/src/lib/components/table/Body.svelte | 0 frontend/src/lib/components/table/Cell.svelte | 25 + .../src/lib/components/table/DataTable.svelte | 53 ++ frontend/src/lib/components/table/Head.svelte | 3 + frontend/src/lib/components/table/Row.svelte | 21 + .../(root)/(logged)/folders/+page.svelte | 70 +-- .../(logged)/runs/[...path]/+page.svelte | 2 +- .../(root)/(logged)/workers/+page.svelte | 80 +-- .../(logged)/workspace_settings/+page.svelte | 499 ++++-------------- 23 files changed, 855 insertions(+), 661 deletions(-) delete mode 100644 frontend/src/lib/components/common/toggleButton/ToggleButton.svelte delete mode 100644 frontend/src/lib/components/common/toggleButton/ToggleButtonGroup.svelte create mode 100644 frontend/src/lib/components/settings/WorkspaceUserSettings.svelte create mode 100644 frontend/src/lib/components/table/Body.svelte create mode 100644 frontend/src/lib/components/table/Cell.svelte create mode 100644 frontend/src/lib/components/table/DataTable.svelte create mode 100644 frontend/src/lib/components/table/Head.svelte create mode 100644 frontend/src/lib/components/table/Row.svelte diff --git a/frontend/src/lib/components/AddUser.svelte b/frontend/src/lib/components/AddUser.svelte index 10c08d0134..cdde727e82 100644 --- a/frontend/src/lib/components/AddUser.svelte +++ b/frontend/src/lib/components/AddUser.svelte @@ -2,11 +2,13 @@ import { createEventDispatcher } from 'svelte' import { globalEmailInvite, superadmin, workspaceStore } from '$lib/stores' import { UserService, WorkspaceService } from '$lib/gen' - import { Button, ToggleButton, ToggleButtonGroup } from './common' - import Tooltip from './Tooltip.svelte' + import { Button, Popup } from './common' import { sendUserToast } from '$lib/toast' import { isCloudHosted } from '$lib/cloud' import { goto } from '$app/navigation' + import ToggleButtonGroup from './common/toggleButton-v2/ToggleButtonGroup.svelte' + import ToggleButton from './common/toggleButton-v2/ToggleButton.svelte' + import { UserPlus } from 'lucide-svelte' const dispatch = createEventDispatcher() @@ -62,39 +64,64 @@ let selected: 'operator' | 'author' | 'admin' = 'author' -
- - - - Operator An operator can only execute and view scripts/flows/apps from your workspace, and only - those that he has visibility on. + + + +
+ Add a new user + + Email + + + Username + + + Role + + + + + + -
+ Add + +
+ diff --git a/frontend/src/lib/components/EditorTheme.svelte b/frontend/src/lib/components/EditorTheme.svelte index 99c27db128..c83f96fa5a 100644 --- a/frontend/src/lib/components/EditorTheme.svelte +++ b/frontend/src/lib/components/EditorTheme.svelte @@ -16,7 +16,7 @@ token: '' }, { - foreground: '616e88', + foreground: '808b9f', token: 'comment' }, { diff --git a/frontend/src/lib/components/FolderEditor.svelte b/frontend/src/lib/components/FolderEditor.svelte index 2b0246a73e..450e5d2b03 100644 --- a/frontend/src/lib/components/FolderEditor.svelte +++ b/frontend/src/lib/components/FolderEditor.svelte @@ -9,12 +9,13 @@ } from '$lib/gen' import AutoComplete from 'simple-svelte-autocomplete' import TableCustom from './TableCustom.svelte' - import { Alert, Button, Drawer, DrawerContent, ToggleButton, ToggleButtonGroup } from './common' + import { Alert, Button, Drawer, DrawerContent } 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' + import ToggleButtonGroup from './common/toggleButton-v2/ToggleButtonGroup.svelte' + import ToggleButton from './common/toggleButton-v2/ToggleButton.svelte' export let name: string let can_write = false @@ -152,8 +153,8 @@
(ownerItem = '')}> - User - Group + +
@@ -271,24 +272,28 @@ loadFolder() }} > - Viewer A viewer of a folder has read-only access to all the elements - (scripts/flows/apps/schedules/resources/variables) inside the folder - Writer A writer of a folder has read AND write access to all the elements - (scripts/flows/apps/schedules/resources/variables) inside the folder - Admin An admin of a folder has read AND write access to all the elements inside - the folders and can manage the permissions as well as add new admins + + + + +
{:else} diff --git a/frontend/src/lib/components/FolderInfo.svelte b/frontend/src/lib/components/FolderInfo.svelte index 9259b6eb21..90763b28bd 100644 --- a/frontend/src/lib/components/FolderInfo.svelte +++ b/frontend/src/lib/components/FolderInfo.svelte @@ -4,12 +4,12 @@ export let members: string[] -
({members.length}) -
{members?.join(', ')}
- {members?.join(', ')}
+ +
+ ({members.length}) +
+ {members?.join(', ')} +
+
+ {members?.join(', ')} +
diff --git a/frontend/src/lib/components/FolderUsageInfo.svelte b/frontend/src/lib/components/FolderUsageInfo.svelte index ed62fad611..7365914f98 100644 --- a/frontend/src/lib/components/FolderUsageInfo.svelte +++ b/frontend/src/lib/components/FolderUsageInfo.svelte @@ -1,6 +1,7 @@ -
- - - Operator An operator can only execute and view scripts/flows/apps from your workspace, and only - those that he has visibility on. + + + + +
+ + + + + + + + + -
+ Invite + +
+ diff --git a/frontend/src/lib/components/PageHeader.svelte b/frontend/src/lib/components/PageHeader.svelte index 79c673278a..00996dedfe 100644 --- a/frontend/src/lib/components/PageHeader.svelte +++ b/frontend/src/lib/components/PageHeader.svelte @@ -7,19 +7,23 @@ export let primary: boolean = true -
+
{#if primary} - -

{title}

+ +

{title}

{#if tooltip != '' || documentationLink} - {tooltip} + + {tooltip} + {/if}
{:else} -

{title}

+

{title}

{#if tooltip != '' || documentationLink} - {tooltip} + + {tooltip} + {/if}
{/if} diff --git a/frontend/src/lib/components/ShareModal.svelte b/frontend/src/lib/components/ShareModal.svelte index bce73f97f0..758237c4bf 100644 --- a/frontend/src/lib/components/ShareModal.svelte +++ b/frontend/src/lib/components/ShareModal.svelte @@ -6,11 +6,13 @@ import { createEventDispatcher } from 'svelte' import AutoComplete from 'simple-svelte-autocomplete' import { userStore, workspaceStore } from '$lib/stores' - import { Alert, Button, Drawer, ToggleButton, ToggleButtonGroup } from './common' + import { Alert, Button, Drawer } from './common' import DrawerContent from './common/drawer/DrawerContent.svelte' import Tooltip from './Tooltip.svelte' import { sendUserToast } from '$lib/toast' import { isOwner } from '$lib/utils' + import ToggleButtonGroup from './common/toggleButton-v2/ToggleButtonGroup.svelte' + import ToggleButton from './common/toggleButton-v2/ToggleButton.svelte' const dispatch = createEventDispatcher() @@ -117,8 +119,8 @@
(owner = '')}> - User - Group + +
{#key ownerKind} @@ -155,9 +157,8 @@ loadAcls() }} > - Viewer - Writer + +
{:else}{write}{/if} - import { classNames } from '$lib/utils' - import { getContext } from 'svelte' - import Button from '../button/Button.svelte' - import type { ToggleButtonContext } from './ToggleButtonGroup.svelte' - - export let value: any - export let position: 'left' | 'center' | 'right' - export let light = false - export let title: string | undefined = undefined - const { select, selected } = getContext('ToggleButtonGroup') - - - diff --git a/frontend/src/lib/components/common/toggleButton/ToggleButtonGroup.svelte b/frontend/src/lib/components/common/toggleButton/ToggleButtonGroup.svelte deleted file mode 100644 index 7e474f0a33..0000000000 --- a/frontend/src/lib/components/common/toggleButton/ToggleButtonGroup.svelte +++ /dev/null @@ -1,36 +0,0 @@ - - - - -
- -
diff --git a/frontend/src/lib/components/settings/WorkspaceUserSettings.svelte b/frontend/src/lib/components/settings/WorkspaceUserSettings.svelte new file mode 100644 index 0000000000..424295aa64 --- /dev/null +++ b/frontend/src/lib/components/settings/WorkspaceUserSettings.svelte @@ -0,0 +1,372 @@ + + + x.email + ' ' + x.name + ' ' + x.company} +/> + +
+ + +
+ + + + +
+
+ +
+ 30} + loadMore={30} + on:loadMore={() => { + nbDisplayed += 30 + }} + > + + + Email + Username + + + Executions (5w) + + An execution is calculated as 1 for any runs of scripts + 1 for each seconds above the + first one + + + Status + Role + + Actions + + + + + {#if filteredUsers} + {#each filteredUsers.slice(0, nbDisplayed) as { email, username, is_admin, operator, usage, disabled } (email)} + + {email} + {username} + {usage?.executions} + +
+ {#if disabled} + Disabled + {:else} + Enabled + {/if} +
+
+ +
+ { + if (is_admin && email == $userStore?.email && e.detail != 'admin') { + sendUserToast( + 'Admins cannot be demoted by themselves, ask another admin to demote you', + true + ) + e.preventDefault() + listUsers() + return + } + const body = + e.detail == 'admin' + ? { is_admin: true, operator: false } + : e.detail == 'operator' + ? { is_admin: false, operator: true } + : { is_admin: false, operator: false } + await UserService.updateUser({ + workspace: $workspaceStore ?? '', + username, + requestBody: body + }) + listUsers() + }} + > + + + + + + +
+
+ +
+ + + +
+
+ + {/each} + {:else} + {#each new Array(6) as _} + + + + + + {/each} + {/if} + +
+
+ + + + +
+ + + + Email + Role + Actions + + + + {#if invites?.length > 0} + {#each invites as { email, is_admin, operator }} + + {email} + + {#if operator} + Operator + {:else if is_admin} + Admin + {:else} + Author + {/if} + + + + + + {/each} + {:else} + + +
No invites yet
+ + + {/if} + +
+
+ +
+ +
+ {#if auto_invite_domain != domain} +
+ +
+ {/if} + {#if auto_invite_domain} +
+ { + await removeAllInvitesFromDomain() + await WorkspaceService.editAutoInvite({ + workspace: $workspaceStore ?? '', + requestBody: { operator: e.detail } + }) + loadSettings() + listInvites() + }} + /> +
+ +
+
+ {/if} +
+{#if !allowedAutoDomain} +
{domain} domain not allowed for auto-invite
+{/if} diff --git a/frontend/src/lib/components/table/Body.svelte b/frontend/src/lib/components/table/Body.svelte new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frontend/src/lib/components/table/Cell.svelte b/frontend/src/lib/components/table/Cell.svelte new file mode 100644 index 0000000000..1cdefe5123 --- /dev/null +++ b/frontend/src/lib/components/table/Cell.svelte @@ -0,0 +1,25 @@ + + + + + diff --git a/frontend/src/lib/components/table/DataTable.svelte b/frontend/src/lib/components/table/DataTable.svelte new file mode 100644 index 0000000000..525370e37d --- /dev/null +++ b/frontend/src/lib/components/table/DataTable.svelte @@ -0,0 +1,53 @@ + + +
+ + +
+ {#if paginated} +
+ Page: {currentPage} + + {#if showNext} + + {/if} +
+ {:else if shouldLoadMore} +
+ +
+ {/if} +
diff --git a/frontend/src/lib/components/table/Head.svelte b/frontend/src/lib/components/table/Head.svelte new file mode 100644 index 0000000000..b0ed0cb0ad --- /dev/null +++ b/frontend/src/lib/components/table/Head.svelte @@ -0,0 +1,3 @@ + + + diff --git a/frontend/src/lib/components/table/Row.svelte b/frontend/src/lib/components/table/Row.svelte new file mode 100644 index 0000000000..2fd0d244fa --- /dev/null +++ b/frontend/src/lib/components/table/Row.svelte @@ -0,0 +1,21 @@ + + + { + dispatch('click') + }} +> + + diff --git a/frontend/src/routes/(root)/(logged)/folders/+page.svelte b/frontend/src/routes/(root)/(logged)/folders/+page.svelte index a50aa11162..94024865fb 100644 --- a/frontend/src/routes/(root)/(logged)/folders/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/folders/+page.svelte @@ -6,14 +6,16 @@ import Dropdown from '$lib/components/Dropdown.svelte' import FolderEditor from '$lib/components/FolderEditor.svelte' import PageHeader from '$lib/components/PageHeader.svelte' - import SharedBadge from '$lib/components/SharedBadge.svelte' - import TableCustom from '$lib/components/TableCustom.svelte' import { userStore, workspaceStore } from '$lib/stores' import { faEdit, faPlus, faTrash } from '@fortawesome/free-solid-svg-icons' import { Button, Drawer, DrawerContent, Skeleton } from '$lib/components/common' import FolderInfo from '$lib/components/FolderInfo.svelte' import FolderUsageInfo from '$lib/components/FolderUsageInfo.svelte' import { canWrite } from '$lib/utils' + import DataTable from '$lib/components/table/DataTable.svelte' + import Head from '$lib/components/table/Head.svelte' + import Cell from '$lib/components/table/Cell.svelte' + import Row from '$lib/components/table/Row.svelte' type FolderW = Folder & { canWrite: boolean } @@ -95,19 +97,21 @@
- - - - Name - Scripts - Flows - Apps - Schedules - Variables - Resources - Participants - - + + + + Name + Scripts + Flows + Apps + Schedules + Variables + Resources + Participants + + + + {#if folders === undefined} {#each new Array(4) as _} @@ -119,29 +123,25 @@ {:else} {#if folders.length === 0} - No folders yet, create one! + No folders yet, create one! {/if} {#each folders as { name, extra_perms, owners, canWrite }} - - - - - - { - editFolderName = name - folderDrawer.openDrawer() - }} - >{name} - - + { + editFolderName = name + folderDrawer.openDrawer() + }} + > + + {name} + - - + + - - + + {/each} {/if} - +
diff --git a/frontend/src/routes/(root)/(logged)/runs/[...path]/+page.svelte b/frontend/src/routes/(root)/(logged)/runs/[...path]/+page.svelte index 0e2d06448a..f8cdf3dffd 100644 --- a/frontend/src/routes/(root)/(logged)/runs/[...path]/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/runs/[...path]/+page.svelte @@ -55,7 +55,7 @@ function computeJobKinds(jobKindsCat: string | undefined): string { if (jobKindsCat == 'all') { - return `${CompletedJob.job_kind.SCRIPT},${CompletedJob.job_kind.FLOW},${CompletedJob.job_kind.DEPENDENCIES},${CompletedJob.job_kind.PREVIEW},${CompletedJob.job_kind.FLOWPREVIEW},${CompletedJob.job_kind.SCRIPT_HUB},${CompletedJob.job_kind.HTTP}` + return `${CompletedJob.job_kind.SCRIPT},${CompletedJob.job_kind.FLOW},${CompletedJob.job_kind.DEPENDENCIES},${CompletedJob.job_kind.PREVIEW},${CompletedJob.job_kind.FLOWPREVIEW},${CompletedJob.job_kind.SCRIPT_HUB}` } else if (jobKindsCat == 'dependencies') { return CompletedJob.job_kind.DEPENDENCIES } else if (jobKindsCat == 'previews') { diff --git a/frontend/src/routes/(root)/(logged)/workers/+page.svelte b/frontend/src/routes/(root)/(logged)/workers/+page.svelte index a5a86422df..9f260fd3c3 100644 --- a/frontend/src/routes/(root)/(logged)/workers/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/workers/+page.svelte @@ -3,7 +3,9 @@ import { Skeleton } from '$lib/components/common' import Badge from '$lib/components/common/badge/Badge.svelte' import PageHeader from '$lib/components/PageHeader.svelte' - import TableCustom from '$lib/components/TableCustom.svelte' + import Cell from '$lib/components/table/Cell.svelte' + import DataTable from '$lib/components/table/DataTable.svelte' + import Head from '$lib/components/table/Head.svelte' import Tooltip from '$lib/components/Tooltip.svelte' import { WorkerService, type WorkerPing } from '$lib/gen' import { sendUserToast } from '$lib/toast' @@ -64,46 +66,58 @@ {#if groupedWorkers.length == 0}

No workers seems to be available

{/if} - {#each groupedWorkers as [section, workers]} -
- Instance: {section} | IP: - {workers[0].ip} -
- - - Worker - Custom Tags If defined, the workers only pull jobs with the same corresponding tag - Last ping - Worker start - Nb of jobs executed - Liveness + + + + Worker + +
+ Custom Tags + + If defined, the workers only pull jobs with the same corresponding tag + +
+
+ Last ping + Worker start + Nb of jobs executed + Liveness - + + + {#each groupedWorkers as [section, workers]} + + + Instance: {section} + IP: {workers[0].ip} + + + {#if workers} {#each workers as { worker, custom_tags, last_ping, started_at, jobs_executed }} - {worker} - {custom_tags?.join(', ') ?? ''} - {last_ping != undefined ? last_ping + timeSinceLastPing : -1}s ago - {displayDate(started_at)} - {jobs_executed} - {last_ping != undefined ? (last_ping < 60 ? 'Alive' : 'Dead') : 'Unknown'} + {worker} + {custom_tags?.join(', ') ?? ''} + {last_ping != undefined ? last_ping + timeSinceLastPing : -1}s ago + {displayDate(started_at)} + {jobs_executed} + + + {last_ping != undefined ? (last_ping < 60 ? 'Alive' : 'Dead') : 'Unknown'} + + {/each} {/if} - -
- {/each} + {/each} + + {:else}
{#each new Array(4) as _} diff --git a/frontend/src/routes/(root)/(logged)/workspace_settings/+page.svelte b/frontend/src/routes/(root)/(logged)/workspace_settings/+page.svelte index 715c628dd9..12be09b11f 100644 --- a/frontend/src/routes/(root)/(logged)/workspace_settings/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/workspace_settings/+page.svelte @@ -2,30 +2,20 @@ import { goto } from '$app/navigation' import { page } from '$app/stores' import { isCloudHosted } from '$lib/cloud' - import AddUser from '$lib/components/AddUser.svelte' import CenteredPage from '$lib/components/CenteredPage.svelte' - import { Alert, Badge, Button, Skeleton, Tab, Tabs } from '$lib/components/common' - import ToggleButton from '$lib/components/common/toggleButton/ToggleButton.svelte' - import ToggleButtonGroup from '$lib/components/common/toggleButton/ToggleButtonGroup.svelte' + import { Alert, Badge, Button, Tab, Tabs } from '$lib/components/common' + import DeployToSetting from '$lib/components/DeployToSetting.svelte' - import InviteUser from '$lib/components/InviteUser.svelte' import PageHeader from '$lib/components/PageHeader.svelte' import ResourcePicker from '$lib/components/ResourcePicker.svelte' import ScriptPicker from '$lib/components/ScriptPicker.svelte' - import SearchItems from '$lib/components/SearchItems.svelte' import Slider from '$lib/components/Slider.svelte' - import TableCustom from '$lib/components/TableCustom.svelte' - import Toggle from '$lib/components/Toggle.svelte' + import Tooltip from '$lib/components/Tooltip.svelte' + import WorkspaceUserSettings from '$lib/components/settings/WorkspaceUserSettings.svelte' import { WORKSPACE_SHOW_SLACK_CMD, WORKSPACE_SHOW_WEBHOOK_CLI_SYNC } from '$lib/consts' import type { User } from '$lib/gen' - import { - OauthService, - Script, - UserService, - WorkspaceService, - type WorkspaceInvite - } from '$lib/gen' + import { OauthService, Script, WorkspaceService } from '$lib/gen' import { enterpriseLicense, existsOpenaiResourcePath, @@ -38,19 +28,14 @@ import { capitalize, setQueryWithoutLoad } from '$lib/utils' import { faSlack } from '@fortawesome/free-brands-svg-icons' import { faBarsStaggered, faExternalLink, faScroll } from '@fortawesome/free-solid-svg-icons' + import { Slack } from 'lucide-svelte' let users: User[] | undefined = undefined - let invites: WorkspaceInvite[] = [] - let filteredUsers: User[] | undefined = undefined - let userFilter = '' let initialPath: string let scriptPath: string let team_name: string | undefined - let auto_invite_domain: string | undefined let itemKind: 'flow' | 'script' = 'flow' - let operatorOnly: boolean | undefined = undefined let premium_info: { premium: boolean; usage?: number } | undefined = undefined - let nbDisplayed = 30 let plan: string | undefined = undefined let customer_id: string | undefined = undefined let webhook: string | undefined = undefined @@ -151,8 +136,7 @@ async function loadSettings(): Promise { const settings = await WorkspaceService.getSettings({ workspace: $workspaceStore! }) team_name = settings.slack_name - auto_invite_domain = settings.auto_invite_domain - operatorOnly = settings.auto_invite_operator + if (settings.slack_command_script) { itemKind = settings.slack_command_script.split('/')[0] as 'flow' | 'script' } @@ -167,54 +151,19 @@ errorHandlerInitialPath = errorHandlerScriptPath } - async function listUsers(): Promise { - users = await UserService.listUsers({ workspace: $workspaceStore! }) - } - - async function listInvites(): Promise { - invites = await WorkspaceService.listPendingInvites({ workspace: $workspaceStore! }) - } - - let allowedAutoDomain = false - - async function getDisallowedAutoDomain() { - allowedAutoDomain = await WorkspaceService.isDomainAllowed() - } - async function loadPremiumInfo() { if (isCloudHosted()) { premium_info = await WorkspaceService.getPremiumInfo({ workspace: $workspaceStore! }) } } - $: domain = $userStore?.email.split('@')[1] $: { if ($workspaceStore) { - getDisallowedAutoDomain() - listUsers() - listInvites() loadSettings() loadPremiumInfo() } } - async function removeAllInvitesFromDomain() { - await Promise.all( - invites - .filter((x) => x.email.endsWith('@' + auto_invite_domain ?? '')) - .map(({ email, is_admin, operator }) => - WorkspaceService.deleteInvite({ - workspace: $workspaceStore ?? '', - requestBody: { - email, - is_admin, - operator - } - }) - ) - ) - } - async function editErrorHandler() { errorHandlerInitialPath = errorHandlerScriptPath if (errorHandlerScriptPath) { @@ -254,16 +203,9 @@ } - x.email + ' ' + x.name + ' ' + x.company} -/> - {#if $userStore?.is_admin || $superadmin} - +
- +
Users
- +
Dev/Staging/Prod
{#if WORKSPACE_SHOW_SLACK_CMD} - +
Slack Command
{/if} {#if isCloudHosted()} - +
Premium Plans
{/if} - +
Delete Workspace
{#if WORKSPACE_SHOW_WEBHOOK_CLI_SYNC} - +
Webhook
{/if} - +
Error Handler
- +
Windmill AI
{#if tab == 'users'} - - - - -
- -
-
- - - email - username - executions (5w) An execution is calculated as 1 for any runs of scripts + 1 for each seconds above - the first one - - - - - - - {#if filteredUsers} - {#each filteredUsers.slice(0, nbDisplayed) as { email, username, is_admin, operator, usage, disabled } (email)} - - {email} - {username} - {usage?.executions} -
{#if disabled} - disabled - {/if}
- -
- { - if (is_admin && email == $userStore?.email && e.detail != 'admin') { - sendUserToast( - 'Admins cannot be demoted by themselves, ask another admin to demote you', - true - ) - e.preventDefault() - listUsers() - return - } - const body = - e.detail == 'admin' - ? { is_admin: true, operator: false } - : e.detail == 'operator' - ? { is_admin: false, operator: true } - : { is_admin: false, operator: false } - await UserService.updateUser({ - workspace: $workspaceStore ?? '', - username, - requestBody: body - }) - listUsers() - }} - > - Operator An operator can only execute and view scripts/flows/apps from your - workspace, and only those that he has visibility on. - Author An Author can execute and view scripts/flows/apps, but he can also - create new ones. - AdminAn admin has full control over a specific Windmill workspace, including - the ability to manage users, edit entities, and control permissions - within the workspace. - -
- - -
- - | - -
- - - {/each} - {#if filteredUsers?.length > 50} - {nbDisplayed} items out of {filteredUsers.length} - - {/if} - {:else} - {#each new Array(6) as _} - - {#each new Array(4) as _} - - - - {/each} - - {/each} - {/if} - -
-
- - - - -
- - - email - role - - - - {#each invites as { email, is_admin, operator }} - - {email} - {#if operator}operator{:else if is_admin}admin{/if} - - - - - {/each} - - -
- -
- -
- {#if auto_invite_domain != domain} -
- -
- {/if} - {#if auto_invite_domain} -
- { - await removeAllInvitesFromDomain() - await WorkspaceService.editAutoInvite({ - workspace: $workspaceStore ?? '', - requestBody: { operator: e.detail } - }) - loadSettings() - listInvites() - }} - /> -
- -
-
- {/if} -
- {#if !allowedAutoDomain} -
{domain} domain not allowed for auto-invite
- {/if} + {:else if tab == 'deploy_to'}
{/if} -
+
{#if premium_info?.premium}
{#if plan} @@ -678,7 +374,7 @@
{#each Object.entries(plans) as [planTitle, planDesc]} -
+

{planTitle}

    {#each planDesc as item} @@ -721,85 +417,94 @@
{/if} {:else if tab == 'slack'} -
Connect your windmill workspace to your slack workspace to trigger a script or a flow - with a '/windmill' command.
-

- Status: {#if team_name}Connected to slack workspace {team_name}{:else}Not - connected{/if} -

- {#if team_name} -
- - - +
+
+
Send commands from slack
+
+ Connect your windmill workspace to your slack workspace to trigger a script or a flow + with a '/windmill' command. +
- {:else} -
- + + {#if team_name} +
+ + + +
+ {:else} +
+ + Not connnected +
+ {/if} +
+
+
+ Script or flow to run on /windmill command +
+
+ {#if !team_name} +
+ {/if} +
- {/if} -

- Script or flow to run on /windmill command - - The script or flow to be triggered when the `/windmill` command is invoked. - -

- -
-
- Pick a script or flow meant to be triggered when the `/windmill` command is invoked. Upon - connection, templates for a script - and flow are available. -

+
+ Pick a script or flow meant to be triggered when the `/windmill` command is invoked. Upon + connection, templates for a script + and flow are available. - The script or flow chosen is passed the parameters `response_url: string` and `text: string` - respectively the url to reply directly to the trigger and the text of the command. +

-

+ The script or flow chosen is passed the parameters `response_url: string` and `text: + string` respectively the url to reply directly to the trigger and the text of the command. - The script or flow is permissioned as group "slack" that will be automatically created after - connection to Slack. +

-

+ The script or flow is permissioned as group "slack" that will be automatically created + after connection to Slack. - See more on documentation. +

+ + See more on documentation. +
{:else if tab == 'export_delete'} @@ -878,9 +583,7 @@
- +
{:else if tab == 'error_handler'} @@ -894,7 +597,7 @@ />
-
+
The following args will be passed to the error handler:
  • path: The path of the script or flow that errored.