mirror of
https://github.com/Sosokker/chefhai.git
synced 2025-12-19 05:54:08 +01:00
feat: add cooking step page
This commit is contained in:
parent
44a2f89a59
commit
79add76034
@ -1,68 +1,75 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { IconSymbol } from "@/components/ui/IconSymbol";
|
import { IconSymbol } from "@/components/ui/IconSymbol";
|
||||||
import { Image } from "expo-image";
|
import { supabase } from "@/services/supabase";
|
||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { router, useLocalSearchParams } from "expo-router";
|
import { router, useLocalSearchParams } from "expo-router";
|
||||||
|
import "nativewind";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import {
|
import { Alert, ScrollView, Text, TouchableOpacity, View } from "react-native";
|
||||||
Alert,
|
|
||||||
ScrollView,
|
|
||||||
StyleSheet,
|
|
||||||
Text,
|
|
||||||
TouchableOpacity,
|
|
||||||
View,
|
|
||||||
} from "react-native";
|
|
||||||
import { SafeAreaView } from "react-native-safe-area-context";
|
import { SafeAreaView } from "react-native-safe-area-context";
|
||||||
|
|
||||||
|
interface Step {
|
||||||
|
id: string;
|
||||||
|
food_id: string;
|
||||||
|
title: string;
|
||||||
|
step_order: number;
|
||||||
|
description: string;
|
||||||
|
created_at: string;
|
||||||
|
}
|
||||||
|
|
||||||
export default function CookingSessionScreen() {
|
export default function CookingSessionScreen() {
|
||||||
const { id } = useLocalSearchParams();
|
const { id: foodId } = useLocalSearchParams();
|
||||||
const [currentStep, setCurrentStep] = useState(0);
|
const [currentStep, setCurrentStep] = useState(0);
|
||||||
|
|
||||||
// Mock data - in a real app, you would fetch this based on the ID
|
const {
|
||||||
const recipeData = {
|
data: steps,
|
||||||
id: 1,
|
isLoading: stepsLoading,
|
||||||
name: "Pad Kra Pao Moo Sab with Eggs",
|
error: stepsError,
|
||||||
steps: [
|
} = useQuery<Step[], Error>({
|
||||||
{
|
queryKey: ["food-steps", foodId],
|
||||||
title: "Gather and prepare all ingredients",
|
queryFn: async () => {
|
||||||
description:
|
const { data, error } = await supabase
|
||||||
"Chop garlic, Thai chilies, and protein of choice (chicken, pork, beef, or tofu)",
|
.from("cooking_steps")
|
||||||
image: require("@/assets/images/cooking/step1.png"),
|
.select(
|
||||||
},
|
`
|
||||||
{
|
id,
|
||||||
title: "Heat oil in a wok or large frying pan",
|
food_id,
|
||||||
description:
|
title,
|
||||||
"Use medium-high heat. The oil should be hot but not smoking.",
|
step_order,
|
||||||
image: require("@/assets/images/cooking/step2.png"),
|
description,
|
||||||
},
|
created_at
|
||||||
{
|
`
|
||||||
title: "Fry the eggs sunny side up",
|
)
|
||||||
description:
|
.eq("food_id", foodId)
|
||||||
"Heat oil in a separate pan and fry eggs until whites are set but yolks are still runny. Set aside.",
|
.order("step_order", { ascending: true });
|
||||||
image: require("@/assets/images/cooking/step3.png"),
|
if (error) throw error;
|
||||||
},
|
return data ?? [];
|
||||||
{
|
},
|
||||||
title: "Stir-fry garlic and chilies",
|
enabled: !!foodId,
|
||||||
description:
|
});
|
||||||
"Add chopped garlic and chilies to the hot oil and stir-fry until fragrant, about 30 seconds.",
|
|
||||||
image: require("@/assets/images/cooking/step4.png"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Add ground pork and cook until browned",
|
|
||||||
description:
|
|
||||||
"Break up the meat with a spatula and cook until no longer pink, about 3-4 minutes.",
|
|
||||||
image: require("@/assets/images/cooking/step5.png"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Add sauces and basil",
|
|
||||||
description:
|
|
||||||
"Add soy sauce, oyster sauce, sugar, and holy basil. Stir well and cook for another minute. Serve with rice and top with the fried egg.",
|
|
||||||
image: require("@/assets/images/cooking/step6.png"),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
const totalSteps = recipeData.steps.length;
|
if (stepsLoading) {
|
||||||
|
return (
|
||||||
|
<SafeAreaView className="flex-1 bg-white" edges={["top"]}>
|
||||||
|
<View className="flex-1 justify-center items-center">
|
||||||
|
<Text>Loading...</Text>
|
||||||
|
</View>
|
||||||
|
</SafeAreaView>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stepsError) {
|
||||||
|
return (
|
||||||
|
<SafeAreaView className="flex-1 bg-white" edges={["top"]}>
|
||||||
|
<View className="flex-1 justify-center items-center">
|
||||||
|
<Text>Error loading steps</Text>
|
||||||
|
</View>
|
||||||
|
</SafeAreaView>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const totalSteps = steps?.length || 0;
|
||||||
|
|
||||||
const goToNextStep = () => {
|
const goToNextStep = () => {
|
||||||
if (currentStep < totalSteps - 1) {
|
if (currentStep < totalSteps - 1) {
|
||||||
@ -76,24 +83,6 @@ export default function CookingSessionScreen() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getHelpWithStep = () => {
|
|
||||||
Alert.alert(
|
|
||||||
"Need Help?",
|
|
||||||
`Tips for ${recipeData.steps[currentStep].title}:\n\n` +
|
|
||||||
"• Make sure ingredients are properly prepared\n" +
|
|
||||||
"• Watch your heat level\n" +
|
|
||||||
"• Don't overcook the ingredients\n\n" +
|
|
||||||
"Would you like to see a video tutorial?",
|
|
||||||
[
|
|
||||||
{ text: "No, thanks", style: "cancel" },
|
|
||||||
{
|
|
||||||
text: "Yes, show video",
|
|
||||||
onPress: () => console.log("Show video tutorial"),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const stopCookingSession = () => {
|
const stopCookingSession = () => {
|
||||||
Alert.alert(
|
Alert.alert(
|
||||||
"Stop Cooking?",
|
"Stop Cooking?",
|
||||||
@ -104,14 +93,31 @@ export default function CookingSessionScreen() {
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
if (!steps || steps.length === 0) {
|
||||||
|
return (
|
||||||
|
<SafeAreaView className="flex-1 bg-white" edges={["top"]}>
|
||||||
|
<View className="flex-1 items-center justify-center">
|
||||||
|
<Text>No steps found</Text>
|
||||||
|
<TouchableOpacity
|
||||||
|
className="px-4 py-2 bg-yellow-400 rounded-full mt-4"
|
||||||
|
onPress={() => router.back()}
|
||||||
|
>
|
||||||
|
<Text className="text-lg font-bold text-white">
|
||||||
|
Go back to home page
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
</SafeAreaView>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaView style={styles.container} edges={["top"]}>
|
<SafeAreaView className="flex-1 bg-white" edges={["top"]}>
|
||||||
<ScrollView style={styles.scrollView}>
|
<ScrollView className="flex-1">
|
||||||
{/* Header with back button */}
|
{/* Header with back button */}
|
||||||
<View style={styles.header}>
|
<View className="px-4 py-3">
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={styles.backButton}
|
className="w-10 h-10 rounded-full bg-yellow-300 justify-center items-center"
|
||||||
onPress={() => router.back()}
|
onPress={() => router.back()}
|
||||||
>
|
>
|
||||||
<IconSymbol name="chevron.left" size={24} color="#333333" />
|
<IconSymbol name="chevron.left" size={24} color="#333333" />
|
||||||
@ -119,78 +125,77 @@ export default function CookingSessionScreen() {
|
|||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* Step Illustration */}
|
{/* Step Illustration */}
|
||||||
<View style={styles.illustrationContainer}>
|
<View className="items-center my-5">
|
||||||
<Image
|
{/* If your steps have an image property, render it. Otherwise, skip the image or add a placeholder. */}
|
||||||
source={recipeData.steps[currentStep].image}
|
{/* <Image source={{ uri: steps[currentStep].image }} className="w-48 h-48 rounded-full bg-yellow-300" contentFit="contain" /> */}
|
||||||
style={styles.stepImage}
|
|
||||||
contentFit="contain"
|
|
||||||
/>
|
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* Step Information */}
|
{/* Step Information */}
|
||||||
<View style={styles.stepInfoContainer}>
|
<View className="px-6 mb-6 items-center">
|
||||||
<Text style={styles.stepCounter}>
|
<Text className="text-lg text-lime-600 font-bold mb-2">
|
||||||
Step {currentStep + 1} of {totalSteps}
|
Step {currentStep + 1} of {totalSteps}
|
||||||
</Text>
|
</Text>
|
||||||
<Text style={styles.stepTitle}>
|
<Text className="text-2xl font-bold text-gray-800 text-center mb-3">
|
||||||
{recipeData.steps[currentStep].title}
|
{steps && steps[currentStep]?.title}
|
||||||
</Text>
|
</Text>
|
||||||
<Text style={styles.stepDescription}>
|
<Text className="text-base text-gray-600 text-center leading-6">
|
||||||
{recipeData.steps[currentStep].description}
|
{steps && steps[currentStep]?.description}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* Step Indicators */}
|
{/* Step Indicators */}
|
||||||
<View style={styles.stepIndicatorsContainer}>
|
<View className="px-6 mb-6">
|
||||||
<Text style={styles.stepsLabel}>Steps</Text>
|
<Text className="text-lg font-bold text-gray-800 mb-3">Steps</Text>
|
||||||
<View style={styles.stepDots}>
|
<View className="flex-row justify-center">
|
||||||
{recipeData.steps.map((_, index) => (
|
{steps &&
|
||||||
<TouchableOpacity
|
steps.map((_: any, index: any) => (
|
||||||
key={index}
|
<TouchableOpacity
|
||||||
onPress={() => setCurrentStep(index)}
|
key={index}
|
||||||
>
|
onPress={() => setCurrentStep(index)}
|
||||||
<View
|
>
|
||||||
style={[
|
<View
|
||||||
styles.stepDot,
|
className={`w-10 h-10 rounded-full mx-2 ${
|
||||||
currentStep === index && styles.activeStepDot,
|
currentStep === index ? "bg-yellow-300" : "bg-gray-200"
|
||||||
]}
|
}`}
|
||||||
/>
|
/>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
))}
|
))}
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* Navigation Buttons */}
|
{/* Navigation Buttons */}
|
||||||
<View style={styles.navigationContainer}>
|
<View className="px-6 mb-20">
|
||||||
<TouchableOpacity style={styles.helpButton} onPress={getHelpWithStep}>
|
<View className="flex-row justify-between">
|
||||||
<Text style={styles.helpButtonText}>Help me!</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
|
|
||||||
<View style={styles.stepNavigation}>
|
|
||||||
{currentStep > 0 && (
|
{currentStep > 0 && (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={[styles.navButton, styles.prevButton]}
|
className="flex-1 flex-row items-center justify-center py-4 rounded-lg bg-white border border-gray-300 mr-2"
|
||||||
onPress={goToPreviousStep}
|
onPress={goToPreviousStep}
|
||||||
>
|
>
|
||||||
<IconSymbol name="chevron.left" size={20} color="#333333" />
|
<IconSymbol name="chevron.left" size={20} color="#333333" />
|
||||||
<Text style={styles.prevButtonText}>Previous</Text>
|
<Text className="text-base font-bold text-gray-800 ml-2">
|
||||||
|
Previous
|
||||||
|
</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{currentStep < totalSteps - 1 ? (
|
{currentStep < totalSteps - 1 ? (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={[styles.navButton, styles.nextButton]}
|
className="flex-1 flex-row items-center justify-center py-4 rounded-lg bg-yellow-300 ml-2"
|
||||||
onPress={goToNextStep}
|
onPress={goToNextStep}
|
||||||
>
|
>
|
||||||
<Text style={styles.nextButtonText}>Next Step</Text>
|
<Text className="text-base font-bold text-gray-800 mr-2">
|
||||||
|
Next Step
|
||||||
|
</Text>
|
||||||
<IconSymbol name="chevron.right" size={20} color="#333333" />
|
<IconSymbol name="chevron.right" size={20} color="#333333" />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
) : (
|
) : (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={[styles.navButton, styles.finishButton]}
|
className="flex-1 flex-row items-center justify-center py-4 rounded-lg bg-green-600 ml-2"
|
||||||
onPress={() => router.back()}
|
onPress={() => router.back()}
|
||||||
>
|
>
|
||||||
<Text style={styles.finishButtonText}>Finish</Text>
|
<Text className="text-base font-bold text-white mr-2">
|
||||||
|
Finish
|
||||||
|
</Text>
|
||||||
<IconSymbol name="checkmark" size={20} color="#FFFFFF" />
|
<IconSymbol name="checkmark" size={20} color="#FFFFFF" />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
)}
|
)}
|
||||||
@ -199,165 +204,15 @@ export default function CookingSessionScreen() {
|
|||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
||||||
{/* Stop Session Button */}
|
{/* Stop Session Button */}
|
||||||
<TouchableOpacity style={styles.stopButton} onPress={stopCookingSession}>
|
<TouchableOpacity
|
||||||
<Text style={styles.stopButtonText}>Stop Session</Text>
|
className="absolute bottom-0 left-0 right-0 bg-red-800 flex-row justify-center items-center py-4"
|
||||||
|
onPress={stopCookingSession}
|
||||||
|
>
|
||||||
|
<Text className="text-lg font-bold text-yellow-300 mr-2">
|
||||||
|
Stop Session
|
||||||
|
</Text>
|
||||||
<IconSymbol name="fork.knife" size={20} color="#FFCC00" />
|
<IconSymbol name="fork.knife" size={20} color="#FFCC00" />
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
container: {
|
|
||||||
flex: 1,
|
|
||||||
backgroundColor: "#FFFFFF",
|
|
||||||
},
|
|
||||||
scrollView: {
|
|
||||||
flex: 1,
|
|
||||||
},
|
|
||||||
header: {
|
|
||||||
paddingHorizontal: 16,
|
|
||||||
paddingVertical: 12,
|
|
||||||
},
|
|
||||||
backButton: {
|
|
||||||
width: 40,
|
|
||||||
height: 40,
|
|
||||||
borderRadius: 20,
|
|
||||||
backgroundColor: "#FFCC00",
|
|
||||||
justifyContent: "center",
|
|
||||||
alignItems: "center",
|
|
||||||
},
|
|
||||||
illustrationContainer: {
|
|
||||||
alignItems: "center",
|
|
||||||
marginVertical: 20,
|
|
||||||
},
|
|
||||||
stepImage: {
|
|
||||||
width: 200,
|
|
||||||
height: 200,
|
|
||||||
borderRadius: 100,
|
|
||||||
backgroundColor: "#FFCC00",
|
|
||||||
},
|
|
||||||
stepInfoContainer: {
|
|
||||||
paddingHorizontal: 24,
|
|
||||||
alignItems: "center",
|
|
||||||
marginBottom: 30,
|
|
||||||
},
|
|
||||||
stepCounter: {
|
|
||||||
fontSize: 18,
|
|
||||||
color: "#8BC34A",
|
|
||||||
fontWeight: "bold",
|
|
||||||
marginBottom: 8,
|
|
||||||
},
|
|
||||||
stepTitle: {
|
|
||||||
fontSize: 24,
|
|
||||||
fontWeight: "bold",
|
|
||||||
color: "#333333",
|
|
||||||
textAlign: "center",
|
|
||||||
marginBottom: 12,
|
|
||||||
},
|
|
||||||
stepDescription: {
|
|
||||||
fontSize: 16,
|
|
||||||
color: "#666666",
|
|
||||||
textAlign: "center",
|
|
||||||
lineHeight: 24,
|
|
||||||
},
|
|
||||||
stepIndicatorsContainer: {
|
|
||||||
paddingHorizontal: 24,
|
|
||||||
marginBottom: 24,
|
|
||||||
},
|
|
||||||
stepsLabel: {
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: "bold",
|
|
||||||
color: "#333333",
|
|
||||||
marginBottom: 12,
|
|
||||||
},
|
|
||||||
stepDots: {
|
|
||||||
flexDirection: "row",
|
|
||||||
justifyContent: "center",
|
|
||||||
},
|
|
||||||
stepDot: {
|
|
||||||
width: 40,
|
|
||||||
height: 40,
|
|
||||||
borderRadius: 20,
|
|
||||||
backgroundColor: "#EEEEEE",
|
|
||||||
marginHorizontal: 8,
|
|
||||||
},
|
|
||||||
activeStepDot: {
|
|
||||||
backgroundColor: "#FFCC00",
|
|
||||||
},
|
|
||||||
navigationContainer: {
|
|
||||||
paddingHorizontal: 24,
|
|
||||||
marginBottom: 80,
|
|
||||||
},
|
|
||||||
helpButton: {
|
|
||||||
backgroundColor: "#FF6B6B",
|
|
||||||
borderRadius: 8,
|
|
||||||
paddingVertical: 16,
|
|
||||||
alignItems: "center",
|
|
||||||
marginBottom: 16,
|
|
||||||
},
|
|
||||||
helpButtonText: {
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: "bold",
|
|
||||||
color: "#FFFFFF",
|
|
||||||
},
|
|
||||||
stepNavigation: {
|
|
||||||
flexDirection: "row",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
},
|
|
||||||
navButton: {
|
|
||||||
flex: 1,
|
|
||||||
flexDirection: "row",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "center",
|
|
||||||
paddingVertical: 16,
|
|
||||||
borderRadius: 8,
|
|
||||||
},
|
|
||||||
prevButton: {
|
|
||||||
backgroundColor: "#FFFFFF",
|
|
||||||
borderWidth: 1,
|
|
||||||
borderColor: "#DDDDDD",
|
|
||||||
marginRight: 8,
|
|
||||||
},
|
|
||||||
prevButtonText: {
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: "bold",
|
|
||||||
color: "#333333",
|
|
||||||
marginLeft: 8,
|
|
||||||
},
|
|
||||||
nextButton: {
|
|
||||||
backgroundColor: "#FFCC00",
|
|
||||||
},
|
|
||||||
nextButtonText: {
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: "bold",
|
|
||||||
color: "#333333",
|
|
||||||
marginRight: 8,
|
|
||||||
},
|
|
||||||
finishButton: {
|
|
||||||
backgroundColor: "#4CAF50",
|
|
||||||
},
|
|
||||||
finishButtonText: {
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: "bold",
|
|
||||||
color: "#FFFFFF",
|
|
||||||
marginRight: 8,
|
|
||||||
},
|
|
||||||
stopButton: {
|
|
||||||
position: "absolute",
|
|
||||||
bottom: 0,
|
|
||||||
left: 0,
|
|
||||||
right: 0,
|
|
||||||
backgroundColor: "#C62828",
|
|
||||||
flexDirection: "row",
|
|
||||||
justifyContent: "center",
|
|
||||||
alignItems: "center",
|
|
||||||
paddingVertical: 16,
|
|
||||||
},
|
|
||||||
stopButtonText: {
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: "bold",
|
|
||||||
color: "#FFCC00",
|
|
||||||
marginRight: 8,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user