newcandies 0.1.30 → 0.1.31
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 +1 -1
- package/templates/app-briefs/zustand/yummy/README.md +3 -4
- package/templates/app-briefs/zustand/yummy/assets/images/adaptive-icon.png +0 -0
- package/templates/app-briefs/zustand/yummy/assets/images/favicon.png +0 -0
- package/templates/app-briefs/zustand/yummy/assets/images/icon.png +0 -0
- package/templates/app-briefs/zustand/yummy/assets/images/splash-icon.png +0 -0
- package/templates/app-briefs/zustand/yummy/src/app/(tabs)/_layout.tsx +0 -2
- package/templates/app-briefs/zustand/yummy/src/app/(tabs)/cart.tsx +12 -44
- package/templates/app-briefs/zustand/yummy/src/app/(tabs)/index.tsx +46 -52
- package/templates/app-briefs/zustand/yummy/src/lib/constants/index.ts +8 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Yummy - Zustand App Brief
|
|
2
2
|
|
|
3
|
-
A
|
|
3
|
+
A fashion shopping app built with React Native, Expo, and Zustand for state management.
|
|
4
4
|
|
|
5
5
|
## Getting Started
|
|
6
6
|
|
|
@@ -51,13 +51,12 @@ The store should manage:
|
|
|
51
51
|
- `getItemCount()` - Get total number of items
|
|
52
52
|
- `getTotal()` - Calculate cart total
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
Wire up the store to the UI components - the Pressables are ready for your onPress handlers!
|
|
55
55
|
|
|
56
56
|
## Features
|
|
57
57
|
|
|
58
|
-
-
|
|
58
|
+
- 👗 Product grid with fashion items
|
|
59
59
|
- 🛒 Shopping cart with quantity controls
|
|
60
60
|
- 💾 Persistent cart storage with AsyncStorage
|
|
61
61
|
- 🎨 Beautiful orange-themed UI
|
|
62
62
|
- ✨ Smooth animations with react-native-reanimated
|
|
63
|
-
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -16,7 +16,6 @@ export default function TabLayout() {
|
|
|
16
16
|
"--color-muted-foreground"
|
|
17
17
|
) as string;
|
|
18
18
|
|
|
19
|
-
// TODO: Get cart item count from zustand store
|
|
20
19
|
const itemCount = 0;
|
|
21
20
|
|
|
22
21
|
return (
|
|
@@ -60,4 +59,3 @@ export default function TabLayout() {
|
|
|
60
59
|
</Tabs>
|
|
61
60
|
);
|
|
62
61
|
}
|
|
63
|
-
|
|
@@ -1,31 +1,21 @@
|
|
|
1
|
-
import { View, ScrollView, Pressable, Image } from "react-native";
|
|
2
1
|
import IonIcons from "@expo/vector-icons/Ionicons";
|
|
3
|
-
import {
|
|
2
|
+
import { Image, Pressable, ScrollView, View } from "react-native";
|
|
4
3
|
|
|
5
4
|
import { Screen } from "~/components/ui/screen";
|
|
6
|
-
import Text from "~/components/ui/text";
|
|
7
5
|
import { Squircle } from "~/components/ui/squircle";
|
|
6
|
+
import Text from "~/components/ui/text";
|
|
8
7
|
|
|
9
8
|
export default function CartScreen() {
|
|
10
|
-
// TODO: Get cart state and actions from zustand store
|
|
11
9
|
const items: CartItem[] = [];
|
|
12
10
|
|
|
13
|
-
const handleCheckout = () => {
|
|
14
|
-
if (items.length === 0) return;
|
|
15
|
-
toast.success("Order placed!");
|
|
16
|
-
// TODO: Clear cart using zustand store
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
// TODO: Get total from zustand store
|
|
20
11
|
const total = 0;
|
|
21
12
|
|
|
22
13
|
return (
|
|
23
14
|
<Screen className="flex-1 bg-background">
|
|
24
|
-
{/* Header */}
|
|
25
15
|
<View className="px-5 pt-4 pb-2">
|
|
26
16
|
<Text className="text-3xl font-bold text-foreground">My Bag</Text>
|
|
27
17
|
<Text className="text-muted-foreground mt-1">
|
|
28
|
-
{items.length === 0 ? "No
|
|
18
|
+
{items.length === 0 ? "No items yet!" : `${items.length} items`}
|
|
29
19
|
</Text>
|
|
30
20
|
</View>
|
|
31
21
|
|
|
@@ -36,7 +26,7 @@ export default function CartScreen() {
|
|
|
36
26
|
Your bag is empty
|
|
37
27
|
</Text>
|
|
38
28
|
<Text className="text-muted-foreground text-center mt-2">
|
|
39
|
-
Time to add some
|
|
29
|
+
Time to add some stylish pieces!
|
|
40
30
|
</Text>
|
|
41
31
|
</View>
|
|
42
32
|
) : (
|
|
@@ -49,15 +39,13 @@ export default function CartScreen() {
|
|
|
49
39
|
{items.map((item) => (
|
|
50
40
|
<Squircle
|
|
51
41
|
key={item.product.id}
|
|
52
|
-
className="bg-card mb-3 p-4 overflow-hidden"
|
|
42
|
+
className="bg-card mb-3 p-4 overflow-hidden rounded-3xl"
|
|
53
43
|
cornerSmoothing={1}
|
|
54
|
-
borderRadius={24}
|
|
55
44
|
>
|
|
56
45
|
<View className="flex-row items-center">
|
|
57
46
|
<Squircle
|
|
58
|
-
className="w-20 h-20 overflow-hidden"
|
|
47
|
+
className="w-20 h-20 overflow-hidden rounded-2xl"
|
|
59
48
|
cornerSmoothing={1}
|
|
60
|
-
borderRadius={18}
|
|
61
49
|
>
|
|
62
50
|
<Image
|
|
63
51
|
source={{ uri: item.product.coverImg }}
|
|
@@ -75,40 +63,23 @@ export default function CartScreen() {
|
|
|
75
63
|
</Text>
|
|
76
64
|
</View>
|
|
77
65
|
|
|
78
|
-
<Pressable
|
|
79
|
-
onPress={() => {
|
|
80
|
-
// TODO: Remove item using zustand store
|
|
81
|
-
}}
|
|
82
|
-
className="absolute top-3 right-3 w-7 h-7 items-center justify-center"
|
|
83
|
-
>
|
|
66
|
+
<Pressable className="absolute top-3 right-3 w-7 h-7 items-center justify-center">
|
|
84
67
|
<IonIcons name="close-circle" size={22} color="#d4d4d4" />
|
|
85
68
|
</Pressable>
|
|
86
69
|
</View>
|
|
87
70
|
|
|
88
|
-
{/* Quantity Controls */}
|
|
89
71
|
<View className="flex-row items-center justify-end mt-3">
|
|
90
72
|
<Squircle
|
|
91
|
-
className="flex-row items-center bg-background overflow-hidden"
|
|
73
|
+
className="flex-row items-center bg-background overflow-hidden rounded-xl"
|
|
92
74
|
cornerSmoothing={1}
|
|
93
|
-
borderRadius={14}
|
|
94
75
|
>
|
|
95
|
-
<Pressable
|
|
96
|
-
onPress={() => {
|
|
97
|
-
// TODO: Decrease quantity using zustand store
|
|
98
|
-
}}
|
|
99
|
-
className="w-10 h-10 items-center justify-center"
|
|
100
|
-
>
|
|
76
|
+
<Pressable className="w-10 h-10 items-center justify-center">
|
|
101
77
|
<IonIcons name="remove" size={18} color="#f97316" />
|
|
102
78
|
</Pressable>
|
|
103
79
|
<Text className="font-bold text-base w-8 text-center text-foreground">
|
|
104
80
|
{item.quantity}
|
|
105
81
|
</Text>
|
|
106
|
-
<Pressable
|
|
107
|
-
onPress={() => {
|
|
108
|
-
// TODO: Increase quantity using zustand store
|
|
109
|
-
}}
|
|
110
|
-
className="w-10 h-10 items-center justify-center"
|
|
111
|
-
>
|
|
82
|
+
<Pressable className="w-10 h-10 items-center justify-center">
|
|
112
83
|
<IonIcons name="add" size={18} color="#f97316" />
|
|
113
84
|
</Pressable>
|
|
114
85
|
</Squircle>
|
|
@@ -117,7 +88,6 @@ export default function CartScreen() {
|
|
|
117
88
|
))}
|
|
118
89
|
</ScrollView>
|
|
119
90
|
|
|
120
|
-
{/* Checkout Section */}
|
|
121
91
|
<View className="px-5 pb-6 pt-4">
|
|
122
92
|
<View className="flex-row justify-between items-center mb-4">
|
|
123
93
|
<Text className="font-bold text-lg text-foreground">Total</Text>
|
|
@@ -126,11 +96,10 @@ export default function CartScreen() {
|
|
|
126
96
|
</Text>
|
|
127
97
|
</View>
|
|
128
98
|
|
|
129
|
-
<Pressable
|
|
99
|
+
<Pressable>
|
|
130
100
|
<Squircle
|
|
131
|
-
className="bg-primary py-4 items-center justify-center overflow-hidden"
|
|
101
|
+
className="bg-primary py-4 items-center justify-center overflow-hidden rounded-xl"
|
|
132
102
|
cornerSmoothing={1}
|
|
133
|
-
borderRadius={20}
|
|
134
103
|
>
|
|
135
104
|
<Text className="text-white font-bold text-lg">
|
|
136
105
|
Checkout
|
|
@@ -143,4 +112,3 @@ export default function CartScreen() {
|
|
|
143
112
|
</Screen>
|
|
144
113
|
);
|
|
145
114
|
}
|
|
146
|
-
|
|
@@ -1,76 +1,70 @@
|
|
|
1
1
|
import { View, Pressable, Image } from "react-native";
|
|
2
2
|
import { FlashList } from "@shopify/flash-list";
|
|
3
3
|
import IonIcons from "@expo/vector-icons/Ionicons";
|
|
4
|
-
import { toast } from "sonner-native";
|
|
5
4
|
|
|
6
5
|
import { Screen } from "~/components/ui/screen";
|
|
7
6
|
import Text from "~/components/ui/text";
|
|
8
7
|
import { Squircle } from "~/components/ui/squircle";
|
|
9
8
|
import { customProducts } from "~/lib/constants";
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
type ProductCardProps = {
|
|
11
|
+
product: Product;
|
|
12
|
+
};
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
const ProductCard = ({ product }: ProductCardProps) => {
|
|
15
|
+
return (
|
|
16
|
+
<View className="flex-1 mx-2 items-center">
|
|
17
|
+
<View className="relative w-full">
|
|
18
|
+
<Squircle
|
|
19
|
+
className="w-full aspect-[3/4] overflow-hidden rounded-3xl"
|
|
20
|
+
cornerSmoothing={1}
|
|
21
|
+
>
|
|
22
|
+
<Image
|
|
23
|
+
source={{ uri: product.coverImg }}
|
|
24
|
+
className="w-full h-full"
|
|
25
|
+
resizeMode="cover"
|
|
26
|
+
/>
|
|
27
|
+
</Squircle>
|
|
28
|
+
<Pressable className="absolute bottom-4 left-1/2 -translate-x-1/2 flex-row items-center overflow-hidden">
|
|
29
|
+
<Squircle
|
|
30
|
+
className="flex-row items-center bg-white px-3 py-2 rounded-xl"
|
|
31
|
+
cornerSmoothing={1}
|
|
32
|
+
>
|
|
33
|
+
<Text className="text-sm font-medium text-foreground mr-2">
|
|
34
|
+
Add
|
|
35
|
+
</Text>
|
|
36
|
+
<Squircle
|
|
37
|
+
className="bg-primary w-8 h-8 items-center justify-center rounded-xl"
|
|
38
|
+
cornerSmoothing={1}
|
|
39
|
+
>
|
|
40
|
+
<IonIcons name="add" size={20} color="white" />
|
|
41
|
+
</Squircle>
|
|
42
|
+
</Squircle>
|
|
43
|
+
</Pressable>
|
|
44
|
+
</View>
|
|
45
|
+
<Text
|
|
46
|
+
className="text-base font-semibold mt-3 text-center text-foreground"
|
|
47
|
+
numberOfLines={1}
|
|
48
|
+
>
|
|
49
|
+
{product.name}
|
|
50
|
+
</Text>
|
|
51
|
+
<Text className="text-base text-muted-foreground">${product.price}</Text>
|
|
52
|
+
</View>
|
|
53
|
+
);
|
|
54
|
+
};
|
|
18
55
|
|
|
56
|
+
export default function HomeScreen() {
|
|
19
57
|
return (
|
|
20
58
|
<Screen className="flex-1 bg-background">
|
|
21
59
|
<View className="flex-1 px-3 pt-4">
|
|
22
60
|
<FlashList
|
|
23
61
|
data={customProducts}
|
|
24
62
|
numColumns={2}
|
|
25
|
-
estimatedItemSize={280}
|
|
26
63
|
showsVerticalScrollIndicator={false}
|
|
27
64
|
ItemSeparatorComponent={() => <View className="h-4" />}
|
|
28
|
-
renderItem={({ item }) =>
|
|
29
|
-
<View className="flex-1 mx-2 items-center">
|
|
30
|
-
<View className="relative w-full">
|
|
31
|
-
<Squircle
|
|
32
|
-
className="w-full aspect-[3/4] overflow-hidden"
|
|
33
|
-
cornerSmoothing={1}
|
|
34
|
-
borderRadius={28}
|
|
35
|
-
>
|
|
36
|
-
<Image
|
|
37
|
-
source={{ uri: item.coverImg }}
|
|
38
|
-
className="w-full h-full"
|
|
39
|
-
resizeMode="cover"
|
|
40
|
-
/>
|
|
41
|
-
</Squircle>
|
|
42
|
-
<Pressable
|
|
43
|
-
onPress={() => handleAddToCart(item)}
|
|
44
|
-
className="absolute bottom-4 left-1/2 -translate-x-1/2 flex-row items-center overflow-hidden"
|
|
45
|
-
style={{ elevation: 4 }}
|
|
46
|
-
>
|
|
47
|
-
<Squircle
|
|
48
|
-
className="flex-row items-center bg-white px-3 py-2"
|
|
49
|
-
cornerSmoothing={1}
|
|
50
|
-
borderRadius={20}
|
|
51
|
-
>
|
|
52
|
-
<Text className="text-sm font-medium text-foreground mr-2">Add</Text>
|
|
53
|
-
<Squircle
|
|
54
|
-
className="bg-primary w-8 h-8 items-center justify-center"
|
|
55
|
-
cornerSmoothing={1}
|
|
56
|
-
borderRadius={10}
|
|
57
|
-
>
|
|
58
|
-
<IonIcons name="add" size={20} color="white" />
|
|
59
|
-
</Squircle>
|
|
60
|
-
</Squircle>
|
|
61
|
-
</Pressable>
|
|
62
|
-
</View>
|
|
63
|
-
<Text className="text-base font-semibold mt-3 text-center text-foreground" numberOfLines={1}>
|
|
64
|
-
{item.name}
|
|
65
|
-
</Text>
|
|
66
|
-
<Text className="text-base text-muted-foreground">
|
|
67
|
-
${item.price}
|
|
68
|
-
</Text>
|
|
69
|
-
</View>
|
|
70
|
-
)}
|
|
65
|
+
renderItem={({ item }) => <ProductCard product={item} />}
|
|
71
66
|
/>
|
|
72
67
|
</View>
|
|
73
68
|
</Screen>
|
|
74
69
|
);
|
|
75
70
|
}
|
|
76
|
-
|
|
@@ -1,45 +1,44 @@
|
|
|
1
1
|
export const customProducts: Product[] = [
|
|
2
2
|
{
|
|
3
3
|
id: "1",
|
|
4
|
-
name: "
|
|
4
|
+
name: "Cocoa Turtleneck Sweater",
|
|
5
5
|
price: 12.99,
|
|
6
6
|
coverImg: "https://github.com/user-attachments/assets/a080b50c-46bb-4710-9d99-59b3ca4c39e4",
|
|
7
7
|
},
|
|
8
8
|
{
|
|
9
9
|
id: "2",
|
|
10
|
-
name: "
|
|
10
|
+
name: "Teal Plaid Blazer",
|
|
11
11
|
price: 9.50,
|
|
12
12
|
coverImg: "https://github.com/user-attachments/assets/fcaec702-c807-4193-b745-a501eb005bf5",
|
|
13
13
|
},
|
|
14
14
|
{
|
|
15
15
|
id: "3",
|
|
16
|
-
name: "
|
|
16
|
+
name: "Olive Wool Coat",
|
|
17
17
|
price: 14.25,
|
|
18
|
-
coverImg: "https://github.com/user-attachments/assets/
|
|
18
|
+
coverImg: "https://github.com/user-attachments/assets/d0046c75-dd8d-4fd8-9826-f72027f5a0e3",
|
|
19
19
|
},
|
|
20
20
|
{
|
|
21
21
|
id: "4",
|
|
22
|
-
name: "
|
|
22
|
+
name: "Ivory Wool Coat",
|
|
23
23
|
price: 11.80,
|
|
24
24
|
coverImg: "https://github.com/user-attachments/assets/70cd0677-a46d-4816-975b-28fe9d192a72",
|
|
25
25
|
},
|
|
26
26
|
{
|
|
27
27
|
id: "5",
|
|
28
|
-
name: "
|
|
28
|
+
name: "Camel Belted Blazer",
|
|
29
29
|
price: 8.99,
|
|
30
30
|
coverImg: "https://github.com/user-attachments/assets/441796bf-4fdd-4feb-9cf0-b8c1b5d33129",
|
|
31
31
|
},
|
|
32
32
|
{
|
|
33
33
|
id: "6",
|
|
34
|
-
name: "
|
|
34
|
+
name: "Taupe Longline Coat",
|
|
35
35
|
price: 10.50,
|
|
36
36
|
coverImg: "https://github.com/user-attachments/assets/2e1a6a9b-845e-4107-b8a5-4dc5b74da2f8",
|
|
37
37
|
},
|
|
38
38
|
{
|
|
39
39
|
id: "7",
|
|
40
|
-
name: "
|
|
40
|
+
name: "Coral Knit Sweater",
|
|
41
41
|
price: 15.99,
|
|
42
42
|
coverImg: "https://github.com/user-attachments/assets/1c786744-a3fb-4c61-ad7e-b1e601efc248",
|
|
43
43
|
},
|
|
44
44
|
];
|
|
45
|
-
|