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

114 lines
4.0 KiB
TypeScript

import React, { useMemo, useState } from 'react';
import { FlatList, RefreshControl, 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 } from '@/components/ui';
import { DocumentRow } from '@/components/domain';
import { DocWithProperty, fetchAllDocuments } from '@/lib/allDocuments';
import { daysUntil } from '@/lib/format';
import { colors, fontSize, spacing } from '@/theme';
type Filter = 'all' | 'expiring' | 'expired';
export default function ExpiringScreen() {
const router = useRouter();
const t = useT();
const [filter, setFilter] = useState<Filter>('all');
const docsQuery = useQuery({ queryKey: ['all-documents'], queryFn: fetchAllDocuments });
const catsQuery = useQuery({ queryKey: ['categories'], queryFn: () => api.reference.categories() });
const catName = (slug: string) =>
(catsQuery.data as DocCategory[] | undefined)?.find((c) => c.slug === slug)?.name ?? slug;
// Only documents that actually need attention, soonest deadline first.
const attention = useMemo(() => {
const docs: DocWithProperty[] = docsQuery.data ?? [];
return docs
.filter((d) => d.expiryStatus === 'expiring' || d.expiryStatus === 'expired')
.sort((a, b) => (daysUntil(a.expiryDate) ?? 0) - (daysUntil(b.expiryDate) ?? 0));
}, [docsQuery.data]);
const counts = useMemo(
() => ({
all: attention.length,
expiring: attention.filter((d) => d.expiryStatus === 'expiring').length,
expired: attention.filter((d) => d.expiryStatus === 'expired').length,
}),
[attention],
);
const visible = filter === 'all' ? attention : attention.filter((d) => d.expiryStatus === filter);
return (
<View style={{ flex: 1, backgroundColor: colors.bgApp }}>
<AppBar
title={t('expiring.title')}
subtitle={docsQuery.data ? t('expiring.subtitle', { count: counts.all }) : undefined}
large
/>
<ScrollView
horizontal
showsHorizontalScrollIndicator={false}
contentContainerStyle={styles.chips}
style={{ flexGrow: 0 }}
>
<Chip label={t('common.all')} count={counts.all} active={filter === 'all'} onPress={() => setFilter('all')} />
<Chip
label={t('documents.filterExpiring')}
count={counts.expiring}
active={filter === 'expiring'}
onPress={() => setFilter('expiring')}
/>
<Chip
label={t('documents.filterExpired')}
count={counts.expired}
active={filter === 'expired'}
onPress={() => setFilter('expired')}
/>
</ScrollView>
{docsQuery.isLoading ? (
<Loading />
) : (
<FlatList
data={visible}
keyExtractor={(d) => String(d.id)}
contentContainerStyle={styles.list}
refreshControl={
<RefreshControl
refreshing={docsQuery.isRefetching}
onRefresh={() => docsQuery.refetch()}
tintColor={colors.teal600}
/>
}
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="checkmark-circle-outline"
title={t('expiring.emptyTitle')}
subtitle={t('expiring.emptySubtitle')}
/>
}
/>
)}
</View>
);
}
const styles = StyleSheet.create({
chips: { gap: 8, paddingHorizontal: spacing[4], paddingVertical: spacing[2] },
list: { padding: spacing[4], paddingTop: spacing[2], paddingBottom: spacing[9] },
});