diff --git a/app/(tabs)/_layout.tsx b/app/(tabs)/_layout.tsx
index cfbc1e2..f891b8b 100644
--- a/app/(tabs)/_layout.tsx
+++ b/app/(tabs)/_layout.tsx
@@ -1,12 +1,12 @@
-import { Tabs } from 'expo-router';
-import React from 'react';
-import { Platform } from 'react-native';
+import { Tabs } from "expo-router";
+import React from "react";
+import { Platform } from "react-native";
-import { HapticTab } from '@/components/HapticTab';
-import { IconSymbol } from '@/components/ui/IconSymbol';
-import TabBarBackground from '@/components/ui/TabBarBackground';
-import { Colors } from '@/constants/Colors';
-import { useColorScheme } from '@/hooks/useColorScheme';
+import { HapticTab } from "@/components/HapticTab";
+import { IconSymbol } from "@/components/ui/IconSymbol";
+import TabBarBackground from "@/components/ui/TabBarBackground";
+import { Colors } from "@/constants/Colors";
+import { useColorScheme } from "@/hooks/useColorScheme";
export default function TabLayout() {
const colorScheme = useColorScheme();
@@ -14,30 +14,35 @@ export default function TabLayout() {
return (
+ }}
+ >
,
+ title: "Home",
+ tabBarIcon: ({ color }) => (
+
+ ),
}}
/>
,
+ title: "Explore",
+ tabBarIcon: ({ color }) => (
+
+ ),
}}
/>
diff --git a/app/_layout.tsx b/app/_layout.tsx
index e6a660f..924acb8 100644
--- a/app/_layout.tsx
+++ b/app/_layout.tsx
@@ -4,7 +4,7 @@ import {
ThemeProvider,
} from "@react-navigation/native";
import { useFonts } from "expo-font";
-import { Stack } from "expo-router";
+import { Slot } from "expo-router";
import { StatusBar } from "expo-status-bar";
import "react-native-reanimated";
import "./globals.css";
@@ -26,10 +26,7 @@ export default function RootLayout() {
return (
-
-
-
-
+
diff --git a/app/register/_layout.tsx b/app/register/_layout.tsx
new file mode 100644
index 0000000..f76d82f
--- /dev/null
+++ b/app/register/_layout.tsx
@@ -0,0 +1,23 @@
+import { useColorScheme } from "@/hooks/useColorScheme";
+import {
+ DarkTheme,
+ DefaultTheme,
+ ThemeProvider,
+} from "@react-navigation/native";
+import { Stack } from "expo-router";
+import { RegisterProvider } from "../../contexts/RegisterContext";
+
+export default function RegisterLayout() {
+ const colorScheme = useColorScheme();
+ return (
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/app/register/account.tsx b/app/register/account.tsx
new file mode 100644
index 0000000..51f9dd8
--- /dev/null
+++ b/app/register/account.tsx
@@ -0,0 +1,62 @@
+import { useState } from "react";
+import { Alert, Pressable, Text, TextInput, View } from "react-native";
+import { useRegister } from "../../contexts/RegisterContext";
+
+export default function AccountPage() {
+ const [email, setEmail] = useState("");
+ const [firstName, setFirstName] = useState("");
+ const [lastName, setLastName] = useState("");
+ const [password, setPassword] = useState("");
+ const { setAccountInfo, data } = useRegister();
+
+ const handleCreateAccount = () => {
+ setAccountInfo({ email, firstName, lastName, password });
+
+ console.log("π Final Register Data:", {
+ ...data,
+ email,
+ firstName,
+ lastName,
+ password,
+ });
+
+ Alert.alert("Account Created", "You can now log in!");
+ };
+
+ return (
+
+
+ {/* Title and Step Indicator */}
+
+ {/* Step Numbers */}
+
+ 1
+ 2
+ 3
+
+
+ Guardian information
+
+
+
+
+
+
+ {/* */}
+
+
+
+ Create Account
+
+
+
+
+ );
+}
diff --git a/app/register/index.tsx b/app/register/index.tsx
new file mode 100644
index 0000000..6c28de1
--- /dev/null
+++ b/app/register/index.tsx
@@ -0,0 +1,99 @@
+import { useRouter } from "expo-router";
+import React, { useState } from "react";
+import { Pressable, Text, TouchableOpacity, View } from "react-native";
+import { useRegister } from "../../contexts/RegisterContext";
+
+export default function SelectUserType() {
+ const router = useRouter();
+ const { setUserType } = useRegister();
+ const [selectedType, setSelectedType] = useState("Guardian");
+
+ const handleNext = () => {
+ setUserType(selectedType);
+ router.push("/register/player-info");
+ };
+
+ const options: { label: string; value: string }[] = [
+ { label: "Guardian", value: "Guardian" },
+ { label: "Under 13", value: "Under-13" },
+ { label: "Over 13", value: "Over-13" },
+ ];
+
+ const handleBackToLogin = () => {
+ router.push("/");
+ };
+
+ return (
+
+
+
+ β
+ Back to Login
+
+
+
+ {/* Title and Step Indicator */}
+
+ {/* Step Numbers */}
+
+ 1
+ 2
+ 3
+
+
+ Account type
+
+
+
+
+ {options.map((option) => (
+ setSelectedType(option.value)}
+ className={`p-4 mt-2 rounded-xl
+ ${selectedType === option.value ? "bg-yellow-400" : "bg-gray-200 "}`}
+ >
+
+ {option.label}
+
+
+ ))}
+
+
+
+ {/* */}
+
+
+ Next
+
+
+
+
+ );
+}
diff --git a/app/register/player-info.tsx b/app/register/player-info.tsx
new file mode 100644
index 0000000..f160b25
--- /dev/null
+++ b/app/register/player-info.tsx
@@ -0,0 +1,138 @@
+import LabeledInput from "@/components/LabeledInput";
+import { useRouter } from "expo-router";
+import { useState } from "react";
+import { Pressable, ScrollView, Text, TextInput, View } from "react-native";
+import { useRegister } from "../../contexts/RegisterContext";
+
+const initialPlayer = {
+ name: "",
+ birthDate: "",
+ gender: "",
+ ageGroup: "",
+ experience: "",
+ level: "",
+ club: "",
+ dominantFoot: "",
+};
+
+export default function PlayerInfo() {
+ const [player, setPlayer] = useState(initialPlayer);
+ const { addPlayer } = useRegister();
+ const router = useRouter();
+
+ const handleAdd = () => {
+ if (!player.name) return;
+ addPlayer(player);
+ setPlayer(initialPlayer);
+ };
+
+ const handleNext = () => {
+ if (player.name) addPlayer(player);
+ router.push("/register/account");
+ };
+
+ return (
+
+ {/* Title and Step Indicator */}
+
+
+ {/* Step Numbers */}
+
+ 1
+ 2
+ 3
+
+
+ Player information
+
+
+
+ setPlayer((p) => ({ ...p, name: text }))}
+ />
+ setPlayer((p) => ({ ...p, birthDate: val }))}
+ />
+
+ setPlayer((p) => ({ ...p, gender: val }))}
+ />
+
+ setPlayer((p) => ({ ...p, experience: text }))}
+ />
+
+ setPlayer((p) => ({ ...p, name: text }))}
+ />
+ setPlayer((p) => ({ ...p, birthDate: text }))}
+ />
+ setPlayer((p) => ({ ...p, gender: text }))}
+ />
+ setPlayer((p) => ({ ...p, ageGroup: text }))}
+ />
+ setPlayer((p) => ({ ...p, experience: text }))}
+ />
+ setPlayer((p) => ({ ...p, level: text }))}
+ />
+ setPlayer((p) => ({ ...p, club: text }))}
+ />
+
+ setPlayer((p) => ({ ...p, dominantFoot: text }))
+ }
+ />
+
+ {/*
+ */}
+
+
+
+ Add Another Player
+
+
+
+
+
+ Next
+
+
+
+
+ );
+}
diff --git a/components/AuthScreen.tsx b/components/AuthScreen.tsx
index fb196c1..4c31d53 100644
--- a/components/AuthScreen.tsx
+++ b/components/AuthScreen.tsx
@@ -1,158 +1,174 @@
+import { Link, useRouter } from "expo-router";
import React, { useState } from "react";
import {
- Alert,
- KeyboardAvoidingView,
- Platform,
- Text,
- TextInput,
- TouchableOpacity,
- View,
+ Alert,
+ KeyboardAvoidingView,
+ Platform,
+ Text,
+ TextInput,
+ TouchableOpacity,
+ View,
} from "react-native";
import { useAuth } from "../contexts/AuthContext";
type AuthMode = "signin" | "signup";
const AuthScreen: React.FC = () => {
- const [email, setEmail] = useState("");
- const [password, setPassword] = useState("");
- const [firstName, setFirstName] = useState("");
- const [lastName, setLastName] = useState("");
- const [mode, setMode] = useState("signin");
- const [loading, setLoading] = useState(false);
-
- const { signIn, signUp, resetPassword } = useAuth();
-
- const handleAuth = async () => {
- if (!email || !password) {
- Alert.alert("Error", "Please fill in all fields");
- return;
- }
-
- setLoading(true);
-
- try {
- if (mode === "signin") {
- const { error } = await signIn(email, password);
- if (error) throw error;
- } else {
- const { error } = await signUp(email, password, firstName, lastName);
- if (error) throw error;
- Alert.alert("Success", "Check your email for the confirmation link!");
- }
- } catch (error: any) {
- Alert.alert("Error", error.message);
- } finally {
- setLoading(false);
- }
- };
-
- const handleResetPassword = async () => {
- if (!email) {
- Alert.alert("Error", "Please enter your email address");
- return;
- }
-
- try {
- const { error } = await resetPassword(email);
- if (error) throw error;
- Alert.alert("Success", "Check your email for the reset link!");
- } catch (error: any) {
- Alert.alert("Error", error.message);
- }
- };
-
- return (
-
-
-
- {mode === "signin" ? "Welcome" : "Create Account"}
-
-
-
- Email
-
-
- {mode === "signup" && (
- <>
-
- First Name
-
-
-
- Last Name
-
-
- >
- )}
-
-
- Password
-
-
-
-
-
- {loading ? "Loading..." : mode === "signin" ? "Sign In" : "Sign Up"}
-
-
-
- setMode(mode === "signin" ? "signup" : "signin")}>
-
- {mode === "signin"
- ? "Don't have an account? Sign up"
- : "Already have an account? Sign in"}
-
-
-
- {mode === "signin" && (
-
-
- Forgot your password?
-
-
- )}
-
-
- );
+ const [email, setEmail] = useState("");
+ const [password, setPassword] = useState("");
+ const [firstName, setFirstName] = useState("");
+ const [lastName, setLastName] = useState("");
+ const [mode, setMode] = useState("signin");
+ const [loading, setLoading] = useState(false);
+
+ const router = useRouter();
+
+ const { signIn, signUp, resetPassword } = useAuth();
+
+ const handleAuth = async () => {
+ if (!email || !password) {
+ Alert.alert("Error", "Please fill in all fields");
+ return;
+ }
+
+ setLoading(true);
+
+ try {
+ if (mode === "signin") {
+ const { error } = await signIn(email, password);
+ if (error) throw error;
+ } else {
+ const { error } = await signUp(email, password, firstName, lastName);
+ if (error) throw error;
+ Alert.alert("Success", "Check your email for the confirmation link!");
+ }
+ } catch (error: any) {
+ Alert.alert("Error", error.message);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const handleResetPassword = async () => {
+ if (!email) {
+ Alert.alert("Error", "Please enter your email address");
+ return;
+ }
+
+ try {
+ const { error } = await resetPassword(email);
+ if (error) throw error;
+ Alert.alert("Success", "Check your email for the reset link!");
+ } catch (error: any) {
+ Alert.alert("Error", error.message);
+ }
+ };
+
+ return (
+
+
+
+ {mode === "signin" ? "Welcome" : "Create Account"}
+
+
+
+ Email
+
+
+ {mode === "signup" && (
+ <>
+
+ First Name
+
+
+
+ Last Name
+
+
+ >
+ )}
+
+
+ Password
+
+
+
+
+
+ {loading ? "Loading..." : mode === "signin" ? "Sign In" : "Sign Up"}
+
+
+
+ {mode === "signin" ? (
+ <>
+
+
+
+ Don't have an account? Sign up
+
+
+
+ >
+ ) : (
+ setMode("signin")}
+ >
+
+ Already have an account? Sign in
+
+
+ )}
+
+ {mode === "signin" && (
+
+
+ Forgot your password?
+
+
+ )}
+
+
+ );
};
export default AuthScreen;
diff --git a/components/LabeledInput.tsx b/components/LabeledInput.tsx
new file mode 100644
index 0000000..c4a60a8
--- /dev/null
+++ b/components/LabeledInput.tsx
@@ -0,0 +1,98 @@
+import DateTimePicker from "@react-native-community/datetimepicker";
+import { Picker } from "@react-native-picker/picker";
+import React, { useState } from "react";
+import {
+ Platform,
+ Text,
+ TextInput,
+ TouchableOpacity,
+ View,
+} from "react-native";
+
+interface LabeledInputProps {
+ label: string;
+ value: string;
+ placeholder?: string;
+ onChange: (text: string) => void;
+ type?: "text" | "date" | "select";
+ options?: string[];
+}
+
+export default function LabeledInput({
+ label,
+ value,
+ placeholder,
+ onChange,
+ type = "text",
+ options = [],
+}: LabeledInputProps) {
+ const [showDatePicker, setShowDatePicker] = useState(false);
+
+ const renderInput = () => {
+ switch (type) {
+ case "date":
+ return (
+ <>
+ setShowDatePicker(true)}
+ className="px-4 py-3 bg-gray-200 rounded-md"
+ >
+
+ {value || placeholder || "Select date"}
+
+
+ {showDatePicker && (
+ {
+ setShowDatePicker(false);
+ if (selectedDate) {
+ const formatted = selectedDate.toISOString().split("T")[0]; // YYYY-MM-DD
+ onChange(formatted);
+ }
+ }}
+ />
+ )}
+ >
+ );
+
+ case "select":
+ return (
+
+ onChange(itemValue)}
+ style={{ color: "#1f2937" }} // text-gray-800
+ >
+
+ {options.map((opt) => (
+
+ ))}
+
+
+ );
+
+ case "text":
+ default:
+ return (
+
+ );
+ }
+ };
+
+ return (
+
+ {label}
+ {renderInput()}
+
+ );
+}
diff --git a/contexts/RegisterContext.tsx b/contexts/RegisterContext.tsx
new file mode 100644
index 0000000..767ef91
--- /dev/null
+++ b/contexts/RegisterContext.tsx
@@ -0,0 +1,70 @@
+import React, { createContext, ReactNode, useContext, useState } from "react";
+
+type Player = {
+ name: string;
+ birthDate: string;
+ gender: string;
+ ageGroup: string;
+ experience: string;
+ level: string;
+ club: string;
+ dominantFoot: string;
+};
+
+type RegisterData = {
+ userType: string;
+ players: Player[];
+ email: string;
+ firstName: string;
+ lastName: string;
+ password: string;
+};
+
+type RegisterContextType = {
+ data: RegisterData;
+ setUserType: (type: string) => void;
+ addPlayer: (player: Player) => void;
+ setAccountInfo: (info: Partial) => void;
+};
+
+const RegisterContext = createContext(
+ undefined
+);
+
+export const RegisterProvider = ({ children }: { children: ReactNode }) => {
+ const [data, setData] = useState({
+ userType: "",
+ players: [],
+ email: "",
+ firstName: "",
+ lastName: "",
+ password: "",
+ });
+
+ const setUserType = (type: string) => {
+ setData((prev) => ({ ...prev, userType: type }));
+ };
+
+ const addPlayer = (player: Player) => {
+ setData((prev) => ({ ...prev, players: [...prev.players, player] }));
+ };
+
+ const setAccountInfo = (info: Partial) => {
+ setData((prev) => ({ ...prev, ...info }));
+ };
+
+ return (
+
+ {children}
+
+ );
+};
+
+export const useRegister = () => {
+ const context = useContext(RegisterContext);
+ if (!context)
+ throw new Error("useRegister must be used within RegisterProvider");
+ return context;
+};
diff --git a/package-lock.json b/package-lock.json
index 216a5d2..1d70964 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,6 +11,8 @@
"@expo/ngrok": "^4.1.3",
"@expo/vector-icons": "^14.1.0",
"@react-native-async-storage/async-storage": "2.1.2",
+ "@react-native-community/datetimepicker": "^8.4.2",
+ "@react-native-picker/picker": "^2.11.1",
"@react-navigation/bottom-tabs": "^7.3.10",
"@react-navigation/elements": "^2.3.8",
"@react-navigation/native": "^7.1.6",
@@ -2958,6 +2960,42 @@
"react-native": "^0.0.0-0 || >=0.65 <1.0"
}
},
+ "node_modules/@react-native-community/datetimepicker": {
+ "version": "8.4.2",
+ "resolved": "https://registry.npmjs.org/@react-native-community/datetimepicker/-/datetimepicker-8.4.2.tgz",
+ "integrity": "sha512-V/s+foBfjlWGV8MKdMhxugq0SPMtYqUEYlf+sMrKUUm5Gx3pA9Qoum2ZQUqBfI4A8kgaEPIGyG/YsNX7ycnNSA==",
+ "license": "MIT",
+ "dependencies": {
+ "invariant": "^2.2.4"
+ },
+ "peerDependencies": {
+ "expo": ">=52.0.0",
+ "react": "*",
+ "react-native": "*",
+ "react-native-windows": "*"
+ },
+ "peerDependenciesMeta": {
+ "expo": {
+ "optional": true
+ },
+ "react-native-windows": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@react-native-picker/picker": {
+ "version": "2.11.1",
+ "resolved": "https://registry.npmjs.org/@react-native-picker/picker/-/picker-2.11.1.tgz",
+ "integrity": "sha512-ThklnkK4fV3yynnIIRBkxxjxR4IFbdMNJVF6tlLdOJ/zEFUEFUEdXY0KmH0iYzMwY8W4/InWsLiA7AkpAbnexA==",
+ "license": "MIT",
+ "workspaces": [
+ "example"
+ ],
+ "peerDependencies": {
+ "react": "*",
+ "react-native": "*"
+ }
+ },
"node_modules/@react-native/assets-registry": {
"version": "0.79.5",
"resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.79.5.tgz",
diff --git a/package.json b/package.json
index f3fcdea..3bd3bf9 100644
--- a/package.json
+++ b/package.json
@@ -14,6 +14,8 @@
"@expo/ngrok": "^4.1.3",
"@expo/vector-icons": "^14.1.0",
"@react-native-async-storage/async-storage": "2.1.2",
+ "@react-native-community/datetimepicker": "^8.4.2",
+ "@react-native-picker/picker": "^2.11.1",
"@react-navigation/bottom-tabs": "^7.3.10",
"@react-navigation/elements": "^2.3.8",
"@react-navigation/native": "^7.1.6",