initial commit

This commit is contained in:
Juthatip McDevitt 2024-07-22 22:03:28 -05:00
parent 9e8c79c046
commit cfb7924b57
26 changed files with 19505 additions and 0 deletions

22
donutshop/.gitignore vendored Normal file
View file

@ -0,0 +1,22 @@
node_modules/
.expo/
dist/
npm-debug.*
*.jks
*.p8
*.p12
*.key
*.mobileprovision
*.orig.*
web-build/
# macOS
.DS_Store
# @generated expo-cli sync-2b81b286409207a5da26e14c78851eb30d8ccbdb
# The following patterns were generated by expo-cli
expo-env.d.ts
# @end expo-cli
assets/images/*

50
donutshop/README.md Normal file
View file

@ -0,0 +1,50 @@
# Welcome to your Expo app 👋
This is an [Expo](https://expo.dev) project created with [`create-expo-app`](https://www.npmjs.com/package/create-expo-app).
## Get started
1. Install dependencies
```bash
npm install
```
2. Start the app
```bash
npx expo start
```
In the output, you'll find options to open the app in a
- [development build](https://docs.expo.dev/develop/development-builds/introduction/)
- [Android emulator](https://docs.expo.dev/workflow/android-studio-emulator/)
- [iOS simulator](https://docs.expo.dev/workflow/ios-simulator/)
- [Expo Go](https://expo.dev/go), a limited sandbox for trying out app development with Expo
You can start developing by editing the files inside the **app** directory. This project uses [file-based routing](https://docs.expo.dev/router/introduction).
## Get a fresh project
When you're ready, run:
```bash
npm run reset-project
```
This command will move the starter code to the **app-example** directory and create a blank **app** directory where you can start developing.
## Learn more
To learn more about developing your project with Expo, look at the following resources:
- [Expo documentation](https://docs.expo.dev/): Learn fundamentals, or go into advanced topics with our [guides](https://docs.expo.dev/guides).
- [Learn Expo tutorial](https://docs.expo.dev/tutorial/introduction/): Follow a step-by-step tutorial where you'll create a project that runs on Android, iOS, and the web.
## Join the community
Join our community of developers creating universal apps.
- [Expo on GitHub](https://github.com/expo/expo): View our open source platform and contribute.
- [Discord community](https://chat.expo.dev): Chat with Expo users and ask questions.

36
donutshop/app.json Normal file
View file

@ -0,0 +1,36 @@
{
"expo": {
"name": "donutshop",
"slug": "donutshop",
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/images/icon.png",
"scheme": "myapp",
"userInterfaceStyle": "automatic",
"splash": {
"image": "./assets/images/splash.png",
"resizeMode": "contain",
"backgroundColor": "#ffffff"
},
"ios": {
"supportsTablet": true
},
"android": {
"adaptiveIcon": {
"foregroundImage": "./assets/images/adaptive-icon.png",
"backgroundColor": "#ffffff"
}
},
"web": {
"bundler": "metro",
"output": "static",
"favicon": "./assets/images/favicon.png"
},
"plugins": [
"expo-router"
],
"experiments": {
"typedRoutes": true
}
}
}

View file

@ -0,0 +1,56 @@
import { Dimensions, ImageBackground, ImageProps, Pressable, StyleSheet, Text, View } from 'react-native'
import { AntDesign, MaterialIcons} from '@expo/vector-icons';
import React from 'react'
const card_width = Dimensions.get('window').width * 0.3;
interface DrinkCardProps {
id: string;
index: number;
type: string;
name: string;
image_item: ImageProps;
average_rating: number;
prices: any;
buttonPressHandler: any;
}
const DrinkCard:React.FC<DrinkCardProps> = ({
id,
index,
type,
name,
image_item,
average_rating,
prices,
buttonPressHandler,
}) => {
return (
<View style={{justifyContent: 'space-between', borderWidth: 1, borderRightColor: '#DA7297', borderLeftColor: '#DA7297', borderTopColor: '#DA7297', borderBottomColor: '#DA7297', padding: 10, borderRadius: 10, height: 250, width: 150}}>
<View>
<ImageBackground source={image_item} style={{width:120, height: 120, overflow: 'hidden'}} resizeMode= "contain">
<View style={{flexDirection: 'row', gap: 3}}>
<AntDesign name="star" size={16} color="#DA7297"/>
<Text style={{color: '#EF5A6F', textAlign: 'right'}}>{average_rating}</Text>
</View>
</ImageBackground>
<View style={{margin: 10}}>
<Text style={{textAlign: 'center', color: '#DA7297', fontWeight: 500}}>{name}</Text>
</View>
</View>
<View style={{flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between'}}>
<Text style={{fontWeight: 600, color: '#EF5A6F'}}>$<Text>{prices.price}</Text></Text>
<Pressable onPress={() => {}}>
<MaterialIcons name="add-circle" size={24} color="#EF5A6F" />
</Pressable>
</View>
</View>
)
}
export default DrinkCard
const styles = StyleSheet.create({})

View file

@ -0,0 +1,48 @@
import { Pressable, StyleSheet, Text, View } from 'react-native'
import React from 'react'
import { FontAwesome, Feather } from '@expo/vector-icons';
import { useNavigation } from '@react-navigation/native';
const HomeHeader = () => {
const navigation = useNavigation();
const date = new Date();
const hours = date.getHours();
let message;
if (hours < 12) {
message = <Text>Good Morning &#9728;</Text>;
} else if (hours < 18) {
message = <Text>Good Afternoon &#9728;</Text>;
} else {
message = <Text>Good Evening &#57420;</Text>;
}
return (
<>
<View style={{marginTop: 20, paddingHorizontal: 20, paddingVertical: 10}}>
<View>
<Text style={{fontSize: 30, fontWeight: 600}}>{message}</Text>
</View>
<View style={{marginTop: 30, flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between'}}>
<Pressable onPress={() => navigation.navigate('inbox')} style={{flexDirection: 'row', gap: 5, alignItems: 'flex-end'}}>
<Feather name="mail" size={24} color="gray" /><Text style={{fontSize: 16}}>Inbox</Text>
</Pressable>
<Pressable onPress={() => navigation.navigate('profile')}>
<FontAwesome name="user-circle-o" size={24} color="gray" />
</Pressable>
</View>
</View>
<View style={{height: 1, backgroundColor: "#B4B4B8", shadowColor: '#B4B4B8', shadowOffset: { width: 0, height: 1 }, shadowOpacity: 0.5, shadowRadius: 1, }}></View>
</>
)
}
export default HomeHeader
const styles = StyleSheet.create({
Emoji:{
padding: 20
}
})

View file

@ -0,0 +1,57 @@
import { Dimensions, ImageBackground, ImageProps, Pressable, StyleSheet, Text, View } from 'react-native'
import React from 'react'
import { AntDesign, MaterialIcons} from '@expo/vector-icons';
const card_width = Dimensions.get('window').width * 0.3;
interface donutCardProps {
id: string;
index: number;
type: string;
donutname: string;
name: string;
image_item: ImageProps;
average_rating: number;
prices: any;
buttonPressHandler: any;
}
const MenuCard:React.FC<donutCardProps> = ({
id,
index,
type,
name,
donutname,
image_item,
average_rating,
prices,
buttonPressHandler,
}) => {
return (
<View style={{justifyContent: 'space-between', borderWidth: 1, borderRightColor: '#DA7297', borderLeftColor: '#DA7297', borderTopColor: '#DA7297', borderBottomColor: '#DA7297', padding: 10, borderRadius: 10, height: 250, width: 150}}>
<View>
<ImageBackground source={image_item} style={{width:card_width, height: card_width, overflow: 'hidden'}} resizeMode= "contain">
<View style={{flexDirection: 'row', gap: 3}}>
<AntDesign name="star" size={16} color="#DA7297"/>
<Text style={{color: '#EF5A6F', textAlign: 'right'}}>{average_rating}</Text>
</View>
</ImageBackground>
<View style={{margin: 10}}>
<Text style={{textAlign: 'center', color: '#DA7297', fontWeight: 500}}>{donutname}</Text>
</View>
</View>
<View style={{flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between'}}>
<Text style={{fontWeight: 600, color: '#EF5A6F'}}>$<Text>{prices.price}</Text></Text>
<Pressable onPress={() => {}}>
<MaterialIcons name="add-circle" size={24} color="#EF5A6F" />
</Pressable>
</View>
</View>
)
}
export default MenuCard
const styles = StyleSheet.create({})

View file

@ -0,0 +1,212 @@
const DonutData =[
{
id: 'D1',
name: 'Classic',
donutname: 'Chocolate Beurre noisette Walnut Cream',
description: `A decadent treat that combines rich, velvety chocolate with the nutty flavor of beurre noisette and the crunch of walnuts. The beurre noisette adds a deep, nutty flavor and aroma to the donut, while the cream filling provides a smooth and indulgent texture.`,
calories: '295',
image_item: require('../../assets/images/menu/donut1.png'),
prices: [
{price: '4.99', currency: '$'},
],
average_rating: 4.7,
ratings_count: '6,879',
favourite: false,
type: 'Classic Donut',
index: 0,
},
{
id: 'D2',
name: 'Classic',
donutname: 'Black sesame matcha',
description: `A fusion of Asian-inspired flavors. On top or mixed into the dough, you'll find black sesame seeds, which add a nutty flavor and a contrasting texture.`,
calories: '295',
image_item: require('../../assets/images/menu/donut2.png'),
prices: [
{price: '4.99', currency: '$'},
],
average_rating: 4.7,
ratings_count: '6,879',
favourite: false,
type: 'Classic Donut',
index: 1,
},
{
id: 'D3',
name: 'Classic',
donutname: 'Passionfruit Creamcheese',
description: `A delightful combination that brings together the tangy sweetness of passionfruit with the rich creaminess of cream cheese`,
calories: '295',
image_item: require('../../assets/images/menu/donut3.png'),
prices: [
{price: '4.99', currency: '$'},
],
average_rating: 4.7,
ratings_count: '6,879',
favourite: false,
type: 'Classic Donut',
index: 2,
},
{
id: 'D4',
name: 'Classic',
donutname: 'Strawberry glazed with white chocolate',
description: `A decadent dessert that combines the fruity sweetness of strawberries with the rich creaminess of white chocolate.`,
calories: '295',
image_item: require('../../assets/images/menu/donut4.png'),
prices: [
{price: '4.99', currency: '$'},
],
average_rating: 4.7,
ratings_count: '6,879',
favourite: false,
type: 'Classic Donut',
index: 3,
},
{
id: 'D5',
name: 'Classic',
donutname: 'Boston Caramel Cream',
description: `A rich and indulgent combination of flavors vanilla and caramel.`,
calories: '295',
image_item: require('../../assets/images/menu/donut5.png'),
prices: [
{price: '4.99', currency: '$'},
],
average_rating: 4.7,
ratings_count: '6,879',
favourite: false,
type: 'Classic Donut',
index: 4,
},
{
id: 'D6',
name: 'Special',
donutname: 'Oreo Cookie Crumble',
description: `A delightful fusion of a classic American cookie and a beloved pastry.`,
calories: '295',
image_item: require('../../assets/images/menu/donut6.png'),
prices: [
{price: '5.99', currency: '$'},
],
average_rating: 4.7,
ratings_count: '6,879',
favourite: false,
type: 'Special Donut',
index: 5,
},
{
id: 'D7',
name: 'Special',
donutname: 'Reeses peanut butter Cup',
description: `A decadent treat that combines the beloved flavors of chocolate and peanut butter into a delicious pastry.`,
calories: '295',
image_item: require('../../assets/images/menu/donut7.png'),
prices: [
{price: '5.99', currency: '$'},
],
average_rating: 4.7,
ratings_count: '6,879',
favourite: false,
type: 'Special Donut',
index: 6,
},
{
id: 'D8',
name: 'Special',
donutname: 'M&M Chocolate Boston Cream',
description: `A delightful variation of the classic Boston Cream donut, incorporating colorful M&M candies for added texture and flavor.`,
calories: '295',
image_item: require('../../assets/images/menu/donut8.png'),
prices: [
{price: '5.99', currency: '$'},
],
average_rating: 4.7,
ratings_count: '6,879',
favourite: false,
type: 'Special Donut',
index: 7,
},
{
id: 'D9',
name: 'Special',
donutname: 'White Chocolate & Nutella Bueno',
description: `A flavorful and indulgent treat that combines creamy white chocolate with the beloved hazelnut flavor of Nutella. `,
calories: '295',
image_item: require('../../assets/images/menu/donut9.png'),
prices: [
{price: '6.99', currency: '$'},
],
average_rating: 4.7,
ratings_count: '6,879',
favourite: false,
type: 'Special Donut',
index: 8,
},
{
id: 'D10',
name: 'Special',
donutname: 'White chocolate matcha pistachio',
description: `Indulge in the creamy goodness of White Chocolate Matcha Donut with a delightful crunch of crushed pistachios.`,
calories: '295',
image_item: require('../../assets/images/menu/donut10.png'),
prices: [
{price: '5.99', currency: '$'},
],
average_rating: 4.7,
ratings_count: '6,879',
favourite: false,
type: 'Special Donut',
index: 9,
},
{
id: 'D11',
name: 'Party',
donutname: 'Classic Half Dozen',
description: `Staff-picked(just the classics). Please note that no modifications can be accommodated.`,
calories: '-',
image_item: require('../../assets/images/menu/box_half_dozen.jpg'),
prices: [
{price: '26.99', currency: '$'},
],
average_rating: 4.7,
ratings_count: '6,879',
favourite: false,
type: 'Box Donut',
index: 10,
},
{
id: 'D12',
name: 'Party',
donutname: 'Classic Dozen',
description: `Staff-picked(just the classics). Please note that no modifications can be accommodated.`,
calories: '-',
image_item: require('../../assets/images/menu/box_dozen.jpg'),
prices: [
{price: '52.99', currency: '$'},
],
average_rating: 4.7,
ratings_count: '6,879',
favourite: false,
type: 'Box Donut',
index: 11,
},
{
id: 'D13',
name: 'Party',
donutname: 'Mothers Day Box',
description: `Five special donuts only on mother's day.`,
calories: '-',
image_item: require('../../assets/images/menu/box_special.jpg'),
prices: [
{price: '22.99', currency: '$'},
],
average_rating: 4.7,
ratings_count: '6,879',
favourite: false,
type: 'Box Donut',
index: 12,
},
]
export default DonutData;

View file

@ -0,0 +1,63 @@
const DrinkData = [
{
id: 'D1',
name: 'Chocolate Milkshake',
description: `A creamy, thick chocolate shake topped with a nice dollop of whipped cream.`,
image_item: require('../../assets/images/menu/drink1.png'),
prices: [
{size: 'S', price: '6.99', currency: '$'},
{size: 'M', price: '8.49', currency: '$'},
{size: 'L', price: '8.99', currency: '$'},
{toping_name: 'Mini cookies', price: '1.5', currency: '$'},
{toping_name: 'Sprinkles', price: '0.5', currency: '$'},
{toping_name: 'Marshmallows', price: '1.25', currency: '$'},
{toping_name: 'Pretzels', price: '1.25', currency: '$'},
{toping_name: 'Popcorn', price: '1.25', currency: '$'},
{toping_name: 'Chocolate Chips', price: '1.25', currency: '$'},
],
average_rating: 4.7,
ratings_count: '6,879',
favourite: false,
type: 'Drink',
index: 0,
},
{
id: 'D2',
name: 'Strawberry Milkshake',
description: `A creamy, thick strawberry shake topped with nice dollop of whipped cream and fresh strawberries.`,
image_item: require('../../assets/images/menu/drink3.png'),
prices: [
{size: 'S', price: '6.99', currency: '$'},
{size: 'M', price: '8.49', currency: '$'},
{size: 'L', price: '8.99', currency: '$'},
],
average_rating: 4.7,
ratings_count: '6,879',
favourite: false,
type: 'Drink',
index: 1,
},
{
id: 'D3',
name: 'Coffee Affogato Milkshake',
description: `A creamy, thick coffee affogato shake topped with dollop of whipped cream and caramel syrup.`,
image_item: require('../../assets/images/menu/drink2.png'),
prices: [
{size: 'S', price: '7.99', currency: '$'},
{size: 'M', price: '9.49', currency: '$'},
{size: 'L', price: '9.99', currency: '$'},
{toping_name: 'Mini cookies', price: '1.5', currency: '$'},
{toping_name: 'Sprinkles', price: '0.5', currency: '$'},
{toping_name: 'Marshmallows', price: '1.25', currency: '$'},
{toping_name: 'Pretzels', price: '1.25', currency: '$'},
{toping_name: 'Popcorn', price: '1.25', currency: '$'},
{toping_name: 'Chocolate Chips', price: '1.25', currency: '$'},
],
average_rating: 4.7,
ratings_count: '6,879',
favourite: false,
type: 'Drink',
index: 2,
},
]
export default DrinkData;

30
donutshop/app/index.js Normal file
View file

@ -0,0 +1,30 @@
import React from 'react';
import { KeyboardAvoidingView, SafeAreaView, View} from "react-native";
import { NavigationContainer } from "@react-navigation/native";
import { createStackNavigator } from '@react-navigation/stack';
import HomeNavigator from "./navigation/HomeNavigator";
import Detail from "./screen/Detail";
import Payment from "./screen/Payment";
import Inbox from "./screen/Inbox"
import Profile from "./screen/Profile"
const Stack = createStackNavigator();
export default function Index() {
return (
<NavigationContainer independent={true}>
<KeyboardAvoidingView>
<View style={{height: '100%', width: '100%'}}>
<Stack.Navigator screenOptions={{headerShown: false}}>
<Stack.Screen name='Tab' component={HomeNavigator}/>
<Stack.Screen name='detail' component={Detail}/>
<Stack.Screen name='payment' component={Payment}/>
<Stack.Screen name='inbox' component={Inbox}/>
<Stack.Screen name='profile' component={Profile}/>
</Stack.Navigator>
</View>
</KeyboardAvoidingView>
</NavigationContainer>
);
}

View file

@ -0,0 +1,93 @@
import React from 'react';
import {StyleSheet} from "react-native";
import {createBottomTabNavigator} from "@react-navigation/bottom-tabs";
import {Entypo, MaterialCommunityIcons, Fontisto, MaterialIcons, Ionicons} from '@expo/vector-icons';
import Home from '../screen/Home';
import Menu from '../screen/Menu';
import Cart from '../screen/Cart';
import Favorite from '../screen/Favorite';
import OrderReceipt from '../screen/OrderReceipt';
const Tab = createBottomTabNavigator();
const HomeNavigator = () => {
return (
<Tab.Navigator screenOptions={{headerShown: false, tabBarHideOnKeyboard: true, tabBarStyle: styles.tabBarStyle}}>
<Tab.Screen name='home' component={Home}
options={{
tabBarLabel: "Home",
tabBarActiveTintColor: '#DA7297',
tabBarInactiveTintColor: 'gray',
tabBarIcon:({focused}) =>
focused? (
<Entypo name="home" size={26} color="#DA7297" style={{marginTop: 5, marginBottom: 5}}/>
) : (
<Entypo name="home" size={26} color="gray" style={{marginTop: 5, marginBottom: 5}}/>
)
}}
/>
<Tab.Screen name='menu' component={Menu}
options={{
tabBarLabel: "Menu",
tabBarActiveTintColor: '#DA7297',
tabBarInactiveTintColor: 'gray',
tabBarIcon:({focused}) =>
focused? (
<Ionicons name="fast-food-sharp" size={26} color="#DA7297" style={{marginTop: 5, marginBottom: 5}}/>
) : (
<Ionicons name="fast-food-sharp" size={26} color="gray" style={{marginTop: 5, marginBottom: 5}}/>
)
}}
/>
<Tab.Screen name='favorit' component={Favorite}
options={{
tabBarLabel: "Favorite",
tabBarActiveTintColor: '#DA7297',
tabBarInactiveTintColor: 'gray',
tabBarIcon:({focused}) =>
focused? (
<MaterialIcons name="favorite" size={26} color="#DA7297" style={{marginTop: 5, marginBottom: 5}}/>
) : (
<MaterialIcons name="favorite" size={26} color="gray" style={{marginTop: 5, marginBottom: 5}}/>
)
}}
/>
<Tab.Screen name='cart' component={Cart}
options={{
tabBarLabel: "Your order",
tabBarActiveTintColor: '#DA7297',
tabBarInactiveTintColor: 'gray',
tabBarIcon:({focused}) =>
focused? (
<Fontisto name="shopping-bag" size={22} color="#DA7297" style={{marginTop: 5, marginBottom: 5}}/>
) : (
<Fontisto name="shopping-bag" size={22} color="gray" style={{marginTop: 5, marginBottom: 5}}/>
)
}}
/>
<Tab.Screen name='receipt' component={OrderReceipt}
options={{
tabBarLabel: "Receipt",
tabBarActiveTintColor: '#DA7297',
tabBarInactiveTintColor: 'gray',
tabBarIcon:({focused}) =>
focused? (
<Ionicons name="receipt" size={24} color="#DA7297" style={{marginTop: 5, marginBottom: 5}}/>
) : (
<Ionicons name="receipt" size={24} color="gray" style={{marginTop: 5, marginBottom: 5}}/>
)
}}
/>
</Tab.Navigator>
)
}
export default HomeNavigator
const styles = StyleSheet.create({
tabBarStyle: {
height: 80,
}
})

View file

@ -0,0 +1,12 @@
import { View, Text } from 'react-native'
import React from 'react'
const Cart = () => {
return (
<View>
<Text>Cart</Text>
</View>
)
}
export default Cart

View file

@ -0,0 +1,12 @@
import { View, Text } from 'react-native'
import React from 'react'
const Detail = () => {
return (
<View>
<Text>Detail</Text>
</View>
)
}
export default Detail

View file

@ -0,0 +1,12 @@
import { View, Text } from 'react-native'
import React from 'react'
const Favorite = () => {
return (
<View>
<Text>Favorite</Text>
</View>
)
}
export default Favorite

View file

@ -0,0 +1,24 @@
import { View, Text, ScrollView, StyleSheet } from 'react-native'
import React from 'react'
import HomeHeader from '../components/HomeHeader'
import { StatusBar } from 'expo-status-bar';
const Home = () => {
return (
<View style={{flex: 1, backgroundColor: 'white'}}>
<StatusBar style="dark"/>
<ScrollView showsVerticalScrollIndicator={false} contentContainerStyle={styles.ScrollViewFlex}>
<HomeHeader />
</ScrollView>
</View>
)
}
export default Home
const styles = StyleSheet.create({
ScrollViewFlex:{
flexGrow: 1,
}
})

View file

@ -0,0 +1,28 @@
import { View, Text, Pressable, Image, StyleSheet } from 'react-native'
import React from 'react'
import { Ionicons } from '@expo/vector-icons';
import { useNavigation } from '@react-navigation/native';
const Inbox = () => {
const navigation = useNavigation();
return (
<View style={{flex: 1, backgroundColor: "white"}}>
<View style={{marginTop: 20, paddingHorizontal: 20, paddingVertical: 10}}>
<Pressable onPress={() => navigation.goBack()} style={{flexDirection: 'row', gap: 5, alignItems: 'flex-end'}}>
<Ionicons name="chevron-back" size={24} color="gray" />
</Pressable>
<Text style={{paddingHorizontal: 10, paddingTop: 10, fontSize: 30, fontWeight: 600}}>Inbox</Text>
</View>
<View style={{height: 1, backgroundColor: "#B4B4B8", shadowColor: '#B4B4B8', shadowOffset: { width: 0, height: 1 }, shadowOpacity: 0.5, shadowRadius: 1, }}></View>
<View style={{marginVertical: 50, alignItems: 'center'}}>
<Image source={require('../../assets/images/misc/empty-inbox.png')} style={{ width: 200, height: 200, resizeMode: "contain"}} />
<Text style={{marginTop: 30, fontSize: 16, fontWeight: 500}}>No messages right now</Text>
</View>
</View>
)
}
export default Inbox

View file

@ -0,0 +1,146 @@
import { View, Text, ScrollView, StyleSheet, Pressable } from 'react-native'
import React, { useState } from 'react'
import { useStore } from '../store/store';
import { useBottomTabBarHeight } from '@react-navigation/bottom-tabs';
import { FlatList, TextInput } from 'react-native-gesture-handler';
import { AntDesign } from '@expo/vector-icons';
import MenuCard from "../components/MenuCard";
import DrinkCard from "../components/DrinkCard";
const getCategoriesFromData = (data: any) => {
let temp: any = {};
for (let i = 0; i < data.length; i++) {
if (temp[data[i].name] == undefined) {
temp[data[i].name] = 1;
} else {
temp[data[i].name]++;
}
}
let categories = Object.keys(temp);
categories.unshift('All');
return categories;
};
const getDonutList = (category: string, data: any) => {
if (category == 'All') {
return data;
} else {
let donutlist = data.filter((item: any) => item.name == category);
return donutlist;
}
};
const Menu = () => {
const DonutList = useStore((state: any) => state.AllDonut);
const DrinkList = useStore((state: any) => state.AllDrinkList);
const [categories, setCategories] = useState(getCategoriesFromData(DonutList));
const [searchMenu, setSearchMenu] = useState('');
const [categoryIndex, setCategoryIndex] = useState({index: 0, category: categories[0]});
const [sortItem, setSortItem] = useState(getDonutList(categoryIndex.category, DonutList));
const tabBar = useBottomTabBarHeight();
//console.log(sortItem.length)
return (
<View style={{flex: 1, backgroundColor: 'white'}}>
<ScrollView showsVerticalScrollIndicator={false} contentContainerStyle={styles.ScrollViewFlex}>
<View style={{marginTop: 20, paddingHorizontal: 20, paddingVertical: 10}}>
<Text style={{fontSize: 30, fontWeight: 600}}>Menu</Text>
<View style={{marginVertical: 20, backgroundColor: '#EEEEEE', padding: 10, borderRadius: 10, flexDirection: 'row', justifyContent: 'space-between'}}>
<TextInput value={searchMenu} onChangeText={text =>setSearchMenu(text)} placeholder='Search...' style={{fontSize: 18, flex:1}}/>
<Pressable onPress={() => {}}>
<AntDesign name="search1" size={24} color="gray" />
</Pressable>
</View>
<Text style={{fontSize: 26, fontWeight: 600, color: '#EF5A6F'}}>Donuts</Text>
</View>
{/*===== Tab category =====*/}
<ScrollView horizontal showsHorizontalScrollIndicator={false} contentContainerStyle={styles.CategoryView}>
{categories.map((item, index) => (
<View key={index.toString()} style={styles.CategoryScroll}>
<Pressable onPress={() => {setCategoryIndex({index:index, category:categories[index]}); setSortItem([...getDonutList(categories[index], DonutList)])}}>
<Text style={[styles.catText,
categoryIndex.index == index ? {color: '#DA7297'} : {}, ]}>
{item}
</Text>
{categoryIndex.index == index ? (
<View style={styles.activeCategory}/>
) : (
<></>
)}
</Pressable>
</View>
))}
</ScrollView>
{/*===== donuts=====*/}
<FlatList horizontal showsHorizontalScrollIndicator={false} data={sortItem} contentContainerStyle={styles.MenuListContainer} keyExtractor={item => item.id} renderItem={({item}) => {
return <Pressable onPress={() => {}}>
<MenuCard
id={item.id}
index={item.index}
type={item.type}
name={item.name}
donutname={item.donutname}
image_item={item.image_item}
average_rating={item.average_rating}
prices={item.prices[0]}
buttonPressHandler={() => {}}
/>
</Pressable>
}}/>
{/*===== drinks =====*/}
<View>
<Text style={{fontSize: 26, fontWeight: 600, color: '#EF5A6F' ,marginTop: 20, paddingHorizontal: 20}}>Drinks</Text>
<FlatList horizontal showsHorizontalScrollIndicator={false} data={DrinkList} contentContainerStyle={[styles.MenuListContainer, {marginBottom: tabBar}]} keyExtractor={item => item.id} renderItem={({item}) => {
return <Pressable onPress={() => {}}>
<DrinkCard
id={item.id}
index={item.index}
type={item.type}
name={item.name}
image_item={item.image_item}
average_rating={item.average_rating}
prices={item.prices[0]}
buttonPressHandler={() => {}}
/>
</Pressable>
}}/>
</View>
</ScrollView>
</View>
)
}
export default Menu
const styles = StyleSheet.create({
ScrollViewFlex:{
flexGrow: 1,
},
CategoryView:{
paddingHorizontal: 5
},
CategoryScroll:{
paddingHorizontal: 20
},
catText:{
fontSize: 16,
fontWeight: 600,
color: 'gray'
},
activeCategory:{
backgroundColor: '#DA7297',
padding: 1
},
MenuListContainer:{
gap: 20,
paddingVertical: 20,
paddingHorizontal: 20
}
})

View file

@ -0,0 +1,12 @@
import { View, Text } from 'react-native'
import React from 'react'
const OrderReceipt = () => {
return (
<View>
<Text>OrderHistory</Text>
</View>
)
}
export default OrderReceipt

View file

@ -0,0 +1,12 @@
import { View, Text } from 'react-native'
import React from 'react'
const Payment = () => {
return (
<View>
<Text>Payment</Text>
</View>
)
}
export default Payment

View file

@ -0,0 +1,29 @@
import { View, Text, Pressable, Image, StyleSheet } from 'react-native'
import { Ionicons } from '@expo/vector-icons';
import React from 'react'
import { useNavigation } from '@react-navigation/native';
const Profile = () => {
const navigation = useNavigation();
return (
<View style={{flex: 1, backgroundColor: "white"}}>
<View style={{marginTop: 20, paddingHorizontal: 20, paddingVertical: 10}}>
<Pressable onPress={() => navigation.goBack()} style={{flexDirection: 'row', gap: 5, alignItems: 'flex-end'}}>
<Ionicons name="chevron-back" size={24} color="gray" />
</Pressable>
<Text style={{paddingHorizontal: 10, paddingTop: 10, fontSize: 30, fontWeight: 600}}>Account</Text>
</View>
<View style={{height: 1, backgroundColor: "#B4B4B8", shadowColor: '#B4B4B8', shadowOffset: { width: 0, height: 1 }, shadowOpacity: 0.5, shadowRadius: 1, }}></View>
<View style={{marginVertical: 50, alignItems: 'center'}}>
<Image source={require('../../assets/images/misc/cannot_load.png')} style={{ width: 200, height: 200, resizeMode: "contain"}} />
<Text style={{marginTop: 30, fontSize: 22, fontWeight: 500}}>Something went wrong</Text>
<Text style={{margin: 10, fontSize: 16, color: "gray", textAlign: 'center'}}>There was a problem and the page could not be loaded at this moment.</Text>
</View>
</View>
)
}
export default Profile

View file

@ -0,0 +1,22 @@
import {create} from "zustand";
import {produce} from "immer";
import {persist, createJSONStorage} from "zustand/middleware";
import AsynStorage from "@react-native-async-storage/async-storage";
import DonutData from "../data/DonutData";
import DrinkData from "../data/DrinkData";
export const useStore = create(
persist(
(set, get) => ({
AllDonut: DonutData,
AllDrinkList: DrinkData,
CartPrice: 0,
FavoriteList: [],
CartList: [],
ReceiptList: []
}), {
name: 'donutshop',
storage: createJSONStorage(() => AsynStorage),
}
),
)

Binary file not shown.

View file

@ -0,0 +1,6 @@
module.exports = function (api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
};
};

18378
donutshop/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

55
donutshop/package.json Normal file
View file

@ -0,0 +1,55 @@
{
"name": "donutshop",
"main": "expo-router/entry",
"version": "1.0.0",
"scripts": {
"start": "expo start",
"reset-project": "node ./scripts/reset-project.js",
"android": "expo start --android",
"ios": "expo start --ios",
"web": "expo start --web",
"test": "jest --watchAll",
"lint": "expo lint"
},
"jest": {
"preset": "jest-expo"
},
"dependencies": {
"@expo/vector-icons": "^14.0.2",
"@react-native-async-storage/async-storage": "^1.24.0",
"@react-navigation/bottom-tabs": "^6.6.1",
"@react-navigation/native": "^6.1.18",
"@react-navigation/stack": "^6.4.1",
"expo": "~51.0.21",
"expo-blur": "^13.0.2",
"expo-constants": "~16.0.2",
"expo-font": "~12.0.9",
"expo-linking": "~6.3.1",
"expo-router": "~3.5.18",
"expo-splash-screen": "~0.27.5",
"expo-status-bar": "~1.12.1",
"expo-system-ui": "~3.0.7",
"expo-web-browser": "~13.0.3",
"immer": "^10.1.1",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-native": "0.74.3",
"react-native-gesture-handler": "~2.16.1",
"react-native-reanimated": "~3.10.1",
"react-native-safe-area-context": "4.10.5",
"react-native-screens": "3.31.1",
"react-native-web": "~0.19.10",
"zustand": "^4.5.4"
},
"devDependencies": {
"@babel/core": "^7.20.0",
"@types/jest": "^29.5.12",
"@types/react": "~18.2.45",
"@types/react-test-renderer": "^18.0.7",
"jest": "^29.2.1",
"jest-expo": "~51.0.3",
"react-test-renderer": "18.2.0",
"typescript": "~5.3.3"
},
"private": true
}

View file

@ -0,0 +1,73 @@
#!/usr/bin/env node
/**
* This script is used to reset the project to a blank state.
* It moves the /app directory to /app-example and creates a new /app directory with an index.tsx and _layout.tsx file.
* You can remove the `reset-project` script from package.json and safely delete this file after running it.
*/
const fs = require('fs');
const path = require('path');
const root = process.cwd();
const oldDirPath = path.join(root, 'app');
const newDirPath = path.join(root, 'app-example');
const newAppDirPath = path.join(root, 'app');
const indexContent = `import { Text, View } from "react-native";
export default function Index() {
return (
<View
style={{
flex: 1,
justifyContent: "center",
alignItems: "center",
}}
>
<Text>Edit app/index.tsx to edit this screen.</Text>
</View>
);
}
`;
const layoutContent = `import { Stack } from "expo-router";
export default function RootLayout() {
return (
<Stack>
<Stack.Screen name="index" />
</Stack>
);
}
`;
fs.rename(oldDirPath, newDirPath, (error) => {
if (error) {
return console.error(`Error renaming directory: ${error}`);
}
console.log('/app moved to /app-example.');
fs.mkdir(newAppDirPath, { recursive: true }, (error) => {
if (error) {
return console.error(`Error creating new app directory: ${error}`);
}
console.log('New /app directory created.');
const indexPath = path.join(newAppDirPath, 'index.tsx');
fs.writeFile(indexPath, indexContent, (error) => {
if (error) {
return console.error(`Error creating index.tsx: ${error}`);
}
console.log('app/index.tsx created.');
const layoutPath = path.join(newAppDirPath, '_layout.tsx');
fs.writeFile(layoutPath, layoutContent, (error) => {
if (error) {
return console.error(`Error creating _layout.tsx: ${error}`);
}
console.log('app/_layout.tsx created.');
});
});
});
});

17
donutshop/tsconfig.json Normal file
View file

@ -0,0 +1,17 @@
{
"extends": "expo/tsconfig.base",
"compilerOptions": {
"strict": true,
"paths": {
"@/*": [
"./*"
]
}
},
"include": [
"**/*.ts",
"**/*.tsx",
".expo/types/**/*.ts",
"expo-env.d.ts"
, "app/index.js", "app/components/MenuDrink.js" ]
}