newcandies 0.1.32 → 0.1.33
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/dist/index.js +6 -3
- package/package.json +1 -1
- package/templates/boilerplate/holidia/.vscode/settings.json +1 -0
- package/templates/boilerplate/holidia/.vscode/snippets.code-snippets +144 -0
- package/templates/boilerplate/holidia/.zed/settings.json +7 -0
- package/templates/boilerplate/holidia/README.md +25 -2
- package/templates/boilerplate/holidia/app/(tabs)/_layout.tsx +0 -0
- package/templates/boilerplate/holidia/app/(tabs)/bookings.tsx +0 -0
- package/templates/boilerplate/holidia/app/(tabs)/favorite.tsx +0 -0
- package/templates/boilerplate/holidia/app/(tabs)/index.tsx +0 -0
- package/templates/boilerplate/holidia/app/(tabs)/profile.tsx +0 -0
- package/templates/boilerplate/holidia/app/+html.tsx +46 -0
- package/templates/boilerplate/holidia/app/+not-found.tsx +23 -0
- package/templates/boilerplate/holidia/app/_layout.tsx +0 -0
- package/templates/boilerplate/holidia/app/checkout.tsx +0 -0
- package/templates/boilerplate/holidia/app/login.tsx +0 -0
- package/templates/boilerplate/holidia/app/payment-successful.tsx +0 -0
- package/templates/boilerplate/holidia/app/properties/[id].tsx +264 -0
- package/templates/boilerplate/holidia/app/search.tsx +0 -0
- package/templates/boilerplate/holidia/app/signup.tsx +0 -0
- package/templates/boilerplate/holidia/app/welcome.tsx +0 -0
- package/templates/boilerplate/holidia/app-env.d.ts +2 -0
- package/templates/boilerplate/holidia/app.json +66 -0
- package/templates/boilerplate/holidia/assets/adaptive-icon.png +0 -0
- package/templates/boilerplate/holidia/assets/favicon.png +0 -0
- package/templates/boilerplate/holidia/assets/icon.png +0 -0
- package/templates/boilerplate/holidia/assets/logo.png +0 -0
- package/templates/boilerplate/holidia/assets/logo.svg +46 -0
- package/templates/boilerplate/holidia/assets/splash.png +0 -0
- package/templates/boilerplate/holidia/babel.config.js +5 -6
- package/templates/boilerplate/holidia/cesconfig.json +35 -0
- package/templates/boilerplate/holidia/components/container.tsx +11 -0
- package/templates/boilerplate/holidia/core/api/api-provider.tsx +0 -0
- package/templates/boilerplate/holidia/core/api/client.ts +0 -0
- package/templates/boilerplate/holidia/core/auth/index.ts +0 -0
- package/templates/boilerplate/holidia/core/constants/data.ts +559 -0
- package/templates/boilerplate/holidia/core/constants/index.ts +0 -0
- package/templates/boilerplate/holidia/core/hooks/use-image-colors.ts +0 -0
- package/templates/boilerplate/holidia/core/hooks/use-toggle-favorite.ts +0 -0
- package/templates/boilerplate/holidia/core/storage.ts +0 -0
- package/templates/boilerplate/holidia/core/store/index.ts +0 -0
- package/templates/boilerplate/holidia/core/theme/calendar-theme.ts +0 -0
- package/templates/boilerplate/holidia/core/theme/colors.ts +2 -0
- package/templates/boilerplate/holidia/core/theme/use-theme-config.ts +0 -0
- package/templates/boilerplate/holidia/core/types/index.d.ts +63 -0
- package/templates/boilerplate/holidia/core/types/lib.d.ts +4 -0
- package/templates/boilerplate/holidia/core/utils/layout.ts +0 -0
- package/templates/boilerplate/holidia/core/utils/log.ts +0 -0
- package/templates/boilerplate/holidia/eas.json +18 -0
- package/templates/boilerplate/holidia/env.development +2 -0
- package/templates/boilerplate/holidia/env.prod +2 -0
- package/templates/boilerplate/holidia/global.css +0 -1
- package/templates/boilerplate/holidia/metro.config.js +6 -4
- package/templates/boilerplate/holidia/nativewind-env.d.ts +3 -0
- package/templates/boilerplate/holidia/package.json +74 -21
- package/templates/boilerplate/holidia/prettier.config.js +10 -0
- package/templates/boilerplate/holidia/tailwind.config.js +6 -7
- package/templates/boilerplate/holidia/tsconfig.json +11 -0
- package/templates/boilerplate/holidia/src/app/_layout.tsx +0 -16
- package/templates/boilerplate/holidia/src/app/holi.tsx +0 -9
- package/templates/boilerplate/holidia/src/app/index.tsx +0 -34
package/dist/index.js
CHANGED
|
@@ -156,9 +156,12 @@ async function main() {
|
|
|
156
156
|
await fs.writeJSON(path.join(dest, "app.json"), mergedApp, { spaces: 2 });
|
|
157
157
|
}
|
|
158
158
|
}
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
159
|
+
const destFiles = await fs.readdir(dest);
|
|
160
|
+
for (const file of destFiles) {
|
|
161
|
+
if (file === "env" || file.startsWith("env.")) {
|
|
162
|
+
const newName = "." + file;
|
|
163
|
+
await fs.move(path.join(dest, file), path.join(dest, newName), { overwrite: true });
|
|
164
|
+
}
|
|
162
165
|
}
|
|
163
166
|
const pkgPath = path.join(dest, "package.json");
|
|
164
167
|
if (await fs.pathExists(pkgPath)) {
|
package/package.json
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
{
|
|
2
|
+
"View": {
|
|
3
|
+
"prefix": "v",
|
|
4
|
+
"body": ["<View className=\"$1\">", " $2", "</View>"],
|
|
5
|
+
"description": "A Simple View",
|
|
6
|
+
},
|
|
7
|
+
"ViewRow": {
|
|
8
|
+
"prefix": "vr",
|
|
9
|
+
"body": ["<View className=\"flex flex-row items-center justify-center\">", " $1", "</View>"],
|
|
10
|
+
"description": "A Simple Row View",
|
|
11
|
+
},
|
|
12
|
+
"Text": {
|
|
13
|
+
"prefix": "t",
|
|
14
|
+
"body": ["<Text variant=\"body\" className=\"text-center\">", " $1", "</Text>"],
|
|
15
|
+
"description": "A Simple Text",
|
|
16
|
+
},
|
|
17
|
+
"export *": {
|
|
18
|
+
"prefix": "ex *",
|
|
19
|
+
"body": ["export * from './$1';"],
|
|
20
|
+
"description": "export *",
|
|
21
|
+
},
|
|
22
|
+
"Component": {
|
|
23
|
+
"prefix": "co",
|
|
24
|
+
"body": [
|
|
25
|
+
"import { View } from 'react-native';",
|
|
26
|
+
"",
|
|
27
|
+
"import Text from '~/components/text';",
|
|
28
|
+
"",
|
|
29
|
+
"type Props = {",
|
|
30
|
+
" $2",
|
|
31
|
+
"};",
|
|
32
|
+
"const ${1:CompName} = ({}: Props) => {",
|
|
33
|
+
" return (",
|
|
34
|
+
" <View>",
|
|
35
|
+
" <Text>${1:CompName} Component</Text>",
|
|
36
|
+
" </View>",
|
|
37
|
+
" );",
|
|
38
|
+
"};",
|
|
39
|
+
"",
|
|
40
|
+
"export default ${1:CompName};",
|
|
41
|
+
],
|
|
42
|
+
"description": "Default Export Component",
|
|
43
|
+
},
|
|
44
|
+
"useQuery with variables": {
|
|
45
|
+
"prefix": "useqv",
|
|
46
|
+
"body": [
|
|
47
|
+
"import type { AxiosError } from 'axios';",
|
|
48
|
+
"import { createQuery } from 'react-query-kit';",
|
|
49
|
+
"",
|
|
50
|
+
"import { client } from '../common';",
|
|
51
|
+
"",
|
|
52
|
+
"type Variables = { $3 };",
|
|
53
|
+
"type Response = { $4 };",
|
|
54
|
+
"",
|
|
55
|
+
"export const use$1 = createQuery<Response, Variables, AxiosError>({",
|
|
56
|
+
" queryKey: ['$2'],",
|
|
57
|
+
" fetcher: (variables) => {",
|
|
58
|
+
" return client",
|
|
59
|
+
" .get(`$2/\\${variables.$5}`)",
|
|
60
|
+
" .then((response) => response.data);",
|
|
61
|
+
" },",
|
|
62
|
+
"});",
|
|
63
|
+
],
|
|
64
|
+
"description": "useQuery with variables",
|
|
65
|
+
},
|
|
66
|
+
"useQuery": {
|
|
67
|
+
"prefix": "useq",
|
|
68
|
+
"body": [
|
|
69
|
+
"import type { AxiosError } from 'axios';",
|
|
70
|
+
"import { createQuery } from 'react-query-kit';",
|
|
71
|
+
"",
|
|
72
|
+
"import { client } from '../common';",
|
|
73
|
+
"",
|
|
74
|
+
"type Response = { $3 };",
|
|
75
|
+
"type Variables = void;",
|
|
76
|
+
"",
|
|
77
|
+
"export const use$1 = createQuery<Response, Variables, AxiosError>({",
|
|
78
|
+
" queryKey: ['$2'],",
|
|
79
|
+
" fetcher: () => {",
|
|
80
|
+
" return client.get(`$2`).then((response) => response.data.posts);",
|
|
81
|
+
" },",
|
|
82
|
+
"});",
|
|
83
|
+
],
|
|
84
|
+
"description": "useQuery",
|
|
85
|
+
},
|
|
86
|
+
"useInfiniteQuery": {
|
|
87
|
+
"prefix": "useiq",
|
|
88
|
+
"body": [
|
|
89
|
+
"import type { AxiosError } from 'axios';",
|
|
90
|
+
"import { createInfiniteQuery } from 'react-query-kit';",
|
|
91
|
+
"",
|
|
92
|
+
"import { client } from '../common/client';",
|
|
93
|
+
"import { DEFAULT_LIMIT, getNextPageParam } from '../common/utils';",
|
|
94
|
+
"import type { PaginateQuery } from '../types';",
|
|
95
|
+
"",
|
|
96
|
+
"type Response = void;",
|
|
97
|
+
"type Variables = PaginateQuery<$3>;",
|
|
98
|
+
"",
|
|
99
|
+
"export const use$1 = createInfiniteQuery<Response, Variables, AxiosError>({",
|
|
100
|
+
" queryKey: ['$2'],",
|
|
101
|
+
" fetcher: (_variables, { pageParam }) => {",
|
|
102
|
+
" return client({",
|
|
103
|
+
" url: `/$2/`,",
|
|
104
|
+
" method: 'GET',",
|
|
105
|
+
" params: {",
|
|
106
|
+
" limit: DEFAULT_LIMIT,",
|
|
107
|
+
" offset: pageParam",
|
|
108
|
+
" }",
|
|
109
|
+
" }).then((response) => response.data);",
|
|
110
|
+
" },",
|
|
111
|
+
" getNextPageParam,",
|
|
112
|
+
" initialPageParam: 0",
|
|
113
|
+
"});",
|
|
114
|
+
],
|
|
115
|
+
"description": "useInfiniteQuery",
|
|
116
|
+
},
|
|
117
|
+
"useMutation": {
|
|
118
|
+
"prefix": "usem",
|
|
119
|
+
"body": [
|
|
120
|
+
"import type { AxiosError } from 'axios';",
|
|
121
|
+
"import { createMutation } from 'react-query-kit';",
|
|
122
|
+
"",
|
|
123
|
+
"import { client } from '../common';",
|
|
124
|
+
"",
|
|
125
|
+
"type Variables = { $3 };",
|
|
126
|
+
"type Response = { $4 };",
|
|
127
|
+
"",
|
|
128
|
+
"export const use$1 = createMutation<Response, Variables, AxiosError>({",
|
|
129
|
+
" mutationFn: async (variables) =>",
|
|
130
|
+
" client({",
|
|
131
|
+
" url: '$2',",
|
|
132
|
+
" method: 'POST',",
|
|
133
|
+
" data: variables",
|
|
134
|
+
" }).then((response) => response.data)",
|
|
135
|
+
"});",
|
|
136
|
+
],
|
|
137
|
+
"description": "useMutation",
|
|
138
|
+
},
|
|
139
|
+
"export default": {
|
|
140
|
+
"prefix": "expd",
|
|
141
|
+
"body": ["export default $1;"],
|
|
142
|
+
"description": "Export Default",
|
|
143
|
+
},
|
|
144
|
+
}
|
|
@@ -1,3 +1,26 @@
|
|
|
1
|
-
# Holidia
|
|
1
|
+
# Holidia App
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Holidia app boilerplate with NativeWind styling.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
## Installation
|
|
7
|
+
|
|
8
|
+
1. Install dependencies:
|
|
9
|
+
```bash
|
|
10
|
+
pnpm install
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
2. Prebuild the project:
|
|
14
|
+
```bash
|
|
15
|
+
pnpm prebuild --clean
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
3. Run the app:
|
|
19
|
+
- For Android:
|
|
20
|
+
```bash
|
|
21
|
+
pnpm android
|
|
22
|
+
```
|
|
23
|
+
- For iOS:
|
|
24
|
+
```bash
|
|
25
|
+
pnpm ios
|
|
26
|
+
```
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { ScrollViewStyleReset } from 'expo-router/html';
|
|
2
|
+
|
|
3
|
+
// This file is web-only and used to configure the root HTML for every
|
|
4
|
+
// web page during static rendering.
|
|
5
|
+
// The contents of this function only run in Node.js environments and
|
|
6
|
+
// do not have access to the DOM or browser APIs.
|
|
7
|
+
export default function Root({ children }: { children: React.ReactNode }) {
|
|
8
|
+
return (
|
|
9
|
+
<html lang="en">
|
|
10
|
+
<head>
|
|
11
|
+
<meta charSet="utf-8" />
|
|
12
|
+
<meta httpEquiv="X-UA-Compatible" content="IE=edge" />
|
|
13
|
+
|
|
14
|
+
{/*
|
|
15
|
+
This viewport disables scaling which makes the mobile website act more like a native app.
|
|
16
|
+
However this does reduce built-in accessibility. If you want to enable scaling, use this instead:
|
|
17
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
|
|
18
|
+
*/}
|
|
19
|
+
<meta
|
|
20
|
+
name="viewport"
|
|
21
|
+
content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1.00001,viewport-fit=cover"
|
|
22
|
+
/>
|
|
23
|
+
{/*
|
|
24
|
+
Disable body scrolling on web. This makes ScrollView components work closer to how they do on native.
|
|
25
|
+
However, body scrolling is often nice to have for mobile web. If you want to enable it, remove this line.
|
|
26
|
+
*/}
|
|
27
|
+
<ScrollViewStyleReset />
|
|
28
|
+
|
|
29
|
+
{/* Using raw CSS styles as an escape-hatch to ensure the background color never flickers in dark-mode. */}
|
|
30
|
+
<style dangerouslySetInnerHTML={{ __html: responsiveBackground }} />
|
|
31
|
+
{/* Add any additional <head> elements that you want globally available on web... */}
|
|
32
|
+
</head>
|
|
33
|
+
<body>{children}</body>
|
|
34
|
+
</html>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const responsiveBackground = `
|
|
39
|
+
body {
|
|
40
|
+
background-color: #fff;
|
|
41
|
+
}
|
|
42
|
+
@media (prefers-color-scheme: dark) {
|
|
43
|
+
body {
|
|
44
|
+
background-color: #000;
|
|
45
|
+
}
|
|
46
|
+
}`;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Link, Stack } from 'expo-router';
|
|
2
|
+
import { Text, View } from 'react-native';
|
|
3
|
+
|
|
4
|
+
export default function NotFoundScreen() {
|
|
5
|
+
return (
|
|
6
|
+
<>
|
|
7
|
+
<Stack.Screen options={{ title: 'Oops!' }} />
|
|
8
|
+
<View className={styles.container}>
|
|
9
|
+
<Text className={styles.title}>This screen doesn't exist.</Text>
|
|
10
|
+
<Link href="/" className={styles.link}>
|
|
11
|
+
<Text className={styles.linkText}>Go to home screen!</Text>
|
|
12
|
+
</Link>
|
|
13
|
+
</View>
|
|
14
|
+
</>
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const styles = {
|
|
19
|
+
container: `items-center flex-1 justify-center p-5`,
|
|
20
|
+
title: `text-xl font-bold`,
|
|
21
|
+
link: `mt-4 pt-4`,
|
|
22
|
+
linkText: `text-base text-[#2e78b7]`,
|
|
23
|
+
};
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,264 @@
|
|
|
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;
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"expo": {
|
|
3
|
+
"name": "holidia",
|
|
4
|
+
"slug": "holidia",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"scheme": "holidia",
|
|
7
|
+
"web": {
|
|
8
|
+
"bundler": "metro",
|
|
9
|
+
"output": "static",
|
|
10
|
+
"favicon": "./assets/favicon.png"
|
|
11
|
+
},
|
|
12
|
+
"newArchEnabled": true,
|
|
13
|
+
"plugins": [
|
|
14
|
+
"expo-router",
|
|
15
|
+
[
|
|
16
|
+
"expo-dev-launcher",
|
|
17
|
+
{
|
|
18
|
+
"launchMode": "most-recent"
|
|
19
|
+
}
|
|
20
|
+
],
|
|
21
|
+
"expo-web-browser",
|
|
22
|
+
[
|
|
23
|
+
"react-native-edge-to-edge",
|
|
24
|
+
{
|
|
25
|
+
"android": {
|
|
26
|
+
"parentTheme": "Default",
|
|
27
|
+
"enforceNavigationBarContrast": false
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
],
|
|
32
|
+
"experiments": {
|
|
33
|
+
"typedRoutes": true,
|
|
34
|
+
"tsconfigPaths": true
|
|
35
|
+
},
|
|
36
|
+
"orientation": "portrait",
|
|
37
|
+
"icon": "./assets/icon.png",
|
|
38
|
+
"userInterfaceStyle": "light",
|
|
39
|
+
"splash": {
|
|
40
|
+
"image": "./assets/splash.png",
|
|
41
|
+
"resizeMode": "contain",
|
|
42
|
+
"backgroundColor": "#ffffff"
|
|
43
|
+
},
|
|
44
|
+
"assetBundlePatterns": ["**/*"],
|
|
45
|
+
"ios": {
|
|
46
|
+
"supportsTablet": true,
|
|
47
|
+
"bundleIdentifier": "com.omkhetwal.holidia"
|
|
48
|
+
},
|
|
49
|
+
"android": {
|
|
50
|
+
"adaptiveIcon": {
|
|
51
|
+
"foregroundImage": "./assets/adaptive-icon.png",
|
|
52
|
+
"backgroundColor": "#ffffff"
|
|
53
|
+
},
|
|
54
|
+
"package": "com.omkhetwal.holidia",
|
|
55
|
+
"edgeToEdgeEnabled": true
|
|
56
|
+
},
|
|
57
|
+
"extra": {
|
|
58
|
+
"router": {
|
|
59
|
+
"origin": false
|
|
60
|
+
},
|
|
61
|
+
"eas": {
|
|
62
|
+
"projectId": "42ef7830-fd34-45dc-a680-356231697eaf"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|