"use client"; import * as React from 'react'; import { useEffect, useState, useRef } from 'react'; import type { Database } from '@/types/supabase'; import { getSupabaseClient } from '../../utils/supabase'; import { AuthChangeEvent, Session } from '@supabase/supabase-js'; import { Loader2, X, Check } from 'lucide-react'; import { cn } from '@/lib/utils'; import { limqRequest } from '@/lib/api'; type Team = Database['limq']['Tables']['teams']['Row']; // TeamSelector component with multi-select support export function TeamSelector({ value, onChange, className, multiple = false, }: { value?: string | string[]; onChange?: (teamId: string | string[]) => void; className?: string; multiple?: boolean; }) { const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [teams, setTeams] = useState([]); const [selectedIds, setSelectedIds] = useState([]); const [isOpen, setIsOpen] = useState(false); const selectorRef = useRef(null); // Initialize selected teams based on value prop useEffect(() => { if (value) { if (Array.isArray(value)) { setSelectedIds(value); } else { setSelectedIds(value ? [value] : []); } } else { setSelectedIds([]); } }, [value]); // Add click outside listener to close dropdown useEffect(() => { function handleClickOutside(event: MouseEvent) { if (selectorRef.current && !selectorRef.current.contains(event.target as Node)) { setIsOpen(false); } } // Only add the event listener if the dropdown is open if (isOpen) { document.addEventListener('mousedown', handleClickOutside); return () => { document.removeEventListener('mousedown', handleClickOutside); }; } }, [isOpen]); useEffect(() => { let isMounted = true; const fetchTeams = async (userId: string) => { if (!isMounted) return; setLoading(true); setError(null); try { const supabase = getSupabaseClient(); // 尝试创建默认团队和项目(如果用户还没有) try { const response = await limqRequest('team/create-default', 'POST'); console.log('Default team creation response:', response); } catch (teamError) { console.error('Error creating default team:', teamError); } const { data: memberships, error: membershipError } = await supabase .from('team_membership') .select('team_id') .eq('user_id', userId); if (membershipError) throw membershipError; if (!memberships || memberships.length === 0) { if (isMounted) setTeams([]); return; } const teamIds = memberships.map(m => m.team_id); const { data: teamsData, error: teamsError } = await supabase .from('teams') .select('*') .in('id', teamIds) .is('deleted_at', null); if (teamsError) throw teamsError; if (isMounted && teamsData) { setTeams(teamsData); } } catch (err) { if (isMounted) { setError(err instanceof Error ? err.message : 'Failed to load teams'); } } finally { if (isMounted) { setLoading(false); } } }; const supabase = getSupabaseClient(); const { data: { subscription } } = supabase.auth.onAuthStateChange((event: AuthChangeEvent, session: Session | null) => { if (event === 'SIGNED_IN' && session?.user?.id) { fetchTeams(session.user.id); } else if (event === 'SIGNED_OUT') { setTeams([]); setError(null); } }); supabase.auth.getSession().then(({ data: { session } }) => { if (session?.user?.id) { fetchTeams(session.user.id); } }); return () => { isMounted = false; subscription.unsubscribe(); }; }, []); const handleToggle = () => { if (!loading && !error && teams.length > 0) { setIsOpen(!isOpen); } }; const handleTeamSelect = (teamId: string) => { let newSelected: string[]; if (multiple) { // For multi-select: toggle team in/out of selection if (selectedIds.includes(teamId)) { newSelected = selectedIds.filter(id => id !== teamId); } else { newSelected = [...selectedIds, teamId]; } } else { // For single-select: replace selection with the new team newSelected = [teamId]; setIsOpen(false); } setSelectedIds(newSelected); if (onChange) { onChange(multiple ? newSelected : newSelected[0] || ''); } }; const removeTeam = (e: React.MouseEvent, teamId: string) => { e.stopPropagation(); const newSelected = selectedIds.filter(id => id !== teamId); setSelectedIds(newSelected); if (onChange) { onChange(multiple ? newSelected : newSelected[0] || ''); } }; if (loading) { return (
); } if (error) { return (
{error}
); } if (teams.length === 0) { return (
No teams available
); } const selectedTeams = teams.filter(team => selectedIds.includes(team.id)); return (
{selectedTeams.length > 0 ? (
{selectedTeams.map(team => (
{team.name} {multiple && ( removeTeam(e, team.id)} /> )}
))}
) : (
Select a team
)}
{isOpen && (
{teams.map(team => (
handleTeamSelect(team.id)} > {team.name} {selectedIds.includes(team.id) && ( )}
))}
)}
); }