newcandies 0.1.40 → 0.1.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,264 +0,0 @@
|
|
|
1
|
-
import Ionicons from '@expo/vector-icons/Ionicons';
|
|
2
|
-
import BottomSheet, {
|
|
3
|
-
BottomSheetBackdrop,
|
|
4
|
-
BottomSheetBackdropProps,
|
|
5
|
-
BottomSheetView,
|
|
6
|
-
} from '@gorhom/bottom-sheet';
|
|
7
|
-
import { Calendar, fromDateId, toDateId, useDateRange } from '@marceloterreiro/flash-calendar';
|
|
8
|
-
import { nanoid } from 'nanoid/non-secure';
|
|
9
|
-
import { toast } from 'sonner-native';
|
|
10
|
-
|
|
11
|
-
import { useQuery } from '@tanstack/react-query';
|
|
12
|
-
import {
|
|
13
|
-
addMonths,
|
|
14
|
-
differenceInDays,
|
|
15
|
-
isBefore,
|
|
16
|
-
isSameMonth,
|
|
17
|
-
startOfMonth,
|
|
18
|
-
subMonths,
|
|
19
|
-
} from 'date-fns';
|
|
20
|
-
import { router, useLocalSearchParams } from 'expo-router';
|
|
21
|
-
import { SquircleButton } from 'expo-squircle-view';
|
|
22
|
-
import { useCallback, useMemo, useRef, useState } from 'react';
|
|
23
|
-
import { Pressable, ScrollView, View } from 'react-native';
|
|
24
|
-
import Container from '~/components/container';
|
|
25
|
-
import Header from '~/components/header';
|
|
26
|
-
import LoadingIndicator from '~/components/loading-indicator';
|
|
27
|
-
import AmenitiesList from '~/components/property/amenities-list';
|
|
28
|
-
import PropertyImage from '~/components/property/property-image';
|
|
29
|
-
import Text from '~/components/text';
|
|
30
|
-
import { client } from '~/core/api/client';
|
|
31
|
-
import { today } from '~/core/constants';
|
|
32
|
-
import useShoppingCartStore from '~/core/store';
|
|
33
|
-
import { calendarTheme } from '~/core/theme/calendar-theme';
|
|
34
|
-
import { PRIMARY } from '~/core/theme/colors';
|
|
35
|
-
|
|
36
|
-
type Props = {};
|
|
37
|
-
const Property = ({}: Props) => {
|
|
38
|
-
const { id } = useLocalSearchParams();
|
|
39
|
-
|
|
40
|
-
const { data: property, isLoading } = useQuery<Property>({
|
|
41
|
-
queryKey: ['property' + id],
|
|
42
|
-
queryFn: async () => {
|
|
43
|
-
const { data } = await client.get(`/properties/${id}`);
|
|
44
|
-
return data.property;
|
|
45
|
-
},
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
const { addItem } = useShoppingCartStore();
|
|
49
|
-
|
|
50
|
-
const { calendarActiveDateRanges, onCalendarDayPress } = useDateRange();
|
|
51
|
-
|
|
52
|
-
console.log({ calendarActiveDateRanges });
|
|
53
|
-
|
|
54
|
-
const bottomSheetRef = useRef<BottomSheet>(null);
|
|
55
|
-
|
|
56
|
-
const snapPoints = useMemo(() => ['60%'], []);
|
|
57
|
-
|
|
58
|
-
const renderBackdrop = useCallback((props: BottomSheetBackdropProps) => {
|
|
59
|
-
return (
|
|
60
|
-
<BottomSheetBackdrop
|
|
61
|
-
{...props}
|
|
62
|
-
disappearsOnIndex={-1}
|
|
63
|
-
appearsOnIndex={0}
|
|
64
|
-
pressBehavior={'close'}
|
|
65
|
-
/>
|
|
66
|
-
);
|
|
67
|
-
}, []);
|
|
68
|
-
|
|
69
|
-
const calculateDays = () => {
|
|
70
|
-
if (!calendarActiveDateRanges[0]?.startId) return 0;
|
|
71
|
-
if (!calendarActiveDateRanges[0]?.endId) return 1;
|
|
72
|
-
|
|
73
|
-
const startDate = new Date(calendarActiveDateRanges[0].startId);
|
|
74
|
-
const endDate = new Date(calendarActiveDateRanges[0].endId);
|
|
75
|
-
|
|
76
|
-
return differenceInDays(endDate, startDate) + 1;
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
const hasSelectedDates = Boolean(calendarActiveDateRanges[0]?.startId);
|
|
80
|
-
const [calendarMonthId, setCalendarMonthId] = useState(today);
|
|
81
|
-
|
|
82
|
-
const nextMonth = () => {
|
|
83
|
-
const month = addMonths(calendarMonthId, 1);
|
|
84
|
-
setCalendarMonthId(toDateId(month));
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
const currentDisplayMonth = fromDateId(calendarMonthId);
|
|
88
|
-
const canGoBack =
|
|
89
|
-
!isSameMonth(currentDisplayMonth, today) && !isBefore(currentDisplayMonth, startOfMonth(today));
|
|
90
|
-
|
|
91
|
-
const prevMonth = () => {
|
|
92
|
-
if (canGoBack) {
|
|
93
|
-
const month = subMonths(calendarMonthId, 1);
|
|
94
|
-
setCalendarMonthId(toDateId(month));
|
|
95
|
-
}
|
|
96
|
-
};
|
|
97
|
-
if (isLoading || !property) {
|
|
98
|
-
return <LoadingIndicator />;
|
|
99
|
-
}
|
|
100
|
-
const days = calculateDays();
|
|
101
|
-
const totalPrice = days * property.price_per_night;
|
|
102
|
-
|
|
103
|
-
return (
|
|
104
|
-
<Container>
|
|
105
|
-
<Header title="Property" />
|
|
106
|
-
<ScrollView className="flex-1 bg-gray-100 p-4">
|
|
107
|
-
<PropertyImage
|
|
108
|
-
imageUrl={property?.images[1]}
|
|
109
|
-
isFavorite={property?.is_favorite}
|
|
110
|
-
rating={5}
|
|
111
|
-
/>
|
|
112
|
-
<View className="flex flex-row items-center justify-between">
|
|
113
|
-
<Text variant="subtitle-primary" className="mt-4">
|
|
114
|
-
{property.name}
|
|
115
|
-
</Text>
|
|
116
|
-
<View className="flex flex-row items-center justify-center">
|
|
117
|
-
<Ionicons name="pricetag" size={12} color={PRIMARY} />
|
|
118
|
-
<Text variant="body-primary" className="ml-2">
|
|
119
|
-
${property.price_per_night} per night
|
|
120
|
-
</Text>
|
|
121
|
-
</View>
|
|
122
|
-
</View>
|
|
123
|
-
<View className="flex flex-row items-center">
|
|
124
|
-
<Ionicons name="location" size={16} color={PRIMARY} />
|
|
125
|
-
<Text variant="body-primary" className="">
|
|
126
|
-
{property.city}, {property.country}
|
|
127
|
-
</Text>
|
|
128
|
-
</View>
|
|
129
|
-
<Text variant="body" className="mt-1 text-gray-700">
|
|
130
|
-
{property.description}
|
|
131
|
-
</Text>
|
|
132
|
-
<AmenitiesList amenities={property.amenities} />
|
|
133
|
-
</ScrollView>
|
|
134
|
-
|
|
135
|
-
<BottomSheet
|
|
136
|
-
ref={bottomSheetRef}
|
|
137
|
-
snapPoints={snapPoints}
|
|
138
|
-
backdropComponent={renderBackdrop}
|
|
139
|
-
index={-1}
|
|
140
|
-
enablePanDownToClose={true}
|
|
141
|
-
enableDynamicSizing={false}>
|
|
142
|
-
<BottomSheetView style={{ flex: 1 }}>
|
|
143
|
-
<View className="my-4 flex flex-row items-center justify-between px-4">
|
|
144
|
-
<View className="flex flex-row items-center justify-center">
|
|
145
|
-
<Ionicons name="wallet" color={PRIMARY} size={24} />
|
|
146
|
-
<Text variant="subtitle" className="mx-4">
|
|
147
|
-
Price : ${hasSelectedDates ? totalPrice : property.price_per_night}
|
|
148
|
-
{!hasSelectedDates && ' per night'}
|
|
149
|
-
</Text>
|
|
150
|
-
</View>
|
|
151
|
-
</View>
|
|
152
|
-
<BottomSheetView style={{ flex: 1, paddingHorizontal: 4, position: 'relative' }}>
|
|
153
|
-
<View className="mt-20 flex flex-row justify-between">
|
|
154
|
-
<Pressable onPress={prevMonth} disabled={!canGoBack}>
|
|
155
|
-
<Ionicons name="arrow-back" size={24} color={canGoBack ? PRIMARY : 'gray'} />
|
|
156
|
-
</Pressable>
|
|
157
|
-
<Pressable onPress={nextMonth}>
|
|
158
|
-
<Ionicons name="arrow-forward" size={24} color={PRIMARY} />
|
|
159
|
-
</Pressable>
|
|
160
|
-
</View>
|
|
161
|
-
|
|
162
|
-
<Calendar
|
|
163
|
-
calendarMonthId={calendarMonthId}
|
|
164
|
-
calendarActiveDateRanges={calendarActiveDateRanges}
|
|
165
|
-
calendarMinDateId={today}
|
|
166
|
-
onCalendarDayPress={onCalendarDayPress}
|
|
167
|
-
theme={calendarTheme}
|
|
168
|
-
/>
|
|
169
|
-
<SquircleButton
|
|
170
|
-
backgroundColor={PRIMARY}
|
|
171
|
-
cornerSmoothing={100}
|
|
172
|
-
onPress={() => {
|
|
173
|
-
bottomSheetRef.current?.close();
|
|
174
|
-
|
|
175
|
-
if (!hasSelectedDates || !calendarActiveDateRanges[0]?.startId) {
|
|
176
|
-
console.log('error: please select dates');
|
|
177
|
-
return;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
const cartItem: ICartItem = {
|
|
181
|
-
id: 'cart' + nanoid(),
|
|
182
|
-
image: property.images[0],
|
|
183
|
-
name: property.name,
|
|
184
|
-
product: property.id,
|
|
185
|
-
price_per_night: property.price_per_night,
|
|
186
|
-
quantity: 1,
|
|
187
|
-
startDate: calendarActiveDateRanges[0].startId,
|
|
188
|
-
endDate:
|
|
189
|
-
calendarActiveDateRanges[0]?.endId ?? calendarActiveDateRanges[0].startId,
|
|
190
|
-
days: calculateDays(),
|
|
191
|
-
};
|
|
192
|
-
addItem(cartItem);
|
|
193
|
-
bottomSheetRef.current?.close();
|
|
194
|
-
}}
|
|
195
|
-
preserveSmoothing
|
|
196
|
-
className="m-8 flex flex-row items-center justify-center px-4 "
|
|
197
|
-
style={{
|
|
198
|
-
paddingVertical: 16,
|
|
199
|
-
position: 'absolute',
|
|
200
|
-
bottom: -120,
|
|
201
|
-
left: 0,
|
|
202
|
-
right: 0,
|
|
203
|
-
}}
|
|
204
|
-
borderRadius={24}>
|
|
205
|
-
<Ionicons name="checkmark-circle" size={20} color={'white'} />
|
|
206
|
-
<Text variant="button" className="mx-2 text-center">
|
|
207
|
-
Confirm
|
|
208
|
-
</Text>
|
|
209
|
-
</SquircleButton>
|
|
210
|
-
</BottomSheetView>
|
|
211
|
-
</BottomSheetView>
|
|
212
|
-
</BottomSheet>
|
|
213
|
-
|
|
214
|
-
<View className="bottom-0 left-0 right-0 -z-10 mx-4 mt-auto flex flex-row items-center justify-center py-2">
|
|
215
|
-
{hasSelectedDates ? (
|
|
216
|
-
<Pressable
|
|
217
|
-
className="mr-4"
|
|
218
|
-
onPress={() => {
|
|
219
|
-
bottomSheetRef.current?.expand();
|
|
220
|
-
}}>
|
|
221
|
-
<View className="flex flex-row items-center">
|
|
222
|
-
<Ionicons name="pricetag" color={PRIMARY} size={16} />
|
|
223
|
-
<Text variant="body-primary" className="text-center">
|
|
224
|
-
${totalPrice}
|
|
225
|
-
</Text>
|
|
226
|
-
</View>
|
|
227
|
-
<Text variant="caption" className="text-center underline">
|
|
228
|
-
{days === 1 ? '1 Night' : `${days} nights`}
|
|
229
|
-
</Text>
|
|
230
|
-
</Pressable>
|
|
231
|
-
) : (
|
|
232
|
-
<Pressable
|
|
233
|
-
className="mr-4 flex flex-row items-center"
|
|
234
|
-
onPress={() => {
|
|
235
|
-
bottomSheetRef.current?.expand();
|
|
236
|
-
}}>
|
|
237
|
-
<Ionicons name="calendar-outline" size={24} color={PRIMARY} />
|
|
238
|
-
<Text variant="body-primary" className="ml-2 text-center underline">
|
|
239
|
-
Select dates
|
|
240
|
-
</Text>
|
|
241
|
-
</Pressable>
|
|
242
|
-
)}
|
|
243
|
-
<SquircleButton
|
|
244
|
-
onPress={() => {
|
|
245
|
-
toast.success('Property added to cart');
|
|
246
|
-
router.push('/checkout');
|
|
247
|
-
}}
|
|
248
|
-
className="flex-grow"
|
|
249
|
-
backgroundColor={PRIMARY}
|
|
250
|
-
borderRadius={16}
|
|
251
|
-
style={{
|
|
252
|
-
paddingVertical: 16,
|
|
253
|
-
marginVertical: 4,
|
|
254
|
-
}}>
|
|
255
|
-
<Text variant="button" className="text-center">
|
|
256
|
-
Book Now
|
|
257
|
-
</Text>
|
|
258
|
-
</SquircleButton>
|
|
259
|
-
</View>
|
|
260
|
-
</Container>
|
|
261
|
-
);
|
|
262
|
-
};
|
|
263
|
-
|
|
264
|
-
export default Property;
|