107 lines
3.9 KiB
TypeScript
107 lines
3.9 KiB
TypeScript
import React, { useMemo, useState } from 'react';
|
|
import { FlatList, ScrollView, StyleSheet, Text, View } from 'react-native';
|
|
import { useRouter } from 'expo-router';
|
|
import { useQuery } from '@tanstack/react-query';
|
|
import { api, DocCategory } from '@/api';
|
|
import { useT } from '@/i18n';
|
|
import { AppBar } from '@/components/AppBar';
|
|
import { Chip, EmptyState, Loading, TextField } from '@/components/ui';
|
|
import { DocumentRow } from '@/components/domain';
|
|
import { DocWithProperty, fetchAllDocuments } from '@/lib/allDocuments';
|
|
import { colors, fontSize, spacing } from '@/theme';
|
|
|
|
export default function SearchScreen() {
|
|
const router = useRouter();
|
|
const t = useT();
|
|
const [query, setQuery] = useState('');
|
|
const [category, setCategory] = useState<string>('all');
|
|
|
|
const docsQuery = useQuery({ queryKey: ['all-documents'], queryFn: fetchAllDocuments });
|
|
const catsQuery = useQuery({ queryKey: ['categories'], queryFn: () => api.reference.categories() });
|
|
|
|
const categories: DocCategory[] = catsQuery.data ?? [];
|
|
const catName = (slug: string) => categories.find((c) => c.slug === slug)?.name ?? slug;
|
|
|
|
const results = useMemo(() => {
|
|
const docs: DocWithProperty[] = docsQuery.data ?? [];
|
|
const q = query.trim().toLowerCase();
|
|
return docs.filter((d) => {
|
|
const matchesCat = category === 'all' || d.category === category;
|
|
const matchesQuery =
|
|
!q || d.name.toLowerCase().includes(q) || catName(d.category).toLowerCase().includes(q);
|
|
return matchesCat && matchesQuery;
|
|
});
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [docsQuery.data, query, category, categories]);
|
|
|
|
return (
|
|
<View style={{ flex: 1, backgroundColor: colors.bgApp }}>
|
|
<AppBar title={t('tabs.search')} large />
|
|
|
|
<View style={styles.controls}>
|
|
<TextField
|
|
icon="search"
|
|
value={query}
|
|
onChangeText={setQuery}
|
|
placeholder={t('properties.searchPlaceholder')}
|
|
autoCapitalize="none"
|
|
autoFocus
|
|
/>
|
|
<ScrollView
|
|
horizontal
|
|
showsHorizontalScrollIndicator={false}
|
|
contentContainerStyle={styles.chips}
|
|
>
|
|
<Chip label={t('common.all')} active={category === 'all'} onPress={() => setCategory('all')} />
|
|
{categories.map((c) => (
|
|
<Chip
|
|
key={c.slug}
|
|
label={c.name}
|
|
active={category === c.slug}
|
|
onPress={() => setCategory(c.slug)}
|
|
/>
|
|
))}
|
|
</ScrollView>
|
|
</View>
|
|
|
|
{docsQuery.isLoading ? (
|
|
<Loading />
|
|
) : (
|
|
<FlatList
|
|
data={results}
|
|
keyExtractor={(d) => String(d.id)}
|
|
contentContainerStyle={styles.list}
|
|
ListHeaderComponent={
|
|
<Text style={styles.count}>
|
|
{t('properties.docCount', { count: results.length })}
|
|
</Text>
|
|
}
|
|
renderItem={({ item }) => (
|
|
<View style={{ marginBottom: spacing[3] }}>
|
|
<DocumentRow
|
|
document={item}
|
|
subtitle={`${item.propertyName} · ${catName(item.category)}`}
|
|
onPress={() => router.push(`/document/${item.id}?propertyId=${item.propertyId}`)}
|
|
/>
|
|
</View>
|
|
)}
|
|
ListEmptyComponent={
|
|
<EmptyState
|
|
icon="search-outline"
|
|
title={t('documents.emptyTitle')}
|
|
subtitle={t('documents.emptySubtitle')}
|
|
/>
|
|
}
|
|
/>
|
|
)}
|
|
</View>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
controls: { paddingHorizontal: spacing[4], gap: spacing[3], paddingBottom: spacing[2] },
|
|
chips: { gap: 8, paddingVertical: spacing[1], paddingRight: spacing[4] },
|
|
list: { padding: spacing[4], paddingTop: spacing[2], paddingBottom: spacing[9] },
|
|
count: { fontSize: fontSize.sm, color: colors.slate500, marginBottom: spacing[3], fontWeight: '600' },
|
|
});
|