Files
windmill/frontend/src/lib/components/instanceSettings/TeamsChannelCard.svelte
T
GuilhemandAlexander Petric 210b8285d4 fix(frontend): settings redesign (#7406)
* improve collapsible link

* do not show superadmin ws link when already in it

* improve OAuth UI

* sso/oauth instance settings ui

* refactor instance settings alerts WIP

* Indexer and Oauth to brand guidelines

* refactor ws error handler page

* Create a tab SMTP in the Instance Settings

* Ractivity isssue fix for tabs

* nit

* Add smtp settings status in Error handler

* Add smtp configuration status

* Display teams connection status for instance alerts

* nit

* Add critical alerts description

* nit

* nit

* improve ee display

* nit

* nit

* fix typo

* nit

* restore vit config

---------

Co-authored-by: Alexander Petric <alex@windmill.dev>
2025-12-19 20:33:03 +00:00

174 lines
4.4 KiB
Svelte

<script lang="ts">
import { X, Plus } from 'lucide-svelte'
import MSTeamsIcon from '$lib/components/icons/MSTeamsIcon.svelte'
import IntegrationCard from './IntegrationCard.svelte'
import TeamSelector from '../TeamSelector.svelte'
import ChannelSelector from '../ChannelSelector.svelte'
import { Button } from '$lib/components/common'
import { sendUserToast } from '$lib/toast'
import TeamsConnectionStatus from '../common/teams/TeamsConnectionStatus.svelte'
interface TeamsChannelEntry {
teams_channel?: {
team_id: string
team_name: string
channel_id?: string
channel_name?: string
}
}
interface Props {
channels: TeamsChannelEntry[]
disabled?: boolean
onAddChannel: () => void
onRemoveChannel: (index: number) => void
onTeamChange: (
teamItem: { team_id: string; team_name: string } | undefined,
channel: TeamsChannelEntry
) => void
onChannelChange: (
channelItem: { channel_id?: string; channel_name?: string } | undefined,
channel: TeamsChannelEntry
) => void
findChannelIndex: (channel: TeamsChannelEntry) => number
isTeamsConnected?: boolean
class?: string
style?: string
}
let {
channels,
disabled = false,
onAddChannel,
onRemoveChannel,
onTeamChange,
onChannelChange,
findChannelIndex,
isTeamsConnected,
class: clazz,
style
}: Props = $props()
function handleRemoveChannel(channel: TeamsChannelEntry) {
const index = findChannelIndex(channel)
if (index !== -1) {
onRemoveChannel(index)
}
}
</script>
{#if channels.length > 0}
<!-- Connected Teams Card -->
<IntegrationCard
title="Microsoft Teams"
icon={MSTeamsIcon}
isPlaceholder={false}
class={clazz}
{style}
>
{#snippet actions()}
<TeamsConnectionStatus isConnected={isTeamsConnected} mode="instance" />
{/snippet}
{#snippet children()}
{#if channels.length > 0}
<span class="text-xs text-secondary"> Channels to send alerts to. </span>
{/if}
<!-- Channel Configuration -->
{#if channels.length > 0}
<!-- Column Headers -->
<div class="flex items-center gap-2 w-full">
<div class="flex flex-row gap-2 flex-1">
<div class="w-44">
<span class="block text-xs font-normal text-secondary">Team</span>
</div>
<div class="flex-1">
<span class="block text-xs font-normal text-secondary">Channel</span>
</div>
</div>
<div class="w-6"></div>
<!-- Space for remove button -->
</div>
{/if}
<div class="space-y-2">
{#each channels as channel}
{@const currentTeam = channel?.teams_channel
? {
team_id: channel.teams_channel.team_id,
team_name: channel.teams_channel.team_name
}
: undefined}
{@const currentChannel = channel?.teams_channel?.channel_id
? {
channel_id: channel.teams_channel.channel_id,
channel_name: channel.teams_channel.channel_name
}
: undefined}
<div class="flex items-start gap-2 w-full">
<div class="flex flex-row gap-2 flex-1">
<TeamSelector
containerClass="w-44"
minWidth="140px"
showRefreshButton={false}
selectedTeam={currentTeam}
onSelectedTeamChange={(team) => onTeamChange(team, channel)}
{disabled}
/>
{#if channel?.teams_channel?.team_id}
<ChannelSelector
containerClass="flex-1"
placeholder="Search channels"
teamId={channel.teams_channel.team_id}
selectedChannel={currentChannel}
onSelectedChannelChange={(channelItem) => onChannelChange(channelItem, channel)}
{disabled}
onError={(e) => sendUserToast('Failed to load channels: ' + e.message, true)}
/>
{/if}
</div>
<Button
onclick={() => handleRemoveChannel(channel)}
title="Remove channel"
{disabled}
startIcon={{ icon: X }}
iconOnly
unifiedSize="md"
variant="subtle"
destructive
></Button>
</div>
{/each}
</div>
<!-- Add Channel Button -->
<div class="flex justify-start">
<Button
variant="default"
size="xs"
onclick={onAddChannel}
btnClasses="text-xs flex items-center gap-2"
{disabled}
>
<Plus size={14} />
Add Teams channel
</Button>
</div>
{/snippet}
</IntegrationCard>
{:else}
<!-- Placeholder Card -->
<IntegrationCard
title="Microsoft Teams"
icon={MSTeamsIcon}
isPlaceholder={true}
onAdd={onAddChannel}
class={clazz}
{style}
>
{#snippet children()}{/snippet}
</IntegrationCard>
{/if}