index.tsx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. import React, { useCallback, useEffect, useState } from 'react';
  2. import { View, Text, TouchableOpacity, FlatList, Platform } from 'react-native';
  3. import * as Progress from 'react-native-progress';
  4. import { useFocusEffect, useNavigation } from '@react-navigation/native';
  5. import moment from 'moment';
  6. import { EditNmModal, Header, Input, PageWrapper } from 'src/components';
  7. import { CustomButton } from '../Components';
  8. import { NmRegionItem } from '../Components/MyRegionsItems/NmRegionItem';
  9. import { RegionItem } from '../Components/MyRegionsItems/RegionItem';
  10. import MegaregionsModal from '../Components/MegaregionsModal';
  11. import { StoreType, storage } from 'src/storage';
  12. import { NmRegion, TCCRegion } from '../utils/types';
  13. import { Colors } from 'src/theme';
  14. import { styles } from './styles';
  15. import {
  16. useGetMegaregionsQuery,
  17. useGetRegionQeQuery,
  18. useGetRegionsQuery,
  19. usePostSetTCCRegionMutation
  20. } from '@api/myRegions';
  21. import { qualityOptions } from '../utils/constants';
  22. import ChevronIcon from 'assets/icons/travels-screens/down-arrow.svg';
  23. import { NAVIGATION_PAGES } from 'src/types';
  24. import { useRegion } from 'src/contexts/RegionContext';
  25. import SearchIcon from 'assets/icons/search.svg';
  26. import { useGetListCountriesQuery } from '@api/countries';
  27. const RegionsScreen = () => {
  28. const token = storage.get('token', StoreType.STRING) as string;
  29. const { data: megaregions } = useGetMegaregionsQuery(String(token), true);
  30. const { data: countriesList } = useGetListCountriesQuery(true);
  31. const [megaSelectorVisible, setMegaSelectorVisible] = useState(false);
  32. const [countrySelectorVisible, setCountrySelectorVisible] = useState(false);
  33. // const [selectedMega, setSelectedMega] = useState<{ name: string; id: number }>({
  34. // id: 1,
  35. // name: 'SOUTHERN EUROPE'
  36. // });
  37. const [selectedMega, setSelectedMega] = useState<{ name: string; id: number | 'all' }>({
  38. id: 'all',
  39. name: 'ALL MEGAREGIONS'
  40. });
  41. const [selectedCountry, setSelectedCountry] = useState<{ name: string; id: number } | null>(null);
  42. // const { data: regionsQe } = useGetRegionQeQuery(
  43. // selectedCountry ? undefined : selectedMega.id,
  44. // selectedCountry ? selectedCountry.id : undefined,
  45. // String(token),
  46. // true
  47. // );
  48. const { data: regionsQe } = useGetRegionsQuery(
  49. selectedCountry ? undefined : selectedMega.id,
  50. selectedCountry ? selectedCountry.id : undefined,
  51. String(token),
  52. true
  53. );
  54. const [total, setTotal] = useState(0);
  55. const [isEditModalVisible, setIsEditModalVisible] = useState(false);
  56. const [contentIndex, setContentIndex] = useState(0);
  57. const [filteredNmRegions, setFilteredNmRegions] = useState<NmRegion[] | null>(null);
  58. const [tccRegions, setTccRegions] = useState<TCCRegion[] | null>(null);
  59. const [filteredTccRegions, setFilteredTccRegions] = useState<TCCRegion[] | null>(null);
  60. const { mutate: updateTCC } = usePostSetTCCRegionMutation();
  61. const [modalState, setModalState] = useState({
  62. selectedFirstYear: 2021,
  63. selectedLastYear: 2021,
  64. selectedQuality: qualityOptions[2],
  65. selectedNoOfVisits: 1,
  66. years: [],
  67. id: null
  68. });
  69. const navigation = useNavigation();
  70. const { handleUpdateNMList: handleUpdateNM, nmRegions, setNmRegions, setUserData } = useRegion();
  71. const [search, setSearch] = useState<string>('');
  72. useEffect(() => {
  73. const currentYear = moment().year();
  74. let yearSelector: { label: string; value: number }[] = [{ label: 'visited', value: 1 }];
  75. for (let i = currentYear; i >= 1951; i--) {
  76. yearSelector.push({ label: i.toString(), value: i });
  77. }
  78. handleModalStateChange({ years: yearSelector });
  79. }, []);
  80. const handleModalStateChange = (updates: { [key: string]: any }) => {
  81. setModalState((prevState) => ({ ...prevState, ...updates }));
  82. };
  83. const handleOpenEditModal = (item: NmRegion) => {
  84. handleModalStateChange({
  85. selectedFirstYear: item.year,
  86. selectedLastYear: item.last,
  87. selectedQuality:
  88. qualityOptions.find((quality) => quality.id === item.quality) || qualityOptions[2],
  89. selectedNoOfVisits: item.visits || 1,
  90. id: item.id
  91. });
  92. // setIsEditModalVisible(true);
  93. navigation.navigate(...([NAVIGATION_PAGES.EDIT_NM_DATA, { regionId: item.id }] as never));
  94. };
  95. const handleUpdateTCC = useCallback(
  96. (region: number, visits: 0 | 1) => {
  97. const updatedTCC = tccRegions?.map((item) => {
  98. if (item.id === region) {
  99. return {
  100. ...item,
  101. visited: visits
  102. };
  103. }
  104. return item;
  105. });
  106. const updatedTCCData = {
  107. token,
  108. region,
  109. visits
  110. };
  111. updateTCC(updatedTCCData);
  112. updatedTCC && setTccRegions(updatedTCC);
  113. },
  114. [tccRegions]
  115. );
  116. const handleMegaSelect = (mega: { name: string; id: number }) => {
  117. setSelectedMega(mega);
  118. setSelectedCountry(null);
  119. setMegaSelectorVisible(false);
  120. };
  121. const handleCountrySelect = (country: { name: string; id: number }) => {
  122. setSelectedCountry(country);
  123. setSelectedMega({ id: 'all', name: 'ALL MEGAREGIONS' });
  124. setCountrySelectorVisible(false);
  125. };
  126. useEffect(() => {
  127. if (nmRegions && nmRegions.length && token) {
  128. calcTotalCountries();
  129. }
  130. }, [nmRegions]);
  131. useEffect(() => {
  132. if (regionsQe && regionsQe.result === 'OK') {
  133. setNmRegions(
  134. regionsQe.data.regions.map((region) => ({
  135. id: region.id,
  136. flag_1: region.flag1,
  137. flag_2: region.flag2,
  138. region_name: region.name,
  139. essential: 0,
  140. quality: region.best_visit_quality || null,
  141. year: region.first_visited_in_year || null,
  142. last: region.last_visited_in_year || null,
  143. visits: region.no_of_visits || 0
  144. }))
  145. );
  146. setTccRegions(regionsQe.data.tcc);
  147. }
  148. }, [regionsQe]);
  149. useEffect(() => {
  150. if (megaregions && megaregions.result === 'OK') {
  151. setContentIndex(0);
  152. }
  153. }, [selectedMega, selectedCountry]);
  154. const applyFilters = () => {
  155. let newNmData = nmRegions ?? [];
  156. let newTccData = tccRegions ?? [];
  157. switch (contentIndex) {
  158. case 1:
  159. newNmData = nmRegions?.filter((item: NmRegion) => item.visits <= 0) || [];
  160. newTccData = tccRegions?.filter((item) => item.visited <= 0) || [];
  161. break;
  162. case 2:
  163. newNmData = nmRegions?.filter((item: NmRegion) => item.visits > 0) || [];
  164. newTccData = tccRegions?.filter((item) => item.visited > 0) || [];
  165. break;
  166. }
  167. if (search) {
  168. newNmData = newNmData?.filter((item: NmRegion) => {
  169. const itemData = item.region_name ? item.region_name.toLowerCase() : '';
  170. return itemData.includes(search.toLowerCase());
  171. });
  172. newTccData = newTccData?.filter((item: TCCRegion) => {
  173. const itemData = item.name ? item.name.toLowerCase() : '';
  174. return itemData.includes(search.toLowerCase());
  175. });
  176. }
  177. setFilteredNmRegions(newNmData);
  178. setFilteredTccRegions(newTccData);
  179. };
  180. useEffect(() => {
  181. applyFilters();
  182. }, [contentIndex, nmRegions, tccRegions, search]);
  183. useEffect(() => {
  184. setSearch('');
  185. }, [contentIndex, selectedMega, selectedCountry]);
  186. useEffect(() => {
  187. if (megaregions && megaregions.result === 'OK') {
  188. setSelectedMega({ id: 'all', name: 'ALL MEGAREGIONS' });
  189. }
  190. }, [megaregions]);
  191. const calcTotalCountries = () => {
  192. const visited = nmRegions?.filter((item: NmRegion) => item.visits > 0).length || 0;
  193. setTotal(visited);
  194. };
  195. const searchFilter = (text: string) => {
  196. setSearch(text);
  197. };
  198. const renderItem = ({ item }: { item: NmRegion }) => (
  199. <TouchableOpacity
  200. onPress={() => {
  201. setUserData({
  202. type: 'nm',
  203. id: item.id,
  204. region_flag: item.flag_1,
  205. region_name: item.region_name,
  206. best_visit_quality: item.quality,
  207. first_visit_year: item.year,
  208. last_visit_year: item.last,
  209. no_of_visits: item.visits,
  210. visited: item.visits > 0
  211. });
  212. navigation.navigate(
  213. ...([
  214. NAVIGATION_PAGES.REGION_PREVIEW,
  215. {
  216. regionId: item.id,
  217. isTravelsScreen: true,
  218. type: 'nm',
  219. disabled: token ? false : true
  220. }
  221. ] as never)
  222. );
  223. }}
  224. >
  225. <NmRegionItem
  226. item={item}
  227. openEditModal={handleOpenEditModal}
  228. updateNM={handleUpdateNM}
  229. token={token}
  230. />
  231. </TouchableOpacity>
  232. );
  233. return (
  234. <PageWrapper>
  235. <Header label="Regions" />
  236. <TouchableOpacity style={styles.megaSelector} onPress={() => setMegaSelectorVisible(true)}>
  237. <Text style={styles.megaButtonText}>{selectedMega?.name}</Text>
  238. <ChevronIcon width={18} height={18} />
  239. </TouchableOpacity>
  240. <TouchableOpacity style={styles.megaSelector} onPress={() => setCountrySelectorVisible(true)}>
  241. <Text style={styles.megaButtonText}>{selectedCountry?.name || 'ALL COUNTRIES'}</Text>
  242. <ChevronIcon width={18} height={18} />
  243. </TouchableOpacity>
  244. <View style={{ marginBottom: 16 }}>
  245. <Input
  246. inputMode={'search'}
  247. placeholder={'Search'}
  248. onChange={(text) => searchFilter(text)}
  249. value={search}
  250. icon={<SearchIcon fill={'#C8C8C8'} width={14} height={14} />}
  251. height={34}
  252. />
  253. </View>
  254. {token && (
  255. <View style={styles.buttonContainer}>
  256. <CustomButton
  257. title="All"
  258. onPress={() => setContentIndex(0)}
  259. isActive={contentIndex === 0}
  260. />
  261. <CustomButton
  262. title="Visited"
  263. onPress={() => setContentIndex(2)}
  264. isActive={contentIndex === 2}
  265. />
  266. <CustomButton
  267. title="Not visited"
  268. onPress={() => setContentIndex(1)}
  269. isActive={contentIndex === 1}
  270. />
  271. </View>
  272. )}
  273. <View style={styles.progressHeader}>
  274. <Text style={styles.textSmall}>Visited regions</Text>
  275. <Text style={styles.textSmall}>
  276. {nmRegions?.length
  277. ? `${total}/${nmRegions.length} • ${((total * 100) / nmRegions.length).toFixed(2)}%`
  278. : '0/0 • 100%'}
  279. </Text>
  280. </View>
  281. <Progress.Bar
  282. progress={nmRegions?.length ? total / nmRegions.length : 1}
  283. width={null}
  284. height={4}
  285. color={Colors.DARK_BLUE}
  286. borderWidth={0}
  287. borderRadius={5}
  288. unfilledColor={Colors.FILL_LIGHT}
  289. />
  290. {filteredNmRegions && (filteredNmRegions?.length || filteredTccRegions?.length) ? (
  291. <FlatList
  292. data={filteredNmRegions}
  293. renderItem={renderItem}
  294. keyExtractor={(item) => item.id.toString()}
  295. showsVerticalScrollIndicator={false}
  296. style={{ paddingVertical: 8 }}
  297. ListFooterComponent={
  298. filteredTccRegions && filteredTccRegions.length ? (
  299. <View style={{ marginVertical: 8 }}>
  300. <Text style={[styles.textMedium, { textAlign: 'center' }]}>TCC regions</Text>
  301. {filteredTccRegions?.map((item) => (
  302. <RegionItem
  303. item={item}
  304. updateRegion={handleUpdateTCC}
  305. key={item.id}
  306. token={token}
  307. />
  308. ))}
  309. </View>
  310. ) : null
  311. }
  312. />
  313. ) : null}
  314. <MegaregionsModal
  315. isVisible={megaSelectorVisible}
  316. onClose={() => setMegaSelectorVisible(false)}
  317. onSelect={handleMegaSelect}
  318. data={
  319. megaregions?.data
  320. ? [{ id: 'all' as never, name: 'ALL MEGAREGIONS' }, ...megaregions?.data]
  321. : []
  322. }
  323. />
  324. <MegaregionsModal
  325. isVisible={countrySelectorVisible}
  326. onClose={() => setCountrySelectorVisible(false)}
  327. onSelect={handleCountrySelect}
  328. data={
  329. countriesList?.data
  330. ? [
  331. { id: 'all' as never, name: 'ALL COUNTRIES', flag: null as never },
  332. ...countriesList?.data
  333. ]
  334. : []
  335. }
  336. isCountry={true}
  337. />
  338. <EditNmModal
  339. isVisible={isEditModalVisible}
  340. onClose={() => setIsEditModalVisible(false)}
  341. modalState={modalState}
  342. updateModalState={handleModalStateChange}
  343. updateNM={handleUpdateNM}
  344. />
  345. </PageWrapper>
  346. );
  347. };
  348. export default RegionsScreen;