Browse Source

trips fixes

Viktoriia 4 days ago
parent
commit
15d5646ebb

+ 114 - 110
src/screens/InAppScreens/TravelsScreen/AddNewTrip2025Screen/index.tsx

@@ -28,6 +28,7 @@ interface DateValue {
 }
 
 interface RegionWithDates extends RegionAddData {
+  _instanceId?: string;
   visitStartDate?: DateValue | null;
   visitEndDate?: DateValue | null;
   year_from?: number;
@@ -52,15 +53,25 @@ const AddNewTripScreen = ({ route }: { route: any }) => {
   const [pendingDelete, setPendingDelete] = useState(false);
 
   const [calendarVisibleForIndex, setCalendarVisibleForIndex] = useState<number | null>(null);
-  const [calendarMinDate, setCalendarMinDate] = useState<string | undefined>(undefined);
-  const [calendarMaxDate, setCalendarMaxDate] = useState<string | undefined>(undefined);
 
-  const [regionErrors, setRegionErrors] = useState<{ [index: number]: string }>({});
+  const [regionErrors, setRegionErrors] = useState<{ [instanceId: string]: string }>({});
 
   const { mutate: saveNewTrip } = usePostSetNewTripMutation();
   const { mutate: updateTrip } = usePostUpdateTripMutation();
   const { mutate: deleteTrip } = usePostDeleteTripMutation();
 
+  const scrollRef = React.useRef<ScrollView>(null);
+  const instanceCounterRef = React.useRef(1);
+
+  const scrollToError = (errors: { [instanceId: string]: string }) => {
+    if (!regions || !Object.keys(errors).length) return;
+    const firstInstanceId = Object.keys(errors)[0];
+    const idx = regions.findIndex((r) => r._instanceId === firstInstanceId);
+    if (idx === -1) return;
+    const itemHeight = 160;
+    scrollRef.current?.scrollTo({ y: idx * itemHeight, animated: true });
+  };
+
   const autoFillAfterAppend = (list: RegionWithDates[]) => {
     if (!list || list.length === 0) return list;
 
@@ -69,6 +80,7 @@ const AddNewTripScreen = ({ route }: { route: any }) => {
     let lastWithDateIndex: number | null = null;
     for (let i = updated.length - 1; i >= 0; i--) {
       const r = updated[i];
+      r._instanceId = `r-${instanceCounterRef.current++}`;
       if (
         r.visitStartDate?.year &&
         r.visitStartDate?.month &&
@@ -98,6 +110,7 @@ const AddNewTripScreen = ({ route }: { route: any }) => {
       if (!hasStart || !hasEnd) {
         updated[i] = {
           ...r,
+          _instanceId: `r-${instanceCounterRef.current++}`,
           visitStartDate: lastDate,
           visitEndDate: lastDate
         };
@@ -127,8 +140,10 @@ const AddNewTripScreen = ({ route }: { route: any }) => {
       setDescription(editData.trip.description);
       setRegions(
         editData.trip.regions.map((region: any) => {
+          const instanceId = `r-${instanceCounterRef.current++}`;
           return {
             ...region,
+            _instanceId: instanceId,
             id: region.region,
             flag1: extractNumberAndExtension(region.flag1),
             flag2: extractNumberAndExtension(region.flag2),
@@ -174,7 +189,7 @@ const AddNewTripScreen = ({ route }: { route: any }) => {
 
   const validateRegionsDates = (regionsToValidate?: RegionWithDates[] | null) => {
     const list = regionsToValidate ?? regions;
-    const errors: { [index: number]: string } = {};
+    const errors: { [instanceId: string]: string } = {};
 
     if (!list || list.length === 0) {
       setRegionErrors(errors);
@@ -185,13 +200,14 @@ const AddNewTripScreen = ({ route }: { route: any }) => {
       const r = list[i];
       const s = r.visitStartDate;
       const e = r.visitEndDate;
+      const id = r._instanceId ?? `idx-${i}`;
 
       if (!s?.year || !s?.month || !s?.day) {
-        errors[i] = 'Please select visit dates';
+        errors[id] = 'Please select visit dates';
         continue;
       }
       if (!e?.year || !e?.month || !e?.day) {
-        errors[i] = 'Please select visit dates';
+        errors[id] = 'Please select visit dates';
         continue;
       }
 
@@ -199,7 +215,7 @@ const AddNewTripScreen = ({ route }: { route: any }) => {
       const eM = moment(`${e.year}-${e.month}-${e.day}`, 'YYYY-M-D');
 
       if (sM.isAfter(eM)) {
-        errors[i] = 'Start date cannot be after end date';
+        errors[id] = 'Start date cannot be after end date';
         continue;
       }
 
@@ -225,69 +241,65 @@ const AddNewTripScreen = ({ route }: { route: any }) => {
 
   const openRangeCalendarForRegion = (index: number) => {
     if (!regions) return;
-
-    const prevEnd = regions[index - 1]?.visitEndDate;
-    const nextStart = regions[index + 1]?.visitStartDate;
-
-    const min = isFullDate(prevEnd) ? dateValueToISO(prevEnd) : undefined;
-    const max = isFullDate(nextStart) ? dateValueToISO(nextStart) : undefined;
-
-    setCalendarMinDate(min as never);
-    setCalendarMaxDate(max as never);
     setCalendarVisibleForIndex(index);
   };
 
   const closeRangeCalendar = (startDate?: string | null, endDate?: string | null) => {
-    const idx = calendarVisibleForIndex;
+    const clickedInstanceIndex = calendarVisibleForIndex;
     setCalendarVisibleForIndex(null);
-    setCalendarMinDate(undefined);
-    setCalendarMaxDate(undefined);
-
-    if (idx === null || idx === undefined) return;
 
-    if (!startDate) return;
+    if (clickedInstanceIndex === null || clickedInstanceIndex === undefined || !startDate) return;
 
     const startVal = parseISOToDateValue(startDate);
     const endVal = parseISOToDateValue(endDate ?? startDate);
 
     if (!startVal || !endVal) return;
 
-    setRegions((prev) => {
-      if (!prev) return prev;
-      const updated = [...prev];
+    const openedInstanceId = regions?.[clickedInstanceIndex]?._instanceId;
+    if (!openedInstanceId) {
+      return;
+    }
 
-      updated[idx] = {
-        ...updated[idx],
-        visitStartDate: startVal,
-        visitEndDate: endVal
-      };
+    const updatedBeforeSort = (regions ?? []).map((r) =>
+      r._instanceId === openedInstanceId
+        ? { ...r, visitStartDate: startVal, visitEndDate: endVal }
+        : r
+    );
 
-      const next = updated[idx + 1];
-      if (next && (!next.visitStartDate?.year || !next.visitEndDate?.year)) {
-        updated[idx + 1] = {
+    const sortKey = (r: RegionWithDates) => {
+      const iso = dateValueToISO(r.visitStartDate);
+      return iso ?? '9999-12-31';
+    };
+    const sorted = [...updatedBeforeSort].sort((a, b) => sortKey(a).localeCompare(sortKey(b)));
+
+    const newIndex = sorted.findIndex((r) => r._instanceId === openedInstanceId);
+
+    if (newIndex !== -1) {
+      const next = sorted[newIndex + 1];
+      if (next && !isFullDate(next.visitStartDate)) {
+        sorted[newIndex + 1] = {
           ...next,
           visitStartDate: endVal,
           visitEndDate: endVal
         };
       }
-
-      const prevR = updated[idx - 1];
-      if (prevR && (!prevR.visitStartDate?.year || !prevR.visitEndDate?.year)) {
-        updated[idx - 1] = {
-          ...prevR,
+      const prev = sorted[newIndex - 1];
+      if (prev && !isFullDate(prev.visitStartDate)) {
+        sorted[newIndex - 1] = {
+          ...prev,
           visitStartDate: startVal,
           visitEndDate: startVal
         };
       }
+    }
 
-      validateRegionsDates(updated);
+    setRegions(sorted);
 
-      return updated;
-    });
+    validateRegionsDates(sorted);
 
     setRegionErrors((prev) => {
       const clone = { ...prev };
-      delete clone[idx];
+      delete clone[clickedInstanceIndex];
       return clone;
     });
   };
@@ -339,6 +351,7 @@ const AddNewTripScreen = ({ route }: { route: any }) => {
   const handleSaveNewTrip = () => {
     if (regions) {
       if (!validateRegionsDates(regions)) {
+        scrollToError(regionErrors);
         return;
       }
 
@@ -391,6 +404,7 @@ const AddNewTripScreen = ({ route }: { route: any }) => {
   const handleUpdateTrip = () => {
     if (regions) {
       if (!validateRegionsDates(regions)) {
+        scrollToError(regionErrors);
         return;
       }
 
@@ -445,6 +459,7 @@ const AddNewTripScreen = ({ route }: { route: any }) => {
     <PageWrapper style={{ flex: 1 }}>
       <Header label={editTripId ? 'Edit Trip' : 'Add New Trip'} />
       <ScrollView
+        ref={scrollRef}
         contentContainerStyle={{ flexGrow: 1, gap: 16 }}
         showsVerticalScrollIndicator={false}
       >
@@ -460,19 +475,6 @@ const AddNewTripScreen = ({ route }: { route: any }) => {
 
         <View style={{ marginBottom: 8 }}>
           <Text style={styles.regionsLabel}>Regions</Text>
-          <TouchableOpacity
-            style={styles.addRegionBtn}
-            onPress={() =>
-              navigation.navigate(
-                ...([
-                  NAVIGATION_PAGES.ADD_REGIONS_NEW,
-                  { regionsParams: regions, editId: editTripId }
-                ] as never)
-              )
-            }
-          >
-            <Text style={styles.addRegionBtntext}>Add visit</Text>
-          </TouchableOpacity>
           {regions && regions.length ? (
             <View style={styles.regionsContainer}>
               {regions.map((region, index) => {
@@ -484,7 +486,7 @@ const AddNewTripScreen = ({ route }: { route: any }) => {
                   dateValueToISO(region.visitStartDate) === dateValueToISO(region.visitEndDate)
                     ? startLabel
                     : region.visitStartDate?.year && region.visitEndDate?.year
-                      ? `${startLabel}  ${endLabel}`
+                      ? `${startLabel} - ${endLabel}`
                       : 'Select visit dates';
 
                 return (
@@ -498,23 +500,12 @@ const AddNewTripScreen = ({ route }: { route: any }) => {
                     datesLabel={datesLabel}
                     onMoveUp={() => moveRegionUp(index)}
                     onMoveDown={() => moveRegionDown(index)}
-                    errorMessage={regionErrors[index]}
+                    errorMessage={regionErrors[region?._instanceId ?? index]}
+                    startLabel={startLabel}
+                    endLabel={endLabel}
                   />
                 );
               })}
-              <TouchableOpacity
-                style={[styles.addRegionBtn, { marginTop: 0 }]}
-                onPress={() =>
-                  navigation.navigate(
-                    ...([
-                      NAVIGATION_PAGES.ADD_REGIONS_NEW,
-                      { regionsParams: regions, editId: editTripId }
-                    ] as never)
-                  )
-                }
-              >
-                <Text style={styles.addRegionBtntext}>Add visit</Text>
-              </TouchableOpacity>
             </View>
           ) : (
             <Text style={styles.noRegiosText}>No regions at the moment</Text>
@@ -522,60 +513,73 @@ const AddNewTripScreen = ({ route }: { route: any }) => {
         </View>
       </ScrollView>
 
-      <View style={styles.tabContainer}>
-        {editTripId ? (
-          <>
-            <TouchableOpacity
-              style={[styles.tabStyle, styles.deleteTab]}
-              onPress={() => setIsWarningModalVisible(true)}
-              disabled={isLoading === 'delete'}
-            >
-              {isLoading === 'delete' ? (
-                <ActivityIndicator size={18} color={Colors.RED} />
-              ) : (
-                <Text style={[styles.tabText, styles.deleteTabText]}>Delete Trip</Text>
-              )}
-            </TouchableOpacity>
+      <View style={{ flexDirection: 'column', gap: 6, backgroundColor: 'transparent' }}>
+        <TouchableOpacity
+          style={[styles.addRegionBtn]}
+          onPress={() =>
+            navigation.navigate(
+              ...([
+                NAVIGATION_PAGES.ADD_REGIONS_NEW,
+                { regionsParams: regions, editId: editTripId }
+              ] as never)
+            )
+          }
+        >
+          <Text style={styles.addRegionBtntext}>Add visit</Text>
+        </TouchableOpacity>
+        <View style={styles.tabContainer}>
+          {editTripId ? (
+            <>
+              <TouchableOpacity
+                style={[styles.tabStyle, styles.deleteTab]}
+                onPress={() => setIsWarningModalVisible(true)}
+                disabled={isLoading === 'delete'}
+              >
+                {isLoading === 'delete' ? (
+                  <ActivityIndicator size={18} color={Colors.RED} />
+                ) : (
+                  <Text style={[styles.tabText, styles.deleteTabText]}>Delete Trip</Text>
+                )}
+              </TouchableOpacity>
+              <TouchableOpacity
+                style={[
+                  styles.tabStyle,
+                  styles.addNewTab,
+                  disabled && { backgroundColor: Colors.LIGHT_GRAY, borderColor: Colors.LIGHT_GRAY }
+                ]}
+                onPress={handleUpdateTrip}
+                disabled={disabled || isLoading === 'update'}
+              >
+                {isLoading === 'update' ? (
+                  <ActivityIndicator size={18} color={Colors.WHITE} />
+                ) : (
+                  <Text style={[styles.tabText, styles.addNewTabText]}>Save Trip</Text>
+                )}
+              </TouchableOpacity>
+            </>
+          ) : (
             <TouchableOpacity
               style={[
                 styles.tabStyle,
                 styles.addNewTab,
-                disabled && { backgroundColor: Colors.LIGHT_GRAY, borderColor: Colors.LIGHT_GRAY }
+                disabled && { backgroundColor: Colors.LIGHT_GRAY, borderColor: Colors.LIGHT_GRAY },
+                { paddingVertical: 12 }
               ]}
-              onPress={handleUpdateTrip}
-              disabled={disabled || isLoading === 'update'}
+              onPress={handleSaveNewTrip}
+              disabled={disabled || isLoading === 'save'}
             >
-              {isLoading === 'update' ? (
+              {isLoading === 'save' ? (
                 <ActivityIndicator size={18} color={Colors.WHITE} />
               ) : (
-                <Text style={[styles.tabText, styles.addNewTabText]}>Save Trip</Text>
+                <Text style={[styles.tabText, styles.addNewTabText]}>Save New Trip</Text>
               )}
             </TouchableOpacity>
-          </>
-        ) : (
-          <TouchableOpacity
-            style={[
-              styles.tabStyle,
-              styles.addNewTab,
-              disabled && { backgroundColor: Colors.LIGHT_GRAY, borderColor: Colors.LIGHT_GRAY },
-              { paddingVertical: 12 }
-            ]}
-            onPress={handleSaveNewTrip}
-            disabled={disabled || isLoading === 'save'}
-          >
-            {isLoading === 'save' ? (
-              <ActivityIndicator size={18} color={Colors.WHITE} />
-            ) : (
-              <Text style={[styles.tabText, styles.addNewTabText]}>Add New Trip</Text>
-            )}
-          </TouchableOpacity>
-        )}
+          )}
+        </View>
       </View>
 
       <RangeCalendar
         isModalVisible={calendarVisibleForIndex !== null}
-        minDate={calendarMinDate}
-        maxDate={calendarMaxDate}
         allowRangeSelection={true}
         closeModal={(startDate?: string | null, endDate?: string | null) =>
           closeRangeCalendar(startDate, endDate)

+ 3 - 3
src/screens/InAppScreens/TravelsScreen/AddNewTrip2025Screen/styles.tsx

@@ -64,12 +64,12 @@ export const styles = StyleSheet.create({
     alignItems: 'center',
     borderRadius: 4,
     gap: 10,
-    padding: 10,
     borderColor: Colors.DARK_BLUE,
     borderWidth: 1,
     borderStyle: 'solid',
-    marginTop: 8,
-    marginBottom: 8
+    backgroundColor: Colors.WHITE,
+    paddingHorizontal: 16,
+    paddingVertical: 12
   },
   addRegionBtntext: {
     color: Colors.DARK_BLUE,

+ 13 - 13
src/screens/InAppScreens/TravelsScreen/AddRegionsNewScreen/index.tsx

@@ -289,18 +289,18 @@ const AddRegionsScreen = ({ route }: { route: any }) => {
               setRepeatPopup(null);
             }
 
-            if (regionsList) {
-              const region = regionsList.data.find((r) => r.id === id);
-              if (region) {
-                const bounds = turf.bbox(region.bbox);
-                cameraController.fitBounds(
-                  [bounds[2], bounds[3]],
-                  [bounds[0], bounds[1]],
-                  [50, 50, 50, 50],
-                  600
-                );
-              }
-            }
+            // if (regionsList) {
+            //   const region = regionsList.data.find((r) => r.id === id);
+            //   if (region) {
+            //     const bounds = turf.bbox(region.bbox);
+            //     cameraController.fitBounds(
+            //       [bounds[2], bounds[3]],
+            //       [bounds[0], bounds[1]],
+            //       [50, 50, 50, 50],
+            //       600
+            //     );
+            //   }
+            // }
           }
         }
       } catch (error) {
@@ -382,7 +382,7 @@ const AddRegionsScreen = ({ route }: { route: any }) => {
                 color: !selectedRegions.length ? Colors.LIGHT_GRAY : Colors.WHITE
               }}
             >
-              Save
+              Add
             </Text>
           </TouchableOpacity>
         </View>

+ 46 - 52
src/screens/InAppScreens/TravelsScreen/Components/RegionItemNew/index.tsx

@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { useState } from 'react';
 import { View, Text, Image, TouchableOpacity } from 'react-native';
 import { MaterialCommunityIcons } from '@expo/vector-icons';
 
@@ -20,6 +20,8 @@ interface RegionItemProps {
   index: number;
   total: number;
   errorMessage?: string;
+  startLabel: string;
+  endLabel: string;
 }
 
 const RegionItem = ({
@@ -31,10 +33,13 @@ const RegionItem = ({
   onMoveDown,
   index,
   total,
-  errorMessage
+  errorMessage,
+  startLabel,
+  endLabel
 }: RegionItemProps) => {
   const [name, ...rest] = region.region_name?.split(/ – | - /);
   const subname = rest?.join(' - ');
+  const [datesTooLong, setDatesTooLong] = useState(false);
 
   return (
     <View key={region.id} style={styles.regionItem}>
@@ -62,58 +67,47 @@ const RegionItem = ({
 
       <View style={styles.divider}></View>
 
-      <View style={styles.regionDatesContainer}>
-        <View style={styles.dateRow}>
-          <View style={{ flex: 1 }}>
-            <TouchableOpacity style={styles.regionSelector} onPress={onSelectDates}>
-              <CalendarSvg
-                fill={
-                  !region?.visitStartDate?.year || !region?.visitEndDate?.year
-                    ? Colors.TEXT_GRAY
-                    : Colors.DARK_BLUE
+      <View style={styles.dateRow}>
+        <View style={{ flex: 1 }}>
+          <TouchableOpacity style={styles.regionSelector} onPress={onSelectDates}>
+            <CalendarSvg
+              fill={
+                !region?.visitStartDate?.year || !region?.visitEndDate?.year
+                  ? Colors.TEXT_GRAY
+                  : Colors.DARK_BLUE
+              }
+              height={15}
+              width={15}
+            />
+            <Text
+              style={[
+                styles.dateText,
+                (!region?.visitStartDate?.year || !region?.visitEndDate?.year) &&
+                  styles.placeholderText
+              ]}
+              numberOfLines={2}
+              onTextLayout={(e) => {
+                if (e.nativeEvent.lines.length > 1) {
+                  setDatesTooLong(true);
+                } else {
+                  setDatesTooLong(false);
                 }
-                height={15}
-                width={15}
-              />
-              <Text
-                style={[
-                  styles.dateText,
-                  (!region?.visitStartDate?.year || !region?.visitEndDate?.year) &&
-                    styles.placeholderText
-                ]}
-              >
-                {datesLabel ?? 'Select visit dates'}
-              </Text>
-            </TouchableOpacity>
-
-            {errorMessage ? (
-              <Text style={{ color: Colors.RED, marginTop: 4, marginLeft: 4, fontSize: 12 }}>
-                {errorMessage}
-              </Text>
-            ) : null}
-          </View>
-
-          <View style={styles.actionsRow}>
-            <TouchableOpacity onPress={onMoveUp} style={styles.iconButton} disabled={index === 0}>
-              <MaterialCommunityIcons
-                name="arrow-up"
-                size={20}
-                color={index === 0 ? Colors.LIGHT_GRAY : Colors.DARK_BLUE}
-              />
-            </TouchableOpacity>
-
-            <TouchableOpacity
-              onPress={onMoveDown}
-              style={styles.iconButton}
-              disabled={index === total - 1}
+              }}
             >
-              <MaterialCommunityIcons
-                name="arrow-down"
-                size={20}
-                color={index === total - 1 ? Colors.LIGHT_GRAY : Colors.DARK_BLUE}
-              />
-            </TouchableOpacity>
-          </View>
+              {datesTooLong &&
+              region?.visitStartDate?.year &&
+              region?.visitEndDate?.year &&
+              startLabel !== endLabel
+                ? `${startLabel}\n${endLabel}`
+                : (datesLabel ?? 'Select visit dates')}
+            </Text>
+          </TouchableOpacity>
+
+          {errorMessage ? (
+            <Text style={{ color: Colors.RED, marginTop: 4, marginLeft: 4, fontSize: 12 }}>
+              {errorMessage}
+            </Text>
+          ) : null}
         </View>
       </View>
     </View>

+ 3 - 5
src/screens/InAppScreens/TravelsScreen/Components/RegionItemNew/styles.tsx

@@ -99,9 +99,6 @@ export const styles = StyleSheet.create({
   },
   divider: { height: 1, backgroundColor: Colors.DARK_LIGHT, marginVertical: 16 },
   completedContainer: { gap: 8, flexDirection: 'row', alignItems: 'center' },
-  regionDatesContainer: {
-    paddingBottom: 12
-  },
   regionDatesLabel: {
     fontSize: getFontSize(12),
     color: Colors.DARK_BLUE,
@@ -138,14 +135,15 @@ export const styles = StyleSheet.create({
     paddingHorizontal: 8,
     borderRadius: 6,
     height: 36,
-    backgroundColor: Colors.DARK_LIGHT,
+    backgroundColor: Colors.WHITE,
     justifyContent: 'flex-start',
     gap: 6
   },
   dateText: {
     fontSize: getFontSize(13),
     color: Colors.DARK_BLUE,
-    fontWeight: '600'
+    fontWeight: '600',
+    flex: 1
   },
   placeholderText: {
     color: Colors.TEXT_GRAY,