Skip to content

Commit

Permalink
Re-added keyword + category count
Browse files Browse the repository at this point in the history
  • Loading branch information
fxi committed Nov 20, 2024
1 parent 51f06f7 commit 5f2d599
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 26 deletions.
68 changes: 66 additions & 2 deletions src/components/data/DataExplorer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useState, useEffect } from 'react';
import { FilterPanel } from './FilterPanel';
import { IndicatorList } from './IndicatorList';
import { VisualizationPanel } from './VisualizationPanel';
import type { Indicator, IndicatorData, FilterState } from '@/lib/types';
import type { Indicator, IndicatorData, FilterState } from '../../lib/types';

const initialFilters: FilterState = {
search: '',
Expand Down Expand Up @@ -55,6 +55,66 @@ export function DataExplorer() {
fetchIndicatorData();
}, [selectedIndicator]);

// Calculate remaining counts for categories and keywords
const calculateFilteredCounts = (indicators: Indicator[], currentFilters: FilterState) => {
const categoryCount: Record<string, number> = {};
const keywordCount: Record<string, number> = {};

indicators.forEach((indicator) => {
// Check if indicator matches current filters excluding the category being counted
const matchesSearch =
!currentFilters.search ||
indicator.title.toLowerCase().includes(currentFilters.search.toLowerCase()) ||
indicator.description.toLowerCase().includes(currentFilters.search.toLowerCase());

const matchesKeywords =
currentFilters.keywords.length === 0 ||
currentFilters.keywords.some((keyword) =>
indicator.keywords.some((k) => k.toLowerCase().includes(keyword.toLowerCase()))
);

// Count categories
indicator.collections.forEach((collection) => {
const categoryTitle = collection.title;
// When counting for a category, exclude it from the filter check
const otherCategories = currentFilters.categories.filter((c) => c !== categoryTitle);
const matchesOtherCategories =
otherCategories.length === 0 ||
indicator.collections.some((col) =>
otherCategories.includes(col.title)
);

if (matchesSearch && matchesKeywords && matchesOtherCategories) {
categoryCount[categoryTitle] = (categoryCount[categoryTitle] || 0) + 1;
}
});

// Count keywords
if (matchesSearch && (currentFilters.categories.length === 0 ||
currentFilters.categories.some((category) =>
indicator.collections.some((collection) =>
collection.title.toLowerCase().includes(category.toLowerCase())
)
))) {
indicator.keywords.forEach((keyword) => {
// When counting for a keyword, exclude it from the filter check
const otherKeywords = currentFilters.keywords.filter((k) => k !== keyword);
const matchesOtherKeywords =
otherKeywords.length === 0 ||
otherKeywords.some((k) =>
indicator.keywords.some((ik) => ik.toLowerCase().includes(k.toLowerCase()))
);

if (matchesOtherKeywords) {
keywordCount[keyword] = (keywordCount[keyword] || 0) + 1;
}
});
}
});

return { categoryCount, keywordCount };
};

// Filter indicators based on search and filters
const filteredIndicators = indicators.filter((indicator) => {
const matchesSearch =
Expand Down Expand Up @@ -92,6 +152,8 @@ export function DataExplorer() {
new Set(indicators.flatMap((indicator) => indicator.keywords))
);

const { categoryCount, keywordCount } = calculateFilteredCounts(indicators, filters);

return (
<div className="flex h-[calc(100vh-4rem)]">
<div className="w-64 flex-none">
Expand All @@ -100,6 +162,8 @@ export function DataExplorer() {
keywords={keywords}
filters={filters}
onFilterChange={setFilters}
categoryCount={categoryCount}
keywordCount={keywordCount}
/>
</div>

Expand All @@ -118,4 +182,4 @@ export function DataExplorer() {
</div>
</div>
);
}
}
76 changes: 52 additions & 24 deletions src/components/data/FilterPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,25 @@ import { Button } from '@/components/ui/button';
import { ScrollArea } from '@/components/ui/scroll-area';
import { Separator } from '@/components/ui/separator';
import { Card } from '@/components/ui/card';
import type { FilterState } from '@/lib/types';
import { Badge } from '@/components/ui/badge';
import type { FilterState } from '../../lib/types';

interface FilterPanelProps {
categories: string[];
keywords: string[];
filters: FilterState;
onFilterChange: (filters: FilterState) => void;
categoryCount: Record<string, number>;
keywordCount: Record<string, number>;
}

export function FilterPanel({
categories,
keywords,
filters,
onFilterChange,
categoryCount,
keywordCount,
}: FilterPanelProps) {
const [searchInput, setSearchInput] = useState(filters.search);

Expand Down Expand Up @@ -85,19 +90,30 @@ export function FilterPanel({
<div>
<h3 className="mb-2 text-sm font-medium">Categories</h3>
<div className="space-y-2">
{categories.map((category) => (
<Card
key={category}
className={`cursor-pointer p-2 transition-colors ${
filters.categories.includes(category)
? 'bg-primary text-primary-foreground'
: 'hover:bg-muted'
}`}
onClick={() => toggleCategory(category)}
>
<p className="text-sm">{category}</p>
</Card>
))}
{categories.map((category) => {
const count = categoryCount[category] || 0;
const isDisabled = count === 0 && !filters.categories.includes(category);
return (
<Card
key={category}
className={`cursor-pointer p-2 transition-colors ${
filters.categories.includes(category)
? 'bg-primary text-primary-foreground'
: isDisabled
? 'opacity-50 hover:bg-muted/50'
: 'hover:bg-muted'
}`}
onClick={() => !isDisabled && toggleCategory(category)}
>
<div className="flex items-center justify-between">
<p className="text-sm">{category}</p>
<Badge variant="secondary" className="ml-2">
{count}
</Badge>
</div>
</Card>
);
})}
</div>
</div>

Expand All @@ -106,16 +122,28 @@ export function FilterPanel({
<div>
<h3 className="mb-2 text-sm font-medium">Keywords</h3>
<div className="flex flex-wrap gap-2">
{keywords.map((keyword) => (
<Button
key={keyword}
variant={filters.keywords.includes(keyword) ? 'default' : 'outline'}
className="h-7 rounded-full text-xs"
onClick={() => toggleKeyword(keyword)}
>
{keyword}
</Button>
))}
{keywords.map((keyword) => {
const count = keywordCount[keyword] || 0;
const isDisabled = count === 0 && !filters.keywords.includes(keyword);
return (
<Button
key={keyword}
variant={filters.keywords.includes(keyword) ? 'default' : 'outline'}
className={`h-7 rounded-full text-xs ${
isDisabled ? 'opacity-50 cursor-default' : ''
}`}
onClick={() => !isDisabled && toggleKeyword(keyword)}
>
{keyword}
<Badge
variant="secondary"
className="ml-1 h-4 rounded-full px-1 text-[10px]"
>
{count}
</Badge>
</Button>
);
})}
</div>
</div>
</div>
Expand Down

0 comments on commit 5f2d599

Please sign in to comment.