123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276 |
- import React, { useCallback, useEffect, useState } from 'react';
- import { useFocusEffect } from '@react-navigation/native';
- import { View, Text, FlatList } from 'react-native';
- import { Button, Header, Input, Loading, Modal, PageWrapper } from 'src/components';
- import { TabView, TabBar } from 'react-native-tab-view';
- import SearchIcon from '../../../../../assets/icons/search.svg';
- import { fetchItemsForSeries, usePostSetToggleItem } from '@api/series';
- import { Colors } from 'src/theme';
- import { ButtonVariants } from 'src/types/components';
- import { AccordionListItem } from '../Components/AccordionListItem';
- import { styles } from './styles';
- interface SeriesItem {
- series_id: number;
- item_id: number;
- icon: string | null;
- name: string;
- readonly: boolean;
- info: string;
- new: boolean;
- checked: boolean;
- checked_double: boolean;
- double: boolean;
- }
- interface SeriesGroup {
- name: string;
- icon: string;
- series_id: number;
- series_name: string;
- items: SeriesItem[];
- }
- interface FilteredData {
- [key: string]: SeriesGroup[];
- }
- interface Route {
- key: string;
- title: string;
- }
- export const SeriesItemScreen = ({ route }: { route: any }) => {
- const { id, name, token } = route.params;
- const { mutate: updateSeriesItem } = usePostSetToggleItem();
- const [search, setSearch] = useState<string>('');
- const [filteredData, setFilteredData] = useState<FilteredData>({});
- const [seriesData, setSeriesData] = useState<SeriesGroup[]>([]);
- const [index, setIndex] = useState<number>(0);
- const [isLoading, setIsLoading] = useState<boolean>(true);
- const [isInfoModalVisible, setIsInfoModalVisible] = useState<boolean>(false);
- const [infoItem, setInfoItem] = useState<SeriesItem | null>(null);
- const [activeFilteredData, setActiveFilteredData] = useState<SeriesGroup[]>([]);
- useFocusEffect(
- useCallback(() => {
- const fetchGroups = async () => {
- const data = await fetchItemsForSeries(token, id);
- if (!data) {
- setIsLoading(false);
- return;
- }
- const allItems = data.groups;
- const newItems = data.groups
- .map((group) => ({
- ...group,
- items: group.items.filter((item) => item.new)
- }))
- .filter((group) => group.items.length > 0);
- setFilteredData({ all: allItems, new: newItems, unchecked: [], checked: [] });
- setSeriesData(data.groups);
- setIsLoading(false);
- };
- fetchGroups();
- }, [])
- );
- const [routes] = useState([
- { key: 'all', title: 'All items' },
- { key: 'new', title: 'New' },
- { key: 'unchecked', title: 'Unchecked' },
- { key: 'checked', title: 'Checked' }
- ]);
- const handleIndexChange = (index: number) => {
- let dataToFilter = [...seriesData];
- switch (index) {
- case 1:
- dataToFilter = filteredData[routes[index].key];
- break;
- case 2:
- dataToFilter = dataToFilter
- .map((group) => ({
- ...group,
- items: group.items.filter((item) => !item.checked)
- }))
- .filter((group) => group.items.length > 0);
- break;
- case 3:
- dataToFilter = dataToFilter
- .map((group) => ({
- ...group,
- items: group.items.filter((item) => item.checked)
- }))
- .filter((group) => group.items.length > 0);
- break;
- default:
- break;
- }
- setActiveFilteredData(dataToFilter);
- setFilteredData((prevState) => ({
- ...prevState,
- [routes[index].key]: dataToFilter
- }));
- };
- useEffect(() => {
- handleIndexChange(index);
- }, [seriesData]);
- useEffect(() => {
- setActiveFilteredData(filteredData[routes[index].key]);
- }, [filteredData]);
- const renderScene = ({ route }: { route: Route }) => {
- return isLoading ? (
- <Loading />
- ) : (
- <FlatList
- key={routes[index].key}
- keyExtractor={(item, index) => index.toString()}
- showsVerticalScrollIndicator={false}
- initialNumToRender={15}
- style={{ paddingTop: 10 }}
- data={activeFilteredData}
- renderItem={({ item }) => (
- <AccordionListItem
- item={item}
- onCheckboxChange={handleCheckboxChange}
- setIsInfoModalVisible={setIsInfoModalVisible}
- setInfoItem={setInfoItem}
- isSeries={id === -1}
- />
- )}
- />
- );
- };
- useEffect(() => {
- const searchText = search.toLowerCase();
- const searchData = filteredData[routes[index].key]
- ?.map((group) => {
- const groupNameMatch = group.name.toLowerCase().includes(searchText);
- if (groupNameMatch) {
- return group;
- } else {
- const filteredItems = group.items.filter((item) =>
- item.name.toLowerCase().includes(searchText)
- );
- return filteredItems.length > 0 ? { ...group, items: filteredItems } : null;
- }
- })
- .filter((group) => group !== null);
- setActiveFilteredData(searchData as SeriesGroup[]);
- }, [search]);
- const handleCheckboxChange = useCallback(
- async (item: SeriesItem, groupName: string, double?: boolean) => {
- setSeriesData((currentData) => {
- const groupIndex = currentData.findIndex((group) => group.name === groupName);
- if (groupIndex === -1) return currentData;
- const newData = [...currentData];
- const newGroup = { ...newData[groupIndex] };
- newGroup.items = newGroup.items.map((subItem) =>
- subItem.item_id === item.item_id
- ? {
- ...subItem,
- ...(double
- ? { checked_double: !subItem.checked_double }
- : { checked: !subItem.checked })
- }
- : subItem
- );
- newData[groupIndex] = newGroup;
- return newData;
- });
- const itemData = {
- token: token,
- series_id: item.series_id,
- item_id: item.item_id,
- checked: (!double ? Number(!item.checked) : Number(item.checked)) as 0 | 1,
- double: (double ? Number(!item.checked_double) : Number(item.checked_double)) as 0 | 1
- };
- try {
- updateSeriesItem(itemData);
- } catch (error) {
- console.error('Failed to update checkbox state', error);
- }
- },
- [token, updateSeriesItem]
- );
- return (
- <PageWrapper>
- <Header label={name} />
- <Modal
- visible={isInfoModalVisible}
- children={
- <View style={styles.modalView}>
- <Text style={styles.infoTitle}>{infoItem?.name}</Text>
- <Text style={styles.infoText}>{infoItem?.info}</Text>
- <Button
- variant={ButtonVariants.OPACITY}
- containerStyles={styles.btnContainer}
- textStyles={{
- color: Colors.DARK_BLUE
- }}
- onPress={() => setIsInfoModalVisible(false)}
- children={'Got it'}
- />
- </View>
- }
- onRequestClose={() => setIsInfoModalVisible(false)}
- headerTitle={'Info'}
- visibleInPercent={'auto'}
- />
- <Input
- inputMode={'search'}
- placeholder={'Search'}
- onChange={(text) => setSearch(text)}
- value={search}
- icon={<SearchIcon fill={'#C8C8C8'} width={14} height={14} />}
- />
- <TabView
- navigationState={{ index, routes }}
- renderScene={renderScene}
- onIndexChange={(i) => {
- handleIndexChange(i);
- setIndex(i);
- }}
- lazy={true}
- renderTabBar={(props) => (
- <TabBar
- {...props}
- indicatorStyle={{ backgroundColor: Colors.DARK_BLUE }}
- style={styles.tabBar}
- tabStyle={styles.tabStyle}
- pressColor={'transparent'}
- renderLabel={({ route, focused }) => (
- <Text
- style={[styles.tabLabel, { color: Colors.DARK_BLUE, opacity: focused ? 1 : 0.4 }]}
- >
- {route.title}
- </Text>
- )}
- />
- )}
- />
- </PageWrapper>
- );
- };
|