import React, { useState, useEffect, useMemo } from 'react'; import { Autocomplete, TextField, Chip, Box, Paper, Typography, Divider, IconButton, Tooltip, Dialog, DialogTitle, DialogContent, DialogActions, Button, } from '@mui/material'; import { Add as AddIcon, Edit as EditIcon } from '@mui/icons-material'; import { useTranslation } from 'react-i18next'; import Label, { type LabelData } from './Label'; import LabelCreateDialog from './LabelCreateDialog'; interface LabelSelectorProps { selectedLabels: LabelData[]; availableLabels: LabelData[]; onLabelsChange: (labels: LabelData[]) => void; onCreateLabel?: (labelData: Omit) => Promise; placeholder?: string; size?: 'small' | 'medium'; disabled?: boolean; multiple?: boolean; showCreateButton?: boolean; maxTags?: number; } const LabelSelector: React.FC = ({ selectedLabels, availableLabels, onLabelsChange, onCreateLabel, placeholder = "Search or create labels...", size = 'medium', disabled = false, multiple = true, showCreateButton = true, maxTags }) => { const { t } = useTranslation(); const [inputValue, setInputValue] = useState(''); const [createDialogOpen, setCreateDialogOpen] = useState(false); const [prefilledName, setPrefilledName] = useState(''); // Memoize filtered options for performance const filteredOptions = useMemo(() => { const selectedIds = new Set(selectedLabels.map(label => label.id)); return availableLabels.filter(label => !selectedIds.has(label.id)); }, [availableLabels, selectedLabels]); // Group options by system vs user labels const groupedOptions = useMemo(() => { const systemLabels = filteredOptions.filter(label => label.is_system); const userLabels = filteredOptions.filter(label => !label.is_system); return [ ...(systemLabels.length > 0 ? [{ group: 'System Labels', options: systemLabels }] : []), ...(userLabels.length > 0 ? [{ group: 'My Labels', options: userLabels }] : []) ]; }, [filteredOptions]); const handleLabelChange = (event: any, newValue: LabelData | LabelData[] | null) => { if (!multiple) { onLabelsChange(newValue ? [newValue as LabelData] : []); return; } const newLabels = newValue as LabelData[] || []; // Check max tags limit if (maxTags && newLabels.length > maxTags) { return; } onLabelsChange(newLabels); }; const handleCreateNew = () => { setPrefilledName(inputValue); setCreateDialogOpen(true); }; const handleCreateLabel = async (labelData: Omit) => { if (onCreateLabel) { try { const newLabel = await onCreateLabel(labelData); onLabelsChange([...selectedLabels, newLabel]); setCreateDialogOpen(false); setInputValue(''); setPrefilledName(''); } catch (error) { console.error('Failed to create label:', error); } } }; const canCreateNew = inputValue.trim() && !availableLabels.some(label => label.name.toLowerCase() === inputValue.trim().toLowerCase() ) && onCreateLabel && showCreateButton; return ( <> multiple={multiple} value={multiple ? selectedLabels : selectedLabels[0] || null} onChange={handleLabelChange} inputValue={inputValue} onInputChange={(event, newInputValue) => setInputValue(newInputValue)} options={filteredOptions} groupBy={(option: LabelData) => option.is_system ? t('labels.selector.systemLabels') : t('labels.selector.myLabels')} getOptionLabel={(option: LabelData) => option.name} isOptionEqualToValue={(option: LabelData, value: LabelData) => option.id === value.id} disabled={disabled} size={size} renderInput={(params) => ( {canCreateNew && ( )} {params.InputProps.endAdornment} ), }} /> )} renderTags={(tagValue, getTagProps) => tagValue.map((option, index) => { const tagProps = getTagProps({ index }); const { key, ...restTagProps } = tagProps; return (