diff --git a/README.md b/README.md
index 9fe26d1..96662cf 100644
--- a/README.md
+++ b/README.md
@@ -27,10 +27,6 @@ EXPO_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=
EXPO_PUBLIC_FIREBASE_APP_ID=
```
-4. Dans Firebase Console, active `Authentication > Sign-in method > Email/Password`
-
-5. Crée Firestore dans le même projet Firebase
-
## Lancer le projet
```bash
diff --git a/app/(tabs)/_layout.tsx b/app/(tabs)/_layout.tsx
index e8b2ef5..6333ccc 100644
--- a/app/(tabs)/_layout.tsx
+++ b/app/(tabs)/_layout.tsx
@@ -18,6 +18,13 @@ export default function TabLayout() {
tabBarIcon: ({ color }) => ,
}}
/>
+ ,
+ }}
+ />
);
}
diff --git a/app/(tabs)/settings.tsx b/app/(tabs)/settings.tsx
new file mode 100644
index 0000000..61b0f7d
--- /dev/null
+++ b/app/(tabs)/settings.tsx
@@ -0,0 +1,165 @@
+import { MaterialCommunityIcons } from '@expo/vector-icons';
+import { Redirect, router } from 'expo-router';
+import { ActivityIndicator, Pressable, StyleSheet, Text, View } from 'react-native';
+
+import { Avatar } from '@/components/common/Avatar';
+import { Catamaran, FunnelSans } from '@/constants/fonts';
+import { Palette } from '@/constants/palette';
+import { Radius, Spacing } from '@/constants/styles';
+import { useAuth } from '@/providers/AuthProvider';
+
+function SettingsRow({
+ label,
+ description,
+ onPress,
+}: {
+ label: string;
+ description: string;
+ onPress: () => void;
+}) {
+ return (
+
+
+ {label}
+ {description}
+
+
+
+ );
+}
+
+export default function SettingsScreen() {
+ const { initializing, user } = useAuth();
+
+ if (initializing) {
+ return (
+
+
+
+ );
+ }
+
+ if (!user) {
+ return ;
+ }
+
+ return (
+
+
+ Settings
+ Gere ton compte et ton profil.
+
+
+
+
+
+
+ {user.username || 'Profil'}
+ {user.email}
+
+
+
+
+ Account
+
+ router.push('/profile')}
+ />
+
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ centered: {
+ flex: 1,
+ alignItems: 'center',
+ justifyContent: 'center',
+ backgroundColor: Palette.cream,
+ },
+ container: {
+ flex: 1,
+ paddingHorizontal: Spacing.lg,
+ paddingTop: Spacing.xl,
+ gap: Spacing.lg,
+ backgroundColor: Palette.cream,
+ },
+ header: {
+ gap: Spacing.xs,
+ },
+ title: {
+ ...Catamaran.extraBold34,
+ color: Palette.black,
+ },
+ subtitle: {
+ ...FunnelSans.regular16,
+ color: Palette.gray,
+ },
+ previewCard: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ gap: Spacing.md,
+ padding: Spacing.md,
+ borderWidth: 1,
+ borderColor: Palette.cream,
+ borderRadius: Radius.xl,
+ backgroundColor: Palette.white,
+ },
+ previewContent: {
+ flex: 1,
+ gap: Spacing.xs,
+ },
+ previewTitle: {
+ ...FunnelSans.medium16,
+ color: Palette.black,
+ },
+ previewSubtitle: {
+ ...FunnelSans.medium14,
+ color: Palette.gray,
+ },
+ section: {
+ gap: Spacing.sm,
+ },
+ sectionTitle: {
+ ...FunnelSans.medium12,
+ color: Palette.gray,
+ textTransform: 'uppercase',
+ },
+ sectionCard: {
+ borderWidth: 1,
+ borderColor: Palette.cream,
+ borderRadius: Radius.xl,
+ backgroundColor: Palette.white,
+ overflow: 'hidden',
+ },
+ row: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ gap: Spacing.md,
+ paddingHorizontal: Spacing.md,
+ paddingVertical: Spacing.md,
+ },
+ rowContent: {
+ flex: 1,
+ gap: Spacing.xs,
+ },
+ rowLabel: {
+ ...FunnelSans.medium16,
+ color: Palette.black,
+ },
+ rowDescription: {
+ ...FunnelSans.regular16,
+ color: Palette.gray,
+ },
+});
diff --git a/app/_layout.tsx b/app/_layout.tsx
index ceec77e..af55a8f 100644
--- a/app/_layout.tsx
+++ b/app/_layout.tsx
@@ -4,6 +4,7 @@ import { useEffect } from 'react';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import { AuthProvider } from '@/providers/AuthProvider';
+import { Palette } from '@/constants/palette';
SplashScreen.preventAutoHideAsync();
@@ -34,6 +35,14 @@ export default function RootLayout() {
+
diff --git a/app/profile.tsx b/app/profile.tsx
new file mode 100644
index 0000000..0c304ae
--- /dev/null
+++ b/app/profile.tsx
@@ -0,0 +1,124 @@
+import { Redirect } from 'expo-router';
+import { ScrollView, StyleSheet, Text, View } from 'react-native';
+
+import { Avatar } from '@/components/common/Avatar';
+import { Catamaran, FunnelSans } from '@/constants/fonts';
+import { Palette } from '@/constants/palette';
+import { Radius, Spacing } from '@/constants/styles';
+import { useAuth } from '@/providers/AuthProvider';
+
+function formatDate(value?: Date | null) {
+ if (!value) {
+ return '-';
+ }
+
+ return value.toLocaleDateString('fr-FR', {
+ day: '2-digit',
+ month: 'long',
+ year: 'numeric',
+ });
+}
+
+function InfoRow({ label, value }: { label: string; value: string }) {
+ return (
+
+ {label}
+ {value}
+
+ );
+}
+
+export default function ProfileScreen() {
+ const { initializing, user } = useAuth();
+
+ if (initializing) {
+ return ;
+ }
+
+ if (!user) {
+ return ;
+ }
+
+ return (
+
+
+
+
+ {user.username || 'Profil'}
+ {user.email}
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ screen: {
+ flex: 1,
+ backgroundColor: Palette.cream,
+ },
+ content: {
+ paddingHorizontal: Spacing.lg,
+ paddingTop: Spacing.xl,
+ paddingBottom: Spacing.xxl,
+ gap: Spacing.lg,
+ },
+ header: {
+ alignItems: 'center',
+ gap: Spacing.md,
+ },
+ headerContent: {
+ alignItems: 'center',
+ gap: Spacing.xs,
+ },
+ title: {
+ ...Catamaran.extraBold34,
+ color: Palette.black,
+ },
+ subtitle: {
+ ...FunnelSans.regular16,
+ color: Palette.gray,
+ },
+ card: {
+ gap: Spacing.md,
+ padding: Spacing.md,
+ borderWidth: 1,
+ borderColor: Palette.cream,
+ borderRadius: Radius.xl,
+ backgroundColor: Palette.white,
+ },
+ infoRow: {
+ gap: Spacing.xs,
+ paddingBottom: Spacing.sm,
+ borderBottomWidth: 1,
+ borderBottomColor: Palette.cream,
+ },
+ infoLabel: {
+ ...FunnelSans.medium12,
+ color: Palette.gray,
+ textTransform: 'uppercase',
+ },
+ infoValue: {
+ ...FunnelSans.regular16,
+ color: Palette.black,
+ },
+});
diff --git a/components/common/Avatar.tsx b/components/common/Avatar.tsx
new file mode 100644
index 0000000..f726692
--- /dev/null
+++ b/components/common/Avatar.tsx
@@ -0,0 +1,104 @@
+import { Image } from 'expo-image';
+import {
+ StyleSheet,
+ Text,
+ View,
+ type ImageStyle,
+ type StyleProp,
+ type ViewStyle,
+} from 'react-native';
+
+import { Catamaran, FunnelSans } from '@/constants/fonts';
+import { Palette } from '@/constants/palette';
+import { Radius } from '@/constants/styles';
+
+type AvatarSize = 'sm' | 'md' | 'lg';
+
+type AvatarProps = {
+ uri?: string | null;
+ name?: string | null;
+ size?: AvatarSize;
+ style?: StyleProp;
+};
+
+const avatarSizeMap = {
+ sm: 40,
+ md: 64,
+ lg: 104,
+} as const;
+
+function getInitials(name?: string | null) {
+ if (!name?.trim()) {
+ return '?';
+ }
+
+ const parts = name
+ .trim()
+ .split(/\s+/)
+ .slice(0, 2);
+
+ return parts.map((part) => part[0]?.toUpperCase() ?? '').join('') || '?';
+}
+
+export function Avatar({ uri, name, size = 'md', style }: AvatarProps) {
+ const avatarSize = avatarSizeMap[size];
+ const initials = getInitials(name);
+
+ if (uri) {
+ return (
+
+ );
+ }
+
+ return (
+
+ {initials}
+
+ );
+}
+
+const styles = StyleSheet.create({
+ base: {
+ overflow: 'hidden',
+ },
+ fallback: {
+ alignItems: 'center',
+ justifyContent: 'center',
+ backgroundColor: Palette.black,
+ borderWidth: 1,
+ borderColor: Palette.gray,
+ },
+ text: {
+ ...FunnelSans.semiBold14,
+ color: Palette.white,
+ },
+ largeText: {
+ ...Catamaran.bold18,
+ color: Palette.white,
+ },
+});
+
+export default Avatar;