Viktoriia 1 settimana fa
parent
commit
9e9a04e401

+ 299 - 46
src/components/AddVisitModal/index.tsx

@@ -1,88 +1,342 @@
-import React, { useState, useEffect } from 'react';
-import { View, StyleSheet, Platform, Text } from 'react-native';
+import React, { useState, useEffect, useRef } from 'react';
+import {
+  View,
+  StyleSheet,
+  Platform,
+  Text,
+  TouchableOpacity,
+  Animated,
+  LayoutChangeEvent
+} from 'react-native';
 import { Picker as WheelPicker } from 'react-native-wheel-pick';
+import DateTimePicker, { DateType } from 'react-native-ui-datepicker';
+import dayjs from 'dayjs';
+import 'dayjs/locale/en';
+import calendar from 'dayjs/plugin/calendar';
+import updateLocale from 'dayjs/plugin/updateLocale';
+import localeData from 'dayjs/plugin/localeData';
+import customParseFormat from 'dayjs/plugin/customParseFormat';
+
+dayjs.locale('en');
+dayjs.extend(calendar);
+dayjs.extend(updateLocale);
+dayjs.extend(localeData);
+dayjs.extend(customParseFormat);
+
 import { Modal } from '../Modal';
 import { Button } from '../Button';
 import { ButtonVariants } from 'src/types/components';
 import { Colors } from 'src/theme';
+import Navigation from '../Calendars/RangeCalendar/Navigation';
+
+export type CalendarMode = 'exact' | 'approx';
 
 interface AddVisitModalProps {
   isVisible: boolean;
   onClose: () => void;
   regionName: string;
-  onSave: (year: number | null) => void;
+  onSave: (
+    startDate?: string | null,
+    endDate?: string | null,
+    approxYear?: number | null
+  ) => void;
 }
 
-const CURRENT_YEAR = new Date().getFullYear();
+export const isVisitInFuture = (
+  startDate?: string | null,
+  endDate?: string | null,
+  approxYear?: number | null
+): boolean => {
+  const currentYear = dayjs().year();
+  const todayISO = dayjs().format('YYYY-MM-DD');
+
+  if (approxYear !== undefined && approxYear !== null) {
+    return approxYear > currentYear;
+  }
+
+  if (startDate) {
+    if (!startDate.includes('-') && !isNaN(Number(startDate))) {
+      return Number(startDate) > currentYear;
+    }
+    return startDate > todayISO;
+  }
+
+  return false;
+};
+
+const CURRENT_YEAR = dayjs().year();
 const YEARS_DATA = [
   'No year',
   ...Array.from({ length: 100 }, (_, i) => String(CURRENT_YEAR - i))
 ];
 
+const FIXED_CONTENT_HEIGHT = 380;
+
+function TabSwitcher({
+  activeTab,
+  onTabChange
+}: {
+  activeTab: CalendarMode;
+  onTabChange: (tab: CalendarMode) => void;
+}) {
+  const slideAnim = useRef(new Animated.Value(activeTab === 'exact' ? 0 : 1)).current;
+  const [pillWidth, setPillWidth] = useState(0);
+
+  useEffect(() => {
+    Animated.spring(slideAnim, {
+      toValue: activeTab === 'exact' ? 0 : 1,
+      useNativeDriver: true,
+      tension: 68,
+      friction: 12
+    }).start();
+  }, [activeTab]);
+
+  const handleLayout = (e: LayoutChangeEvent) => {
+    setPillWidth((e.nativeEvent.layout.width - 8) / 2);
+  };
+
+  const translateX = slideAnim.interpolate({
+    inputRange: [0, 1],
+    outputRange: [0, pillWidth]
+  });
+
+  return (
+    <View style={tabStyles.wrapper} onLayout={handleLayout}>
+      <Animated.View style={[tabStyles.pill, { width: pillWidth, transform: [{ translateX }] }]} />
+      {(['exact', 'approx'] as CalendarMode[]).map((tab) => (
+        <TouchableOpacity
+          key={tab}
+          style={tabStyles.tab}
+          onPress={() => onTabChange(tab)}
+          activeOpacity={0.85}
+        >
+          <Text style={[tabStyles.label, activeTab === tab && tabStyles.labelActive]}>
+            {tab === 'exact' ? 'Exact dates' : 'Year'}
+          </Text>
+        </TouchableOpacity>
+      ))}
+    </View>
+  );
+}
+
+const tabStyles = StyleSheet.create({
+  wrapper: {
+    flexDirection: 'row',
+    backgroundColor: Colors.FILL_LIGHT,
+    borderRadius: 50,
+    padding: 4,
+    marginHorizontal: 16,
+    marginBottom: 12,
+    position: 'relative',
+    overflow: 'hidden'
+  },
+  pill: {
+    position: 'absolute',
+    top: 4,
+    left: 4,
+    bottom: 4,
+    borderRadius: 50,
+    backgroundColor: Colors.DARK_BLUE,
+    shadowColor: '#000',
+    shadowOffset: { width: 0, height: 2 },
+    shadowOpacity: 0.14,
+    shadowRadius: 4,
+    elevation: 4
+  },
+  tab: {
+    flex: 1,
+    paddingVertical: 8,
+    alignItems: 'center',
+    zIndex: 1
+  },
+  label: {
+    fontSize: 14,
+    fontWeight: '500',
+    color: Colors.TEXT_GRAY
+  },
+  labelActive: {
+    color: Colors.WHITE,
+    fontWeight: '600'
+  }
+});
+
 export const AddVisitModal: React.FC<AddVisitModalProps> = ({
   isVisible,
   onClose,
   regionName,
   onSave
 }) => {
+  const [activeTab, setActiveTab] = useState<CalendarMode>('approx');
   const [selectedYear, setSelectedYear] = useState<number | null>(CURRENT_YEAR);
 
+  const [selectedStartDate, setSelectedStartDate] = useState<string | null>(null);
+  const [selectedEndDate, setSelectedEndDate] = useState<string | null>(null);
+  const [startDate, setStartDate] = useState<DateType>(undefined);
+  const [endDate, setEndDate] = useState<DateType>(undefined);
+
   useEffect(() => {
     if (isVisible) {
+      setActiveTab('approx');
       setSelectedYear(CURRENT_YEAR);
+      setSelectedStartDate(null);
+      setSelectedEndDate(null);
+      setStartDate(undefined);
+      setEndDate(undefined);
     }
   }, [isVisible]);
 
+  const handleDateChange = (params: any) => {
+    setStartDate(params.startDate);
+    setEndDate(params.endDate);
+    if (params.startDate) setSelectedStartDate(dayjs(params.startDate).format('YYYY-MM-DD'));
+    setSelectedEndDate(params.endDate ? dayjs(params.endDate).format('YYYY-MM-DD') : null);
+  };
+
   const handleConfirm = () => {
-    onSave(selectedYear);
+    if (activeTab === 'approx') {
+      onSave(null, null, selectedYear);
+    } else {
+      onSave(selectedStartDate, selectedEndDate, null);
+    }
     onClose();
   };
 
+  const isDoneEnabled = activeTab === 'approx' ? true : !!selectedStartDate;
+
   return (
     <Modal
       visible={isVisible}
       onRequestClose={onClose}
-      headerTitle="Select year"
+      headerTitle="Select Dates"
       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 style={styles.content}>
+        {!!regionName && <Text style={styles.regionText}>{regionName}</Text>}
+        <TabSwitcher activeTab={activeTab} onTabChange={setActiveTab} />
+
+        <View style={{ height: FIXED_CONTENT_HEIGHT }}>
+          <View
+            style={[StyleSheet.absoluteFill, { opacity: activeTab === 'exact' ? 1 : 0 }]}
+            pointerEvents={activeTab === 'exact' ? 'auto' : 'none'}
+          >
+            <DateTimePicker
+              mode="range"
+              startDate={startDate}
+              endDate={endDate}
+              onChange={handleDateChange}
+              firstDayOfWeek={1}
+              timePicker={false}
+              showOutsideDays={true}
+              weekdaysFormat={'short'}
+              components={{
+                IconPrev: <Navigation direction="prev" />,
+                IconNext: <Navigation direction="next" />
+              }}
+              styles={{
+                header: { paddingBottom: 10 },
+                month_selector_label: { color: Colors.DARK_BLUE, fontWeight: 'bold', fontSize: 15 },
+                year_selector_label: { color: Colors.DARK_BLUE, fontWeight: 'bold', fontSize: 15 },
+                button_prev: {},
+                button_next: {},
+                weekday_label: { color: Colors.DARK_BLUE, fontWeight: 'bold', fontSize: 12 },
+                days: {},
+                day_cell: {},
+                day: {},
+                day_label: { color: Colors.DARK_BLUE, fontSize: 14, fontWeight: 'normal' },
+                today: {
+                  borderWidth: 1,
+                  borderRadius: 23,
+                  width: 46,
+                  height: 46,
+                  maxHeight: 46,
+                  borderColor: Colors.ORANGE
+                },
+                today_label: { color: Colors.DARK_BLUE, fontWeight: '500' },
+                selected: {
+                  backgroundColor: Colors.ORANGE,
+                  borderRadius: 23,
+                  width: 46,
+                  height: 46,
+                  maxHeight: 46,
+                  marginVertical: 'auto',
+                  alignItems: 'center',
+                  justifyContent: 'center'
+                },
+                selected_label: { color: 'white', fontWeight: '500' },
+                range_start: {
+                  backgroundColor: Colors.ORANGE,
+                  borderRadius: 23,
+                  width: 46,
+                  height: 46,
+                  alignItems: 'center',
+                  justifyContent: 'center'
+                },
+                range_start_label: { color: 'white' },
+                range_end: {
+                  backgroundColor: Colors.ORANGE,
+                  borderRadius: 23,
+                  width: 46,
+                  height: 46,
+                  alignItems: 'center',
+                  justifyContent: 'center'
+                },
+                range_end_label: { color: 'white' },
+                range_middle_label: { color: Colors.DARK_BLUE, fontWeight: '500' },
+                range_fill: { backgroundColor: Colors.ORANGE, opacity: 0.2 },
+                disabled: { opacity: 0.3 },
+                disabled_label: { color: Colors.TEXT_GRAY },
+                outside_label: { color: Colors.LIGHT_GRAY }
+              }}
+            />
+          </View>
+
+          <View
+            style={[
+              StyleSheet.absoluteFill,
+              { opacity: activeTab === 'approx' ? 1 : 0 },
+              Platform.OS === 'android' ? { justifyContent: 'center' } : {}
+            ]}
+            pointerEvents={activeTab === 'approx' ? 'auto' : 'none'}
+          >
+            <WheelPicker
+              style={[styles.wheel, Platform.OS === 'android' ? { height: '75%' } : {}]}
+              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: FIXED_CONTENT_HEIGHT
+              }}
+              selectedItemTextStyle={{
+                fontSize: 30,
+                fontFamily: 'montserrat-700',
+                color: Colors.DARK_BLUE
+              }}
+            />
+          </View>
+        </View>
       </View>
       <View style={styles.modalFooter}>
         <Button
           children="Add visit"
           onPress={handleConfirm}
-          variant={ButtonVariants.FILL}
+          disabled={!isDoneEnabled}
+          variant={!isDoneEnabled ? ButtonVariants.OPACITY : ButtonVariants.FILL}
           containerStyles={{ borderWidth: 0 }}
         />
       </View>
@@ -93,25 +347,24 @@ export const AddVisitModal: React.FC<AddVisitModalProps> = ({
 const styles = StyleSheet.create({
   content: {
     backgroundColor: Colors.WHITE,
-    justifyContent: 'center',
-    alignItems: 'center',
     paddingTop: 16,
-    paddingBottom: 8,
+    paddingBottom: 8
   },
   regionText: {
     fontSize: 14,
     fontFamily: 'montserrat-600',
     color: Colors.DARK_BLUE,
     textAlign: 'center',
-    paddingHorizontal: 24
+    paddingHorizontal: 24,
+    marginBottom: 12
   },
   wheel: {
     width: '100%',
-    backgroundColor: 'transparent',
+    backgroundColor: 'transparent'
   },
   modalFooter: {
     justifyContent: 'flex-end',
     width: '100%',
-    marginBottom: 24,
-  },
+    marginBottom: 24
+  }
 });

+ 18 - 1
src/components/Calendars/RangeCalendar/RangeCalendarWithTabs.tsx

@@ -195,7 +195,8 @@ export default function RangeCalendarWithTabs({
   initialMonth,
   withHint = false,
   defaultMode = 'exact',
-  initialApproxYear
+  initialApproxYear,
+  regionName
 }: {
   isModalVisible: boolean;
   closeModal: (
@@ -217,6 +218,7 @@ export default function RangeCalendarWithTabs({
   withHint?: boolean;
   defaultMode?: CalendarMode;
   initialApproxYear?: number | null;
+  regionName?: string;
 }) {
   const fallbackYear = CURRENT_YEAR;
 
@@ -346,6 +348,9 @@ export default function RangeCalendarWithTabs({
       headerTitle={allowRangeSelection ? 'Select Dates' : 'Select Date'}
     >
       <View style={styles.modalContent}>
+        {!!regionName && (
+          <Text style={modalStyles.regionText}>{regionName}</Text>
+        )}
         <TabSwitcher activeTab={activeTab} onTabChange={setActiveTab} />
 
         <View style={{ height: FIXED_CONTENT_HEIGHT }}>
@@ -465,3 +470,15 @@ export default function RangeCalendarWithTabs({
     </Modal>
   );
 }
+
+const modalStyles = StyleSheet.create({
+  regionText: {
+    fontSize: 14,
+    fontFamily: 'montserrat-600',
+    color: Colors.DARK_BLUE,
+    textAlign: 'center',
+    paddingHorizontal: 24,
+    marginBottom: 12
+  }
+});
+

+ 25 - 8
src/components/RegionPopup/index.tsx

@@ -14,7 +14,7 @@ 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';
+import { AddVisitModal, isVisitInFuture } from '../AddVisitModal';
 
 interface Region {
   id: number;
@@ -43,7 +43,7 @@ interface RegionPopupProps {
   disabled?: boolean;
   updateSlow: (id: number, v: boolean, s11: boolean, s31: boolean, s101: boolean) => void;
   openEditSlowModal: () => void;
-  onAddVisitSuccess?: (id: number) => void;
+  onAddVisitSuccess?: (id: number, isFuture?: boolean) => void;
 }
 
 const RegionPopup: React.FC<RegionPopupProps> = ({
@@ -225,7 +225,23 @@ const RegionPopup: React.FC<RegionPopupProps> = ({
               </View>
             )}
 
-            {(!userData?.visited || userData?.type === 'dare' || disabled) && (
+            {userData?.future ? (
+              <View style={{ flex: 1, flexShrink: 1, justifyContent: 'center' }}>
+                <Text
+                  style={{
+                    fontSize: 10,
+                    fontStyle: 'italic',
+                    color: Colors.DARK_BLUE,
+                    fontWeight: '500'
+                  }}
+                  numberOfLines={2}
+                >
+                  Visits planned in the future
+                </Text>
+              </View>
+            ) : null}
+
+            {!userData?.future && (!userData?.visited || userData?.type === 'dare' || disabled) && (
               <View style={styles.userImageContainer}>
                 {userAvatars?.map((avatar, index) => (
                   <Image key={index} source={{ uri: avatar }} style={styles.userImage} />
@@ -268,8 +284,8 @@ const RegionPopup: React.FC<RegionPopupProps> = ({
               </TouchableOpacity>
             ) : null}
             {userData?.type === 'nm' ? (
-              <View style={{ flexDirection: 'row', gap: 8, justifyContent: 'flex-end', alignItems: 'center' }}>
-                {(userData?.visited || userData?.edit_in_trips === 1) && !disabled && (
+              <View style={{ flexDirection: 'row', gap: 8, justifyContent: 'flex-end', alignItems: 'center', flexShrink: 0 }}>
+                {(userData?.visited || userData?.edit_in_trips === 1 || userData?.future === 1 || (userData?.no_of_visits ?? 0) > 0) && !disabled && (
                   <TouchableOpacity
                     onPress={openEditModal}
                     style={[styles.btn, styles.visitedButton, isSmallScreen ? { paddingHorizontal: 10 } : {}]}
@@ -374,9 +390,10 @@ const RegionPopup: React.FC<RegionPopupProps> = ({
           isVisible={isAddVisitModalVisible}
           onClose={() => setIsAddVisitModalVisible(false)}
           regionName={regionSubtitle ? `${regionTitle} - ${regionSubtitle}` : regionTitle}
-          onSave={(year) => {
-            handleAddVisitNM(region.id, year);
-            onAddVisitSuccess && onAddVisitSuccess(region.id);
+          onSave={(startDate, endDate, approxYear) => {
+            const isFuture = isVisitInFuture(startDate, endDate, approxYear);
+            handleAddVisitNM(region.id, startDate, endDate, approxYear);
+            onAddVisitSuccess && onAddVisitSuccess(region.id, isFuture);
           }}
         />
       )}

+ 4 - 2
src/components/RegionPopup/style.tsx

@@ -61,7 +61,9 @@ export const styles = StyleSheet.create({
     flexDirection: 'row',
     alignItems: 'center',
     marginLeft: 6,
-    gap: 8
+    gap: 8,
+    flex: 1,
+    flexShrink: 1
   },
   userImageContainer: {
     flexDirection: 'row',
@@ -114,7 +116,7 @@ export const styles = StyleSheet.create({
     justifyContent: 'flex-end',
     flexDirection: 'row',
     gap: 8,
-    flex: 1
+    flexShrink: 0
   },
   btn: {
     paddingVertical: 6,

+ 66 - 15
src/contexts/RegionContext.tsx

@@ -10,6 +10,8 @@ import { Alert } from 'react-native';
 import { DareRegion, NmRegion, SlowData } from 'src/screens/InAppScreens/TravelsScreen/utils/types';
 import { StoreType, storage } from 'src/storage';
 
+import { isVisitInFuture } from 'src/components/AddVisitModal';
+
 const RegionContext = createContext<any>(null);
 
 export const useRegion = () => useContext(RegionContext);
@@ -85,7 +87,52 @@ export const RegionProvider = ({ children }: { children: React.ReactNode }) => {
   );
 
   const handleAddVisitNM = useCallback(
-    async (regionId: number, year: number | null) => {
+    async (
+      regionId: number,
+      startDateOrYear?: string | number | null,
+      endDate?: string | null,
+      approxYear?: number | null
+    ) => {
+      let year_from: number | undefined = undefined;
+      let month_from: number | undefined = undefined;
+      let day_from: number | undefined = undefined;
+      let year_to: number | undefined = undefined;
+      let month_to: number | undefined = undefined;
+      let day_to: number | undefined = undefined;
+
+      let effectiveYear: number | null = null;
+
+      if (typeof startDateOrYear === 'number') {
+        effectiveYear = startDateOrYear;
+        year_from = startDateOrYear;
+        year_to = startDateOrYear;
+      } else if (approxYear !== undefined && approxYear !== null) {
+        effectiveYear = approxYear;
+        year_from = approxYear;
+        year_to = approxYear;
+      } else if (typeof startDateOrYear === 'string' && startDateOrYear) {
+        const startParts = startDateOrYear.split('-');
+        if (startParts.length === 3) {
+          year_from = Number(startParts[0]);
+          month_from = Number(startParts[1]);
+          day_from = Number(startParts[2]);
+          effectiveYear = year_from;
+        }
+        const endString = endDate || startDateOrYear;
+        const endParts = endString.split('-');
+        if (endParts.length === 3) {
+          year_to = Number(endParts[0]);
+          month_to = Number(endParts[1]);
+          day_to = Number(endParts[2]);
+        }
+      }
+
+      const isFuture = isVisitInFuture(
+        typeof startDateOrYear === 'string' ? startDateOrYear : null,
+        endDate,
+        typeof startDateOrYear === 'number' ? startDateOrYear : approxYear
+      );
+
       const prevUserData = { ...userData };
       const prevNmRegions = [...nmRegions];
 
@@ -99,14 +146,15 @@ export const RegionProvider = ({ children }: { children: React.ReactNode }) => {
         let newFirst = currentFirst;
         let newLast = currentLast;
 
-        if (year) {
-          newFirst = (!currentFirst || currentFirst <= 1 || year < currentFirst) ? year : currentFirst;
-          newLast = (!currentLast || currentLast <= 1 || year > currentLast) ? year : currentLast;
+        if (effectiveYear) {
+          newFirst = (!currentFirst || currentFirst <= 1 || effectiveYear < currentFirst) ? effectiveYear : currentFirst;
+          newLast = (!currentLast || currentLast <= 1 || effectiveYear > currentLast) ? effectiveYear : currentLast;
         }
 
         setUserData({
           ...userData,
-          visited: true,
+          visited: isFuture ? (uData.visited ?? false) : true,
+          future: isFuture ? 1 : (uData.future ?? 0),
           no_of_visits: newVisits,
           first_visit_year: newFirst,
           last_visit_year: newLast,
@@ -124,13 +172,14 @@ export const RegionProvider = ({ children }: { children: React.ReactNode }) => {
             let newFirst = currentFirst;
             let newLast = currentLast;
 
-            if (year) {
-              newFirst = (!currentFirst || currentFirst <= 1 || year < currentFirst) ? year : currentFirst;
-              newLast = (!currentLast || currentLast <= 1 || year > currentLast) ? year : currentLast;
+            if (effectiveYear) {
+              newFirst = (!currentFirst || currentFirst <= 1 || effectiveYear < currentFirst) ? effectiveYear : currentFirst;
+              newLast = (!currentLast || currentLast <= 1 || effectiveYear > currentLast) ? effectiveYear : currentLast;
             }
 
             return {
               ...item,
+              future: isFuture ? 1 : item.future,
               visits: newVisits,
               year: newFirst,
               last: newLast,
@@ -144,12 +193,12 @@ export const RegionProvider = ({ children }: { children: React.ReactNode }) => {
         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,
+        year_from,
+        month_from,
+        day_from,
+        year_to,
+        month_to,
+        day_to,
         hidden: 0
       };
 
@@ -164,7 +213,8 @@ export const RegionProvider = ({ children }: { children: React.ReactNode }) => {
           if (uDataCheck && uDataCheck.id === regionId) {
             setUserData((curr: any) => curr.id === regionId ? {
               ...curr,
-              visited: true,
+              visited: isFuture ? (curr.visited ?? false) : (syncedData.no_of_visits > 0),
+              future: isFuture ? 1 : (curr.future ?? 0),
               no_of_visits: syncedData.no_of_visits,
               first_visit_year: syncedData.first_visited_in_year,
               last_visit_year: syncedData.last_visited_in_year,
@@ -177,6 +227,7 @@ export const RegionProvider = ({ children }: { children: React.ReactNode }) => {
               item.id === regionId
                 ? ({
                     ...item,
+                    future: isFuture ? 1 : item.future,
                     visits: syncedData.no_of_visits,
                     year: syncedData.first_visited_in_year,
                     last: syncedData.last_visited_in_year,

+ 21 - 29
src/screens/InAppScreens/MapScreen/RegionViewScreen/index.tsx

@@ -426,8 +426,23 @@ const RegionViewScreen: FC<Props> = ({ navigation, route }) => {
 
         <View style={{ gap: 16 }}>
           <View
-            style={[styles.margin, { flexDirection: 'row', gap: 8, justifyContent: 'flex-end' }]}
+            style={[styles.margin, { flexDirection: 'row', gap: 8, justifyContent: 'flex-end', alignItems: 'center' }]}
           >
+            {regionData?.future ? (
+              <View style={{ flexDirection: 'row', alignItems: 'center', gap: 4 }}>
+                <Text
+                  style={{
+                    fontSize: 11,
+                    fontWeight: '500',
+                    color: Colors.DARK_BLUE,
+                    fontStyle: 'italic'
+                  }}
+                >
+                  Visits planned in the future
+                </Text>
+              </View>
+            ) : null}
+
             {regionData?.visited && regionData?.first_visit_year > 1 && !disabled && (
               <View style={styles.infoContent}>
                 <CalendarSvg height={18} width={18} fill={Colors.DARK_BLUE} />
@@ -446,34 +461,11 @@ const RegionViewScreen: FC<Props> = ({ navigation, route }) => {
           </View>
           <View style={[styles.nameContainer, styles.margin]}>
             <Text style={styles.title}>{name[0]}</Text>
-            <View style={ButtonStyles.btnContainer}>
-              {/* {regionData?.visited &&
-              type === 'nm' &&
-              !disabled &&
-              regionData?.no_of_visits === 1 ? (
-                <TouchableOpacity onPress={handleOpenEditModal} style={ButtonStyles.editBtn}>
-                  <EditSvg width={14} height={14} />
-                </TouchableOpacity>
-              ) : null} */}
-              {regionData.future ? (
-                <View style={{ flex: 1 }}>
-                  <Text
-                    style={{
-                      fontSize: 10,
-                      fontWeight: '500',
-                      color: Colors.DARK_BLUE,
-                      flexShrink: 1,
-                      fontStyle: 'italic'
-                    }}
-                  >
-                    Visits planned in the future
-                  </Text>
-                </View>
-              ) : null}
+            <View style={[ButtonStyles.btnContainer, { flex: 0 }]}>
               {!disabled ? (
                 type === 'nm' ? (
-                  <View style={{ flexDirection: 'row', gap: 8, justifyContent: 'flex-end', alignItems: 'center' }}>
-                    {(regionData?.visited || regionData?.edit_in_trips === 1) && (
+                  <View style={{ flexDirection: 'row', gap: 8, flexShrink: 0, justifyContent: 'flex-end', alignItems: 'center' }}>
+                    {(regionData?.visited || regionData?.edit_in_trips === 1 || regionData?.future === 1 || (regionData?.no_of_visits ?? 0) > 0) && (
                       <TouchableOpacity
                         style={[ButtonStyles.btn, ButtonStyles.visitedButton]}
                         onPress={handleOpenEditModal}
@@ -709,8 +701,8 @@ const RegionViewScreen: FC<Props> = ({ navigation, route }) => {
         isVisible={isAddVisitModalVisible}
         onClose={() => setIsAddVisitModalVisible(false)}
         regionName={name[0] && name[1] ? name.join(' - ') : name[0]}
-        onSave={(year) => {
-          handleAddVisitNM(regionId, year).then(() => {
+        onSave={(startDate, endDate, approxYear) => {
+          handleAddVisitNM(regionId, startDate, endDate, approxYear).then(() => {
             refetchData();
           });
 

+ 80 - 56
src/screens/InAppScreens/MapScreen/index.tsx

@@ -1474,16 +1474,20 @@ const MapScreen: any = ({ navigation, route }: { navigation: any; route: any })
           });
 
         if (tableName === 'regions') {
-          token
-            ? await mutateUserData(
-                { region_id: +foundRegion, token: String(token) },
-                {
-                  onSuccess: (data) => {
-                    setUserData({ type: 'nm', id: +foundRegion, ...data });
-                  }
+          const isVisitedLocal = regionsVisited.includes(+foundRegion);
+          setUserData({ type: 'nm', id: +foundRegion, visited: isVisitedLocal });
+          if (token) {
+            await mutateUserData(
+              { region_id: +foundRegion, token: String(token) },
+              {
+                onSuccess: (data) => {
+                  setUserData({ type: 'nm', id: +foundRegion, ...data });
                 }
-              )
-            : setUserData({ type: 'nm', id: +foundRegion });
+              }
+            );
+          } else {
+            setUserData({ type: 'nm', id: +foundRegion });
+          }
           if (regionsList && regionsList.data) {
             const region = regionsList.data.find((region) => region.id === +foundRegion);
             if (region && region.bbox) {
@@ -1499,16 +1503,20 @@ const MapScreen: any = ({ navigation, route }: { navigation: any; route: any })
             }
           }
         } else if (tableName === 'countries') {
-          token
-            ? await mutateCountriesData(
-                { id: +foundRegion, token },
-                {
-                  onSuccess: (data) => {
-                    setUserData({ type: 'countries', id: +foundRegion, ...data.data });
-                  }
+          const isVisitedLocal = countriesVisited.includes(+foundRegion);
+          setUserData({ type: 'countries', id: +foundRegion, visited: isVisitedLocal });
+          if (token) {
+            await mutateCountriesData(
+              { id: +foundRegion, token },
+              {
+                onSuccess: (data) => {
+                  setUserData({ type: 'countries', id: +foundRegion, ...data.data });
                 }
-              )
-            : setUserData({ type: 'countries', id: +foundRegion });
+              }
+            );
+          } else {
+            setUserData({ type: 'countries', id: +foundRegion });
+          }
           if (countriesList && countriesList.data) {
             const region = countriesList.data.find((region) => region.id === +foundRegion);
             if (region && region.bbox) {
@@ -1524,16 +1532,20 @@ const MapScreen: any = ({ navigation, route }: { navigation: any; route: any })
             }
           }
         } else {
-          token
-            ? await mutateUserDataDare(
-                { dare_id: +foundRegion, token: String(token) },
-                {
-                  onSuccess: (data) => {
-                    setUserData({ type: 'dare', id: +foundRegion, ...data });
-                  }
+          const isVisitedLocal = dareVisited.includes(+foundRegion);
+          setUserData({ type: 'dare', id: +foundRegion, visited: isVisitedLocal });
+          if (token) {
+            await mutateUserDataDare(
+              { dare_id: +foundRegion, token: String(token) },
+              {
+                onSuccess: (data) => {
+                  setUserData({ type: 'dare', id: +foundRegion, ...data });
                 }
-              )
-            : setUserData({ type: 'dare', id: +foundRegion });
+              }
+            );
+          } else {
+            setUserData({ type: 'dare', id: +foundRegion });
+          }
           if (dareList && dareList.data) {
             const region = dareList.data.find((region) => region.id === +foundRegion);
             if (region && region.bbox) {
@@ -1706,16 +1718,20 @@ const MapScreen: any = ({ navigation, route }: { navigation: any; route: any })
         });
 
       if (type === 'regions') {
-        token
-          ? await mutateUserData(
-              { region_id: id, token: String(token) },
-              {
-                onSuccess: (data) => {
-                  setUserData({ type: 'nm', id, ...data });
-                }
+        const isVisitedLocal = regionsVisited.includes(+id);
+        setUserData({ type: 'nm', id, visited: isVisitedLocal });
+        if (token) {
+          await mutateUserData(
+            { region_id: id, token: String(token) },
+            {
+              onSuccess: (data) => {
+                setUserData({ type: 'nm', id, ...data });
               }
-            )
-          : setUserData({ type: 'nm', id });
+            }
+          );
+        } else {
+          setUserData({ type: 'nm', id });
+        }
 
         if (regionsList && regionsList.data) {
           const region = regionsList.data.find((region) => region.id === +id);
@@ -1732,16 +1748,20 @@ const MapScreen: any = ({ navigation, route }: { navigation: any; route: any })
           }
         }
       } else if (type === 'countries') {
-        token
-          ? await mutateCountriesData(
-              { id, token },
-              {
-                onSuccess: (data) => {
-                  setUserData({ type: 'countries', id, ...data.data });
-                }
+        const isVisitedLocal = countriesVisited.includes(+id);
+        setUserData({ type: 'countries', id, visited: isVisitedLocal });
+        if (token) {
+          await mutateCountriesData(
+            { id, token },
+            {
+              onSuccess: (data) => {
+                setUserData({ type: 'countries', id, ...data.data });
               }
-            )
-          : setUserData({ type: 'countries', id });
+            }
+          );
+        } else {
+          setUserData({ type: 'countries', id });
+        }
 
         if (countriesList && countriesList.data) {
           const region = countriesList.data.find((region) => region.id === +id);
@@ -1758,16 +1778,20 @@ const MapScreen: any = ({ navigation, route }: { navigation: any; route: any })
           }
         }
       } else {
-        token
-          ? await mutateUserDataDare(
-              { dare_id: +id, token: String(token) },
-              {
-                onSuccess: (data) => {
-                  setUserData({ type: 'dare', id: +id, ...data });
-                }
+        const isVisitedLocal = dareVisited.includes(+id);
+        setUserData({ type: 'dare', id: +id, visited: isVisitedLocal });
+        if (token) {
+          await mutateUserDataDare(
+            { dare_id: +id, token: String(token) },
+            {
+              onSuccess: (data) => {
+                setUserData({ type: 'dare', id: +id, ...data });
               }
-            )
-          : setUserData({ type: 'dare', id: +id });
+            }
+          );
+        } else {
+          setUserData({ type: 'dare', id: +id });
+        }
 
         if (dareList && dareList.data) {
           const region = dareList.data.find((region) => region.id === +id);
@@ -2751,8 +2775,8 @@ const MapScreen: any = ({ navigation, route }: { navigation: any; route: any })
               setCountriesVisited(updatedIds);
             }}
             openEditSlowModal={handleOpenEditSlowModal}
-            onAddVisitSuccess={(id) => {
-              if (!regionsVisited.includes(id)) {
+            onAddVisitSuccess={(id, isFuture) => {
+              if (!isFuture && !regionsVisited.includes(id)) {
                 setRegionsVisited([...regionsVisited, id]);
               }
               refetchVisitedCountries();

+ 5 - 0
src/screens/InAppScreens/TravelsScreen/AddNewTrip2025Screen/index.tsx

@@ -792,6 +792,11 @@ const AddNewTripScreen = ({ route }: { route: any }) => {
         closeModal={closeRangeCalendar}
         defaultMode={calendarProps.defaultMode}
         initialApproxYear={calendarProps.initialApproxYear}
+        regionName={
+          calendarVisibleForIndex !== null
+            ? regions?.[calendarVisibleForIndex]?.region_name ?? (regions?.[calendarVisibleForIndex] as any)?.name
+            : undefined
+        }
         initialStartDate={
           calendarVisibleForIndex !== null &&
             regions?.[calendarVisibleForIndex]?.visitStartDate?.day

+ 6 - 4
src/screens/InAppScreens/TravelsScreen/Components/MyRegionsItems/NmRegionItem.tsx

@@ -99,22 +99,24 @@ export const NmRegionItem = React.memo(
           {token && (
             <View style={styles.btnContainer}>
               {item.future ? (
-                <View style={{ flex: 1 }}>
+                <View style={{ flex: 1, flexShrink: 1, justifyContent: 'center' }}>
                   <Text
                     style={[
                       styles.regionItemSubname,
                       {
                         fontSize: 10,
-                        fontStyle: 'italic'
+                        fontStyle: 'italic',
+                        color: Colors.DARK_BLUE
                       }
                     ]}
+                    numberOfLines={2}
                   >
                     Visits planned in the future
                   </Text>
                 </View>
               ) : null}
-              <View style={{ flexDirection: 'row', gap: 8, flex: 1, justifyContent: 'flex-end', alignItems: 'center' }}>
-                {(item.visits > 0 || item.edit_in_trips === 1) && (
+              <View style={{ flexDirection: 'row', gap: 8, flexShrink: 0, justifyContent: 'flex-end', alignItems: 'center' }}>
+                {(item.visits > 0 || item.edit_in_trips === 1 || item.future === 1) && (
                   <TouchableOpacity
                     onPress={() => openEditModal(item)}
                     style={[styles.btn, styles.visitedButton, { flex: 0, paddingHorizontal: 12 }, isSmallScreen ? { paddingHorizontal: 10 } : {}]}

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

@@ -369,9 +369,9 @@ const EditCountryDataScreen = ({ route }: { route: any }) => {
         isVisible={isAddVisitModalVisible}
         onClose={() => setIsAddVisitModalVisible(false)}
         regionName={selectedRegionForAddVisit?.region_name || ''}
-        onSave={(year) => {
+        onSave={(startDate, endDate, approxYear) => {
           if (selectedRegionForAddVisit) {
-            handleAddVisitNM(selectedRegionForAddVisit.id, year).then(() => {
+            handleAddVisitNM(selectedRegionForAddVisit.id, startDate, endDate, approxYear).then(() => {
               mutateCountriesData(
                 { id: countryId, token },
                 {

+ 2 - 2
src/screens/InAppScreens/TravelsScreen/RegionsScreen/index.tsx

@@ -397,9 +397,9 @@ const RegionsScreen = () => {
         isVisible={isAddVisitModalVisible}
         onClose={() => setIsAddVisitModalVisible(false)}
         regionName={selectedRegionForAddVisit?.region_name || ''}
-        onSave={(year) => {
+        onSave={(startDate, endDate, approxYear) => {
           if (selectedRegionForAddVisit) {
-            handleAddVisitNM(selectedRegionForAddVisit.id, year).then(() => {
+            handleAddVisitNM(selectedRegionForAddVisit.id, startDate, endDate, approxYear).then(() => {
               refetchRegions();
             });
           }