newcandies 0.1.40 → 0.1.42
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
CHANGED
|
@@ -10,7 +10,7 @@ import { fileURLToPath } from "url";
|
|
|
10
10
|
import { execa } from "execa";
|
|
11
11
|
var __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
12
12
|
var REGISTRY_PATH = path.join(__dirname, "..", "templates", "registry.json");
|
|
13
|
-
var program = new Command().name("newcandies").option("-t, --type <type>").option("--template <name>").option("--category <name>").option("--variant <name>").option("-y, --yes").option("--pm <pm>", "
|
|
13
|
+
var program = new Command().name("newcandies").option("-t, --type <type>").option("--template <name>").option("--category <name>").option("--variant <name>").option("-y, --yes").option("--pm <pm>", "package manager (pnpm for boilerplates, bun for app-briefs)").option("--no-install").option("--no-prebuild").parse(process.argv);
|
|
14
14
|
var opts = program.opts();
|
|
15
15
|
function deepMerge(target, source) {
|
|
16
16
|
for (const key in source) {
|
|
@@ -25,84 +25,108 @@ async function main() {
|
|
|
25
25
|
console.clear();
|
|
26
26
|
p.intro(pc.bgMagenta(pc.black(" newcandies ")));
|
|
27
27
|
const registry = await fs.readJSON(REGISTRY_PATH);
|
|
28
|
-
const project = await p.group(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
28
|
+
const project = await p.group(
|
|
29
|
+
{
|
|
30
|
+
name: () => p.text({
|
|
31
|
+
message: "Project name?",
|
|
32
|
+
placeholder: "my-newcandies-app",
|
|
33
|
+
validate: (v) => !v ? "Enter a name" : /[^a-zA-Z0-9_-]/.test(v) ? "Use letters, numbers, _ or -" : void 0
|
|
34
|
+
}),
|
|
35
|
+
type: async () => {
|
|
36
|
+
if (opts.type) return opts.type;
|
|
37
|
+
return p.select({
|
|
38
|
+
message: "Pick type",
|
|
39
|
+
options: [
|
|
40
|
+
{ value: "boilerplates", label: "Boilerplate (Full Projects)" },
|
|
41
|
+
{ value: "mini-boilerplates", label: "Mini-boilerplate" },
|
|
42
|
+
{ value: "app-briefs", label: "App-brief (2 levels)" },
|
|
43
|
+
{ value: "mini-app-briefs", label: "Mini-app-brief (2 levels)" },
|
|
44
|
+
{ value: "default", label: "Default (Base)" }
|
|
45
|
+
]
|
|
46
|
+
});
|
|
47
|
+
},
|
|
48
|
+
template: async ({ results }) => {
|
|
49
|
+
const type = results.type;
|
|
50
|
+
if (type === "default" || type === "app-briefs" || type === "mini-app-briefs")
|
|
51
|
+
return null;
|
|
52
|
+
if (opts.template) return opts.template;
|
|
53
|
+
const items = registry[type];
|
|
54
|
+
return p.select({
|
|
55
|
+
message: "Pick template",
|
|
56
|
+
options: items.map((i) => ({
|
|
57
|
+
value: i.name,
|
|
58
|
+
label: `${i.name} - ${i.description}`
|
|
59
|
+
}))
|
|
60
|
+
});
|
|
61
|
+
},
|
|
62
|
+
category: async ({ results }) => {
|
|
63
|
+
const type = results.type;
|
|
64
|
+
if (type !== "app-briefs" && type !== "mini-app-briefs") return null;
|
|
65
|
+
if (opts.category) return opts.category;
|
|
66
|
+
const cats = registry[type];
|
|
67
|
+
return p.select({
|
|
68
|
+
message: "Pick category",
|
|
69
|
+
options: cats.map((c) => ({ value: c.category, label: c.category }))
|
|
70
|
+
});
|
|
71
|
+
},
|
|
72
|
+
variant: async ({ results }) => {
|
|
73
|
+
const type = results.type;
|
|
74
|
+
if (type !== "app-briefs" && type !== "mini-app-briefs") return null;
|
|
75
|
+
if (opts.variant) return opts.variant;
|
|
76
|
+
const cats = registry[type];
|
|
77
|
+
const cat = cats.find((c) => c.category === results.category);
|
|
78
|
+
return p.select({
|
|
79
|
+
message: "Pick variant",
|
|
80
|
+
options: cat.variants.map((v) => ({
|
|
81
|
+
value: v.name,
|
|
82
|
+
label: `${v.name} - ${v.description}`
|
|
83
|
+
}))
|
|
84
|
+
});
|
|
85
|
+
},
|
|
86
|
+
install: async () => {
|
|
87
|
+
if (opts.yes !== void 0) return !!opts.yes;
|
|
88
|
+
if (opts.install === false) return false;
|
|
89
|
+
return p.confirm({
|
|
90
|
+
message: "Install dependencies?",
|
|
91
|
+
initialValue: true
|
|
92
|
+
});
|
|
93
|
+
},
|
|
94
|
+
pm: async ({ results }) => {
|
|
95
|
+
const type = results.type;
|
|
96
|
+
if (type === "boilerplates" || type === "mini-boilerplates") {
|
|
97
|
+
if (opts.pm) return opts.pm;
|
|
98
|
+
return p.select({
|
|
99
|
+
message: "Package manager?",
|
|
100
|
+
options: [{ value: "pnpm", label: "pnpm" }],
|
|
101
|
+
initialValue: "pnpm"
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
if (opts.pm) return opts.pm;
|
|
105
|
+
return p.select({
|
|
106
|
+
message: "Package manager?",
|
|
107
|
+
options: [{ value: "bun", label: "bun" }],
|
|
108
|
+
initialValue: "bun"
|
|
109
|
+
});
|
|
110
|
+
}
|
|
82
111
|
},
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
{ value: "pnpm", label: "pnpm" },
|
|
89
|
-
{ value: "npm", label: "npm" },
|
|
90
|
-
{ value: "yarn", label: "yarn" },
|
|
91
|
-
{ value: "bun", label: "bun" }
|
|
92
|
-
],
|
|
93
|
-
initialValue: "pnpm"
|
|
94
|
-
});
|
|
112
|
+
{
|
|
113
|
+
onCancel: () => {
|
|
114
|
+
p.cancel("Aborted");
|
|
115
|
+
process.exit(0);
|
|
116
|
+
}
|
|
95
117
|
}
|
|
96
|
-
|
|
97
|
-
p.cancel("Aborted");
|
|
98
|
-
process.exit(0);
|
|
99
|
-
} });
|
|
118
|
+
);
|
|
100
119
|
const dest = path.resolve(process.cwd(), project.name);
|
|
101
120
|
const s = p.spinner();
|
|
102
121
|
s.start("Scaffolding");
|
|
103
122
|
let selectedItem = null;
|
|
104
123
|
if (project.type === "default") {
|
|
105
|
-
selectedItem = {
|
|
124
|
+
selectedItem = {
|
|
125
|
+
name: "default",
|
|
126
|
+
path: "",
|
|
127
|
+
description: "Default",
|
|
128
|
+
strategy: "base"
|
|
129
|
+
};
|
|
106
130
|
} else if (project.type === "boilerplates" || project.type === "mini-boilerplates") {
|
|
107
131
|
const list = registry[project.type];
|
|
108
132
|
selectedItem = list.find((i) => i.name === project.template) || null;
|
|
@@ -130,24 +154,51 @@ async function main() {
|
|
|
130
154
|
if (await fs.pathExists(appPath)) baseApp = await fs.readJSON(appPath);
|
|
131
155
|
}
|
|
132
156
|
if (selectedItem.path) {
|
|
133
|
-
const templatePath = path.join(
|
|
157
|
+
const templatePath = path.join(
|
|
158
|
+
__dirname,
|
|
159
|
+
"..",
|
|
160
|
+
"templates",
|
|
161
|
+
selectedItem.path
|
|
162
|
+
);
|
|
134
163
|
if (await fs.pathExists(templatePath)) {
|
|
135
164
|
await fs.copy(templatePath, dest, { overwrite: true });
|
|
136
165
|
}
|
|
137
166
|
}
|
|
138
167
|
if (strategy === "base" && basePkg) {
|
|
139
|
-
const templatePkgPath = path.join(
|
|
140
|
-
|
|
168
|
+
const templatePkgPath = path.join(
|
|
169
|
+
__dirname,
|
|
170
|
+
"..",
|
|
171
|
+
"templates",
|
|
172
|
+
selectedItem.path,
|
|
173
|
+
"package.json"
|
|
174
|
+
);
|
|
175
|
+
const templateAppPath = path.join(
|
|
176
|
+
__dirname,
|
|
177
|
+
"..",
|
|
178
|
+
"templates",
|
|
179
|
+
selectedItem.path,
|
|
180
|
+
"app.json"
|
|
181
|
+
);
|
|
141
182
|
if (await fs.pathExists(templatePkgPath)) {
|
|
142
183
|
const tmplPkg = await fs.readJSON(templatePkgPath);
|
|
143
184
|
const isFullPackageJson = tmplPkg.scripts || tmplPkg.main || tmplPkg.version;
|
|
144
185
|
if (isFullPackageJson) {
|
|
145
|
-
await fs.writeJSON(path.join(dest, "package.json"), tmplPkg, {
|
|
186
|
+
await fs.writeJSON(path.join(dest, "package.json"), tmplPkg, {
|
|
187
|
+
spaces: 2
|
|
188
|
+
});
|
|
146
189
|
} else {
|
|
147
|
-
basePkg.dependencies = {
|
|
148
|
-
|
|
190
|
+
basePkg.dependencies = {
|
|
191
|
+
...basePkg.dependencies,
|
|
192
|
+
...tmplPkg.dependencies
|
|
193
|
+
};
|
|
194
|
+
basePkg.devDependencies = {
|
|
195
|
+
...basePkg.devDependencies,
|
|
196
|
+
...tmplPkg.devDependencies
|
|
197
|
+
};
|
|
149
198
|
basePkg.scripts = { ...basePkg.scripts, ...tmplPkg.scripts };
|
|
150
|
-
await fs.writeJSON(path.join(dest, "package.json"), basePkg, {
|
|
199
|
+
await fs.writeJSON(path.join(dest, "package.json"), basePkg, {
|
|
200
|
+
spaces: 2
|
|
201
|
+
});
|
|
151
202
|
}
|
|
152
203
|
}
|
|
153
204
|
if (await fs.pathExists(templateAppPath) && baseApp) {
|
|
@@ -160,7 +211,9 @@ async function main() {
|
|
|
160
211
|
for (const file of destFiles) {
|
|
161
212
|
if (file === "env" || file.startsWith("env.") || file === "npmrc") {
|
|
162
213
|
const newName = "." + file;
|
|
163
|
-
await fs.move(path.join(dest, file), path.join(dest, newName), {
|
|
214
|
+
await fs.move(path.join(dest, file), path.join(dest, newName), {
|
|
215
|
+
overwrite: true
|
|
216
|
+
});
|
|
164
217
|
}
|
|
165
218
|
}
|
|
166
219
|
const pkgPath = path.join(dest, "package.json");
|
|
@@ -191,14 +244,18 @@ async function main() {
|
|
|
191
244
|
const sp = p.spinner();
|
|
192
245
|
sp.start("Generating ios/android (expo prebuild)");
|
|
193
246
|
try {
|
|
194
|
-
await execa("npx", ["expo", "prebuild"], {
|
|
247
|
+
await execa("npx", ["expo", "prebuild"], {
|
|
248
|
+
cwd: dest,
|
|
249
|
+
stdio: "inherit",
|
|
250
|
+
env: { ...process.env, CI: "1" }
|
|
251
|
+
});
|
|
195
252
|
sp.stop("Native projects generated");
|
|
196
253
|
} catch (e) {
|
|
197
254
|
sp.stop("Prebuild failed (check logs)");
|
|
198
255
|
}
|
|
199
256
|
}
|
|
200
257
|
}
|
|
201
|
-
const runCmd = (cmd) => ` ${project.pm} ${
|
|
258
|
+
const runCmd = (cmd) => ` ${project.pm} ${cmd}`;
|
|
202
259
|
const nextSteps = [
|
|
203
260
|
pc.green("Next steps:"),
|
|
204
261
|
` cd ${project.name}`,
|
|
@@ -209,14 +266,8 @@ async function main() {
|
|
|
209
266
|
p.outro(nextSteps.filter(Boolean).join("\n"));
|
|
210
267
|
}
|
|
211
268
|
async function installDeps(pm, cwd) {
|
|
212
|
-
const
|
|
213
|
-
|
|
214
|
-
if (pm === "npm") {
|
|
215
|
-
args.push("--no-audit", "--no-fund", "--loglevel=error");
|
|
216
|
-
} else if (pm === "pnpm") {
|
|
217
|
-
args.push("--no-optional");
|
|
218
|
-
}
|
|
219
|
-
await execa(cmd, args, { cwd, stdio: "pipe" });
|
|
269
|
+
const args = ["install"];
|
|
270
|
+
await execa(pm, args, { cwd, stdio: "pipe" });
|
|
220
271
|
}
|
|
221
272
|
main().catch((e) => {
|
|
222
273
|
p.log.error(e?.message ?? String(e));
|
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;
|