Viktoriia 2 هفته پیش
والد
کامیت
9057dac496

+ 117 - 0
src/components/AddVisitModal/index.tsx

@@ -0,0 +1,117 @@
+import React, { useState, useEffect } from 'react';
+import { View, StyleSheet, Platform, Text } from 'react-native';
+import { Picker as WheelPicker } from 'react-native-wheel-pick';
+import { Modal } from '../Modal';
+import { Button } from '../Button';
+import { ButtonVariants } from 'src/types/components';
+import { Colors } from 'src/theme';
+
+interface AddVisitModalProps {
+  isVisible: boolean;
+  onClose: () => void;
+  regionName: string;
+  onSave: (year: number | null) => void;
+}
+
+const CURRENT_YEAR = new Date().getFullYear();
+const YEARS_DATA = [
+  'No year',
+  ...Array.from({ length: 100 }, (_, i) => String(CURRENT_YEAR - i))
+];
+
+export const AddVisitModal: React.FC<AddVisitModalProps> = ({
+  isVisible,
+  onClose,
+  regionName,
+  onSave
+}) => {
+  const [selectedYear, setSelectedYear] = useState<number | null>(CURRENT_YEAR);
+
+  useEffect(() => {
+    if (isVisible) {
+      setSelectedYear(CURRENT_YEAR);
+    }
+  }, [isVisible]);
+
+  const handleConfirm = () => {
+    onSave(selectedYear);
+    onClose();
+  };
+
+  return (
+    <Modal
+      visible={isVisible}
+      onRequestClose={onClose}
+      headerTitle="Select year"
+      visibleInPercent={'auto'}
+    >
+      <View style={[styles.content, Platform.OS === 'android' ? { maxHeight: 400, paddingTop: 0 } : {}]}>
+        <Text style={[styles.regionText, Platform.OS === 'android' ? { marginBottom: 16 } : {}]}>{regionName}</Text>
+        <WheelPicker
+          style={[styles.wheel, Platform.OS === 'android' ? { height: '75%' } : { height: 280 }]}
+          pickerData={YEARS_DATA}
+          selectedValue={selectedYear === null ? 'No year' : String(selectedYear)}
+          onValueChange={(value: string) => {
+            if (value === 'No year') {
+              setSelectedYear(null);
+            } else {
+              setSelectedYear(Number(value));
+            }
+          }}
+          textColor={Colors.TEXT_GRAY}
+          textSize={22}
+          selectTextColor={Colors.DARK_BLUE}
+          isAtmospheric={true}
+          isCyclic={false}
+          isShowSelectLine={true}
+          selectLineColor={Colors.ORANGE}
+          selectLineSize={1}
+          itemStyle={{
+            fontSize: 22,
+            fontFamily: 'montserrat-600',
+            height: 280,
+          }}
+          selectedItemTextStyle={{
+            fontSize: 30,
+            fontFamily: 'montserrat-700',
+            color: Colors.DARK_BLUE
+          }}
+        />
+      </View>
+      <View style={styles.modalFooter}>
+        <Button
+          children="Add visit"
+          onPress={handleConfirm}
+          variant={ButtonVariants.FILL}
+          containerStyles={{ borderWidth: 0 }}
+        />
+      </View>
+    </Modal>
+  );
+};
+
+const styles = StyleSheet.create({
+  content: {
+    backgroundColor: Colors.WHITE,
+    justifyContent: 'center',
+    alignItems: 'center',
+    paddingTop: 16,
+    paddingBottom: 8,
+  },
+  regionText: {
+    fontSize: 14,
+    fontFamily: 'montserrat-600',
+    color: Colors.DARK_BLUE,
+    textAlign: 'center',
+    paddingHorizontal: 24
+  },
+  wheel: {
+    width: '100%',
+    backgroundColor: 'transparent',
+  },
+  modalFooter: {
+    justifyContent: 'flex-end',
+    width: '100%',
+    marginBottom: 24,
+  },
+});

+ 115 - 73
src/components/RegionPopup/index.tsx

@@ -1,4 +1,4 @@
-import { useCallback, useEffect, useRef } from 'react';
+import { useCallback, useEffect, useRef, useState } from 'react';
 import { Text, TouchableOpacity, View, Image, Animated, Dimensions, Platform } from 'react-native';
 import MarkIcon from 'assets/icons/mark.svg';
 import EditSvg from 'assets/icons/travels-screens/pen-to-square.svg';
@@ -13,6 +13,8 @@ import { Colors } from 'src/theme';
 import { useFocusEffect, useNavigation } from '@react-navigation/native';
 import { NAVIGATION_PAGES } from 'src/types';
 import { useSafeAreaInsets } from 'react-native-safe-area-context';
+import { useRegion } from 'src/contexts/RegionContext';
+import { AddVisitModal } from '../AddVisitModal';
 
 interface Region {
   id: number;
@@ -41,6 +43,7 @@ interface RegionPopupProps {
   disabled?: boolean;
   updateSlow: (id: number, v: boolean, s11: boolean, s31: boolean, s101: boolean) => void;
   openEditSlowModal: () => void;
+  onAddVisitSuccess?: (id: number) => void;
 }
 
 const RegionPopup: React.FC<RegionPopupProps> = ({
@@ -52,8 +55,11 @@ const RegionPopup: React.FC<RegionPopupProps> = ({
   updateDare,
   disabled,
   updateSlow,
-  openEditSlowModal
+  openEditSlowModal,
+  onAddVisitSuccess
 }) => {
+  const { handleAddVisitNM } = useRegion();
+  const [isAddVisitModalVisible, setIsAddVisitModalVisible] = useState(false);
   const fadeAnim = useRef(new Animated.Value(0)).current;
   const navigation = useNavigation();
   const insets = useSafeAreaInsets();
@@ -261,83 +267,119 @@ const RegionPopup: React.FC<RegionPopupProps> = ({
                 <EditSvg width={14} height={14} />
               </TouchableOpacity>
             ) : null}
-            <TouchableOpacity
-              style={[
-                styles.btn,
-                (userData?.visited && !disabled) || userData.edit_in_trips === 1
-                  ? styles.visitedButton
-                  : styles.markVisitedButton
-              ]}
-              onPress={() => {
-                if (
-                  userData?.visited &&
-                  !disabled &&
-                  userData?.type === 'countries' &&
-                  userData?.visited_regions > 1
-                ) {
-                  navigation.navigate(
-                    ...([
-                      NAVIGATION_PAGES.EDIT_COUNTRY_DATA,
-                      {
-                        countryId: region.id,
-                        countryName: regionTitle,
-                        countryFlag: userData?.flag,
-                        slow11: userData?.slow11,
-                        slow31: userData?.slow31,
-                        slow101: userData?.slow101,
-                        isSlowList: false
-                      }
-                    ] as never)
-                  );
-                  return;
-                } else if (userData?.type === 'nm' && userData?.edit_in_trips === 1) {
-                  openEditModal();
-                  return;
-                }
+            {userData?.type === 'nm' ? (
+              <View style={{ flexDirection: 'row', gap: 8, justifyContent: 'flex-end', alignItems: 'center' }}>
+                {(userData?.visited || userData?.edit_in_trips === 1) && !disabled && (
+                  <TouchableOpacity
+                    onPress={openEditModal}
+                    style={[styles.btn, styles.visitedButton]}
+                  >
+                    <View style={styles.visitedContainer}>
+                      <EditSvg width={14} height={14} />
+                      <Text style={styles.visitedButtonText}>Edit</Text>
+                    </View>
+                  </TouchableOpacity>
+                )}
+                {!disabled && (
+                  <TouchableOpacity
+                    style={[styles.btn, styles.markVisitedButton, { width: 110 }]}
+                    onPress={() => setIsAddVisitModalVisible(true)}
+                  >
+                    <Text style={[styles.markVisitedButtonText, { textAlign: 'center' }]}>Add visit</Text>
+                  </TouchableOpacity>
+                )}
+              </View>
+            ) : (
+              <TouchableOpacity
+                style={[
+                  styles.btn,
+                  (userData?.visited && !disabled) || userData.edit_in_trips === 1
+                    ? styles.visitedButton
+                    : styles.markVisitedButton
+                ]}
+                onPress={() => {
+                  if (
+                    userData?.visited &&
+                    !disabled &&
+                    userData?.type === 'countries' &&
+                    userData?.visited_regions > 1
+                  ) {
+                    navigation.navigate(
+                      ...([
+                        NAVIGATION_PAGES.EDIT_COUNTRY_DATA,
+                        {
+                          countryId: region.id,
+                          countryName: regionTitle,
+                          countryFlag: userData?.flag,
+                          slow11: userData?.slow11,
+                          slow31: userData?.slow31,
+                          slow101: userData?.slow101,
+                          isSlowList: false
+                        }
+                      ] as never)
+                    );
+                    return;
+                  } else if (userData?.type === 'nm' && userData?.edit_in_trips === 1) {
+                    openEditModal();
+                    return;
+                  }
 
-                userData?.type === 'nm'
-                  ? updateNM(
-                      region.id,
-                      userData.visited ? 0 : 1,
-                      userData.visited ? 0 : 1,
-                      userData.visited ? 0 : 1,
-                      3
-                    )
-                  : userData?.type === 'countries'
-                    ? updateSlow(
+                  userData?.type === 'nm'
+                    ? updateNM(
                         region.id,
-                        !userData.visited,
-                        Boolean(userData.slow11),
-                        Boolean(userData.slow31),
-                        Boolean(userData.slow101)
+                        userData.visited ? 0 : 1,
+                        userData.visited ? 0 : 1,
+                        userData.visited ? 0 : 1,
+                        3
                       )
-                    : updateDare(region.id, userData.visited ? 0 : 1);
-              }}
-            >
-              {(userData?.visited &&
-                !disabled &&
-                userData?.type === 'countries' &&
-                userData?.visited_regions > 1) ||
-              (userData?.visited &&
-                !disabled &&
-                userData?.type === 'nm' &&
-                userData?.edit_in_trips === 1) ? (
-                <View style={styles.visitedContainer}>
-                  <EditSvg width={14} height={14} />
-                  <Text style={styles.visitedButtonText}>Edit</Text>
-                </View>
-              ) : userData?.visited && !disabled ? (
-                <View style={styles.visitedContainer}>
-                  <MarkIcon width={16} height={16} />
-                  <Text style={styles.visitedButtonText}>Visited</Text>
-                </View>
-              ) : (
-                <Text style={[styles.markVisitedButtonText]}>Mark Visited</Text>
-              )}
-            </TouchableOpacity>
+                    : userData?.type === 'countries'
+                      ? updateSlow(
+                          region.id,
+                          !userData.visited,
+                          Boolean(userData.slow11),
+                          Boolean(userData.slow31),
+                          Boolean(userData.slow101)
+                        )
+                      : updateDare(region.id, userData.visited ? 0 : 1);
+                }}
+              >
+                {(userData?.visited &&
+                  !disabled &&
+                  userData?.type === 'countries' &&
+                  userData?.visited_regions > 1) ||
+                (userData?.visited &&
+                  !disabled &&
+                  userData?.type === 'nm' &&
+                  userData?.edit_in_trips === 1) ? (
+                  <View style={styles.visitedContainer}>
+                    <EditSvg width={14} height={14} />
+                    <Text style={styles.visitedButtonText}>Edit</Text>
+                  </View>
+                ) : userData?.visited && !disabled ? (
+                  <View style={styles.visitedContainer}>
+                    <MarkIcon width={16} height={16} />
+                    <Text style={styles.visitedButtonText}>Visited</Text>
+                  </View>
+                ) : (
+                  <Text style={[styles.markVisitedButtonText]}>Mark Visited</Text>
+                )}
+              </TouchableOpacity>
+            )}
           </View>
         </View>
       </TouchableOpacity>
+
+      {userData?.type === 'nm' && (
+        <AddVisitModal
+          isVisible={isAddVisitModalVisible}
+          onClose={() => setIsAddVisitModalVisible(false)}
+          regionName={regionSubtitle ? `${regionTitle} - ${regionSubtitle}` : regionTitle}
+          onSave={(year) => {
+            handleAddVisitNM(region.id, year);
+            onAddVisitSuccess && onAddVisitSuccess(region.id);
+          }}
+        />
+      )}
     </Animated.View>
   );
 };

+ 1 - 0
src/components/index.ts

@@ -25,3 +25,4 @@ export * from './MultiSelectorCountries';
 export * from './TabViewWrapper';
 export * from './CustomImageViewer';
 export * from './SelectSheet';
+export * from './AddVisitModal';

+ 115 - 1
src/contexts/RegionContext.tsx

@@ -6,6 +6,7 @@ import {
   usePostSetNotVisitedMutation
 } from '@api/myRegions';
 import React, { createContext, useContext, useState, useCallback } from 'react';
+import { Alert } from 'react-native';
 import { DareRegion, NmRegion, SlowData } from 'src/screens/InAppScreens/TravelsScreen/utils/types';
 import { StoreType, storage } from 'src/storage';
 
@@ -28,7 +29,7 @@ export const RegionProvider = ({ children }: { children: React.ReactNode }) => {
 
   const handleUpdateNM = useCallback(
     (region: number, first: number, last: number, visits: number, quality: number) => {
-      const updatedNM = {
+      const updatedNM: any = {
         ...userData,
         first_visit_year: first,
         last_visit_year: last,
@@ -83,6 +84,118 @@ export const RegionProvider = ({ children }: { children: React.ReactNode }) => {
     [userData, token]
   );
 
+  const handleAddVisitNM = useCallback(
+    async (regionId: number, year: number | null) => {
+      const prevUserData = { ...userData };
+      const prevNmRegions = [...nmRegions];
+
+      const uData = userData as any;
+      if (uData && uData.id === regionId) {
+        const currentVisits = uData.no_of_visits || 0;
+        const newVisits = currentVisits + 1;
+        const currentFirst = uData.first_visit_year;
+        const currentLast = uData.last_visit_year;
+
+        let newFirst = currentFirst;
+        let newLast = currentLast;
+
+        if (year) {
+          newFirst = (!currentFirst || currentFirst <= 1 || year < currentFirst) ? year : currentFirst;
+          newLast = (!currentLast || currentLast <= 1 || year > currentLast) ? year : currentLast;
+        }
+
+        setUserData({
+          ...userData,
+          visited: true,
+          no_of_visits: newVisits,
+          first_visit_year: newFirst,
+          last_visit_year: newLast,
+        } as any);
+      }
+
+      setNmRegions((prevList) =>
+        prevList.map((item) => {
+          if (item.id === regionId) {
+            const currentVisits = item.visits || 0;
+            const newVisits = currentVisits + 1;
+            const currentFirst = item.year;
+            const currentLast = item.last;
+
+            let newFirst = currentFirst;
+            let newLast = currentLast;
+
+            if (year) {
+              newFirst = (!currentFirst || currentFirst <= 1 || year < currentFirst) ? year : currentFirst;
+              newLast = (!currentLast || currentLast <= 1 || year > currentLast) ? year : currentLast;
+            }
+
+            return {
+              ...item,
+              visits: newVisits,
+              year: newFirst,
+              last: newLast,
+            } as any;
+          }
+          return item;
+        })
+      );
+
+      const updatedNMData = {
+        token,
+        region: regionId,
+        quality: 3,
+        year_from: year || undefined,
+        month_from: undefined,
+        day_from: undefined,
+        year_to: year || undefined,
+        month_to: undefined,
+        day_to: undefined,
+        hidden: 0
+      };
+
+      try {
+        await addVisit(updatedNMData as never);
+
+        const res = await getRegion({ token, id: regionId });
+        if (res && res.region) {
+          const syncedData = res.region;
+          
+          const uDataCheck = userData as any;
+          if (uDataCheck && uDataCheck.id === regionId) {
+            setUserData((curr: any) => curr.id === regionId ? {
+              ...curr,
+              visited: true,
+              no_of_visits: syncedData.no_of_visits,
+              first_visit_year: syncedData.first_visited_in_year,
+              last_visit_year: syncedData.last_visited_in_year,
+              best_visit_quality: syncedData.best_visit_quality,
+            } : curr);
+          }
+
+          setNmRegions((currList) =>
+            currList.map((item) =>
+              item.id === regionId
+                ? ({
+                    ...item,
+                    visits: syncedData.no_of_visits,
+                    year: syncedData.first_visited_in_year,
+                    last: syncedData.last_visited_in_year,
+                    quality: syncedData.best_visit_quality,
+                  } as any)
+                : item
+            )
+          );
+        }
+      } catch (error) {
+        console.log('addVisit failed:', error);
+        setUserData(prevUserData);
+        setNmRegions(prevNmRegions);
+        Alert.alert('Error', 'Failed to add visit. Please try again.');
+      }
+    },
+    [userData, nmRegions, token, addVisit, getRegion]
+  );
+
   const handleUpdateDare = useCallback(
     (region: number, visits: 0 | 1) => {
       const updatedDARE = { ...userData, visited: visits > 0 ? true : false };
@@ -319,6 +432,7 @@ export const RegionProvider = ({ children }: { children: React.ReactNode }) => {
         setDareRegions,
         dareRegions,
         handleUpdateNM,
+        handleAddVisitNM,
         handleUpdateDare,
         handleUpdateNMList,
         handleUpdateDareList,

+ 14 - 14
src/screens/InAppScreens/MapScreen/RegionViewScreen/TravelSeriesList/index.tsx

@@ -85,21 +85,21 @@ const TravelSeriesList = ({
     <View style={[styles.travelSeriesList]}>
       {(indexSeries === 0 && includeAll)
         ? series.map((s) => {
-            return (
-              <View key={s?.series_id} style={{ minHeight: 40, gap: 8, marginBottom: 5 }}>
-                <Text style={styles.travelSeriesText}>{s?.series_name}</Text>
-                <FlatList
-                  data={s?.items}
-                  renderItem={(item) => renderTravelSeriesItem(item, s.series_id, s?.icon)}
-                  keyExtractor={(item) => item?.id.toString()}
-                  scrollEnabled={false}
-                />
-              </View>
-            );
-          })
+          return (
+            <View key={s?.series_id} style={{ minHeight: 40, gap: 8, marginBottom: 5 }}>
+              <Text style={styles.travelSeriesText}>{s?.series_name}</Text>
+              <FlatList
+                data={s?.items}
+                renderItem={(item) => renderTravelSeriesItem(item, s.series_id, s?.icon)}
+                keyExtractor={(item) => item?.id.toString()}
+                scrollEnabled={false}
+              />
+            </View>
+          );
+        })
         : series
-            .find((s) => s.series_id === routes[indexSeries].series_id)
-            ?.items?.map((item) => renderTravelSeriesItem({ item }, routes[indexSeries].series_id))}
+          .find((s) => s.series_id === routes[indexSeries].series_id)
+          ?.items?.map((item) => renderTravelSeriesItem({ item }, routes[indexSeries].series_id))}
     </View>
   );
 };

+ 67 - 33
src/screens/InAppScreens/MapScreen/RegionViewScreen/index.tsx

@@ -2,6 +2,7 @@ import React, { FC, useCallback, useEffect, useState } from 'react';
 import { View, Text, Image, TouchableOpacity, Platform } from 'react-native';
 import { styles } from './styles';
 import {
+  AddVisitModal,
   Button,
   CustomImageViewer,
   EditNmModal,
@@ -75,11 +76,13 @@ const RegionViewScreen: FC<Props> = ({ navigation, route }) => {
   const {
     handleUpdateNM: updateNM,
     handleUpdateDare: updateDare,
+    handleAddVisitNM,
     userData: regionData,
     setUserData: setRegionData,
     handleUpdateNMList,
     handleUpdateDareList
   } = useRegion();
+  const [isAddVisitModalVisible, setIsAddVisitModalVisible] = useState(false);
 
   useEffect(() => {
     navigation.getParent()?.setOptions({
@@ -127,9 +130,7 @@ const RegionViewScreen: FC<Props> = ({ navigation, route }) => {
         setName([regionName, subname]);
         setSeries(data?.data?.series || []);
         setRoutes(staticGroups);
-        if (regionData?.id !== regionId) {
-          setRegionData({ ...(data?.data || {}), type });
-        }
+        setRegionData({ ...(data?.data || {}), type, id: regionId });
         setIsLoading(false);
       };
 
@@ -147,7 +148,7 @@ const RegionViewScreen: FC<Props> = ({ navigation, route }) => {
 
   useEffect(() => {
     if (data && data.data) {
-      setRegionData({ ...(data?.data || {}), type });
+      setRegionData({ ...(data?.data || {}), type, id: regionId });
     }
   }, [isFetchedAfterMount])
 
@@ -470,35 +471,46 @@ const RegionViewScreen: FC<Props> = ({ navigation, route }) => {
                 </View>
               ) : null}
               {!disabled ? (
-                <TouchableOpacity
-                  style={[
-                    ButtonStyles.btn,
-                    (regionData?.visited || regionData.edit_in_trips === 1) && !disabled
-                      ? ButtonStyles.visitedButton
-                      : ButtonStyles.markVisitedButton
-                  ]}
-                  onPress={() => {
-                    if (regionData?.type === 'nm' && regionData?.edit_in_trips === 1) {
-                      handleOpenEditModal();
-                      return;
-                    }
-                    type === 'nm' ? handleUpdateNm() : handleUpdateDare();
-                  }}
-                >
-                  {regionData.edit_in_trips === 1 && !disabled && regionData?.type === 'nm' ? (
-                    <View style={ButtonStyles.visitedContainer}>
-                      <EditSvg width={14} height={14} />
-                      <Text style={ButtonStyles.visitedButtonText}>Edit</Text>
-                    </View>
-                  ) : regionData?.visited ? (
-                    <View style={ButtonStyles.visitedContainer}>
-                      <MarkIcon width={16} height={16} />
-                      <Text style={ButtonStyles.visitedButtonText}>Visited</Text>
-                    </View>
-                  ) : (
-                    <Text style={[ButtonStyles.markVisitedButtonText]}>Mark Visited</Text>
-                  )}
-                </TouchableOpacity>
+                type === 'nm' ? (
+                  <View style={{ flexDirection: 'row', gap: 8, justifyContent: 'flex-end', alignItems: 'center' }}>
+                    {(regionData?.visited || regionData?.edit_in_trips === 1) && (
+                      <TouchableOpacity
+                        style={[ButtonStyles.btn, ButtonStyles.visitedButton]}
+                        onPress={handleOpenEditModal}
+                      >
+                        <View style={ButtonStyles.visitedContainer}>
+                          <EditSvg width={14} height={14} />
+                          <Text style={ButtonStyles.visitedButtonText}>Edit</Text>
+                        </View>
+                      </TouchableOpacity>
+                    )}
+                    <TouchableOpacity
+                      style={[ButtonStyles.btn, ButtonStyles.markVisitedButton, { width: 110 }]}
+                      onPress={() => setIsAddVisitModalVisible(true)}
+                    >
+                      <Text style={[ButtonStyles.markVisitedButtonText, { textAlign: 'center' }]}>Add visit</Text>
+                    </TouchableOpacity>
+                  </View>
+                ) : (
+                  <TouchableOpacity
+                    style={[
+                      ButtonStyles.btn,
+                      regionData?.visited
+                        ? ButtonStyles.visitedButton
+                        : ButtonStyles.markVisitedButton
+                    ]}
+                    onPress={handleUpdateDare}
+                  >
+                    {regionData?.visited ? (
+                      <View style={ButtonStyles.visitedContainer}>
+                        <MarkIcon width={16} height={16} />
+                        <Text style={ButtonStyles.visitedButtonText}>Visited</Text>
+                      </View>
+                    ) : (
+                      <Text style={[ButtonStyles.markVisitedButtonText]}>Mark Visited</Text>
+                    )}
+                  </TouchableOpacity>
+                )
               ) : null}
             </View>
           </View>
@@ -692,6 +704,28 @@ const RegionViewScreen: FC<Props> = ({ navigation, route }) => {
         updateModalState={handleModalStateChange}
         updateNM={handleUpdateNmModal}
       />
+
+      <AddVisitModal
+        isVisible={isAddVisitModalVisible}
+        onClose={() => setIsAddVisitModalVisible(false)}
+        regionName={name[0] && name[1] ? name.join(' - ') : name[0]}
+        onSave={(year) => {
+          handleAddVisitNM(regionId, year).then(() => {
+            refetchData();
+          });
+
+          setSeries((currSeries) =>
+            currSeries.map((group) => ({
+              ...group,
+              items: group.items.map((subItem) =>
+                subItem.id === regionId
+                  ? { ...subItem, visited: 1 }
+                  : subItem
+              )
+            }))
+          );
+        }}
+      />
     </View>
   );
 };

+ 6 - 0
src/screens/InAppScreens/MapScreen/index.tsx

@@ -2751,6 +2751,12 @@ const MapScreen: any = ({ navigation, route }: { navigation: any; route: any })
               setCountriesVisited(updatedIds);
             }}
             openEditSlowModal={handleOpenEditSlowModal}
+            onAddVisitSuccess={(id) => {
+              if (!regionsVisited.includes(id)) {
+                setRegionsVisited([...regionsVisited, id]);
+              }
+              refetchVisitedCountries();
+            }}
           />
         </>
       ) : (

+ 20 - 47
src/screens/InAppScreens/TravelsScreen/Components/MyRegionsItems/NmRegionItem.tsx

@@ -17,19 +17,13 @@ export const NmRegionItem = React.memo(
   ({
     item,
     openEditModal,
-    updateNM,
+    onAddVisitPress,
     token,
     hideCountry = false
   }: {
     item: NmRegion;
     openEditModal: (item: NmRegion) => void;
-    updateNM: (
-      region: number,
-      first: number,
-      last: number,
-      visits: number,
-      quality: number
-    ) => void;
+    onAddVisitPress: (item: NmRegion) => void;
     token: string;
     hideCountry?: boolean;
   }) => {
@@ -102,11 +96,6 @@ export const NmRegionItem = React.memo(
 
           {token && (
             <View style={styles.btnContainer}>
-              {/* {item.visits === 1 && !item.future ? (
-                <TouchableOpacity onPress={() => openEditModal(item)} style={styles.editBtn}>
-                  <EditSvg width={14} height={14} />
-                </TouchableOpacity>
-              ) : null} */}
               {item.future ? (
                 <View style={{ flex: 1 }}>
                   <Text
@@ -122,41 +111,25 @@ export const NmRegionItem = React.memo(
                   </Text>
                 </View>
               ) : null}
-              <TouchableOpacity
-                style={[
-                  styles.btn,
-                  item.visits > 0 || item.edit_in_trips === 1
-                    ? styles.visitedButton
-                    : styles.markVisitedButton
-                ]}
-                onPress={() => {
-                  if (item.edit_in_trips === 1) {
-                    openEditModal(item);
-                    return;
-                  }
-                  updateNM(
-                    item.id,
-                    item.visits > 0 ? 0 : 1,
-                    item.visits > 0 ? 0 : 1,
-                    item.visits > 0 ? 0 : 1,
-                    3
-                  );
-                }}
-              >
-                {item.edit_in_trips === 1 ? (
-                  <View style={styles.visitedContainer}>
-                    <EditSvg width={14} height={14} />
-                    <Text style={styles.visitedButtonText}>Edit</Text>
-                  </View>
-                ) : item.visits > 0 ? (
-                  <View style={styles.visitedContainer}>
-                    <MarkIcon width={16} height={16} />
-                    <Text style={styles.visitedButtonText}>Visited</Text>
-                  </View>
-                ) : (
-                  <Text style={[styles.markVisitedButtonText]}>Mark visited</Text>
+              <View style={{ flexDirection: 'row', gap: 8, flex: 1, justifyContent: 'flex-end', alignItems: 'center' }}>
+                {(item.visits > 0 || item.edit_in_trips === 1) && (
+                  <TouchableOpacity
+                    onPress={() => openEditModal(item)}
+                    style={[styles.btn, styles.visitedButton, { flex: 0, paddingHorizontal: 12 }]}
+                  >
+                    <View style={styles.visitedContainer}>
+                      <EditSvg width={14} height={14} />
+                      <Text style={styles.visitedButtonText}>Edit</Text>
+                    </View>
+                  </TouchableOpacity>
                 )}
-              </TouchableOpacity>
+                <TouchableOpacity
+                  style={[styles.btn, styles.markVisitedButton, { width: 110 }]}
+                  onPress={() => onAddVisitPress(item)}
+                >
+                  <Text style={[styles.markVisitedButtonText, { textAlign: 'center' }]}>Add visit</Text>
+                </TouchableOpacity>
+              </View>
             </View>
           )}
         </View>

+ 23 - 12
src/screens/InAppScreens/TravelsScreen/Components/TripItem/index.tsx

@@ -13,6 +13,7 @@ import CalendarIcon from 'assets/icons/travels-screens/calendar.svg';
 import EditIcon from 'assets/icons/travels-screens/pen-to-square.svg';
 import ArrowIcon from 'assets/icons/chevron-left.svg';
 import WarningIcon from 'assets/icons/warning.svg';
+import TrashIcon from 'assets/icons/travels-screens/trash-solid.svg';
 import Tooltip from 'react-native-walkthrough-tooltip';
 
 const formatDate = (dateString: string | null) => {
@@ -114,7 +115,7 @@ const getTripDateLabel = (item: TripsData) => {
   );
 };
 
-const TripItem = ({ item }: { item: TripsData }) => {
+const TripItem = ({ item, onDelete }: { item: TripsData; onDelete?: (tripId: number) => void }) => {
   const navigation = useNavigation();
   const [showAllRegions, setShowAllRegions] = useState(false);
   const [toolTipVisible, setToolTipVisible] = useState(false);
@@ -179,17 +180,27 @@ const TripItem = ({ item }: { item: TripsData }) => {
           ) : null}
         </View>
 
-        <TouchableOpacity
-          style={styles.editButton}
-          onPress={() =>
-            navigation.navigate(
-              ...([NAVIGATION_PAGES.ADD_TRIP_2025, { editTripId: item.id }] as never)
-            )
-          }
-        >
-          <EditIcon />
-          <Text style={styles.editButtonText}>Edit</Text>
-        </TouchableOpacity>
+        <View style={{ flexDirection: 'row', gap: 8, alignItems: 'center' }}>
+          {item.regions?.length === 1 && onDelete && (
+            <TouchableOpacity
+              style={styles.deleteButton}
+              onPress={() => onDelete(item.id)}
+            >
+              <TrashIcon fill={Colors.RED} width={16} height={16} />
+            </TouchableOpacity>
+          )}
+          <TouchableOpacity
+            style={styles.editButton}
+            onPress={() =>
+              navigation.navigate(
+                ...([NAVIGATION_PAGES.ADD_TRIP_2025, { editTripId: item.id }] as never)
+              )
+            }
+          >
+            <EditIcon width={16} height={16} />
+            <Text style={styles.editButtonText}>Edit</Text>
+          </TouchableOpacity>
+        </View>
       </View>
       <View style={styles.divider} />
 

+ 8 - 0
src/screens/InAppScreens/TravelsScreen/Components/TripItem/styles.tsx

@@ -37,6 +37,14 @@ export const styles = StyleSheet.create({
     borderColor: Colors.BORDER_LIGHT,
     gap: 6
   },
+  deleteButton: {
+    alignItems: 'center',
+    justifyContent: 'center',
+    borderRadius: 6,
+    padding: 8,
+    borderWidth: 1,
+    borderColor: 'rgba(239, 91, 91, 0.5)'
+  },
   editButtonText: {
     fontSize: 13,
     color: Colors.DARK_BLUE,

+ 29 - 2
src/screens/InAppScreens/TravelsScreen/EditCountryDataScreen/index.tsx

@@ -4,7 +4,7 @@ import * as Progress from 'react-native-progress';
 import { useFocusEffect, useNavigation } from '@react-navigation/native';
 import moment from 'moment';
 
-import { Button, CheckBox, EditNmModal, Header, PageWrapper } from 'src/components';
+import { AddVisitModal, Button, CheckBox, EditNmModal, Header, PageWrapper } from 'src/components';
 import { NmRegionItem } from '../Components/MyRegionsItems/NmRegionItem';
 
 import { StoreType, storage } from 'src/storage';
@@ -57,12 +57,15 @@ const EditCountryDataScreen = ({ route }: { route: any }) => {
   const navigation = useNavigation();
   const {
     handleUpdateNMList: handleUpdateNM,
+    handleAddVisitNM,
     handleUpdateSlowList: handleUpdateSlowList,
     handleUpdateSlow: handleUpdateSlow,
     nmRegions,
     setNmRegions,
     setUserData
   } = useRegion();
+  const [selectedRegionForAddVisit, setSelectedRegionForAddVisit] = useState<NmRegion | null>(null);
+  const [isAddVisitModalVisible, setIsAddVisitModalVisible] = useState(false);
   const { mutateAsync: mutateCountriesData } = fetchCountryUserData();
 
   useEffect(() => {
@@ -211,7 +214,10 @@ const EditCountryDataScreen = ({ route }: { route: any }) => {
       <NmRegionItem
         item={item}
         openEditModal={handleOpenEditModal}
-        updateNM={() => updateRegion(item)}
+        onAddVisitPress={(regionItem) => {
+          setSelectedRegionForAddVisit(regionItem);
+          setIsAddVisitModalVisible(true);
+        }}
         token={token}
         hideCountry={true}
       />
@@ -268,6 +274,7 @@ const EditCountryDataScreen = ({ route }: { route: any }) => {
       {filteredNmRegions && filteredNmRegions?.length ? (
         <FlatList
           data={filteredNmRegions}
+          extraData={filteredNmRegions}
           renderItem={renderItem}
           keyExtractor={(item) => item.id.toString()}
           showsVerticalScrollIndicator={false}
@@ -357,6 +364,26 @@ const EditCountryDataScreen = ({ route }: { route: any }) => {
         updateModalState={handleModalStateChange}
         updateNM={handleUpdateNM}
       />
+
+      <AddVisitModal
+        isVisible={isAddVisitModalVisible}
+        onClose={() => setIsAddVisitModalVisible(false)}
+        regionName={selectedRegionForAddVisit?.region_name || ''}
+        onSave={(year) => {
+          if (selectedRegionForAddVisit) {
+            handleAddVisitNM(selectedRegionForAddVisit.id, year).then(() => {
+              mutateCountriesData(
+                { id: countryId, token },
+                {
+                  onSuccess: (data) => {
+                    setUserData({ type: 'countries', id: countryId, ...data.data });
+                  }
+                }
+              );
+            });
+          }
+        }}
+      />
     </PageWrapper>
   );
 };

+ 29 - 4
src/screens/InAppScreens/TravelsScreen/RegionsScreen/index.tsx

@@ -4,7 +4,7 @@ import * as Progress from 'react-native-progress';
 import { useFocusEffect, useNavigation } from '@react-navigation/native';
 import moment from 'moment';
 
-import { EditNmModal, Header, Input, PageWrapper } from 'src/components';
+import { AddVisitModal, EditNmModal, Header, Input, PageWrapper } from 'src/components';
 import { CustomButton } from '../Components';
 import { NmRegionItem } from '../Components/MyRegionsItems/NmRegionItem';
 import { RegionItem } from '../Components/MyRegionsItems/RegionItem';
@@ -38,7 +38,7 @@ const RegionsScreen = () => {
     name: 'ALL MEGAREGIONS'
   });
   const [selectedCountry, setSelectedCountry] = useState<{ name: string; id: number } | null>(null);
-  const { data: regionsQe } = useGetRegionsQuery(
+  const { data: regionsQe, refetch: refetchRegions } = useGetRegionsQuery(
     selectedCountry ? undefined : selectedMega.id,
     selectedCountry ? selectedCountry.id : undefined,
     String(token),
@@ -60,8 +60,10 @@ const RegionsScreen = () => {
     id: null
   });
   const navigation = useNavigation();
-  const { handleUpdateNMList: handleUpdateNM, nmRegions, setNmRegions, setUserData } = useRegion();
+  const { handleUpdateNMList: handleUpdateNM, handleAddVisitNM, nmRegions, setNmRegions, setUserData } = useRegion();
   const [search, setSearch] = useState<string>('');
+  const [selectedRegionForAddVisit, setSelectedRegionForAddVisit] = useState<NmRegion | null>(null);
+  const [isAddVisitModalVisible, setIsAddVisitModalVisible] = useState(false);
 
   useEffect(() => {
     const currentYear = moment().year();
@@ -72,6 +74,12 @@ const RegionsScreen = () => {
     handleModalStateChange({ years: yearSelector });
   }, []);
 
+  useFocusEffect(
+    useCallback(() => {
+      refetchRegions();
+    }, [refetchRegions])
+  );
+
   const handleModalStateChange = (updates: { [key: string]: any }) => {
     setModalState((prevState) => ({ ...prevState, ...updates }));
   };
@@ -252,7 +260,10 @@ const RegionsScreen = () => {
       <NmRegionItem
         item={item}
         openEditModal={handleOpenEditModal}
-        updateNM={handleUpdateNM}
+        onAddVisitPress={(regionItem) => {
+          setSelectedRegionForAddVisit(regionItem);
+          setIsAddVisitModalVisible(true);
+        }}
         token={token}
       />
     </TouchableOpacity>
@@ -324,6 +335,7 @@ const RegionsScreen = () => {
       {filteredNmRegions && (filteredNmRegions?.length || filteredTccRegions?.length) ? (
         <FlatList
           data={filteredNmRegions}
+          extraData={filteredNmRegions}
           renderItem={renderItem}
           keyExtractor={(item) => item.id.toString()}
           showsVerticalScrollIndicator={false}
@@ -380,6 +392,19 @@ const RegionsScreen = () => {
         updateModalState={handleModalStateChange}
         updateNM={handleUpdateNM}
       />
+
+      <AddVisitModal
+        isVisible={isAddVisitModalVisible}
+        onClose={() => setIsAddVisitModalVisible(false)}
+        regionName={selectedRegionForAddVisit?.region_name || ''}
+        onSave={(year) => {
+          if (selectedRegionForAddVisit) {
+            handleAddVisitNM(selectedRegionForAddVisit.id, year).then(() => {
+              refetchRegions();
+            });
+          }
+        }}
+      />
     </PageWrapper>
   );
 };

+ 69 - 9
src/screens/InAppScreens/TravelsScreen/Trips2025Screen/index.tsx

@@ -10,7 +10,8 @@ import {
   useGetTripsYearsQuery,
   useGetTripsForYearQuery,
   useGetTripsForRegionQuery,
-  useGetRegionsThatHaveTripsQuery
+  useGetRegionsThatHaveTripsQuery,
+  usePostDeleteTripMutation
 } from '@api/trips';
 import { StoreType, storage } from 'src/storage';
 import { TripsData } from '../utils/types';
@@ -88,6 +89,41 @@ const TripsScreen = ({ route }: { route: any }) => {
   } | null>(null);
   const [isWarningModalVisible, setIsWarningModalVisible] = useState(false);
   const [isModalVisible, setIsModalVisible] = useState(false);
+  const [tripToDelete, setTripToDelete] = useState<number | null>(null);
+  const [isDeleteConfirmVisible, setIsDeleteConfirmVisible] = useState(false);
+  const [pendingDelete, setPendingDelete] = useState(false);
+  const { mutate: deleteTrip } = usePostDeleteTripMutation();
+
+  const handleDeleteTrip = () => {
+    setIsDeleteConfirmVisible(false);
+    setPendingDelete(true);
+  };
+
+  const executeDeleteTrip = () => {
+    if (tripToDelete === null) return;
+    deleteTrip(
+      { token, trip_id: tripToDelete },
+      {
+        onSuccess: (res) => {
+          if (res && res.result === 'OK') {
+            refetch();
+            if (selectedYear) refetchTrips();
+            if (selectedRegion) refetchTripsForRegion();
+            refetchRegionsThatHaveTrips();
+            navigation.setParams({ deleted: true } as never);
+            setTripToDelete(null);
+            setTimeout(() => {
+              setIsWarningModalVisible(true);
+            }, 100);
+          }
+        },
+        onError: (err) => {
+          console.error('Delete trip failed:', err);
+          setTripToDelete(null);
+        }
+      }
+    );
+  };
 
   const flashListRef = useRef<FlashListRef<any> | null>(null);
 
@@ -125,12 +161,6 @@ const TripsScreen = ({ route }: { route: any }) => {
     }
   }, [route]);
 
-  useEffect(() => {
-    if (!isWarningModalVisible) {
-      navigation.setParams({ saved: false, updated: false, deleted: false } as never);
-    }
-  }, [isWarningModalVisible]);
-
   useEffect(() => {
     if (tripsData && tripsData.trips && !selectedRegion) {
       setStatistics(tripsData.statistics ?? null);
@@ -146,8 +176,16 @@ const TripsScreen = ({ route }: { route: any }) => {
   }, [tripsDataForRegion, selectedRegion]);
 
   const renderItem = useCallback(
-    ({ item }: { item: TripsData }) => <TripItem item={item} />,
-    [statistics]
+    ({ item }: { item: TripsData }) => (
+      <TripItem
+        item={item}
+        onDelete={(tripId) => {
+          setTripToDelete(tripId);
+          setIsDeleteConfirmVisible(true);
+        }}
+      />
+    ),
+    [statistics, setTripToDelete, setIsDeleteConfirmVisible]
   );
 
   const onAddNewTripPress = useCallback(() => {
@@ -361,6 +399,28 @@ const TripsScreen = ({ route }: { route: any }) => {
               ? 'This trip was successfully deleted.'
               : 'Trip was successfully updated'
         }
+        onModalHide={() => {
+          navigation.setParams({ saved: false, updated: false, deleted: false } as never);
+        }}
+      />
+
+      <WarningModal
+        type={'delete'}
+        isVisible={isDeleteConfirmVisible}
+        onClose={() => {
+          setIsDeleteConfirmVisible(false);
+        }}
+        title="Delete Trip"
+        message="Are you sure you want to delete your trip?"
+        action={handleDeleteTrip}
+        onModalHide={() => {
+          if (pendingDelete) {
+            setPendingDelete(false);
+            executeDeleteTrip();
+          } else {
+            setTripToDelete(null);
+          }
+        }}
       />
 
       <Modal