diff --git a/apps/expo/app/(app)/(drawer)/(tabs)/(stack)/trip/[tripId].tsx b/apps/expo/app/(app)/(drawer)/(tabs)/(stack)/trip/[tripId].tsx index d07f74fe8..ee12d9de1 100644 --- a/apps/expo/app/(app)/(drawer)/(tabs)/(stack)/trip/[tripId].tsx +++ b/apps/expo/app/(app)/(drawer)/(tabs)/(stack)/trip/[tripId].tsx @@ -1,7 +1,6 @@ import React from 'react'; -import PackContainer from 'app/components/pack/PackContainer'; -import { TripDetails } from 'app/screens/trip/TripDetails'; -import { DetailsComponent } from 'app/components/details'; +import EditTripScreen from 'app/screens/trip/editTrip'; + import { Platform } from 'react-native'; import { Stack } from 'expo-router'; import Head from 'expo-router/head'; @@ -24,7 +23,7 @@ export default function Trip() { // https://reactnavigation.org/docs/headers#replacing-the-title-with-a-custom-component }} /> - + ); } diff --git a/apps/next/pages/trip/[tripId].tsx b/apps/next/pages/trip/[tripId].tsx index b01f041b0..bc95d4f7c 100644 --- a/apps/next/pages/trip/[tripId].tsx +++ b/apps/next/pages/trip/[tripId].tsx @@ -1,4 +1,4 @@ -import { TripDetails } from 'app/screens/trip/TripDetails'; +import EditTripScreen from 'app/screens/trip/editTrip'; import { AuthWrapper } from 'app/modules/auth'; // export const runtime = 'experimental-edge' @@ -6,7 +6,7 @@ import { AuthWrapper } from 'app/modules/auth'; export default function Trip() { return ( <> - + ); } diff --git a/apps/tauri/src/routes/trip/$tripId.lazy.tsx b/apps/tauri/src/routes/trip/$tripId.lazy.tsx index 20504816b..5787ccc98 100644 --- a/apps/tauri/src/routes/trip/$tripId.lazy.tsx +++ b/apps/tauri/src/routes/trip/$tripId.lazy.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { TripDetails } from 'app/screens/trip/TripDetails'; +import EditTripScreen from 'app/screens/trip/editTrip'; import { AuthWrapper } from 'app/modules/auth'; import { createLazyFileRoute } from '@tanstack/react-router'; @@ -10,7 +10,7 @@ export const Route = createLazyFileRoute('/trip/$tripId')({ export default function Trip() { return ( - + ); } diff --git a/apps/vite/src/routes/trip/$tripId.lazy.tsx b/apps/vite/src/routes/trip/$tripId.lazy.tsx index 20504816b..eb100d868 100644 --- a/apps/vite/src/routes/trip/$tripId.lazy.tsx +++ b/apps/vite/src/routes/trip/$tripId.lazy.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { TripDetails } from 'app/screens/trip/TripDetails'; +import EditTripScreen from 'app/screens/trip/editTrip'; import { AuthWrapper } from 'app/modules/auth'; import { createLazyFileRoute } from '@tanstack/react-router'; @@ -8,9 +8,10 @@ export const Route = createLazyFileRoute('/trip/$tripId')({ }); export default function Trip() { + const { tripId } = Route.useParams(); return ( - + ); } diff --git a/packages/app/components/GearList/GearList.tsx b/packages/app/components/GearList/GearList.tsx index 35baaf1b9..1c83f3583 100644 --- a/packages/app/components/GearList/GearList.tsx +++ b/packages/app/components/GearList/GearList.tsx @@ -15,77 +15,102 @@ import { Accordion, Paragraph, Square } from 'tamagui'; import { PackSummary } from 'app/modules/pack/components/PackTable/PackSummary'; import { type WeightUnit } from 'app/utils/convertWeight'; import { ChevronDown } from '@tamagui/lucide-icons'; +import { AsyncView } from 'app/components/AsyncView'; +import { useTripPackId } from 'app/screens/trip/useTripPackId'; const RStack: any = OriginalRStack; const RText: any = OriginalRText; -export const GearList = () => { +export const GearList = ({ isViewOnlyMode }: { isViewOnlyMode: boolean }) => { const [isOpen, setIsOpen] = useState(false); const [weightUnit, setWeightUnit] = useState('kg'); - const [packId, setPackIdParam] = usePackId(); - const { data: currentPack } = useFetchSinglePack(packId); + const [packId, setPackIdParam] = useTripPackId(); + const { data: currentPack, isLoading, isError } = useFetchSinglePack(packId); + console.log({ packId }); return ( <> - + - - - - - - - - - - - - + {!isViewOnlyMode && ( + - {({ open }) => ( - <> - - Manage Pack - - - - - - )} - - - - + + Add packs to plan your trip essentials and stay prepared + for any adventure! + + } + /> + + + + )} + {currentPack ? ( + <> + + - - - - + + {!isViewOnlyMode && ( + + + + {({ open }) => ( + <> + + Manage Pack + + + + + + )} + + + + + + + )} + + ) : null} + diff --git a/packages/app/components/ScoreContainer.tsx b/packages/app/components/ScoreContainer.tsx index c81446f12..190c6eb98 100644 --- a/packages/app/components/ScoreContainer.tsx +++ b/packages/app/components/ScoreContainer.tsx @@ -12,6 +12,7 @@ import { } from 'app/hooks/score'; import { View } from 'react-native'; import { useMedia } from 'tamagui'; +import useResponsive from 'app/hooks/useResponsive'; interface ScoreProgressChartProps { score: number; @@ -187,6 +188,7 @@ export const ScoreContainer: React.FC = ({ const handleScoreClick = useCalculateStore(id, type); const media = useMedia(); + const { sm } = useResponsive(); return ( @@ -217,7 +219,10 @@ export const ScoreContainer: React.FC = ({ style={{ flex: 1, flexDirection: media.gtXs ? 'column' : 'row', - gap: 15, + flexWrap: 'wrap', + gap: sm ? 50 : 20, + justifyContent: 'center', + alignItems: 'center', }} > diff --git a/packages/app/components/trip/TripCards/TripMapCard.tsx b/packages/app/components/trip/TripCards/TripMapCard.tsx index 6f17f946a..af4e69897 100644 --- a/packages/app/components/trip/TripCards/TripMapCard.tsx +++ b/packages/app/components/trip/TripCards/TripMapCard.tsx @@ -36,9 +36,9 @@ export const TripMapCard = ({ > diff --git a/packages/app/components/trip/TripCards/TripSearchCard.tsx b/packages/app/components/trip/TripCards/TripSearchCard.tsx index 816944315..46499aecf 100644 --- a/packages/app/components/trip/TripCards/TripSearchCard.tsx +++ b/packages/app/components/trip/TripCards/TripSearchCard.tsx @@ -10,6 +10,7 @@ import { PlacesAutocomplete } from 'app/components/PlacesAutocomplete'; import useResponsive from 'app/hooks/useResponsive'; import { useScreenWidth } from 'app/hooks/common'; import { ArrowLeft } from '@tamagui/lucide-icons'; +import { useTripOSM } from 'app/screens/trip/useTripOSM'; interface TripSearchCardProps { searchRef: any; @@ -24,10 +25,10 @@ export const TripSearchCard = ({ onGoBack, onLocationSelect, }: TripSearchCardProps) => { - const [, setGEOLocation] = useGEOLocationSearch(); + const [, setTripOSM] = useTripOSM(); const handleSelectLocation = (geoJSON) => { - setGEOLocation(geoJSON); + setTripOSM(geoJSON); onLocationSelect(); }; diff --git a/packages/app/components/trip/TripForm.tsx b/packages/app/components/trip/TripForm.tsx index 727ef8af7..e18030411 100644 --- a/packages/app/components/trip/TripForm.tsx +++ b/packages/app/components/trip/TripForm.tsx @@ -7,12 +7,13 @@ import { SubmitButton, } from '@packrat/ui'; import { useRouter } from 'app/hooks/router'; -import { useAddTrip } from 'app/hooks/trips'; +import { useAddTrip, useEditTrips } from 'app/hooks/trips'; import { addTripForm } from '@packrat/validations/src/validations/tripRoutesValidator'; import { formatCreateTripValuesForAPI } from 'app/utils/tripUtils'; import { TripDateRangeCard } from './TripCards'; import { getEnumValues } from 'app/utils/getEnumValues'; import { formatTripActivityLabel } from 'app/utils/tripUtils'; +import { type addTripKey } from 'app/screens/trip/createTripStore/store'; const Form: any = OriginalForm; const FormSelect: any = OriginalFormSelect; @@ -22,6 +23,8 @@ interface TripFormProps { dateRange: any; setDateRange: (range: any) => void; isValid: boolean; + initialState?: Partial>; + tripId?: string; } const isPublicOptions = ['For me only', 'Public'].map((key, index) => ({ @@ -34,8 +37,11 @@ export const TripForm = ({ dateRange, setDateRange, isValid, + initialState, + tripId, }: TripFormProps) => { const { addTrip, isSuccess, data: response } = useAddTrip(); + const { editTrips } = useEditTrips(); const router = useRouter(); const handleCreateTrip = async (values) => { @@ -44,7 +50,7 @@ export const TripForm = ({ ...values, }; - addTrip(data); + tripId ? editTrips({ id: tripId, ...data }) : addTrip(data); }; if (isSuccess && response) { router.push(`/trip/${response.id}`); @@ -54,7 +60,12 @@ export const TripForm = ({
diff --git a/packages/app/hooks/destination/useCurrentDestination.ts b/packages/app/hooks/destination/useCurrentDestination.ts index 3fc90cb55..f0cef3943 100644 --- a/packages/app/hooks/destination/useCurrentDestination.ts +++ b/packages/app/hooks/destination/useCurrentDestination.ts @@ -3,8 +3,7 @@ import { useGEOLocationSearch } from 'app/hooks/geojson'; import { usePhotonDetail } from '../photonDetail'; import { parseCoordinates } from 'app/utils/coordinatesParser'; -export const useCurrentDestination = () => { - const [osm] = useGEOLocationSearch(); +export const useDestination = (osm: any) => { const { data, isError, isLoading } = usePhotonDetail(osm.name, true); const currentDestination = useMemo(() => { return data?.find((destination) => { @@ -24,3 +23,9 @@ export const useCurrentDestination = () => { return { currentDestination, latLng, isError, isLoading }; }; + +export const useCurrentDestination = () => { + const [osm] = useGEOLocationSearch(); + + return useDestination(osm); +}; diff --git a/packages/app/hooks/geojson/useGEOLocationSearch.ts b/packages/app/hooks/geojson/useGEOLocationSearch.ts index fe8505799..19d95fa78 100644 --- a/packages/app/hooks/geojson/useGEOLocationSearch.ts +++ b/packages/app/hooks/geojson/useGEOLocationSearch.ts @@ -1,4 +1,4 @@ -import { useMemo } from 'react'; +import { useMemo, useState } from 'react'; import { createParam } from '@packrat/crosspath'; interface GeoSearchParams { @@ -41,3 +41,25 @@ export const useGEOLocationSearch = (): [ return [formattedOSM, setGEOLocation]; }; + +export const useOSM = (): [GeoSearchParams, (geoJSON: any) => void] => { + const [geoLocation, setGeoLocation] = useState({}); + + const setGEOLocation = (geoJSON) => { + const newSearchParams: GeoSearchParams = {}; + + if ( + geoJSON?.properties?.osm_id && + geoJSON.properties.osm_type && + geoJSON.properties.name + ) { + newSearchParams.osmId = geoJSON.properties.osm_id; + newSearchParams.osmType = geoJSON.properties.osm_type; + newSearchParams.name = geoJSON.properties.name; + } + + setGeoLocation(newSearchParams); + }; + + return [geoLocation, setGEOLocation]; +}; diff --git a/packages/app/hooks/trips/useCreateTripForm.ts b/packages/app/hooks/trips/useCreateTripForm.ts index 4fbca111c..8ab48842d 100644 --- a/packages/app/hooks/trips/useCreateTripForm.ts +++ b/packages/app/hooks/trips/useCreateTripForm.ts @@ -6,11 +6,13 @@ import { type addTripKey } from 'app/screens/trip/createTripStore/store'; import { useAuthUser } from 'app/modules/auth'; import { usePackId } from 'app/modules/pack'; import { formatCreateTripValuesForAPI } from 'app/utils/tripUtils'; +import { useCurrentTripStore } from 'app/screens/trip/createTripStore/useCreateTripStore'; +import { useTripPackId } from 'app/screens/trip/useTripPackId'; export const useCreateTripForm = (currentDestination, photonDetails) => { - const { store, setTripValue, setDateRange } = useCreateTripStore(); + const { store, setTripValue, setDateRange } = useCurrentTripStore(); const authUser = useAuthUser(); - const [packId] = usePackId(); + const [packId] = useTripPackId(); const { isValid, validate } = useValidateSchema( addTripDetails, @@ -29,16 +31,19 @@ export const useCreateTripForm = (currentDestination, photonDetails) => { setTripValue(name, updatedPlaces); }; - const createTripFormValues = useMemo>>( - () => ({ + const createTripFormValues = useMemo>>(() => { + const res = { ...store, - destination: currentDestination?.properties?.name, owner_id: authUser?.id, pack_id: packId, - geoJSON: photonDetails, - }), - [store, packId, photonDetails, authUser?.id], - ); + }; + if (photonDetails) { + res.geoJSON = photonDetails; + res.destination = currentDestination?.properties?.name; + } + + return res; + }, [store, packId, photonDetails, authUser?.id]); useEffect(() => { validate(createTripFormValues); diff --git a/packages/app/modules/item/components/ItemDetailsContent.tsx b/packages/app/modules/item/components/ItemDetailsContent.tsx index 860db0831..27347e2cd 100644 --- a/packages/app/modules/item/components/ItemDetailsContent.tsx +++ b/packages/app/modules/item/components/ItemDetailsContent.tsx @@ -7,6 +7,7 @@ import { SMALLEST_ITEM_UNIT } from '../constants'; import useTheme from 'app/hooks/useTheme'; import useResponsive from 'app/hooks/useResponsive'; import { openExternalLink } from 'app/utils'; +import RPrimaryButton from 'app/components/RPrimaryButton'; interface ItemData { title: string; @@ -62,14 +63,12 @@ const ItemDetailsContent = ({ itemData }: { itemData: ItemData }) => { Seller: {itemData.seller} - { openExternalLink(itemData.productUrl); }} - style={styles.GoToStoreButton} - > - Go to Store - + label="Go to Store" + /> ); @@ -82,7 +81,7 @@ const loadStyles = (theme: any) => { return { container: { flex: 1, - padding: xxs ? 0 : xs ? 8 : 10, + flexWrap: 'wrap', }, detailsContainer: { flex: 1, @@ -93,64 +92,42 @@ const loadStyles = (theme: any) => { title: { fontSize: xxs ? 16 : xs ? 16 : sm ? 18 : 20, fontWeight: 'bold', - maxHeight: 60, + maxHeight: 30, marginVertical: xxs ? 0 : 5, }, infoRow: { flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - marginVertical: xxs ? 0 : xs ? 3 : 5, - flexShrink: 1, + flexWrap: 'wrap', }, categoryText: { - fontSize: xxs ? 12 : xs ? 12 : sm ? 14 : 16, - fontWeight: '400', + fontSize: xxs ? 12 : xs ? 12 : sm ? 14 : 14, + fontWeight: 400, }, weightText: { fontSize: xxs ? 16 : xs ? 16 : 18, - fontWeight: xxs ? '800' : xs ? '700' : '600', + fontWeight: xxs ? 800 : xs ? 700 : 600, }, descriptionSection: { maxHeight: 60, marginVertical: xxs ? 0 : 5, - padding: xxs ? 0 : 5, + // padding: xs ? 0 : 5, }, descriptionText: { - fontSize: xxs ? 12 : xs ? 12 : sm ? 14 : 16, + fontSize: xxs ? 12 : xs ? 12 : sm ? 14 : 14, }, skuSellerRow: { - flexDirection: 'row', - justifyContent: 'space-between', - alignItems: 'center', - paddingBottom: xxs ? 0 : 5, - height: 30, + flexDirection: 'column', + flexWrap: 'wrap', }, skuText: { - fontSize: xxs ? 9 : 10, - fontWeight: '600', - flexShrink: 1, - maxWidth: '40%', - paddingTop: xxs ? 13 : 0, + fontSize: xxs ? 10 : 12, + fontWeight: 400, + maxHeight: 20, }, sellerText: { - fontSize: xxs ? 9 : 10, - fontWeight: '600', - flexGrow: 0, - flexShrink: 0, - }, - GoToStoreButton: { - backgroundColor: currentTheme.colors.secondaryBlue, - paddingVertical: xxs ? 4 : 6, - paddingHorizontal: xxs ? 0 : 10, - borderRadius: 3, - alignItems: 'center', - marginTop: 5, - }, - buttonText: { - color: currentTheme.colors.text, - fontWeight: 'bold', fontSize: xxs ? 10 : 12, + fontWeight: 400, + maxHeight: 20, }, }; }; diff --git a/packages/app/modules/pack/widgets/AddPackContainer.tsx b/packages/app/modules/pack/widgets/AddPackContainer.tsx index aa05f7be5..a09ff6958 100644 --- a/packages/app/modules/pack/widgets/AddPackContainer.tsx +++ b/packages/app/modules/pack/widgets/AddPackContainer.tsx @@ -16,7 +16,12 @@ export const AddPackContainer = ({ } /> + } + /> } footerComponent={undefined} > diff --git a/packages/app/modules/pack/widgets/PackContainer.tsx b/packages/app/modules/pack/widgets/PackContainer.tsx index ce0898748..68c4143fe 100644 --- a/packages/app/modules/pack/widgets/PackContainer.tsx +++ b/packages/app/modules/pack/widgets/PackContainer.tsx @@ -1,30 +1,20 @@ -import React, { useEffect, useState, useRef, useCallback } from 'react'; +import React, { useCallback } from 'react'; import { View } from 'react-native'; -import { AddItemModal } from 'app/modules/item'; import useCustomStyles from 'app/hooks/useCustomStyles'; -import { useAuthUser } from 'app/modules/auth'; -import { - usePackId, - useUserPacks, - PackPickerOverlay, - useFetchSinglePack, -} from 'app/modules/pack'; -import { - DropdownComponent, - RButton, - RListItem, - RStack, - useModalState, -} from '@packrat/ui'; -import { Spinner } from 'tamagui'; -import useTheme from 'app/hooks/useTheme'; -import { TableContainerComponent } from 'app/screens/trip/TripDetailsComponents'; +import { PackPickerOverlay, useFetchSinglePack } from 'app/modules/pack'; +import { RListItem, useModalState } from '@packrat/ui'; import { Backpack, Edit3 } from '@tamagui/lucide-icons'; +import { useTripPackId } from 'app/screens/trip/useTripPackId'; -export default function PackContainer() { - const [packIdParam, setPackIdParam] = usePackId(); +interface PackContainerProps { + emptyStateComponent?: React.ReactNode; +} +export default function PackContainer({ + emptyStateComponent: EmptyStateComponent, +}: PackContainerProps) { + const [packIdParam, setPackIdParam] = useTripPackId(); const { isModalOpen, onClose, onOpen } = useModalState(); - const { data: currentPack } = useFetchSinglePack(packIdParam); + const { data: currentPack, isLoading } = useFetchSinglePack(packIdParam); const styles = useCustomStyles(loadStyles); const onSelectPack = (packId: string) => { @@ -63,6 +53,7 @@ export default function PackContainer() { {currentPack?.name} ) : null} + {!isLoading && !currentPack && EmptyStateComponent} ); } diff --git a/packages/app/modules/trip/context/tripContext.tsx b/packages/app/modules/trip/context/tripContext.tsx new file mode 100644 index 000000000..16c96e27b --- /dev/null +++ b/packages/app/modules/trip/context/tripContext.tsx @@ -0,0 +1,26 @@ +import { type useOSM } from 'app/hooks/geojson'; +import React, { createContext, useContext, type ReactNode } from 'react'; +import { type useCurrentTripStore } from 'app/screens/trip/createTripStore/useCreateTripStore'; + +interface TripContextType { + tripId?: string; + // used in edit mode + tripPack?: [packId: string, setPackId: (packId: string) => void]; + tripOSM?: ReturnType; + tripStore?: ReturnType; +} +const TripContext = createContext({}); + +export const TripProvider = ({ + children, + value, +}: { + children: ReactNode; + value: TripContextType; +}) => { + return {children}; +}; + +export const useTripContext = () => { + return useContext(TripContext); +}; diff --git a/packages/app/screens/trip/TripInfo.tsx b/packages/app/screens/trip/TripInfo.tsx new file mode 100644 index 000000000..f878d7ef1 --- /dev/null +++ b/packages/app/screens/trip/TripInfo.tsx @@ -0,0 +1,29 @@ +import React from 'react'; +import { RText, View, XStack, YStack } from '@packrat/ui'; +import { LayoutCard } from 'app/components/LayoutCard'; +import { FontAwesome5 } from '@expo/vector-icons'; +import useTheme from 'app/hooks/useTheme'; + +export const TripInfo = ({ tripInfo }: { tripInfo: any }) => { + const { currentTheme } = useTheme(); + + return ( + + + {tripInfo.description} + + + Activity: + {tripInfo.activity} + + + + ); +}; diff --git a/packages/app/screens/trip/TripScreen.tsx b/packages/app/screens/trip/TripScreen.tsx new file mode 100644 index 000000000..7f3ee9da6 --- /dev/null +++ b/packages/app/screens/trip/TripScreen.tsx @@ -0,0 +1,162 @@ +import React, { useState } from 'react'; +import { RStack, RText, XStack, YStack } from '@packrat/ui'; +import { FlatList, View } from 'react-native'; +import { useRef } from 'react'; +import { GearList } from '../../components/GearList/GearList'; +import { TripForm } from 'app/components/trip/TripForm'; +import useTheme from '../../hooks/useTheme'; +import { useCreateTripForm } from 'app/hooks/trips/useCreateTripForm'; +import { useTripsData } from './useTripsData'; +import Layout from 'app/components/layout/Layout'; +import RSecondaryButton from 'app/components/RSecondaryButton'; + +import { TripMapCard, TripSearchCard } from 'app/components/trip/TripCards'; +import { WeatherData } from 'app/components/weather/WeatherData'; +import { useFlatList } from 'app/hooks/useFlatList'; +import useResponsive from 'app/hooks/useResponsive'; +import { LayoutCard } from 'app/components/LayoutCard'; +import { MapPin, MapPinned } from '@tamagui/lucide-icons'; +import { type addTripKey } from './createTripStore/store'; +import { useAuthUser } from 'app/modules/auth'; +import { TripInfo } from './TripInfo'; + +const SECTIONS = { + MAP: 'MAP', + PLACE_NAME: 'PLACE_NAME', + PACK: 'PACK', + WEATHER: 'WEATHER', +}; + +export function TripScreen({ + tripId, + initialBounds, + initialPlaceName, + initialState, + ownerId, +}: { + tripId?: string; + initialBounds?: any; + initialPlaceName?: string; + initialState?: Partial>; + ownerId?: string; +}) { + const placesAutoCompleteRef = useRef({}); + const [isChangePlaceMode, setIsChangePlaceMode] = useState(false); + const { gtSm } = useResponsive(); + const authUser = useAuthUser(); + const hasPermissionToEdit = ownerId === authUser?.id; + const isViewOnlyMode = !hasPermissionToEdit && tripId; + + const { + currentDestination, + photonDetails, + isPhotonLoading, + hasPhotonError, + latLng, + } = useTripsData(); + + const { isValid, dateRange, setDateRange, tripStore, setTripValue } = + useCreateTripForm(currentDestination, photonDetails); + + const { flatListData, keyExtractor, renderItem } = useFlatList(SECTIONS, { + [SECTIONS.MAP]: ( + { + setTripValue('bounds', bounds); + }} + /> + ), + [SECTIONS.PLACE_NAME]: ( + + + + {photonDetails?.features?.[0]?.properties?.['name:en'] || + photonDetails?.features?.[0]?.properties?.name || + initialPlaceName} + + + ), + [SECTIONS.WEATHER]: !tripId ? ( + + + + ) : undefined, + [SECTIONS.PACK]: , + }); + + return ( + + {(isChangePlaceMode || (!latLng && !tripId)) && ( + setIsChangePlaceMode(false)} + onLocationSelect={() => setIsChangePlaceMode(false)} + /> + )} + {latLng || tripId ? ( + + + + {isViewOnlyMode ? 'Trip Details' : 'Plan Your Trip'} + + {!isViewOnlyMode && ( + } + size={36} + borderWidth={2} + onPress={() => setIsChangePlaceMode(true)} + label="Change Direction" + /> + )} + + + + ( + + )} + renderItem={renderItem} + /> + + {!isViewOnlyMode ? ( + + + + ) : ( + + )} + + + ) : null} + + ); +} diff --git a/packages/app/screens/trip/createTrip.tsx b/packages/app/screens/trip/createTrip.tsx index 7c5e056e9..81e4285bc 100644 --- a/packages/app/screens/trip/createTrip.tsx +++ b/packages/app/screens/trip/createTrip.tsx @@ -1,151 +1,13 @@ -import React, { useState } from 'react'; -import { RStack, RText, XStack, YStack } from '@packrat/ui'; -import { FlatList, View } from 'react-native'; -import { useRef } from 'react'; -import { GearList } from '../../components/GearList/GearList'; -import { TripForm } from 'app/components/trip/TripForm'; -import useTheme from '../../hooks/useTheme'; -import { useCreateTripForm } from 'app/hooks/trips/useCreateTripForm'; -import { useTripsData } from './useTripsData'; -import Layout from 'app/components/layout/Layout'; -import RSecondaryButton from 'app/components/RSecondaryButton'; - -import { TripMapCard, TripSearchCard } from 'app/components/trip/TripCards'; -import { WeatherData } from 'app/components/weather/WeatherData'; -import { useFlatList } from 'app/hooks/useFlatList'; -import useResponsive from 'app/hooks/useResponsive'; -import { LayoutCard } from 'app/components/LayoutCard'; -import { MapPin, MapPinned } from '@tamagui/lucide-icons'; - -const SECTIONS = { - MAP: 'MAP', - PLACE_NAME: 'PLACE_NAME', - PACK: 'PACK', - WEATHER: 'WEATHER', -}; - -function Trips() { - const placesAutoCompleteRef = useRef({}); - const [isChangePlaceMode, setIsChangePlaceMode] = useState(false); - const { gtSm } = useResponsive(); - - const { currentTheme } = useTheme(); - const { - currentDestination, - photonDetails, - isPhotonLoading, - hasPhotonError, - latLng, - } = useTripsData(); - - const { isValid, dateRange, setDateRange, tripStore, setTripValue } = - useCreateTripForm(currentDestination, photonDetails); - - const { flatListData, keyExtractor, renderItem } = useFlatList(SECTIONS, { - [SECTIONS.MAP]: ( - { - setTripValue('bounds', bounds); - }} - /> - ), - [SECTIONS.PLACE_NAME]: ( - - - - {photonDetails?.features?.[0]?.properties?.['name:en'] || - photonDetails?.features?.[0]?.properties?.name} - - - ), - [SECTIONS.WEATHER]: ( - - - - ), - [SECTIONS.PACK]: , - }); +import React from 'react'; +import { TripProvider } from 'app/modules/trip/context/tripContext'; +import { TripScreen } from './TripScreen'; +import { useCreateTripStore } from './createTripStore'; +export default function CreateTrip() { + const tripStore = useCreateTripStore(); return ( - - {(isChangePlaceMode || !latLng) && ( - setIsChangePlaceMode(false)} - onLocationSelect={() => setIsChangePlaceMode(false)} - /> - )} - {latLng ? ( - - - - Plan Your Trip - - } - size={36} - borderWidth={2} - onPress={() => setIsChangePlaceMode(true)} - label="Change Direction" - /> - - - - ( - - )} - renderItem={renderItem} - /> - - - - - - - ) : null} - + + + ); } - -const loadStyles = () => { - const { currentTheme } = useTheme(); - - return { - mutualStyles: { - backgroundColor: currentTheme.colors.background, - flex: 1, - flexDirection: 'column', - height: '100%', - paddingBottom: 30, - }, - container: { - gap: 50, - padding: 20, - }, - }; -}; - -export default Trips; diff --git a/packages/app/screens/trip/createTripStore/useCreateTripStore.ts b/packages/app/screens/trip/createTripStore/useCreateTripStore.ts index dc251e3e8..d740d0723 100644 --- a/packages/app/screens/trip/createTripStore/useCreateTripStore.ts +++ b/packages/app/screens/trip/createTripStore/useCreateTripStore.ts @@ -1,5 +1,4 @@ import { useCallback, useEffect, useMemo, useReducer } from 'react'; -import { format } from 'date-fns'; import { createTripInitialState, createTripReducer, @@ -7,14 +6,10 @@ import { setTripFormValue, type addTripKey, } from './store'; -import { useValidateSchema } from 'app/hooks/common'; -import { addTripDetails } from '@packrat/validations'; +import { useTripContext } from 'app/modules/trip/context/tripContext'; -export const useCreateTripStore = () => { - const [store, dispatch] = useReducer( - createTripReducer, - createTripInitialState, - ); +export const useCreateTripStore = (initialState = createTripInitialState) => { + const [store, dispatch] = useReducer(createTripReducer, initialState); const setTripValue = useCallback( (name: addTripKey, value: any) => { @@ -29,3 +24,8 @@ export const useCreateTripStore = () => { return { store, setTripValue, setDateRange }; }; + +export const useCurrentTripStore = () => { + const { tripStore } = useTripContext(); + return tripStore; +}; diff --git a/packages/app/screens/trip/editTrip.tsx b/packages/app/screens/trip/editTrip.tsx new file mode 100644 index 000000000..601c32170 --- /dev/null +++ b/packages/app/screens/trip/editTrip.tsx @@ -0,0 +1,64 @@ +import React, { useState } from 'react'; +import { TripProvider } from 'app/modules/trip/context/tripContext'; +import { TripScreen } from './TripScreen'; +import { useCreateTripStore } from './createTripStore'; +import { AsyncView } from 'app/components/AsyncView'; +import { useFetchSingleTrip } from 'app/hooks/singletrips'; +import { type addTripKey } from './createTripStore/store'; +import { useOSM } from 'app/hooks/geojson'; +import { useTripId } from 'app/hooks/trips'; + +export default function EditTripScreen() { + const [tripId] = useTripId(); + const { isLoading, isError, data } = useFetchSingleTrip(tripId); + + return ( + + + + ); +} + +const TripLoader = ({ + initialState, + packId, + tripId, + bounds, + ownerId, +}: { + initialState: Partial>; + packId: string; + tripId: string; + ownerId: string; + bounds: any; +}) => { + const tripStore = useCreateTripStore(initialState); + const tripPack = useState(packId); + const tripOSM = useOSM(); + + return ( + + + + ); +}; diff --git a/packages/app/screens/trip/useTripOSM.ts b/packages/app/screens/trip/useTripOSM.ts new file mode 100644 index 000000000..bceb533b9 --- /dev/null +++ b/packages/app/screens/trip/useTripOSM.ts @@ -0,0 +1,9 @@ +import { useGEOLocationSearch, useOSM } from 'app/hooks/geojson'; +import { useTripContext } from 'app/modules/trip/context/tripContext'; + +export const useTripOSM = () => { + const osmSearch = useGEOLocationSearch(); + const { tripOSM } = useTripContext(); + + return tripOSM || osmSearch; +}; diff --git a/packages/app/screens/trip/useTripPackId.ts b/packages/app/screens/trip/useTripPackId.ts new file mode 100644 index 000000000..9b8c51b69 --- /dev/null +++ b/packages/app/screens/trip/useTripPackId.ts @@ -0,0 +1,9 @@ +import { usePackId } from 'app/modules/pack'; +import { useTripContext } from 'app/modules/trip/context/tripContext'; + +export const useTripPackId = () => { + const packIdSearch = usePackId(); + const { tripPack } = useTripContext(); + // Trip pack is used in edit mode + return tripPack || packIdSearch; +}; diff --git a/packages/app/screens/trip/useTripsData.ts b/packages/app/screens/trip/useTripsData.ts index b8d553662..56e018b4b 100644 --- a/packages/app/screens/trip/useTripsData.ts +++ b/packages/app/screens/trip/useTripsData.ts @@ -1,15 +1,9 @@ -// useTripsData.js -import useParks from 'app/hooks/parks'; -import useTrails from 'app/hooks/trails'; -import { - useCurrentDestination, - useGetPhotonDetails, -} from 'app/hooks/destination'; -import { useGEOLocationSearch } from 'app/hooks/geojson'; +import { useDestination, useGetPhotonDetails } from 'app/hooks/destination'; +import { useTripOSM } from './useTripOSM'; export const useTripsData = () => { - const [osm] = useGEOLocationSearch(); - const { currentDestination, latLng } = useCurrentDestination(); + const [osm] = useTripOSM(); + const { currentDestination, latLng } = useDestination(osm); const { data: photonDetails, isError: hasPhotonError, @@ -24,18 +18,19 @@ export const useTripsData = () => { false, ); - const { - error: parksError, - isLoading: parksLoading, - filteredParks: parksData, - } = useParks({ - latLng, - }); + // TODO: Uncomment this once we have parks and trails data + // const { + // error: parksError, + // isLoading: parksLoading, + // filteredParks: parksData, + // } = useParks({ + // latLng, + // }); - const { data, filteredTrails, error, isLoading } = useTrails({ - latLng, - selectedSearch: osm.name, - }); + // const { data, filteredTrails, error, isLoading } = useTrails({ + // latLng, + // selectedSearch: osm.name, + // }); return { osm, @@ -44,11 +39,5 @@ export const useTripsData = () => { photonDetails, hasPhotonError, isPhotonLoading, - parksData, - parksError, - parksLoading, - filteredTrails, - trailsError: error, - trailsLoading: isLoading, }; }; diff --git a/packages/ui/src/modal/BaseModal.tsx b/packages/ui/src/modal/BaseModal.tsx index c68aa9206..5861e78d6 100644 --- a/packages/ui/src/modal/BaseModal.tsx +++ b/packages/ui/src/modal/BaseModal.tsx @@ -49,6 +49,7 @@ export const BaseModal = ({ setIsModalOpen(true)} style={{ backgroundColor: 'transparent' }} + unstyled backgroundColor={'transparent'} > {React.cloneElement(triggerComponent, { setIsModalOpen })} diff --git a/packages/validations/src/validations/tripRoutesValidator/tripRoutesValidator.ts b/packages/validations/src/validations/tripRoutesValidator/tripRoutesValidator.ts index 0a3d5c1de..48b0722e1 100644 --- a/packages/validations/src/validations/tripRoutesValidator/tripRoutesValidator.ts +++ b/packages/validations/src/validations/tripRoutesValidator/tripRoutesValidator.ts @@ -49,6 +49,7 @@ export const getTripById = z.object({ export const addTripDetails = z.object({ activity: z.enum(tripActivityValues).optional(), bounds: z.tuple([z.array(z.number()), z.array(z.number())]).optional(), + destination: z.string(), end_date: z.string(), geoJSON: z.string(), pack_id: z.string(), diff --git a/server/migrations-preview/0003_condemned_sugar_man.sql b/server/migrations-preview/0003_condemned_sugar_man.sql new file mode 100644 index 000000000..d94004db9 --- /dev/null +++ b/server/migrations-preview/0003_condemned_sugar_man.sql @@ -0,0 +1,8 @@ +/* + SQLite does not support "Drop not null from column" out of the box, we do not generate automatic migration for that, so it has to be done manually + Please refer to: https://www.techonthenet.com/sqlite/tables/alter_table.php + https://www.sqlite.org/lang_altertable.html + https://stackoverflow.com/questions/2083543/modify-a-columns-type-in-sqlite3 + + Due to that we don't generate migration automatically and it has to be done manually +*/ \ No newline at end of file diff --git a/server/migrations-preview/meta/0003_snapshot.json b/server/migrations-preview/meta/0003_snapshot.json new file mode 100644 index 000000000..ee7e87620 --- /dev/null +++ b/server/migrations-preview/meta/0003_snapshot.json @@ -0,0 +1,1534 @@ +{ + "version": "5", + "dialect": "sqlite", + "id": "463f2375-e852-4541-937a-f1a08337bffa", + "prevId": "a70ff809-b028-4fe7-a94c-31e53cbddaae", + "tables": { + "conversation": { + "name": "conversation", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "itemTypeId": { + "name": "itemTypeId", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "history": { + "name": "history", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "geojson": { + "name": "geojson", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "geoJSON": { + "name": "geoJSON", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "item": { + "name": "item", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "weight": { + "name": "weight", + "type": "real", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "unit": { + "name": "unit", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "category_id": { + "name": "category_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "global": { + "name": "global", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": false + }, + "sku": { + "name": "sku", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "product_url": { + "name": "product_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "product_details": { + "name": "product_details", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "seller": { + "name": "seller", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + } + }, + "indexes": {}, + "foreignKeys": { + "item_category_id_item_category_id_fk": { + "name": "item_category_id_item_category_id_fk", + "tableFrom": "item", + "tableTo": "item_category", + "columnsFrom": [ + "category_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "item_owner_id_user_id_fk": { + "name": "item_owner_id_user_id_fk", + "tableFrom": "item", + "tableTo": "user", + "columnsFrom": [ + "owner_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "item_category": { + "name": "item_category", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "item_image": { + "name": "item_image", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "item_id": { + "name": "item_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + } + }, + "indexes": {}, + "foreignKeys": { + "item_image_item_id_item_id_fk": { + "name": "item_image_item_id_item_id_fk", + "tableFrom": "item_image", + "tableTo": "item", + "columnsFrom": [ + "item_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "item_owners": { + "name": "item_owners", + "columns": { + "item_id": { + "name": "item_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "item_owners_item_id_item_id_fk": { + "name": "item_owners_item_id_item_id_fk", + "tableFrom": "item_owners", + "tableTo": "item", + "columnsFrom": [ + "item_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "item_owners_owner_id_user_id_fk": { + "name": "item_owners_owner_id_user_id_fk", + "tableFrom": "item_owners", + "tableTo": "user", + "columnsFrom": [ + "owner_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "id": { + "columns": [ + "item_id", + "owner_id" + ], + "name": "id" + } + }, + "uniqueConstraints": {} + }, + "item_pack_templates": { + "name": "item_pack_templates", + "columns": { + "item_id": { + "name": "item_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "pack_template_id": { + "name": "pack_template_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "quantity": { + "name": "quantity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + } + }, + "indexes": {}, + "foreignKeys": { + "item_pack_templates_item_id_item_id_fk": { + "name": "item_pack_templates_item_id_item_id_fk", + "tableFrom": "item_pack_templates", + "tableTo": "item", + "columnsFrom": [ + "item_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "item_pack_templates_pack_template_id_pack_template_id_fk": { + "name": "item_pack_templates_pack_template_id_pack_template_id_fk", + "tableFrom": "item_pack_templates", + "tableTo": "pack_template", + "columnsFrom": [ + "pack_template_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "id": { + "columns": [ + "item_id", + "pack_template_id" + ], + "name": "id" + } + }, + "uniqueConstraints": {} + }, + "item_packs": { + "name": "item_packs", + "columns": { + "item_id": { + "name": "item_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "pack_id": { + "name": "pack_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "quantity": { + "name": "quantity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + } + }, + "indexes": {}, + "foreignKeys": { + "item_packs_item_id_item_id_fk": { + "name": "item_packs_item_id_item_id_fk", + "tableFrom": "item_packs", + "tableTo": "item", + "columnsFrom": [ + "item_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "item_packs_pack_id_pack_id_fk": { + "name": "item_packs_pack_id_pack_id_fk", + "tableFrom": "item_packs", + "tableTo": "pack", + "columnsFrom": [ + "pack_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "id": { + "columns": [ + "item_id", + "pack_id" + ], + "name": "id" + } + }, + "uniqueConstraints": {} + }, + "node": { + "name": "node", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "osm_id": { + "name": "osm_id", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "lat": { + "name": "lat", + "type": "real", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "lon": { + "name": "lon", + "type": "real", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "tags": { + "name": "tags", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "offlineMap": { + "name": "offlineMap", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "bounds": { + "name": "bounds", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "minZoom": { + "name": "minZoom", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "maxZoom": { + "name": "maxZoom", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + } + }, + "indexes": { + "offlineMap_name_owner_id_unique": { + "name": "offlineMap_name_owner_id_unique", + "columns": [ + "name", + "owner_id" + ], + "isUnique": true + } + }, + "foreignKeys": { + "offlineMap_owner_id_user_id_fk": { + "name": "offlineMap_owner_id_user_id_fk", + "tableFrom": "offlineMap", + "tableTo": "user", + "columnsFrom": [ + "owner_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "pack": { + "name": "pack", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_public": { + "name": "is_public", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": false + }, + "grades": { + "name": "grades", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'{\"weight\":\"\",\"essentialItems\":\"\",\"redundancyAndVersatility\":\"\"}'" + }, + "scores": { + "name": "scores", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'{\"weightScore\":0,\"essentialItemsScore\":0,\"redundancyAndVersatilityScore\":0}'" + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'pack'" + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + } + }, + "indexes": {}, + "foreignKeys": { + "pack_owner_id_user_id_fk": { + "name": "pack_owner_id_user_id_fk", + "tableFrom": "pack", + "tableTo": "user", + "columnsFrom": [ + "owner_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "pack_template": { + "name": "pack_template", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'packTemplate'" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "refresh_tokens": { + "name": "refresh_tokens", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "refresh_tokens_user_id_user_id_fk": { + "name": "refresh_tokens_user_id_user_id_fk", + "tableFrom": "refresh_tokens", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "relation": { + "name": "relation", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "osm_id": { + "name": "osm_id", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "osm_type": { + "name": "osm_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'relation'" + }, + "tags": { + "name": "tags", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "members": { + "name": "members", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "geo_json": { + "name": "geo_json", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "template": { + "name": "template", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'pack'" + }, + "template_id": { + "name": "template_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "is_global_template": { + "name": "is_global_template", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + } + }, + "indexes": {}, + "foreignKeys": { + "template_created_by_user_id_fk": { + "name": "template_created_by_user_id_fk", + "tableFrom": "template", + "tableTo": "user", + "columnsFrom": [ + "created_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "trip": { + "name": "trip", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "parks": { + "name": "parks", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "start_date": { + "name": "start_date", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "end_date": { + "name": "end_date", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "destination": { + "name": "destination", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "packs_id": { + "name": "packs_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_public": { + "name": "is_public", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "activity": { + "name": "activity", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'trip'" + }, + "bounds": { + "name": "bounds", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'trip'" + }, + "scores": { + "name": "scores", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'{\"totalScore\":0}'" + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + } + }, + "indexes": {}, + "foreignKeys": { + "trip_owner_id_user_id_fk": { + "name": "trip_owner_id_user_id_fk", + "tableFrom": "trip", + "tableTo": "user", + "columnsFrom": [ + "owner_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "trip_packs_id_pack_id_fk": { + "name": "trip_packs_id_pack_id_fk", + "tableFrom": "trip", + "tableTo": "pack", + "columnsFrom": [ + "packs_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "trip_geojsons": { + "name": "trip_geojsons", + "columns": { + "trip_id": { + "name": "trip_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "geojson_id": { + "name": "geojson_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "trip_geojsons_trip_id_trip_id_fk": { + "name": "trip_geojsons_trip_id_trip_id_fk", + "tableFrom": "trip_geojsons", + "tableTo": "trip", + "columnsFrom": [ + "trip_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "trip_geojsons_geojson_id_geojson_id_fk": { + "name": "trip_geojsons_geojson_id_geojson_id_fk", + "tableFrom": "trip_geojsons", + "tableTo": "geojson", + "columnsFrom": [ + "geojson_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "id": { + "columns": [ + "geojson_id", + "trip_id" + ], + "name": "id" + } + }, + "uniqueConstraints": {} + }, + "user": { + "name": "user", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "google_id": { + "name": "google_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "is_certified_guide": { + "name": "is_certified_guide", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "password_reset_token": { + "name": "password_reset_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "password_reset_token_expiration": { + "name": "password_reset_token_expiration", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "offline_maps": { + "name": "offline_maps", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'user'" + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "profile_image": { + "name": "profile_image", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "preferred_weather": { + "name": "preferred_weather", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'celsius'" + }, + "preferred_weight": { + "name": "preferred_weight", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'lb'" + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + } + }, + "indexes": { + "user_email_unique": { + "name": "user_email_unique", + "columns": [ + "email" + ], + "isUnique": true + }, + "user_username_unique": { + "name": "user_username_unique", + "columns": [ + "username" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "user_favorite_packs": { + "name": "user_favorite_packs", + "columns": { + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "pack_id": { + "name": "pack_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "user_favorite_packs_user_id_user_id_fk": { + "name": "user_favorite_packs_user_id_user_id_fk", + "tableFrom": "user_favorite_packs", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "user_favorite_packs_pack_id_pack_id_fk": { + "name": "user_favorite_packs_pack_id_pack_id_fk", + "tableFrom": "user_favorite_packs", + "tableTo": "pack", + "columnsFrom": [ + "pack_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "id": { + "columns": [ + "pack_id", + "user_id" + ], + "name": "id" + } + }, + "uniqueConstraints": {} + }, + "way": { + "name": "way", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "osm_id": { + "name": "osm_id", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "osm_type": { + "name": "osm_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "tags": { + "name": "tags", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "geo_json": { + "name": "geo_json", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + }, + "updated_at": { + "name": "updated_at", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "CURRENT_TIMESTAMP" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "way_nodes": { + "name": "way_nodes", + "columns": { + "way_id": { + "name": "way_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "node_id": { + "name": "node_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "way_nodes_way_id_way_id_fk": { + "name": "way_nodes_way_id_way_id_fk", + "tableFrom": "way_nodes", + "tableTo": "way", + "columnsFrom": [ + "way_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "way_nodes_node_id_node_id_fk": { + "name": "way_nodes_node_id_node_id_fk", + "tableFrom": "way_nodes", + "tableTo": "node", + "columnsFrom": [ + "node_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "id": { + "columns": [ + "node_id", + "way_id" + ], + "name": "id" + } + }, + "uniqueConstraints": {} + } + }, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + } +} \ No newline at end of file diff --git a/server/migrations-preview/meta/_journal.json b/server/migrations-preview/meta/_journal.json index d24518431..cd2aeb5ca 100644 --- a/server/migrations-preview/meta/_journal.json +++ b/server/migrations-preview/meta/_journal.json @@ -22,6 +22,13 @@ "when": 1729778627679, "tag": "0002_luxuriant_gunslinger", "breakpoints": true + }, + { + "idx": 3, + "version": "5", + "when": 1734381220155, + "tag": "0003_condemned_sugar_man", + "breakpoints": true } ] } \ No newline at end of file diff --git a/server/src/db/schema.ts b/server/src/db/schema.ts index 9c88b793f..5a64346ce 100644 --- a/server/src/db/schema.ts +++ b/server/src/db/schema.ts @@ -413,6 +413,7 @@ export const trip = sqliteTable('trip', { >(), start_date: text('start_date').notNull(), end_date: text('end_date').notNull(), + destination: text('destination').notNull(), owner_id: text('owner_id').references(() => user.id, { onDelete: 'cascade', }), diff --git a/server/src/services/trip/addTripService.ts b/server/src/services/trip/addTripService.ts index 286368252..4438ab696 100644 --- a/server/src/services/trip/addTripService.ts +++ b/server/src/services/trip/addTripService.ts @@ -17,6 +17,7 @@ export const addTripService = async ( const newTrip = await tripClass.create({ name: tripData.name, description: tripData.description, + destination: tripData.destination, start_date: tripData.start_date, end_date: tripData.end_date, activity: tripData.activity || 'trip', diff --git a/server/src/services/trip/editTripService.ts b/server/src/services/trip/editTripService.ts index f7083bb43..cae59568c 100644 --- a/server/src/services/trip/editTripService.ts +++ b/server/src/services/trip/editTripService.ts @@ -13,8 +13,10 @@ export const editTripService = async ( const tripClass = new Trip(); const selectedTrip = await tripClass.findById(tripData.id); const updatedTrip = await tripClass.update({ + id: tripData?.id, name: tripData.name || selectedTrip.name, description: tripData.description || selectedTrip.description, + destination: tripData.destination || selectedTrip.destination, start_date: tripData.start_date || selectedTrip.start_date, end_date: tripData.end_date || selectedTrip.end_date, activity: tripData.activity || selectedTrip.activity,