From 35e95c6f64f857d69ad293b1270bf727618b59da Mon Sep 17 00:00:00 2001 From: Alex Petric Date: Wed, 5 Feb 2025 11:30:39 -0500 Subject: [PATCH] teams workspace settings frontend --- .../lib/components/ConnectionSection.svelte | 187 ++++++++++++++++ .../(logged)/workspace_settings/+page.svelte | 210 +++++++++--------- 2 files changed, 288 insertions(+), 109 deletions(-) create mode 100644 frontend/src/lib/components/ConnectionSection.svelte diff --git a/frontend/src/lib/components/ConnectionSection.svelte b/frontend/src/lib/components/ConnectionSection.svelte new file mode 100644 index 0000000000..8fbbaa6cd5 --- /dev/null +++ b/frontend/src/lib/components/ConnectionSection.svelte @@ -0,0 +1,187 @@ + + +
+
Connect Workspace to {platform.charAt(0).toUpperCase() + platform.slice(1)}
+ + Connect your Windmill workspace to your {platform} workspace to trigger a script or a flow with a + '/windmill' command. + +
+ +{#if teamName} +
+
+ + {#if display_name} + Connected to Team '{display_name}' + {/if} +
+ + +
+{:else} +
+ {#if platform === 'teams'} + +
+ +
+
+ +
+ {:else} + + {/if} + Not connected +
+{/if} + +
+
Script or flow to run on /windmill command
+
+ {#if !teamName} +
+ {/if} + +
+ +
+ 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. + +

+ + It can take additionally the following args: channel_id, user_name, user_id, command, + trigger_id, api_app_id + +

+ + + The script or flow is permissioned as group "{platform}" that will be automatically created + after connection to {platform.charAt(0).toUpperCase() + platform.slice(1)}. + + +

+ + See more on + documentation. +
+
diff --git a/frontend/src/routes/(root)/(logged)/workspace_settings/+page.svelte b/frontend/src/routes/(root)/(logged)/workspace_settings/+page.svelte index 5c5e8827ff..749ab13091 100644 --- a/frontend/src/routes/(root)/(logged)/workspace_settings/+page.svelte +++ b/frontend/src/routes/(root)/(logged)/workspace_settings/+page.svelte @@ -3,7 +3,7 @@ import { page } from '$app/stores' import { isCloudHosted } from '$lib/cloud' import CenteredPage from '$lib/components/CenteredPage.svelte' - import { Alert, Badge, Button, Tab, Tabs } from '$lib/components/common' + import { Alert, Button, Tab, Tabs } from '$lib/components/common' import DeployToSetting from '$lib/components/DeployToSetting.svelte' import ErrorOrRecoveryHandler from '$lib/components/ErrorOrRecoveryHandler.svelte' @@ -29,14 +29,11 @@ userStore, usersWorkspaceStore, workspaceStore, - hubBaseUrlStore, isCriticalAlertsUIOpen } from '$lib/stores' import { sendUserToast } from '$lib/toast' import { emptyString, tryEvery } from '$lib/utils' import { - Code2, - Slack, XCircle, RotateCw, CheckCircle2, @@ -46,7 +43,6 @@ Save, ExternalLink } from 'lucide-svelte' - import BarsStaggered from '$lib/components/icons/BarsStaggered.svelte' import PremiumInfo from '$lib/components/settings/PremiumInfo.svelte' import Toggle from '$lib/components/Toggle.svelte' @@ -68,6 +64,7 @@ import ToggleButton from '$lib/components/common/toggleButton-v2/ToggleButton.svelte' import type { AiProviderTypes } from '$lib/components/copilot/lib' import Description from '$lib/components/Description.svelte' + import ConnectionSection from '$lib/components/ConnectionSection.svelte' type GitSyncTypeMap = { scripts: boolean @@ -97,9 +94,13 @@ let s3FileViewer: S3FilePicker - let initialPath: string - let scriptPath: string - let team_name: string | undefined + let slackInitialPath: string + let slackScriptPath: string + let teamsInitialPath: string + let teamsScriptPath: string + let slack_team_name: string | undefined + let teams_team_id: string | undefined + let teams_team_name: string | undefined let itemKind: 'flow' | 'script' = 'flow' let plan: string | undefined = undefined let customer_id: string | undefined = undefined @@ -144,6 +145,7 @@ let encryptionKeyRegex = /^[a-zA-Z0-9]{64}$/ let codeCompletionEnabled: boolean = false let selected: AiProviderTypes = 'openai' + let slack_tabs: 'slack_commands' | 'teams_commands' = 'slack_commands' let tab = ($page.url.searchParams.get('tab') as | 'users' @@ -157,23 +159,44 @@ const latestGitSyncHubScript = hubPaths.gitSync - async function editSlackCommand(): Promise { - initialPath = scriptPath + async function editWorkspaceCommand(platform: 'slack' | 'teams'): Promise { + if (platform === 'slack') { + if (slackInitialPath === slackScriptPath) return + slackInitialPath = slackScriptPath + } else { + if (teamsInitialPath === teamsScriptPath) return + teamsInitialPath = teamsScriptPath + } + + let scriptPath = platform === 'slack' ? slackScriptPath : teamsScriptPath + let commandScriptKey = platform === 'slack' ? 'slack_command_script' : 'teams_command_script' + let updateCommandScript = platform === 'slack' ? WorkspaceService.editSlackCommand : WorkspaceService.editTeamsCommand + if (scriptPath) { - await WorkspaceService.editSlackCommand({ + console.log('editWorkspaceCommand', scriptPath) + console.log('itemKind', itemKind) + await updateCommandScript({ workspace: $workspaceStore!, - requestBody: { slack_command_script: `${itemKind}/${scriptPath}` } - }) - sendUserToast(`slack command script set to ${scriptPath}`) + requestBody: { [commandScriptKey]: `${itemKind}/${scriptPath}` } + }); + sendUserToast(`${platform} command script set to ${scriptPath}`); } else { await WorkspaceService.editSlackCommand({ workspace: $workspaceStore!, - requestBody: { slack_command_script: undefined } - }) - sendUserToast(`slack command script removed`) + requestBody: { [commandScriptKey]: undefined } + }); + sendUserToast(`${platform} command script removed`); } } + async function editSlackCommand(): Promise { + await editWorkspaceCommand('slack'); + } + + async function editTeamsCommand(): Promise { + await editWorkspaceCommand('teams'); + } + async function editWebhook(): Promise { // in JS, an empty string is also falsy if (webhook) { @@ -384,13 +407,19 @@ async function loadSettings(): Promise { const settings = await WorkspaceService.getSettings({ workspace: $workspaceStore! }) - team_name = settings.slack_name - + slack_team_name = settings.slack_name + teams_team_id = settings.teams_team_id + teams_team_name = settings.teams_team_name if (settings.slack_command_script) { itemKind = settings.slack_command_script.split('/')[0] as 'flow' | 'script' } - scriptPath = (settings.slack_command_script ?? '').split('/').slice(1).join('/') - initialPath = scriptPath + if (settings.teams_command_script) { + itemKind = settings.teams_command_script.split('/')[0] as 'flow' | 'script' + } + slackScriptPath = (settings.slack_command_script ?? '').split('/').slice(1).join('/') + teamsScriptPath = (settings.teams_command_script ?? '').split('/').slice(1).join('/') + slackInitialPath = slackScriptPath + teamsInitialPath = teamsScriptPath plan = settings.plan customer_id = settings.customer_id workspaceToDeployTo = settings.deploy_to @@ -665,7 +694,7 @@ {#if WORKSPACE_SHOW_SLACK_CMD} -
Slack
+
Slack / Teams
{/if} {#if isCloudHosted()} @@ -726,99 +755,62 @@ {:else if tab == 'slack'}
-
Connect Workspace to Slack
+
Workspace connections to Slack and Teams
- Connect your Windmill workspace to your Slack workspace to trigger a script or a flow - with a '/windmill' command or to configure Slack error handlers. + With workspace connections, you can trigger scripts or flows with a '/windmill' command with your Slack or Teams bot.
- {#if team_name} -
- - - -
- {:else} -
- - Not connnected -
- {/if} -
-
-
- Script or flow to run on /windmill command -
-
- {#if !team_name} -
- {/if} - + +
Slack
+
+ +
Teams
+
+ + + {#if slack_tabs === 'slack_commands'} + { + await OauthService.disconnectSlack({ workspace: $workspaceStore ?? '' }); + loadSettings(); + sendUserToast('Disconnected Slack'); + }} + onSelect={editSlackCommand} + connectHref="{base}/api/oauth/connect_slack" + createScriptHref="{base}/scripts/add?hub=hub%2F314%2Fslack%2Fexample_of_responding_to_a_slack_command_slack" + createFlowHref="{base}/flows/add?hub=28" + documentationLink="https://www.windmill.dev/docs/integrations/slack" + onLoadSettings={loadSettings} + display_name={slack_team_name} /> -
- -
- 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. - -

- - It can take additionally the following args: channel_id, user_name, user_id, command, - trigger_id, api_app_id - -

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

- - See more on documentation. -
+ {:else if slack_tabs === 'teams_commands'} + { + await OauthService.disconnectTeams({ workspace: $workspaceStore ?? '' }); + loadSettings(); + sendUserToast('Disconnected Teams'); + }} + onSelect={editTeamsCommand} + connectHref={undefined} + createScriptHref="{base}/scripts/add?hub=hub%2F314%2Fteams%2Fexample_of_responding_to_a_teams_command" + createFlowHref="{base}/flows/add?hub=28" + documentationLink="https://www.windmill.dev/docs/integrations/teams" + onLoadSettings={loadSettings} + display_name={teams_team_name} + /> + {/if}
{:else if tab == 'general'}