fix: teams selector svelte5 (#7087)

This commit is contained in:
Alexander Petric
2025-11-07 13:21:55 -05:00
committed by GitHub
parent 180b1523f3
commit 6045f0c406
5 changed files with 115 additions and 98 deletions
@@ -18,6 +18,7 @@
channels?: ChannelItem[]
teamId?: string
onError?: (error: Error) => void
onselectedchannelchange?: (channel: ChannelItem | undefined) => void
}
let {
@@ -28,34 +29,56 @@
minWidth = '160px',
channels = undefined,
teamId,
onError
onError,
onselectedchannelchange
}: Props = $props()
let isFetching = $state(false)
let searchResults = $state<ChannelItem[]>([])
// Only enable search mode if no channels are provided AND teamId is provided
let selectedChannelId = $state<string | undefined>(selectedChannel?.channel_id)
const searchMode = !channels && !!teamId
// Determine which channels to show: provided channels or search results
// In search mode, include the selected channel if it exists
let displayChannels = $derived(() => {
const baseChannels = channels || searchResults;
if (searchMode && selectedChannel && !baseChannels.find(c => c.channel_id === selectedChannel?.channel_id)) {
return [selectedChannel, ...baseChannels];
let displayChannels = $derived.by(() => {
const baseChannels = channels || searchResults
if (selectedChannel && !baseChannels.find(c => c.channel_id === selectedChannel?.channel_id)) {
return [selectedChannel, ...baseChannels]
}
return baseChannels
})
$effect(() => {
const newChannel = selectedChannelId
? displayChannels.find(c => c.channel_id === selectedChannelId)
: undefined
if (newChannel?.channel_id !== selectedChannel?.channel_id) {
selectedChannel = newChannel
}
})
$effect(() => {
if (selectedChannel?.channel_id !== selectedChannelId) {
selectedChannelId = selectedChannel?.channel_id
}
})
let previousChannelId = $state<string | undefined>(undefined)
$effect(() => {
if (selectedChannel?.channel_id !== previousChannelId) {
previousChannelId = selectedChannel?.channel_id
onselectedchannelchange?.(selectedChannel)
}
return baseChannels;
})
// Create separate filter text for search mode
let searchFilterText = $state('')
// Debounced search function
const debouncedSearch = debounce(async (query: string) => {
await searchChannels(query)
}, 500)
// Watch for search filter text changes (only in search mode)
$effect(() => {
if (searchMode) {
if (searchFilterText.length >= 1) {
@@ -97,37 +120,27 @@
{#if searchMode}
<Select
containerStyle={'min-width: ' + minWidth}
items={searchFilterText.length >= 1 || (searchFilterText.length === 0 && selectedChannel) ? displayChannels().filter(channel => channel.channel_id && channel.channel_name).map((channel) => ({
items={displayChannels.filter(channel => channel.channel_id && channel.channel_name).map((channel) => ({
label: channel.channel_name ?? 'Unknown Channel',
value: channel.channel_id ?? ''
})) : []}
}))}
placeholder={isFetching ? "Searching..." : (teamId ? "Search channels..." : "Select a team first")}
clearable
disabled={disabled || isFetching || !teamId}
bind:filterText={searchFilterText}
bind:value={
() => selectedChannel?.channel_id,
(value) => {
selectedChannel = value ? displayChannels().find((channel) => channel.channel_id === value) : undefined
}
}
bind:value={selectedChannelId}
/>
{:else}
<Select
containerStyle={'min-width: ' + minWidth}
items={displayChannels().filter(channel => channel.channel_id && channel.channel_name).map((channel) => ({
items={displayChannels.filter(channel => channel.channel_id && channel.channel_name).map((channel) => ({
label: channel.channel_name ?? 'Unknown Channel',
value: channel.channel_id ?? ''
}))}
{placeholder}
clearable
disabled={disabled || displayChannels().length === 0}
bind:value={
() => selectedChannel?.channel_id,
(value) => {
selectedChannel = value ? displayChannels().find((channel) => channel.channel_id === value) : undefined
}
}
disabled={disabled || displayChannels.length === 0}
bind:value={selectedChannelId}
/>
{/if}
</div>
@@ -49,7 +49,7 @@
hideConnectButton?: boolean
} = $props()
let selectedTeam: TeamItem | undefined = undefined
let selectedTeam: TeamItem | undefined = $state(undefined)
async function connectTeams() {
if (!selectedTeam) return
@@ -496,19 +496,16 @@
minWidth="200px"
placeholder="Search Teams channels"
teamId={teams_team_id}
bind:selectedChannel={
() =>
handlerExtraArgs['channel']
? {
channel_id: handlerExtraArgs['channel'],
channel_name: handlerExtraArgs['channel_name']
}
: undefined,
(channel) => {
handlerExtraArgs['channel'] = channel?.channel_id
handlerExtraArgs['channel_name'] = channel?.channel_name
}
}
selectedChannel={handlerExtraArgs['channel']
? {
channel_id: handlerExtraArgs['channel'],
channel_name: handlerExtraArgs['channel_name']
}
: undefined}
onselectedchannelchange={(channel) => {
handlerExtraArgs['channel'] = channel?.channel_id
handlerExtraArgs['channel_name'] = channel?.channel_name
}}
onError={(e) => sendUserToast('Failed to load channels: ' + e.message, true)}
/>
</div>
@@ -163,13 +163,17 @@
teamItem: { team_id: string; team_name: string } | undefined,
i: number
) {
const currentTeamChannel = $values['critical_error_channels'][i]?.teams_channel
const teamIdChanged = currentTeamChannel?.team_id !== teamItem?.team_id
$values['critical_error_channels'][i] = {
teams_channel: teamItem
? {
team_id: teamItem.team_id,
team_name: teamItem.team_name,
channel_id: undefined, // Will be set when channel is selected
channel_name: undefined
// Preserve existing channel if team didn't actually change
channel_id: teamIdChanged ? undefined : currentTeamChannel?.channel_id,
channel_name: teamIdChanged ? undefined : currentTeamChannel?.channel_name
}
: undefined
}
@@ -191,6 +195,7 @@
}
}
}
</script>
<!-- {JSON.stringify($values, null, 2)} -->
@@ -563,24 +568,25 @@
value={v?.slack_channel ?? ''}
/>
{:else if v && 'teams_channel' in v}
{@const currentTeam = $values['critical_error_channels'][i]?.teams_channel
? {
team_id: $values['critical_error_channels'][i]?.teams_channel?.team_id,
team_name: $values['critical_error_channels'][i]?.teams_channel?.team_name
}
: undefined}
{@const currentChannel = $values['critical_error_channels'][i]?.teams_channel?.channel_id
? {
channel_id: $values['critical_error_channels'][i]?.teams_channel?.channel_id,
channel_name: $values['critical_error_channels'][i]?.teams_channel?.channel_name
}
: undefined}
<div class="flex flex-row gap-2 w-full">
<TeamSelector
containerClass="w-44"
minWidth="140px"
showRefreshButton={false}
bind:selectedTeam={
() =>
$values['critical_error_channels'][i]?.teams_channel
? {
team_id:
$values['critical_error_channels'][i]?.teams_channel?.team_id,
team_name:
$values['critical_error_channels'][i]?.teams_channel
?.team_name
}
: undefined,
(team) => handleTeamChange(team, i)
}
selectedTeam={currentTeam}
onselectedteamchange={(team) => handleTeamChange(team, i)}
/>
{#if $values['critical_error_channels'][i]?.teams_channel?.team_id}
@@ -588,20 +594,8 @@
containerClass=""
placeholder="Search channels"
teamId={$values['critical_error_channels'][i]?.teams_channel?.team_id}
bind:selectedChannel={
() =>
$values['critical_error_channels'][i]?.teams_channel?.channel_id
? {
channel_id:
$values['critical_error_channels'][i]?.teams_channel
?.channel_id,
channel_name:
$values['critical_error_channels'][i]?.teams_channel
?.channel_name
}
: undefined,
(channel) => handleChannelChange(channel, i)
}
selectedChannel={currentChannel}
onselectedchannelchange={(channel) => handleChannelChange(channel, i)}
onError={(e) =>
sendUserToast('Failed to load channels: ' + e.message, true)}
/>
+40 -27
View File
@@ -18,6 +18,7 @@
teams?: TeamItem[] | undefined
minWidth?: string
onError?: (error: Error) => void
onselectedteamchange?: (team: TeamItem | undefined) => void
}
let {
@@ -27,34 +28,56 @@
showRefreshButton = true,
teams = undefined,
minWidth = '160px',
onError
onError,
onselectedteamchange
}: Props = $props()
let isFetching = $state(false)
let searchResults = $state<TeamItem[]>([])
// Only enable search mode if no teams are provided
let selectedTeamId = $state<string | undefined>(selectedTeam?.team_id)
const searchMode = !teams
// Determine which teams to show: provided teams or search results
// In search mode, include the selected team if it exists
let displayTeams = $derived(() => {
const baseTeams = teams || searchResults;
if (searchMode && selectedTeam && !baseTeams.find(t => t.team_id === selectedTeam?.team_id)) {
return [selectedTeam, ...baseTeams];
let displayTeams = $derived.by(() => {
const baseTeams = teams || searchResults
if (selectedTeam && !baseTeams.find(t => t.team_id === selectedTeam?.team_id)) {
return [selectedTeam, ...baseTeams]
}
return baseTeams
})
$effect(() => {
const newTeam = selectedTeamId
? displayTeams.find(t => t.team_id === selectedTeamId)
: undefined
if (newTeam?.team_id !== selectedTeam?.team_id) {
selectedTeam = newTeam
}
})
$effect(() => {
if (selectedTeam?.team_id !== selectedTeamId) {
selectedTeamId = selectedTeam?.team_id
}
})
let previousTeamId = $state<string | undefined>(undefined)
$effect(() => {
if (selectedTeam?.team_id !== previousTeamId) {
previousTeamId = selectedTeam?.team_id
onselectedteamchange?.(selectedTeam)
}
return baseTeams;
})
// Create separate filter text for search mode
let searchFilterText = $state('')
// Debounced search function
const debouncedSearch = debounce(async (query: string) => {
await searchTeams(query)
}, 500)
// Watch for search filter text changes (only in search mode)
$effect(() => {
if (searchMode) {
if (searchFilterText.length >= 1) {
@@ -100,37 +123,27 @@
{#if searchMode}
<Select
containerStyle={'min-width: ' + minWidth}
items={searchFilterText.length >= 1 || (searchFilterText.length === 0 && selectedTeam) ? displayTeams().map((team) => ({
items={displayTeams.map((team) => ({
label: team.team_name,
value: team.team_id
})) : []}
}))}
placeholder={isFetching ? "Searching..." : "Search teams..."}
clearable
disabled={disabled || isFetching}
bind:filterText={searchFilterText}
bind:value={
() => selectedTeam?.team_id,
(value) => {
selectedTeam = value ? displayTeams().find((team) => team.team_id === value) : undefined
}
}
bind:value={selectedTeamId}
/>
{:else}
<Select
containerStyle={'min-width: ' + minWidth}
items={displayTeams().map((team) => ({
items={displayTeams.map((team) => ({
label: team.team_name,
value: team.team_id
}))}
placeholder="Select a team"
clearable
disabled={disabled || isFetching}
bind:value={
() => selectedTeam?.team_id,
(value) => {
selectedTeam = value ? displayTeams().find((team) => team.team_id === value) : undefined
}
}
bind:value={selectedTeamId}
/>
{/if}
</div>