All files / src/components/TopicBacklogManager TopicFilterPanel.tsx

88.88% Statements 8/9
80% Branches 8/10
75% Functions 3/4
88.88% Lines 8/9

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138                                                        1x 5x   5x 2x     5x           5x 1x     5x                                                                                                                                                                                        
/**
 * Topic Filter Panel Component (Story 5.2 - AC1)
 *
 * Filter controls for topic list:
 * - Category filter
 * - Status filter (available, caution, unavailable)
 * - Sort options
 */
 
import React from 'react';
import {
  Paper,
  Typography,
  FormControl,
  InputLabel,
  Select,
  MenuItem,
  Box,
  SelectChangeEvent,
} from '@mui/material';
import { useTranslation } from 'react-i18next';
import type { TopicFilters } from '@/types/topic.types';
 
export interface TopicFilterPanelProps {
  filters: TopicFilters;
  onFilterChange: (filters: Partial<TopicFilters>) => void;
}
 
export const TopicFilterPanel: React.FC<TopicFilterPanelProps> = ({ filters, onFilterChange }) => {
  const { t } = useTranslation('organizer');
 
  const handleCategoryChange = (event: SelectChangeEvent<string>) => {
    onFilterChange({ category: event.target.value || undefined });
  };
 
  const handleStatusChange = (event: SelectChangeEvent<string>) => {
    onFilterChange({
      status: (event.target.value as 'AVAILABLE' | 'CAUTION' | 'UNAVAILABLE') || undefined,
    });
  };
 
  const handleSortChange = (event: SelectChangeEvent<string>) => {
    onFilterChange({ sort: event.target.value });
  };
 
  return (
    <Paper sx={{ p: 2 }}>
      <Typography variant="h6" gutterBottom>
        {t('topicBacklog.filters.title', 'Filters')}
      </Typography>
 
      <Box sx={{ display: 'flex', flexDirection: 'column', gap: 2, mt: 2 }}>
        {/* Category Filter */}
        <FormControl fullWidth size="small">
          <InputLabel>{t('topicBacklog.filters.category', 'Category')}</InputLabel>
          <Select
            value={filters.category || ''}
            onChange={handleCategoryChange}
            label={t('topicBacklog.filters.category', 'Category')}
            data-testid="filter-category"
          >
            <MenuItem value="">
              <em>{t('topicBacklog.filters.all', 'All Categories')}</em>
            </MenuItem>
            <MenuItem value="technical">
              {t('topicBacklog.filters.categories.technical', 'Technical')}
            </MenuItem>
            <MenuItem value="management">
              {t('topicBacklog.filters.categories.management', 'Management')}
            </MenuItem>
            <MenuItem value="soft_skills">
              {t('topicBacklog.filters.categories.softSkills', 'Soft Skills')}
            </MenuItem>
            <MenuItem value="industry_trends">
              {t('topicBacklog.filters.categories.industryTrends', 'Industry Trends')}
            </MenuItem>
            <MenuItem value="tools_platforms">
              {t('topicBacklog.filters.categories.toolsPlatforms', 'Tools & Platforms')}
            </MenuItem>
          </Select>
        </FormControl>
 
        {/* Status Filter (AC3 - Color-coded freshness) */}
        <FormControl fullWidth size="small">
          <InputLabel>{t('common:labels.status')}</InputLabel>
          <Select
            value={filters.status || ''}
            onChange={handleStatusChange}
            label={t('common:labels.status')}
          >
            <MenuItem value="">
              <em>{t('topicBacklog.filters.allStatuses', 'All Statuses')}</em>
            </MenuItem>
            <MenuItem value="AVAILABLE">
              {t('topicBacklog.filters.statuses.available', 'Available (Green)')}
            </MenuItem>
            <MenuItem value="CAUTION">
              {t('topicBacklog.filters.statuses.caution', 'Use with Caution (Yellow)')}
            </MenuItem>
            <MenuItem value="UNAVAILABLE">
              {t('topicBacklog.filters.statuses.unavailable', 'Too Recent (Red)')}
            </MenuItem>
          </Select>
        </FormControl>
 
        {/* Sort Options */}
        <FormControl fullWidth size="small">
          <InputLabel>{t('common:labels.sortBy', 'Sort By')}</InputLabel>
          <Select
            value={filters.sort || '-stalenessScore'}
            onChange={handleSortChange}
            label={t('common:labels.sortBy', 'Sort By')}
            data-testid="filter-sort"
          >
            <MenuItem value="-stalenessScore">
              {t('topicBacklog.filters.sortOptions.safestFirst', 'Safest First (Staleness ↓)')}
            </MenuItem>
            <MenuItem value="stalenessScore">
              {t('topicBacklog.filters.sortOptions.recentFirst', 'Recent First (Staleness ↑)')}
            </MenuItem>
            <MenuItem value="-createdAt">
              {t('topicBacklog.filters.sortOptions.newestFirst', 'Newest First (Recently Added)')}
            </MenuItem>
            <MenuItem value="title">
              {t('topicBacklog.filters.sortOptions.titleAZ', 'Title (A-Z)')}
            </MenuItem>
            <MenuItem value="-usageCount">
              {t('topicBacklog.filters.sortOptions.mostUsed', 'Most Used')}
            </MenuItem>
          </Select>
        </FormControl>
      </Box>
    </Paper>
  );
};
 
export default TopicFilterPanel;