mirror of
https://github.com/DocNR/POWR.git
synced 2025-04-19 19:01:18 +00:00
128 lines
3.8 KiB
TypeScript
128 lines
3.8 KiB
TypeScript
import React from 'react';
|
|
import { View } from 'react-native';
|
|
import { Text } from '@/components/ui/text';
|
|
import { Button } from '@/components/ui/button';
|
|
import { Sheet, SheetContent, SheetHeader, SheetTitle } from '@/components/ui/sheet';
|
|
import { Badge } from '@/components/ui/badge';
|
|
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@/components/ui/accordion';
|
|
|
|
export type SourceType = 'local' | 'powr' | 'nostr';
|
|
|
|
export interface FilterOptions {
|
|
equipment: string[];
|
|
tags: string[];
|
|
source: SourceType[];
|
|
}
|
|
|
|
interface FilterSheetProps {
|
|
isOpen: boolean;
|
|
onClose: () => void;
|
|
options: FilterOptions;
|
|
onApplyFilters: (filters: FilterOptions) => void;
|
|
availableFilters: {
|
|
equipment: string[];
|
|
tags: string[];
|
|
source: SourceType[];
|
|
};
|
|
}
|
|
|
|
function renderFilterSection<T extends string>(
|
|
title: string,
|
|
category: keyof FilterOptions,
|
|
values: T[],
|
|
selectedValues: T[],
|
|
onToggle: (category: keyof FilterOptions, value: T) => void
|
|
) {
|
|
return (
|
|
<AccordionItem value={category}>
|
|
<AccordionTrigger>
|
|
<View className="flex-row items-center gap-2">
|
|
<Text className="text-base font-medium">{title}</Text>
|
|
{selectedValues.length > 0 && (
|
|
<Badge variant="secondary">
|
|
<Text>{selectedValues.length}</Text>
|
|
</Badge>
|
|
)}
|
|
</View>
|
|
</AccordionTrigger>
|
|
<AccordionContent>
|
|
<View className="flex-row flex-wrap gap-2">
|
|
{values.map(value => {
|
|
const isSelected = selectedValues.includes(value);
|
|
return (
|
|
<Button
|
|
key={value}
|
|
variant={isSelected ? 'purple' : 'outline'}
|
|
onPress={() => onToggle(category, value)}
|
|
>
|
|
<Text className={isSelected ? 'text-white' : ''}>{value}</Text>
|
|
</Button>
|
|
);
|
|
})}
|
|
</View>
|
|
</AccordionContent>
|
|
</AccordionItem>
|
|
);
|
|
}
|
|
|
|
export function FilterSheet({
|
|
isOpen,
|
|
onClose,
|
|
options,
|
|
onApplyFilters,
|
|
availableFilters
|
|
}: FilterSheetProps) {
|
|
const [localOptions, setLocalOptions] = React.useState(options);
|
|
|
|
const handleReset = () => {
|
|
const resetOptions = { equipment: [], tags: [], source: [] };
|
|
setLocalOptions(resetOptions);
|
|
// Immediately apply reset
|
|
onApplyFilters(resetOptions);
|
|
};
|
|
|
|
const toggleFilter = (
|
|
category: keyof FilterOptions,
|
|
value: string | SourceType
|
|
) => {
|
|
setLocalOptions(prev => ({
|
|
...prev,
|
|
[category]: prev[category].includes(value as any)
|
|
? prev[category].filter(v => v !== value)
|
|
: [...prev[category], value as any]
|
|
}));
|
|
};
|
|
|
|
return (
|
|
<Sheet isOpen={isOpen} onClose={onClose}>
|
|
<SheetContent>
|
|
<View className="gap-4">
|
|
<Accordion type="single" collapsible>
|
|
{renderFilterSection('Equipment', 'equipment', availableFilters.equipment, localOptions.equipment, toggleFilter)}
|
|
{renderFilterSection('Tags', 'tags', availableFilters.tags, localOptions.tags, toggleFilter)}
|
|
{renderFilterSection<SourceType>('Source', 'source', availableFilters.source, localOptions.source, toggleFilter)}
|
|
</Accordion>
|
|
<View className="flex-row gap-2 mt-4">
|
|
<Button
|
|
variant="outline"
|
|
className="flex-1"
|
|
onPress={handleReset}
|
|
>
|
|
<Text>Reset</Text>
|
|
</Button>
|
|
<Button
|
|
className="flex-1"
|
|
variant="purple"
|
|
onPress={() => {
|
|
onApplyFilters(localOptions);
|
|
onClose();
|
|
}}
|
|
>
|
|
<Text className="text-white font-semibold">Apply Filters</Text>
|
|
</Button>
|
|
</View>
|
|
</View>
|
|
</SheetContent>
|
|
</Sheet>
|
|
);
|
|
} |