Files
casadoc-app/app/(tabs)/search.tsx
T
2026-08-26 23:01:07 +03:00

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' },
});