diff --git a/back/services/user.service.js b/back/services/user.service.js
index 48f09cf..973c9ea 100644
--- a/back/services/user.service.js
+++ b/back/services/user.service.js
@@ -64,14 +64,9 @@ class UserService {
async deleteAccount(userId) {
const id = new Types.ObjectId(userId);
- const exerciseResult = await ExerciseRecord.deleteMany({ user: id });
- console.log(`๐๏ธ [deleteAccount] ExerciseRecords supprimรฉs : ${exerciseResult.deletedCount}`);
-
- const workoutResult = await Workout.deleteMany({ user: id });
- console.log(`๐๏ธ [deleteAccount] Workouts supprimรฉs : ${workoutResult.deletedCount}`);
-
+ await ExerciseRecord.deleteMany({ user: id });
+ await Workout.deleteMany({ user: id });
await User.findByIdAndDelete(id);
- console.log(`๐๏ธ [deleteAccount] Utilisateur supprimรฉ : ${userId}`);
}
/**
diff --git a/front/App.js b/front/App.js
index efc7cbb..e45555d 100644
--- a/front/App.js
+++ b/front/App.js
@@ -6,7 +6,7 @@ import { ToastProvider } from './src/context/ToastContext';
import AppNavigator from './src/navigation';
import DesktopInstallPage from './src/components/web/DesktopInstallPage';
-import ErrorBoundary from './src/components/common/ErrorBoundary';
+import { ErrorBoundary } from './src/components/common';
const isDesktopWeb =
Platform.OS === 'web' &&
diff --git a/front/src/components/brand/AthlyLogo.js b/front/src/components/brand/AthlyLogo.js
deleted file mode 100644
index 533c679..0000000
--- a/front/src/components/brand/AthlyLogo.js
+++ /dev/null
@@ -1,95 +0,0 @@
-import React, { useEffect, useRef } from 'react';
-import { Image, Animated, Easing, StyleSheet } from 'react-native';
-
-// Rรฉfรฉrentiel officiel des assets
-// type='full' + color='orange' โ logo-orange.png (logo complet, couleur principale)
-// type='full' + color='violet' โ logo-violet.png (logo complet, gamification)
-// type='icon' + color='orange' โ minimalist-logo-orange.png (icรดne "A" seule)
-// type='icon' + color='violet' โ minimalist-logo-violet.png (icรดne "A" seule)
-const SOURCES = {
- full: {
- orange: require('../../../assets/logo-orange.png'),
- violet: require('../../../assets/logo-violet.png'),
- },
- icon: {
- orange: require('../../../assets/minimalist-logo-orange.png'),
- violet: require('../../../assets/minimalist-logo-violet.png'),
- },
-};
-
-// Props :
-// type โ 'full' (logo complet texte+icรดne) | 'icon' (icรดne "A" seule)
-// color โ 'orange' | 'violet'
-// width โ largeur explicite pour type="full" (dรฉfaut 180)
-// height โ hauteur explicite pour type="full" (dรฉfaut : width ร 0.67)
-// size โ taille carrรฉe pour type="icon" (dรฉfaut 40)
-// animate โ FadeInUp au montage (AuthScreen)
-// pulse โ respiration lรฉgรจre en boucle (รฉtats de chargement)
-// style โ styles de positionnement uniquement (margin, alignSelfโฆ)
-export default function AthlyLogo({
- type = 'full',
- color = 'orange',
- width = 180,
- height,
- size = 40,
- animate = false,
- pulse = false,
- style,
-}) {
- const opacity = useRef(new Animated.Value(animate ? 0 : 1)).current;
- const translateY = useRef(new Animated.Value(animate ? 14 : 0)).current;
- const scale = useRef(new Animated.Value(1)).current;
-
- // FadeInUp โ dรฉclenchรฉ une seule fois au montage
- useEffect(() => {
- if (!animate) return;
- Animated.parallel([
- Animated.timing(opacity, {
- toValue: 1, duration: 520, delay: 80,
- easing: Easing.out(Easing.quad), useNativeDriver: true,
- }),
- Animated.timing(translateY, {
- toValue: 0, duration: 520, delay: 80,
- easing: Easing.out(Easing.cubic), useNativeDriver: true,
- }),
- ]).start();
- }, []);
-
- // Pulse loop โ activรฉ/dรฉsactivรฉ par la prop `pulse`
- useEffect(() => {
- if (!pulse) { scale.setValue(1); return; }
- const loop = Animated.loop(
- Animated.sequence([
- Animated.timing(scale, { toValue: 1.07, duration: 900, easing: Easing.inOut(Easing.ease), useNativeDriver: true }),
- Animated.timing(scale, { toValue: 1, duration: 900, easing: Easing.inOut(Easing.ease), useNativeDriver: true }),
- ]),
- );
- loop.start();
- return () => loop.stop();
- }, [pulse]);
-
- const source = (SOURCES[type] || SOURCES.full)[color] || SOURCES.full.orange;
-
- const bounds =
- type === 'icon'
- ? { width: size, height: size }
- : { width, height: height ?? Math.round(width * 0.67) };
-
- return (
-
-
-
- );
-}
diff --git a/front/src/components/buttons/PrimaryButton.js b/front/src/components/buttons/PrimaryButton.js
deleted file mode 100644
index a720a73..0000000
--- a/front/src/components/buttons/PrimaryButton.js
+++ /dev/null
@@ -1,38 +0,0 @@
-// ============================================================================================
-// Composant REUTILISABLE : PrimaryButton
-// ============================================================================================
-// Bouton principal (orange) utilisรฉ partout dans l'app.
-// Props :
-// - label : texte du bouton
-// - onPress : fonction ร exรฉcuter au clic
-//
-// Avantages :
-// โ Style centralisรฉ
-// โ Reutilisable sur plusieurs รฉcrans
-// ============================================================================================
-
-import React from "react";
-import { TouchableOpacity, Text, StyleSheet } from "react-native";
-
-export default function PrimaryButton({ label, onPress }) {
- return (
-
- {label}
-
- );
-}
-
-const styles = StyleSheet.create({
- button: {
- backgroundColor: "#ff7a33",
- paddingVertical: 16,
- borderRadius: 12,
- marginTop: 10,
- alignItems: "center",
- },
- text: {
- color: "#fff",
- fontWeight: "bold",
- fontSize: 16,
- },
-});
\ No newline at end of file
diff --git a/front/src/components/cards/Card.js b/front/src/components/cards/Card.js
deleted file mode 100644
index f9c2935..0000000
--- a/front/src/components/cards/Card.js
+++ /dev/null
@@ -1,29 +0,0 @@
-// -------------------------------------------------------------
-// Composant Card rรฉutilisable
-// -------------------------------------------------------------
-// Ce composant sert de conteneur "carte" pour afficher
-// du contenu avec un style uniforme (fond, arrondi, padding, margin).
-// Il utilise `props.children` pour englober n'importe quel contenu.
-// -------------------------------------------------------------
-
-import React from "react";
-import { View, StyleSheet } from "react-native";
-import { Colors } from "../../constants/theme";
-
-export default function Card({ children, style }) {
- return {children};
-}
-
-const styles = StyleSheet.create({
- card: {
- backgroundColor: Colors.card, // Couleur de fond des cartes
- padding: 20, // Padding interne
- borderRadius: 16, // Arrondi des coins
- marginBottom: 16, // Marge en bas
- shadowColor: "#000", // Optionnel : ombre lรฉgรจre pour iOS
- shadowOffset: { width: 0, height: 2 },
- shadowOpacity: 0.1,
- shadowRadius: 4,
- elevation: 3, // Ombre pour Android
- },
-});
\ No newline at end of file
diff --git a/front/src/components/cards/ExerciseCard.js b/front/src/components/cards/ExerciseCard.js
index f5fa7d8..60a7d07 100644
--- a/front/src/components/cards/ExerciseCard.js
+++ b/front/src/components/cards/ExerciseCard.js
@@ -147,7 +147,7 @@ const styles = StyleSheet.create({
cardInSuperset: {
marginHorizontal: 10,
marginBottom: 8,
- backgroundColor: '#1f1f27',
+ backgroundColor: Colors.borderSubtle,
},
row: {
flexDirection: 'row',
@@ -157,7 +157,7 @@ const styles = StyleSheet.create({
width: 52,
height: 52,
borderRadius: 12,
- backgroundColor: '#0e0e12',
+ backgroundColor: Colors.cardInner,
justifyContent: 'center',
alignItems: 'center',
marginRight: 14,
diff --git a/front/src/components/cards/SetRow.js b/front/src/components/cards/SetRow.js
index 81f8cb1..82ede21 100644
--- a/front/src/components/cards/SetRow.js
+++ b/front/src/components/cards/SetRow.js
@@ -8,7 +8,7 @@ import {
} from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import { Colors } from '../../constants/theme';
-import { haptics } from '../../services/haptics.service';
+import { haptics } from '../../services';
// Ligne de sรฉrie : [-] SET | POIDS (KG) | REPS | VALIDER
//
diff --git a/front/src/components/cards/StatBox.js b/front/src/components/cards/StatBox.js
deleted file mode 100644
index 661c807..0000000
--- a/front/src/components/cards/StatBox.js
+++ /dev/null
@@ -1,36 +0,0 @@
-// ============================================================================================
-// Composant StatBox
-// Petite carte affichant une statistique : valeur + label
-// ============================================================================================
-
-import React from "react";
-import { View, Text, StyleSheet } from "react-native";
-import { Colors } from "../../constants/theme";
-
-export default function StatBox({ value, label }) {
- return (
-
- {value}
- {label}
-
- );
-}
-
-const styles = StyleSheet.create({
- box: {
- backgroundColor: Colors.card,
- width: "30%",
- padding: 14,
- borderRadius: 14,
- },
- value: {
- color: Colors.textPrimary,
- fontSize: 22,
- fontWeight: "bold",
- },
- label: {
- color: Colors.textSecondary,
- marginTop: 4,
- fontSize: 12,
- },
-});
\ No newline at end of file
diff --git a/front/src/components/cards/WorkoutItem.js b/front/src/components/cards/WorkoutItem.js
deleted file mode 100644
index 7eb138b..0000000
--- a/front/src/components/cards/WorkoutItem.js
+++ /dev/null
@@ -1,38 +0,0 @@
-// ============================================================================================
-// Composant REUTILISABLE : WorkoutItem
-// ============================================================================================
-// Carte affichant une sรฉance :
-// - titre (Push, Pull...)
-// - informations complรฉmentaires (nombre d'exercices, durรฉe...)
-// - action au clic (ouvrir la sรฉance)
-// ============================================================================================
-import React from "react";
-import { View, Text, StyleSheet, TouchableOpacity } from "react-native";
-import { Colors } from "../../constants/theme";
-
-export default function WorkoutItem({ title, info, onPress }) {
- return (
-
- {title}
- {info}
-
- );
-}
-
-const styles = StyleSheet.create({
- card: {
- backgroundColor: Colors.card,
- padding: 18,
- borderRadius: 14,
- marginBottom: 12,
- },
- title: {
- color: Colors.textPrimary,
- fontSize: 16,
- fontWeight: "bold",
- },
- info: {
- color: Colors.textSecondary,
- marginTop: 4,
- },
-});
diff --git a/front/src/components/common/ActionSheetModal.js b/front/src/components/common/ActionSheetModal.js
index ef3f7d2..34789e0 100644
--- a/front/src/components/common/ActionSheetModal.js
+++ b/front/src/components/common/ActionSheetModal.js
@@ -56,7 +56,7 @@ const styles = StyleSheet.create({
},
card: {
width: '100%',
- backgroundColor: '#13131C',
+ backgroundColor: Colors.bgDeep2,
borderRadius: 18,
borderWidth: 1,
borderColor: 'rgba(255,255,255,0.08)',
@@ -82,7 +82,7 @@ const styles = StyleSheet.create({
},
optionBtnLast: { marginBottom: 8 },
optionTxt: { color: Colors.textPrimary, fontSize: 15.5, fontWeight: '600' },
- optionTxtDestructive: { color: '#EF4444' },
+ optionTxtDestructive: { color: Colors.destructive },
cancelBtn: {
height: 50,
justifyContent: 'center',
diff --git a/front/src/components/common/ConfirmModal.js b/front/src/components/common/ConfirmModal.js
index 04aa539..684808f 100644
--- a/front/src/components/common/ConfirmModal.js
+++ b/front/src/components/common/ConfirmModal.js
@@ -2,6 +2,7 @@ import React from 'react';
import { View, Text, StyleSheet, Modal, TouchableOpacity } from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import { Colors } from '../../constants/theme';
+import { modalStyles } from './modalStyles';
// โโโ ConfirmModal โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// Modale de confirmation stylisรฉe (fond sombre, carte, boutons) ร utiliser ร la
@@ -24,25 +25,25 @@ export default function ConfirmModal({
confirmLabel, cancelLabel = 'Annuler', destructive = false,
onConfirm, onCancel,
}) {
- const accent = destructive ? '#EF4444' : Colors.primary;
+ const accent = destructive ? Colors.destructive : Colors.primary;
return (
-
-
-
+
+
+
- {title}
- {body ? {body} : null}
+ {title}
+ {body ? {body} : null}
- {confirmLabel}
+ {confirmLabel}
@@ -55,63 +56,7 @@ export default function ConfirmModal({
}
const styles = StyleSheet.create({
- backdrop: {
- flex: 1,
- backgroundColor: 'rgba(0,0,0,0.82)',
- justifyContent: 'center',
- alignItems: 'center',
- paddingHorizontal: 24,
- },
- card: {
- width: '100%',
- backgroundColor: '#13131C',
- borderRadius: 22,
- borderWidth: 1,
- padding: 28,
- alignItems: 'center',
- shadowColor: '#000',
- shadowOffset: { width: 0, height: 16 },
- shadowOpacity: 0.65,
- shadowRadius: 32,
- elevation: 20,
- },
- iconWrap: {
- width: 60,
- height: 60,
- borderRadius: 18,
- borderWidth: 1,
- justifyContent: 'center',
- alignItems: 'center',
- marginBottom: 18,
- },
- title: {
- color: Colors.textPrimary,
- fontSize: 18,
- fontWeight: '800',
- letterSpacing: -0.3,
- marginBottom: 10,
- textAlign: 'center',
- },
- body: {
- color: Colors.textSecondary,
- fontSize: 14,
- lineHeight: 21,
- textAlign: 'center',
- marginBottom: 24,
- },
- confirmBtn: {
- width: '100%',
- height: 50,
- borderRadius: 13,
- justifyContent: 'center',
- alignItems: 'center',
- marginBottom: 10,
- shadowOffset: { width: 0, height: 6 },
- shadowOpacity: 0.40,
- shadowRadius: 12,
- elevation: 6,
- },
- confirmTxt: { color: '#fff', fontSize: 15, fontWeight: '700', letterSpacing: 0.2 },
+ confirmSpacing: { marginBottom: 10 },
cancelBtn: { width: '100%', height: 44, borderRadius: 13, justifyContent: 'center', alignItems: 'center' },
cancelTxt: { color: Colors.textMuted, fontSize: 14, fontWeight: '500' },
});
diff --git a/front/src/components/common/InfoModal.js b/front/src/components/common/InfoModal.js
index 0aba99b..f7cba07 100644
--- a/front/src/components/common/InfoModal.js
+++ b/front/src/components/common/InfoModal.js
@@ -1,7 +1,8 @@
import React from 'react';
-import { View, Text, StyleSheet, Modal, TouchableOpacity } from 'react-native';
+import { View, Text, Modal, TouchableOpacity } from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import { Colors } from '../../constants/theme';
+import { modalStyles } from './modalStyles';
// โโโ InfoModal โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// Alerte ร un seul bouton (information, erreur, validation simple) โ remplace
@@ -21,87 +22,28 @@ export default function InfoModal({
visible, icon = 'information-circle-outline', title, body,
closeLabel = 'OK', destructive = false, onClose,
}) {
- const accent = destructive ? '#EF4444' : Colors.primary;
+ const accent = destructive ? Colors.destructive : Colors.primary;
return (
-
-
-
+
+
+
- {title}
- {body ? {body} : null}
+ {title}
+ {body ? {body} : null}
- {closeLabel}
+ {closeLabel}
);
}
-
-const styles = StyleSheet.create({
- backdrop: {
- flex: 1,
- backgroundColor: 'rgba(0,0,0,0.82)',
- justifyContent: 'center',
- alignItems: 'center',
- paddingHorizontal: 24,
- },
- card: {
- width: '100%',
- backgroundColor: '#13131C',
- borderRadius: 22,
- borderWidth: 1,
- padding: 28,
- alignItems: 'center',
- shadowColor: '#000',
- shadowOffset: { width: 0, height: 16 },
- shadowOpacity: 0.65,
- shadowRadius: 32,
- elevation: 20,
- },
- iconWrap: {
- width: 60,
- height: 60,
- borderRadius: 18,
- borderWidth: 1,
- justifyContent: 'center',
- alignItems: 'center',
- marginBottom: 18,
- },
- title: {
- color: Colors.textPrimary,
- fontSize: 18,
- fontWeight: '800',
- letterSpacing: -0.3,
- marginBottom: 10,
- textAlign: 'center',
- },
- body: {
- color: Colors.textSecondary,
- fontSize: 14,
- lineHeight: 21,
- textAlign: 'center',
- marginBottom: 24,
- },
- closeBtn: {
- width: '100%',
- height: 50,
- borderRadius: 13,
- justifyContent: 'center',
- alignItems: 'center',
- shadowOffset: { width: 0, height: 6 },
- shadowOpacity: 0.40,
- shadowRadius: 12,
- elevation: 6,
- },
- closeTxt: { color: '#fff', fontSize: 15, fontWeight: '700', letterSpacing: 0.2 },
-});
diff --git a/front/src/components/common/NotificationBanner.js b/front/src/components/common/NotificationBanner.js
index 07cc357..f05ff1c 100644
--- a/front/src/components/common/NotificationBanner.js
+++ b/front/src/components/common/NotificationBanner.js
@@ -1,11 +1,12 @@
import React, { useEffect, useRef } from 'react';
import { View, Text, StyleSheet, Animated } from 'react-native';
import { Ionicons } from '@expo/vector-icons';
+import { Colors } from '../../constants/theme';
const VARIANTS = {
error: {
icon: 'alert-circle',
- color: '#FF4D4D',
+ color: Colors.error,
bg: 'rgba(255, 77, 77, 0.10)',
border: 'rgba(255, 77, 77, 0.30)',
iconBg: 'rgba(255, 77, 77, 0.15)',
@@ -26,7 +27,7 @@ const VARIANTS = {
},
success: {
icon: 'checkmark-circle',
- color: '#44FF88',
+ color: Colors.success,
bg: 'rgba(68, 255, 136, 0.10)',
border: 'rgba(68, 255, 136, 0.28)',
iconBg: 'rgba(68, 255, 136, 0.15)',
diff --git a/front/src/components/common/QuestToast.js b/front/src/components/common/QuestToast.js
index 3da6ee3..8c89d99 100644
--- a/front/src/components/common/QuestToast.js
+++ b/front/src/components/common/QuestToast.js
@@ -79,7 +79,7 @@ export default function QuestToast({ visible, questLabel, isBonus = false, onHid
@@ -145,6 +145,6 @@ const styles = StyleSheet.create({
marginTop: 2,
},
labelBonus: {
- color: '#FFD700',
+ color: Colors.gold,
},
});
diff --git a/front/src/components/common/WeightEntryModal.js b/front/src/components/common/WeightEntryModal.js
index 80c61cb..a9c5d13 100644
--- a/front/src/components/common/WeightEntryModal.js
+++ b/front/src/components/common/WeightEntryModal.js
@@ -98,7 +98,7 @@ const styles = StyleSheet.create({
},
card: {
width: '100%',
- backgroundColor: '#13131C',
+ backgroundColor: Colors.bgDeep2,
borderRadius: 22,
borderWidth: 1,
borderColor: `${Colors.primary}38`,
diff --git a/front/src/components/common/index.js b/front/src/components/common/index.js
new file mode 100644
index 0000000..c913598
--- /dev/null
+++ b/front/src/components/common/index.js
@@ -0,0 +1,13 @@
+// โโโ Barrel des composants communs โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+// Point d'entrรฉe unique : `import { ConfirmModal, InfoModal } from '../components/common';`
+// Rรจgle : ne jamais importer ce barrel depuis un fichier de ce dossier
+// (imports directs entre composants communs) pour exclure tout cycle.
+
+export { default as ActionSheetModal } from './ActionSheetModal';
+export { default as ConfirmModal } from './ConfirmModal';
+export { default as ErrorBoundary } from './ErrorBoundary';
+export { default as InfoModal } from './InfoModal';
+export { default as NotificationBanner } from './NotificationBanner';
+export { default as QuestToast } from './QuestToast';
+export { default as WeightEntryModal } from './WeightEntryModal';
+export { modalStyles } from './modalStyles';
diff --git a/front/src/components/common/modalStyles.js b/front/src/components/common/modalStyles.js
new file mode 100644
index 0000000..473e4fc
--- /dev/null
+++ b/front/src/components/common/modalStyles.js
@@ -0,0 +1,69 @@
+import { StyleSheet } from 'react-native';
+import { Colors } from '../../constants/theme';
+
+// โโโ modalStyles โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+// Styles partagรฉs par la famille des modales centrรฉes (ConfirmModal, InfoModal).
+// Source de vรฉritรฉ unique pour le rendu "carte sombre centrรฉe + badge d'icรดne
+// + CTA plein" โ toute รฉvolution visuelle de ces modales se fait ici, une fois.
+//
+// Les accents (couleur du badge, du CTA, des bordures) restent passรฉs en inline
+// par chaque modale car ils dรฉpendent d'un รฉtat (destructive โ rouge).
+
+export const modalStyles = StyleSheet.create({
+ backdrop: {
+ flex: 1,
+ backgroundColor: 'rgba(0,0,0,0.82)',
+ justifyContent: 'center',
+ alignItems: 'center',
+ paddingHorizontal: 24,
+ },
+ card: {
+ width: '100%',
+ backgroundColor: Colors.bgDeep2,
+ borderRadius: 22,
+ borderWidth: 1,
+ padding: 28,
+ alignItems: 'center',
+ shadowColor: '#000',
+ shadowOffset: { width: 0, height: 16 },
+ shadowOpacity: 0.65,
+ shadowRadius: 32,
+ elevation: 20,
+ },
+ iconWrap: {
+ width: 60,
+ height: 60,
+ borderRadius: 18,
+ borderWidth: 1,
+ justifyContent: 'center',
+ alignItems: 'center',
+ marginBottom: 18,
+ },
+ title: {
+ color: Colors.textPrimary,
+ fontSize: 18,
+ fontWeight: '800',
+ letterSpacing: -0.3,
+ marginBottom: 10,
+ textAlign: 'center',
+ },
+ body: {
+ color: Colors.textSecondary,
+ fontSize: 14,
+ lineHeight: 21,
+ textAlign: 'center',
+ marginBottom: 24,
+ },
+ ctaBtn: {
+ width: '100%',
+ height: 50,
+ borderRadius: 13,
+ justifyContent: 'center',
+ alignItems: 'center',
+ shadowOffset: { width: 0, height: 6 },
+ shadowOpacity: 0.40,
+ shadowRadius: 12,
+ elevation: 6,
+ },
+ ctaTxt: { color: '#fff', fontSize: 15, fontWeight: '700', letterSpacing: 0.2 },
+});
diff --git a/front/src/components/home/DailyQuestsCard.js b/front/src/components/home/DailyQuestsCard.js
index 721d66e..e0476eb 100644
--- a/front/src/components/home/DailyQuestsCard.js
+++ b/front/src/components/home/DailyQuestsCard.js
@@ -3,7 +3,7 @@ import { View, Text, StyleSheet, ActivityIndicator } from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import { Colors } from '../../constants/theme';
import { useQuests } from '../../context/QuestContext';
-import { QUEST_XP, BONUS_XP } from '../../services/quest.service';
+import { QUEST_XP, BONUS_XP } from '../../services';
export default function DailyQuestsCard() {
const { quests, completedCount, bonusClaimed, loading } = useQuests();
@@ -56,13 +56,13 @@ export default function DailyQuestsCard() {
Bonus toutes quรชtes : +{BONUS_XP} XP
{allDone && (
-
+
)}
@@ -230,5 +230,5 @@ const styles = StyleSheet.create({
fontSize: 10,
fontWeight: '600',
},
- bonusTextDone: { color: '#FFD700' },
+ bonusTextDone: { color: Colors.gold },
});
diff --git a/front/src/components/home/RecoveryRitualsCard.js b/front/src/components/home/RecoveryRitualsCard.js
index 82c1d4b..63769a7 100644
--- a/front/src/components/home/RecoveryRitualsCard.js
+++ b/front/src/components/home/RecoveryRitualsCard.js
@@ -629,7 +629,7 @@ const styles = StyleSheet.create({
},
modalCard: {
width: '100%',
- backgroundColor: '#13131C',
+ backgroundColor: Colors.bgDeep2,
borderRadius: 26,
borderWidth: 1,
paddingHorizontal: 24,
diff --git a/front/src/components/inventory/ChestOpeningModal.js b/front/src/components/inventory/ChestOpeningModal.js
index 5b6a244..c7a8f61 100644
--- a/front/src/components/inventory/ChestOpeningModal.js
+++ b/front/src/components/inventory/ChestOpeningModal.js
@@ -2,7 +2,7 @@ import React, { useRef, useEffect, useState } from 'react';
import { View, Text, StyleSheet, Modal, TouchableOpacity, Animated, Easing } from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import { Colors } from '../../constants/theme';
-import { ITEM_CATALOG, RARITY_META } from '../../services/inventory.service';
+import { ITEM_CATALOG, RARITY_META } from '../../services';
import BirthdayConfetti from '../profile/BirthdayConfetti';
// โโโ ChestOpeningModal โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
diff --git a/front/src/components/profile/AchievementShowcase.js b/front/src/components/profile/AchievementShowcase.js
index 9a7f6f4..13af518 100644
--- a/front/src/components/profile/AchievementShowcase.js
+++ b/front/src/components/profile/AchievementShowcase.js
@@ -2,7 +2,7 @@ import React, { useState } from 'react';
import { View, Text, StyleSheet, Modal, ScrollView, TouchableOpacity, Pressable, Dimensions } from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import { Colors } from '../../constants/theme';
-import { RARITY_META } from '../../services/inventory.service';
+import { RARITY_META } from '../../services';
import { FeaturedModal, TrophyIcon } from './TrophySlot';
// โโโ AchievementShowcase โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
@@ -78,14 +78,14 @@ const CATEGORY_COLOR = {
profile: Colors.rankViolet,
collection: Colors.primary,
social: '#22D3EE',
- heritage: '#FE7439',
+ heritage: Colors.primary,
force: '#DC2626',
- exploration: '#22C55E',
+ exploration: Colors.valid,
secret: '#6366F1',
corps: '#D1D5DB',
regularite: '#10B981',
- special: '#FE7439',
- ultime: '#FFD700',
+ special: Colors.primary,
+ ultime: Colors.gold,
};
const CATEGORY_LABELS = {
diff --git a/front/src/components/profile/ActivityHeatmap.js b/front/src/components/profile/ActivityHeatmap.js
index a934df5..2e6ac92 100644
--- a/front/src/components/profile/ActivityHeatmap.js
+++ b/front/src/components/profile/ActivityHeatmap.js
@@ -18,7 +18,7 @@ const INTENSITY_COLOR = {
1: '#4D2718', // lรฉger
2: '#85391E',
3: '#C44C24',
- 4: '#FE7439', // plein
+ 4: Colors.primary, // plein
};
function monthLabelFor(dateKey) {
diff --git a/front/src/components/profile/BirthdatePicker.js b/front/src/components/profile/BirthdatePicker.js
index 22e8713..ed596f0 100644
--- a/front/src/components/profile/BirthdatePicker.js
+++ b/front/src/components/profile/BirthdatePicker.js
@@ -198,7 +198,7 @@ const styles = StyleSheet.create({
},
card: {
width: '100%',
- backgroundColor: '#13131C',
+ backgroundColor: Colors.bgDeep2,
borderRadius: 22,
borderWidth: 1,
borderColor: 'rgba(255,255,255,0.09)',
diff --git a/front/src/components/profile/BirthdayCelebration.js b/front/src/components/profile/BirthdayCelebration.js
index 97dfdcd..b03c30d 100644
--- a/front/src/components/profile/BirthdayCelebration.js
+++ b/front/src/components/profile/BirthdayCelebration.js
@@ -1,7 +1,7 @@
import React, { useState, useEffect, useCallback } from 'react';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { useAuth } from '../../context/AuthContext';
-import { checkBirthday } from '../../services/reward.service';
+import { checkBirthday } from '../../services';
import BirthdayModal from './BirthdayModal';
const SHOWN_KEY = 'athly:birthday:shown_date:v1';
diff --git a/front/src/components/profile/BirthdayModal.js b/front/src/components/profile/BirthdayModal.js
index d326b3b..d8c93ea 100644
--- a/front/src/components/profile/BirthdayModal.js
+++ b/front/src/components/profile/BirthdayModal.js
@@ -117,7 +117,7 @@ const styles = StyleSheet.create({
},
card: {
width: '100%',
- backgroundColor: '#13131C',
+ backgroundColor: Colors.bgDeep2,
borderRadius: 22,
borderWidth: 1,
borderColor: 'rgba(139,92,246,0.35)',
diff --git a/front/src/components/profile/BorderPicker.js b/front/src/components/profile/BorderPicker.js
index 58fd2cf..63a40ab 100644
--- a/front/src/components/profile/BorderPicker.js
+++ b/front/src/components/profile/BorderPicker.js
@@ -290,7 +290,7 @@ const styles = StyleSheet.create({
bottom: 0,
left: 0,
right: 0,
- backgroundColor: '#13131C',
+ backgroundColor: Colors.bgDeep2,
borderTopLeftRadius: 28,
borderTopRightRadius: 28,
paddingTop: 12,
@@ -369,7 +369,7 @@ const styles = StyleSheet.create({
width: 16, height: 16, borderRadius: 8,
backgroundColor: Colors.primary,
alignItems: 'center', justifyContent: 'center',
- borderWidth: 1.5, borderColor: '#13131C',
+ borderWidth: 1.5, borderColor: Colors.bgDeep2,
},
itemName: { fontSize: 9, fontWeight: '700', textAlign: 'center', color: Colors.textPrimary },
itemUnlock: { color: Colors.textMuted, fontSize: 8, fontWeight: '500', marginTop: 1, textAlign: 'center' },
diff --git a/front/src/components/profile/EmberParticles.js b/front/src/components/profile/EmberParticles.js
index 6b17ffe..98e8e11 100644
--- a/front/src/components/profile/EmberParticles.js
+++ b/front/src/components/profile/EmberParticles.js
@@ -1,5 +1,6 @@
import React, { useRef, useEffect } from 'react';
import { View, StyleSheet, Animated } from 'react-native';
+import { Colors } from '../../constants/theme';
// โโโ EmberParticles โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// Braises ascendantes pour les rangs Lรฉgende (171+) et ATHLY GOD (200).
@@ -16,7 +17,7 @@ function randomBetween(min, max) {
return min + Math.random() * (max - min);
}
-export default function EmberParticles({ visible = false, color = '#C084FC' }) {
+export default function EmberParticles({ visible = false, color = Colors.legendAccent }) {
const particles = useRef(
Array.from({ length: PARTICLE_COUNT }, (_, i) => ({
y: new Animated.Value(0),
diff --git a/front/src/components/profile/HeroLevelCard.js b/front/src/components/profile/HeroLevelCard.js
index afa646d..eaf0e0e 100644
--- a/front/src/components/profile/HeroLevelCard.js
+++ b/front/src/components/profile/HeroLevelCard.js
@@ -2,7 +2,7 @@ import React, { useMemo, useRef, useEffect } from 'react';
import { View, Text, StyleSheet, Animated } from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import { Colors } from '../../constants/theme';
-import { xpToLevel, getRank } from '../../services/stats.service';
+import { xpToLevel, getRank } from '../../services';
import AvatarFrame, { getFrameFootprint } from './AvatarFrame';
const AVATAR_SIZE = 90;
diff --git a/front/src/components/profile/LevelUpModal.js b/front/src/components/profile/LevelUpModal.js
index 148d162..50fde07 100644
--- a/front/src/components/profile/LevelUpModal.js
+++ b/front/src/components/profile/LevelUpModal.js
@@ -8,16 +8,16 @@ import BirthdayConfetti from './BirthdayConfetti';
// getRankForLevel() (back/utils/levelHelpers.js), indexรฉ par le libellรฉ
// backend puisque cette modale reรงoit `rank` en toutes lettres.
const RANK_COLORS = {
- 'ATHLY GOD': '#FFD700',
- 'Lรฉgende': '#C084FC',
- 'Grand Maรฎtre': '#A855F7',
- 'Maรฎtre': '#8B5CF6',
- 'รlite': '#6E6AF0',
- 'Warrior': '#6E6AF0',
+ 'ATHLY GOD': Colors.gold,
+ 'Lรฉgende': Colors.legendAccent,
+ 'Grand Maรฎtre': Colors.rankPurple,
+ 'Maรฎtre': Colors.rankViolet,
+ 'รlite': Colors.secondaryAccent,
+ 'Warrior': Colors.secondaryAccent,
'Compรฉtiteur': '#3B82F6',
- 'Athlรจte': '#22C55E',
+ 'Athlรจte': Colors.valid,
'Initiรฉ': '#FBBF24',
- 'Novice': '#FE7439',
+ 'Novice': Colors.primary,
};
// โโโ LevelUpModal โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
@@ -88,7 +88,7 @@ const styles = StyleSheet.create({
},
card: {
width: '100%',
- backgroundColor: '#13131C',
+ backgroundColor: Colors.bgDeep2,
borderRadius: 22,
borderWidth: 1,
padding: 28,
diff --git a/front/src/components/profile/RecordsShowcasePicker.js b/front/src/components/profile/RecordsShowcasePicker.js
index 7f706fa..4675283 100644
--- a/front/src/components/profile/RecordsShowcasePicker.js
+++ b/front/src/components/profile/RecordsShowcasePicker.js
@@ -2,8 +2,8 @@ import React, { useState, useEffect, useMemo } from 'react';
import { View, Text, StyleSheet, Modal, TouchableOpacity, TextInput, FlatList, ActivityIndicator } from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import { Colors } from '../../constants/theme';
-import { getMyRecords } from '../../services/social.service';
-import { updateRecordsShowcase } from '../../services/profile.service';
+import { getMyRecords } from '../../services';
+import { updateRecordsShowcase } from '../../services';
const MAX_SHOWCASED = 6;
@@ -166,7 +166,7 @@ const styles = StyleSheet.create({
justifyContent: 'flex-end',
},
sheet: {
- backgroundColor: '#13131C',
+ backgroundColor: Colors.bgDeep2,
borderTopLeftRadius: 24,
borderTopRightRadius: 24,
paddingHorizontal: 20,
diff --git a/front/src/components/profile/StreakBadge.js b/front/src/components/profile/StreakBadge.js
index ef34ed1..5bd3695 100644
--- a/front/src/components/profile/StreakBadge.js
+++ b/front/src/components/profile/StreakBadge.js
@@ -6,7 +6,7 @@ import {
const SCROLL_MAX_H = Math.round(Dimensions.get('window').height * 0.38);
import { Ionicons } from '@expo/vector-icons';
-import { getStreakMultiplier, STREAK_MILESTONES } from '../../services/stats.service';
+import { getStreakMultiplier, STREAK_MILESTONES } from '../../services';
import { Colors } from '../../constants/theme';
// โโโ StreakBonusModal โ Roadmap des multiplicateurs โโโโโโโโโโโโโโโโโโโโโโโโโโโ
@@ -37,7 +37,7 @@ function StreakBonusModal({ visible, streak, onClose }) {
{/* Header */}
-
+
Rรฉgularitรฉ & Multiplicateurs
@@ -133,7 +133,7 @@ export default function StreakBadge({ streak = 0, compact = false }) {
if (streak === 0) return null;
- const flameColor = color || '#FE7439';
+ const flameColor = color || Colors.primary;
const openModal = () => setModalVisible(true);
@@ -270,8 +270,8 @@ const ms = StyleSheet.create({
letterSpacing: 1.2, marginBottom: 8,
},
currentRow: { flexDirection: 'row', alignItems: 'baseline', marginBottom: 12 },
- currentDays: { color: '#FE7439', fontSize: 36, fontWeight: '900' },
- currentUnit: { color: '#FE7439', fontSize: 16, fontWeight: '600' },
+ currentDays: { color: Colors.primary, fontSize: 36, fontWeight: '900' },
+ currentUnit: { color: Colors.primary, fontSize: 16, fontWeight: '600' },
multChip: {
marginLeft: 10, paddingHorizontal: 10, paddingVertical: 4,
borderRadius: 12, borderWidth: 1,
diff --git a/front/src/components/profile/TitlePickerModal.js b/front/src/components/profile/TitlePickerModal.js
index 0285a31..d3630a3 100644
--- a/front/src/components/profile/TitlePickerModal.js
+++ b/front/src/components/profile/TitlePickerModal.js
@@ -3,9 +3,9 @@ import { View, Text, StyleSheet, ScrollView, TouchableOpacity, ActivityIndicator
import { Ionicons } from '@expo/vector-icons';
import { Colors } from '../../constants/theme';
-import { getTitles, equipTitle } from '../../services/title.service';
-import { RARITY_META } from '../../services/inventory.service';
-import { haptics } from '../../services/haptics.service';
+import { getTitles, equipTitle } from '../../services';
+import { RARITY_META } from '../../services';
+import { haptics } from '../../services';
// โโโ TitleInfoModal โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// Affiche la quรชte exacte pour un titre encore verrouillรฉ, avec une barre de
@@ -188,7 +188,7 @@ const s = StyleSheet.create({
justifyContent: 'center', alignItems: 'center', paddingHorizontal: 20,
},
sheet: {
- width: '100%', maxHeight: '85%', backgroundColor: '#13131C',
+ width: '100%', maxHeight: '85%', backgroundColor: Colors.bgDeep2,
borderRadius: 24, borderWidth: 1, borderColor: `${Colors.primary}38`,
padding: 22, paddingTop: 40,
},
@@ -227,7 +227,7 @@ const s = StyleSheet.create({
justifyContent: 'center', alignItems: 'center', paddingHorizontal: 24,
},
infoCard: {
- width: '100%', backgroundColor: '#13131C', borderRadius: 22, borderWidth: 1,
+ width: '100%', backgroundColor: Colors.bgDeep2, borderRadius: 22, borderWidth: 1,
padding: 28, alignItems: 'center',
shadowColor: '#000', shadowOffset: { width: 0, height: 16 }, shadowOpacity: 0.65, shadowRadius: 32, elevation: 20,
},
diff --git a/front/src/components/profile/TrophyGrid.js b/front/src/components/profile/TrophyGrid.js
index 6918ae3..f9c929b 100644
--- a/front/src/components/profile/TrophyGrid.js
+++ b/front/src/components/profile/TrophyGrid.js
@@ -1,6 +1,6 @@
import React, { useState } from 'react';
import { View, StyleSheet } from 'react-native';
-import { MAX_FEATURED } from '../../hooks/useFeaturedTrophies';
+import { MAX_FEATURED } from '../../hooks';
import { TrophySlot, EmptySlot, FeaturedModal } from './TrophySlot';
// featuredIds + toggleFeatured proviennent du parent (ProfileScreen via useFeaturedTrophies)
diff --git a/front/src/components/social/ActivityFeedModal.js b/front/src/components/social/ActivityFeedModal.js
index 21472b6..759c755 100644
--- a/front/src/components/social/ActivityFeedModal.js
+++ b/front/src/components/social/ActivityFeedModal.js
@@ -3,7 +3,7 @@ import { View, Text, StyleSheet, Modal, TouchableOpacity, ScrollView } from 'rea
import { Ionicons } from '@expo/vector-icons';
import { Colors } from '../../constants/theme';
import { useAuth } from '../../context/AuthContext';
-import { getActivityFeed, reactToActivityEvent } from '../../services/social.service';
+import { getActivityFeed, reactToActivityEvent } from '../../services';
// โโโ ActivityFeedModal โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
// Flux d'activitรฉ ยซ Taquineries & High-Fives ยป (Section IV) : au lancement de
@@ -138,7 +138,7 @@ const styles = StyleSheet.create({
card: {
width: '100%',
maxHeight: '78%',
- backgroundColor: '#13131C',
+ backgroundColor: Colors.bgDeep2,
borderRadius: 22,
borderWidth: 1,
borderColor: 'rgba(255,255,255,0.08)',
diff --git a/front/src/components/social/AddFriendModal.js b/front/src/components/social/AddFriendModal.js
index 0abdba0..ee86b7a 100644
--- a/front/src/components/social/AddFriendModal.js
+++ b/front/src/components/social/AddFriendModal.js
@@ -120,7 +120,7 @@ const styles = StyleSheet.create({
},
card: {
width: '100%',
- backgroundColor: '#13131C',
+ backgroundColor: Colors.bgDeep2,
borderRadius: 22,
borderWidth: 1,
borderColor: `${Colors.primary}38`,
diff --git a/front/src/components/social/ExercisePickerModal.js b/front/src/components/social/ExercisePickerModal.js
index f6c7f0c..8f227b4 100644
--- a/front/src/components/social/ExercisePickerModal.js
+++ b/front/src/components/social/ExercisePickerModal.js
@@ -121,7 +121,7 @@ const styles = StyleSheet.create({
justifyContent: 'flex-end',
},
sheet: {
- backgroundColor: '#13131C',
+ backgroundColor: Colors.bgDeep2,
borderTopLeftRadius: 24,
borderTopRightRadius: 24,
paddingHorizontal: 20,
diff --git a/front/src/components/social/FriendPreviewModal.js b/front/src/components/social/FriendPreviewModal.js
index 58e16ec..ee5036a 100644
--- a/front/src/components/social/FriendPreviewModal.js
+++ b/front/src/components/social/FriendPreviewModal.js
@@ -104,7 +104,7 @@ const styles = StyleSheet.create({
},
card: {
width: '100%',
- backgroundColor: '#13131C',
+ backgroundColor: Colors.bgDeep2,
borderRadius: 22,
borderWidth: 1,
borderColor: `${Colors.primary}38`,
diff --git a/front/src/components/social/FriendshipLevelUpModal.js b/front/src/components/social/FriendshipLevelUpModal.js
index 9663bc5..67cd3ef 100644
--- a/front/src/components/social/FriendshipLevelUpModal.js
+++ b/front/src/components/social/FriendshipLevelUpModal.js
@@ -8,7 +8,7 @@ import BirthdayConfetti from '../profile/BirthdayConfetti';
// mรชme esprit que RANK_COLORS dans LevelUpModal.js, mais indexรฉ par niveau
// d'amitiรฉ (1 ร 5) plutรดt que par rang de niveau de joueur.
const FRIENDSHIP_LEVEL_COLORS = {
- 1: '#9AA0AE',
+ 1: Colors.textSecondary,
2: '#FF8FA3',
3: '#FF4D6D',
4: '#E11D48',
@@ -87,7 +87,7 @@ const styles = StyleSheet.create({
},
card: {
width: '100%',
- backgroundColor: '#13131C',
+ backgroundColor: Colors.bgDeep2,
borderRadius: 22,
borderWidth: 1,
padding: 28,
diff --git a/front/src/components/stats/WeightReminderCheck.js b/front/src/components/stats/WeightReminderCheck.js
index 0b1cae4..61b8f5d 100644
--- a/front/src/components/stats/WeightReminderCheck.js
+++ b/front/src/components/stats/WeightReminderCheck.js
@@ -1,7 +1,7 @@
import React, { useState, useEffect, useCallback } from 'react';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { useAuth } from '../../context/AuthContext';
-import { getWeightHistory } from '../../services/weight.service';
+import { getWeightHistory } from '../../services';
import WeightReminderModal from './WeightReminderModal';
import WeightEntryModal from '../common/WeightEntryModal';
diff --git a/front/src/components/stats/WeightReminderModal.js b/front/src/components/stats/WeightReminderModal.js
index 407f2a0..7292cb2 100644
--- a/front/src/components/stats/WeightReminderModal.js
+++ b/front/src/components/stats/WeightReminderModal.js
@@ -54,7 +54,7 @@ const styles = StyleSheet.create({
},
card: {
width: '100%',
- backgroundColor: '#13131C',
+ backgroundColor: Colors.bgDeep2,
borderRadius: 22,
borderWidth: 1,
borderColor: `${Colors.primary}38`,
diff --git a/front/src/components/stats/XPProgressBar.js b/front/src/components/stats/XPProgressBar.js
index 0e465df..924e7cb 100644
--- a/front/src/components/stats/XPProgressBar.js
+++ b/front/src/components/stats/XPProgressBar.js
@@ -1,7 +1,7 @@
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { Colors } from '../../constants/theme';
-import { xpToLevel } from '../../services/stats.service';
+import { xpToLevel } from '../../services';
// Bar de progression niveau / XP. Reรงoit le total XP cumulรฉ.
// Visualise : niveau actuel + barre + xp restant pour next level.
diff --git a/front/src/components/tutorial/TutorialOverlay.js b/front/src/components/tutorial/TutorialOverlay.js
index b34cf25..6dbf80d 100644
--- a/front/src/components/tutorial/TutorialOverlay.js
+++ b/front/src/components/tutorial/TutorialOverlay.js
@@ -6,7 +6,7 @@ import {
import { Ionicons } from '@expo/vector-icons';
import { useTutorial } from '../../context/TutorialContext';
import { CHAPTER_IDS } from '../../data/tutorialChapters';
-import { haptics } from '../../services/haptics.service';
+import { haptics } from '../../services';
import { Colors } from '../../constants/theme';
// Padding visuel autour du spotlight (lรฉger, ne perturbe pas les coordonnรฉes)
@@ -262,7 +262,7 @@ export default function TutorialOverlay({ navigation }) {
Passer
diff --git a/front/src/components/typography/SectionTitle.js b/front/src/components/typography/SectionTitle.js
deleted file mode 100644
index 782dcec..0000000
--- a/front/src/components/typography/SectionTitle.js
+++ /dev/null
@@ -1,21 +0,0 @@
-// ============================================================================================
-// Composant SectionTitle
-// Affiche un titre de section avec un style uniforme
-// ============================================================================================
-
-import React from "react";
-import { Text, StyleSheet } from "react-native";
-import { Colors } from "../../constants/theme";
-
-export default function SectionTitle({ title }) {
- return {title};
-}
-
-const styles = StyleSheet.create({
- section: {
- color: Colors.textPrimary,
- fontSize: 18,
- marginVertical: 16,
- fontWeight: "bold",
- },
-});
\ No newline at end of file
diff --git a/front/src/components/ui/AppToast.js b/front/src/components/ui/AppToast.js
index ea8d914..fbeaeb7 100644
--- a/front/src/components/ui/AppToast.js
+++ b/front/src/components/ui/AppToast.js
@@ -4,13 +4,14 @@ import {
} from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { Ionicons } from '@expo/vector-icons';
+import { Colors } from '../../constants/theme';
// โโโ Config par type โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
const TYPE_CONFIG = {
- success: { icon: 'checkmark-circle', color: '#22C55E' },
- error: { icon: 'close-circle', color: '#FF4D4D' },
- warning: { icon: 'warning', color: '#F59E0B' },
+ success: { icon: 'checkmark-circle', color: Colors.valid },
+ error: { icon: 'close-circle', color: Colors.error },
+ warning: { icon: 'warning', color: Colors.warningAmber },
info: { icon: 'information-circle', color: '#4A9EFF' },
};
@@ -132,7 +133,7 @@ const styles = StyleSheet.create({
},
message: {
flex: 1,
- color: '#FFFFFF',
+ color: Colors.textPrimary,
fontSize: 14,
fontWeight: '600',
lineHeight: 20,
diff --git a/front/src/components/web/DesktopInstallPage.js b/front/src/components/web/DesktopInstallPage.js
index fe227b0..eb7e80a 100644
--- a/front/src/components/web/DesktopInstallPage.js
+++ b/front/src/components/web/DesktopInstallPage.js
@@ -1,6 +1,7 @@
import React from 'react';
import { View, Text, Image, StyleSheet } from 'react-native';
import { Ionicons } from '@expo/vector-icons';
+import { Colors } from '../../constants/theme';
const LOGO = require('../../../assets/logo-orange.png');
@@ -60,7 +61,7 @@ export default function DesktopInstallPage() {
icon="logo-chrome"
title="Chrome"
platform="Android"
- accent="#FE7439"
+ accent={Colors.primary}
steps={[
'Ouvrez ce lien dans Chrome sur votre Android',
'Appuyez sur โฎ (3 points) en haut ร droite',
@@ -71,7 +72,7 @@ export default function DesktopInstallPage() {
icon="logo-apple"
title="Safari"
platform="iPhone ยท iPad"
- accent="#6E6AF0"
+ accent={Colors.secondaryAccent}
steps={[
'Ouvrez ce lien dans Safari sur votre iPhone',
'Appuyez sur l\'icรดne Partager (โกโ) en bas',
@@ -105,7 +106,7 @@ const s = StyleSheet.create({
card: {
width: '100%',
maxWidth: 700,
- backgroundColor: '#080910',
+ backgroundColor: Colors.bgAbyss,
borderRadius: 24,
borderWidth: 1,
borderColor: 'rgba(110,106,240,0.18)',
@@ -126,7 +127,7 @@ const s = StyleSheet.create({
headline: {
fontSize: 32,
fontWeight: '800',
- color: '#FFFFFF',
+ color: Colors.textPrimary,
textAlign: 'center',
letterSpacing: -0.8,
marginBottom: 12,
@@ -159,7 +160,7 @@ const s = StyleSheet.create({
},
urlText: {
fontSize: 15,
- color: '#6E6AF0',
+ color: Colors.secondaryAccent,
fontWeight: '600',
letterSpacing: 0.2,
},
diff --git a/front/src/components/workouts/AddExerciseSheet.js b/front/src/components/workouts/AddExerciseSheet.js
index 1c53dd3..d7ae4f8 100644
--- a/front/src/components/workouts/AddExerciseSheet.js
+++ b/front/src/components/workouts/AddExerciseSheet.js
@@ -203,7 +203,7 @@ const styles = StyleSheet.create({
width: 44,
height: 44,
borderRadius: 10,
- backgroundColor: '#0e0e12',
+ backgroundColor: Colors.cardInner,
alignItems: 'center',
justifyContent: 'center',
marginRight: 12,
diff --git a/front/src/components/workouts/ExerciseHeader.js b/front/src/components/workouts/ExerciseHeader.js
index bec7f83..295dad7 100644
--- a/front/src/components/workouts/ExerciseHeader.js
+++ b/front/src/components/workouts/ExerciseHeader.js
@@ -83,7 +83,7 @@ const styles = StyleSheet.create({
paddingTop: 12,
paddingBottom: 22,
borderBottomWidth: StyleSheet.hairlineWidth,
- borderBottomColor: '#1f1f27',
+ borderBottomColor: Colors.borderSubtle,
},
topRow: {
flexDirection: 'row',
diff --git a/front/src/components/workouts/LobbyInviteCheck.js b/front/src/components/workouts/LobbyInviteCheck.js
index 12550c2..0c05b33 100644
--- a/front/src/components/workouts/LobbyInviteCheck.js
+++ b/front/src/components/workouts/LobbyInviteCheck.js
@@ -2,7 +2,7 @@ import React, { useState, useEffect, useCallback, useRef } from 'react';
import * as Notifications from 'expo-notifications';
import { Platform } from 'react-native';
import { useAuth } from '../../context/AuthContext';
-import { joinLobby } from '../../services/lobby.service';
+import { joinLobby } from '../../services';
import { navigate } from '../../navigation/navigationRef';
import LobbyInviteModal from './LobbyInviteModal';
diff --git a/front/src/components/workouts/LobbyInviteModal.js b/front/src/components/workouts/LobbyInviteModal.js
index 11b0bed..07ddd41 100644
--- a/front/src/components/workouts/LobbyInviteModal.js
+++ b/front/src/components/workouts/LobbyInviteModal.js
@@ -52,7 +52,7 @@ const styles = StyleSheet.create({
},
card: {
width: '100%',
- backgroundColor: '#13131C',
+ backgroundColor: Colors.bgDeep2,
borderRadius: 22,
borderWidth: 1,
borderColor: `${Colors.primary}38`,
diff --git a/front/src/components/workouts/LobbyWaitingOverlay.js b/front/src/components/workouts/LobbyWaitingOverlay.js
index 4ccc661..54be323 100644
--- a/front/src/components/workouts/LobbyWaitingOverlay.js
+++ b/front/src/components/workouts/LobbyWaitingOverlay.js
@@ -62,7 +62,7 @@ const styles = StyleSheet.create({
},
card: {
width: '100%',
- backgroundColor: '#13131C',
+ backgroundColor: Colors.bgDeep2,
borderRadius: 22,
borderWidth: 1,
borderColor: `${Colors.primary}38`,
diff --git a/front/src/components/workouts/MultiLobbyModal.js b/front/src/components/workouts/MultiLobbyModal.js
index d597d8f..625640d 100644
--- a/front/src/components/workouts/MultiLobbyModal.js
+++ b/front/src/components/workouts/MultiLobbyModal.js
@@ -2,8 +2,8 @@ import React, { useState, useEffect, useCallback, useRef } from 'react';
import { View, Text, StyleSheet, Modal, TouchableOpacity, ActivityIndicator, FlatList } from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import { Colors } from '../../constants/theme';
-import { createLobby, getLobby, joinLobby, inviteToLobby, readyLobby, unreadyLobby } from '../../services/lobby.service';
-import { getFriendsList } from '../../services/social.service';
+import { createLobby, getLobby, joinLobby, inviteToLobby, readyLobby, unreadyLobby } from '../../services';
+import { getFriendsList } from '../../services';
import { useUser } from '../../context/UserContext';
const POLL_MS = 3000;
@@ -328,7 +328,7 @@ const styles = StyleSheet.create({
},
card: {
width: '100%',
- backgroundColor: '#13131C',
+ backgroundColor: Colors.bgDeep2,
borderRadius: 22,
borderWidth: 1,
borderColor: `${Colors.primary}38`,
@@ -361,7 +361,7 @@ const styles = StyleSheet.create({
statusDot: {
position: 'absolute', bottom: -4, right: -4,
width: 16, height: 16, borderRadius: 8,
- borderWidth: 1.5, borderColor: '#13131C',
+ borderWidth: 1.5, borderColor: Colors.bgDeep2,
justifyContent: 'center', alignItems: 'center',
},
memberName: { color: Colors.textMuted, fontSize: 10.5, fontWeight: '600', marginTop: 5, textAlign: 'center' },
@@ -413,7 +413,7 @@ const styles = StyleSheet.create({
// โโ BonusTableModal โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
tableCard: {
width: '100%',
- backgroundColor: '#13131C',
+ backgroundColor: Colors.bgDeep2,
borderRadius: 22,
borderWidth: 1,
borderColor: `${Colors.primary}38`,
diff --git a/front/src/components/workouts/MultiLootModal.js b/front/src/components/workouts/MultiLootModal.js
index 651f6e3..88528cf 100644
--- a/front/src/components/workouts/MultiLootModal.js
+++ b/front/src/components/workouts/MultiLootModal.js
@@ -56,7 +56,7 @@ const styles = StyleSheet.create({
},
card: {
width: '100%',
- backgroundColor: '#13131C',
+ backgroundColor: Colors.bgDeep2,
borderRadius: 24,
borderWidth: 1,
borderColor: 'rgba(255,215,0,0.4)',
diff --git a/front/src/components/workouts/MuscleHierarchyPicker.js b/front/src/components/workouts/MuscleHierarchyPicker.js
index 8ad90a7..eca1daf 100644
--- a/front/src/components/workouts/MuscleHierarchyPicker.js
+++ b/front/src/components/workouts/MuscleHierarchyPicker.js
@@ -168,7 +168,7 @@ const styles = StyleSheet.create({
marginBottom: 10,
overflow: 'hidden',
borderWidth: 1,
- borderColor: '#1f1f27',
+ borderColor: Colors.borderSubtle,
},
groupCardActive: {
borderColor: 'rgba(254, 116, 57, 0.5)',
@@ -183,7 +183,7 @@ const styles = StyleSheet.create({
width: 42,
height: 42,
borderRadius: 10,
- backgroundColor: '#0e0e12',
+ backgroundColor: Colors.cardInner,
alignItems: 'center',
justifyContent: 'center',
marginRight: 12,
diff --git a/front/src/components/workouts/SetTable.js b/front/src/components/workouts/SetTable.js
index 9967e72..a38c5d9 100644
--- a/front/src/components/workouts/SetTable.js
+++ b/front/src/components/workouts/SetTable.js
@@ -125,7 +125,7 @@ const styles = StyleSheet.create({
justifyContent: 'center',
},
headerText: {
- color: '#8B95A3',
+ color: Colors.textDim,
fontSize: 11,
fontWeight: '700',
letterSpacing: 1,
diff --git a/front/src/components/workouts/ShortSessionWarningModal.js b/front/src/components/workouts/ShortSessionWarningModal.js
index 16bb23d..e4a2fd1 100644
--- a/front/src/components/workouts/ShortSessionWarningModal.js
+++ b/front/src/components/workouts/ShortSessionWarningModal.js
@@ -80,7 +80,7 @@ const styles = StyleSheet.create({
},
card: {
width: '100%',
- backgroundColor: '#13131C',
+ backgroundColor: Colors.bgDeep2,
borderRadius: 24,
borderWidth: 1,
borderColor: 'rgba(255,77,77,0.28)',
@@ -88,7 +88,7 @@ const styles = StyleSheet.create({
paddingTop: 32,
paddingBottom: 28,
alignItems: 'center',
- shadowColor: '#FF4D4D',
+ shadowColor: Colors.error,
shadowOffset: { width: 0, height: 12 },
shadowOpacity: 0.25,
shadowRadius: 28,
diff --git a/front/src/components/workouts/WorkoutHeader.js b/front/src/components/workouts/WorkoutHeader.js
deleted file mode 100644
index 41ce9b0..0000000
--- a/front/src/components/workouts/WorkoutHeader.js
+++ /dev/null
@@ -1,72 +0,0 @@
-import React from 'react';
-import { View, Text, StyleSheet } from 'react-native';
-import { Colors } from '../../constants/theme';
-
-// Logo ATHLY (mark + wordmark) pour rester pixel-perfect avec la maquette.
-function AthlyLogo() {
- return (
-
- โณ
- ATHLY
-
- );
-}
-
-export default function WorkoutHeader({ title = 'Sรฉance', subtitle = null }) {
- return (
-
-
- {title}
- {subtitle ? {subtitle} : null}
-
-
-
- );
-}
-
-const styles = StyleSheet.create({
- container: {
- flexDirection: 'row',
- alignItems: 'center',
- justifyContent: 'space-between',
- paddingHorizontal: 20,
- paddingTop: 40,
- paddingBottom: 14,
- backgroundColor: Colors.background,
- borderBottomWidth: StyleSheet.hairlineWidth,
- borderBottomColor: '#1f1f27',
- },
- titleBlock: {
- flex: 1,
- paddingRight: 12,
- },
- title: {
- color: Colors.textPrimary,
- fontSize: 26,
- fontWeight: '700',
- letterSpacing: 0.2,
- },
- subtitle: {
- color: Colors.textSecondary,
- fontSize: 13,
- marginTop: 4,
- },
- logoWrap: {
- alignItems: 'center',
- justifyContent: 'center',
- paddingHorizontal: 6,
- },
- logoMark: {
- color: Colors.primary,
- fontSize: 26,
- lineHeight: 28,
- fontWeight: '900',
- marginBottom: 2,
- },
- logoText: {
- color: Colors.primary,
- fontSize: 11,
- fontWeight: '900',
- letterSpacing: 1.5,
- },
-});
diff --git a/front/src/components/workouts/WorkoutRecapModal.js b/front/src/components/workouts/WorkoutRecapModal.js
index 43b2073..870a09a 100644
--- a/front/src/components/workouts/WorkoutRecapModal.js
+++ b/front/src/components/workouts/WorkoutRecapModal.js
@@ -16,8 +16,8 @@ const SCREEN_H = Dimensions.get('window').height;
import { Ionicons } from '@expo/vector-icons';
import * as Haptics from 'expo-haptics';
import { Colors } from '../../constants/theme';
-import { xpToLevel, getRank } from '../../services/stats.service';
-import { QUEST_XP, BONUS_XP } from '../../services/quest.service';
+import { xpToLevel, getRank } from '../../services';
+import { QUEST_XP, BONUS_XP } from '../../services';
const LOGO_VIOLET = require('../../../assets/logo-violet.png');
@@ -464,13 +464,13 @@ export default function WorkoutRecapModal({
{streakBonusXP > 0 && (
Bonus Rรฉgularitรฉ ร{xpMultiplier}
- +{streakBonusXP.toLocaleString('fr-FR')} XP
+ +{streakBonusXP.toLocaleString('fr-FR')} XP
)}
{questXPEarned > 0 && (
Bonus Quรชtes
- +{questXPEarned.toLocaleString('fr-FR')} XP
+ +{questXPEarned.toLocaleString('fr-FR')} XP
)}
@@ -557,9 +557,9 @@ export default function WorkoutRecapModal({
))}
{bonusUnlocked ? (
-
- Bonus toutes quรชtes
- +{BONUS_XP} XP
+
+ Bonus toutes quรชtes
+ +{BONUS_XP} XP
) : null}
diff --git a/front/src/constants/theme.js b/front/src/constants/theme.js
index 2bb4801..4142b4a 100644
--- a/front/src/constants/theme.js
+++ b/front/src/constants/theme.js
@@ -51,9 +51,10 @@ export const Colors = {
uniqueBloodGlow: 'rgba(163,0,0,0.65)',
// โโ Feedback โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- valid: '#22C55E', // sets / exercices validรฉs (vert chaud)
- success: '#44FF88', // XP / gains (vert nรฉon)
- error: '#FF4D4D',
+ valid: '#22C55E', // sets / exercices validรฉs (vert chaud)
+ success: '#44FF88', // XP / gains (vert nรฉon)
+ error: '#FF4D4D',
+ destructive: '#EF4444', // actions destructrices (Supprimerโฆ), accent rouge des modales
// โโ Utilitaires โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
chevron: '#888888',
diff --git a/front/src/context/CustomExercisesContext.js b/front/src/context/CustomExercisesContext.js
index 48cd449..8bfcd38 100644
--- a/front/src/context/CustomExercisesContext.js
+++ b/front/src/context/CustomExercisesContext.js
@@ -4,7 +4,7 @@ import {
addCustomExercise,
updateCustomExercise,
removeCustomExercise,
-} from '../services/customExercises.service';
+} from '../services';
// Context global pour les exercices personnalisรฉs.
// - Charge la liste depuis AsyncStorage au montage
diff --git a/front/src/context/QuestContext.js b/front/src/context/QuestContext.js
index d15a0f6..e60ea5d 100644
--- a/front/src/context/QuestContext.js
+++ b/front/src/context/QuestContext.js
@@ -12,7 +12,7 @@ import {
getTemplateById,
QUEST_XP,
BONUS_XP,
-} from '../services/quest.service';
+} from '../services';
import { useWorkoutLogs } from './WorkoutLogsContext';
const QuestContext = createContext(null);
diff --git a/front/src/context/SavedWorkoutsContext.js b/front/src/context/SavedWorkoutsContext.js
index da59366..4c69a56 100644
--- a/front/src/context/SavedWorkoutsContext.js
+++ b/front/src/context/SavedWorkoutsContext.js
@@ -4,7 +4,7 @@ import {
saveWorkout,
updateSavedWorkout,
removeSavedWorkout,
-} from '../services/savedWorkouts.service';
+} from '../services';
// Context global pour les sรฉances sauvegardรฉes (favoris / rรฉutilisables).
// Chargement initial depuis AsyncStorage, mise ร jour optimiste aprรจs chaque action.
diff --git a/front/src/context/TutorialContext.js b/front/src/context/TutorialContext.js
index 87bd0b8..a549e40 100644
--- a/front/src/context/TutorialContext.js
+++ b/front/src/context/TutorialContext.js
@@ -1,7 +1,7 @@
import React, { createContext, useState, useContext, useCallback, useEffect, useRef } from 'react';
import AsyncStorage from '@react-native-async-storage/async-storage';
-import { TUTORIAL_CHAPTERS, CHAPTER_MAP, CHAPTER_IDS } from '../data/tutorialChapters';
-import { completeOnboarding } from '../services/onboarding.service';
+import { CHAPTER_MAP, CHAPTER_IDS } from '../data/tutorialChapters';
+import { completeOnboarding } from '../services';
const DONE_KEY = 'athly:tutorial:completed:v1';
const PENDING_KEY = 'athly:tutorial:pendingChapter:v1';
diff --git a/front/src/context/WorkoutInProgressContext.js b/front/src/context/WorkoutInProgressContext.js
index 1853d4e..8807bf7 100644
--- a/front/src/context/WorkoutInProgressContext.js
+++ b/front/src/context/WorkoutInProgressContext.js
@@ -1,8 +1,8 @@
import React, { createContext, useContext, useCallback, useMemo } from 'react';
-import useWorkoutState from '../hooks/useWorkoutState';
+import { useWorkoutState } from '../hooks';
import { useWorkoutLogs } from './WorkoutLogsContext';
import { useQuests } from './QuestContext';
-import { buildLogFromWorkout, findNewPRsInLog } from '../services/stats.service';
+import { buildLogFromWorkout, findNewPRsInLog } from '../services';
// Context global pour la sรฉance en cours.
// Centralise useWorkoutState : tous les รฉcrans consomment le mรชme รฉtat + actions
diff --git a/front/src/context/WorkoutLogsContext.js b/front/src/context/WorkoutLogsContext.js
index 8b0f178..7945e1e 100644
--- a/front/src/context/WorkoutLogsContext.js
+++ b/front/src/context/WorkoutLogsContext.js
@@ -1,7 +1,7 @@
import React, { createContext, useContext, useState, useEffect, useRef, useCallback, useMemo } from 'react';
import { AppState } from 'react-native';
-import { listLogs, addLog, removeLog, totalCumulativeXP, addRitualLog, addBonusXpLog } from '../services/stats.service';
-import { syncXp, retryPendingXpSync } from '../services/xpSync.service';
+import { listLogs, addLog, removeLog, totalCumulativeXP, addRitualLog, addBonusXpLog } from '../services';
+import { syncXp, retryPendingXpSync } from '../services';
// Context global pour l'historique des sรฉances finalisรฉes (logs).
// Source de vรฉritรฉ unique pour StatsScreen, ExerciseStatsScreen, ProfileScreen.
diff --git a/front/src/hooks/index.js b/front/src/hooks/index.js
new file mode 100644
index 0000000..ab25cbd
--- /dev/null
+++ b/front/src/hooks/index.js
@@ -0,0 +1,11 @@
+// โโโ Barrel des hooks โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+// Point d'entrรฉe unique : `import { useAvatarFrame, useEffortTimer } from '../hooks';`
+// Les hooks ร export default sont rรฉ-exportรฉs sous leur nom canonique.
+
+export { useAvatarFrame } from './useAvatarFrame';
+export { useDevSettings } from './useDevSettings';
+export { default as useEffortTimer, formatDuration } from './useEffortTimer';
+export { default as useExerciseSorting, isFiltering } from './useExerciseSorting';
+export { useFeaturedTrophies, MAX_FEATURED } from './useFeaturedTrophies';
+export { useGoogleAuth } from './useGoogleAuth';
+export { default as useWorkoutState } from './useWorkoutState';
diff --git a/front/src/hooks/useWorkoutState.js b/front/src/hooks/useWorkoutState.js
index 0afd2e0..aeb5cbe 100644
--- a/front/src/hooks/useWorkoutState.js
+++ b/front/src/hooks/useWorkoutState.js
@@ -1,5 +1,5 @@
import { useReducer, useEffect, useRef, useCallback, useMemo } from 'react';
-import API from '../api/api';
+import { createWorkoutDraft, updateWorkoutDraft, finalizeWorkout } from '../services/workouts.service';
// ---------------------------------------------------------------------------
// useWorkoutState
@@ -229,27 +229,22 @@ export default function useWorkoutState(initial = {}) {
isSaving.current = true;
try {
if (!state.id) {
- const res = await API.post('/workouts/draft', {
+ const workout = await createWorkoutDraft({
name: state.name,
exercises: state.exercises,
notes: state.notes,
- status: 'draft',
});
- const data = res && res.data ? res.data : res;
- const workout = data && data.workout ? data.workout : null;
if (workout) {
dispatch({ type: ACTIONS.SET_WORKOUT, payload: { id: workout._id } });
return workout;
}
return null;
}
- const res = await API.patch(`/workouts/${state.id}/draft`, {
+ return await updateWorkoutDraft(state.id, {
exercises: state.exercises,
notes: state.notes,
durationSeconds: state.durationSeconds,
});
- const data = res && res.data ? res.data : res;
- return data && data.workout ? data.workout : data;
} catch (error) {
// Pas de Alert ici pour รฉviter le spam โ on le centralise dans finalize/handler explicite.
return null;
@@ -267,13 +262,12 @@ export default function useWorkoutState(initial = {}) {
const saved = await saveDraft();
const workoutId = state.id || (saved && (saved._id || saved.id));
if (!workoutId) throw new Error('no_id');
- const res = await API.post(`/workouts/${workoutId}/finalize`, {
+ const data = await finalizeWorkout(workoutId, {
exercises: state.exercises,
notes: state.notes,
durationSeconds: state.durationSeconds,
...options,
});
- const data = res && res.data ? res.data : res;
if (data && data.stats) {
dispatch({ type: ACTIONS.MARK_FINISHED });
return data.stats;
diff --git a/front/src/navigation/index.js b/front/src/navigation/index.js
index c1d0963..de3746f 100644
--- a/front/src/navigation/index.js
+++ b/front/src/navigation/index.js
@@ -19,8 +19,8 @@ import { QuestProvider } from '../context/QuestContext';
import { UserProvider } from '../context/UserContext';
import { TutorialProvider } from '../context/TutorialContext';
import AsyncStorage from '@react-native-async-storage/async-storage';
-import { setupNotificationChannels, ensureDailyRemindersScheduled, getExpoPushToken } from '../services/notificationService';
-import { registerPushToken } from '../services/profile.service';
+import { setupNotificationChannels, ensureDailyRemindersScheduled, getExpoPushToken } from '../services';
+import { registerPushToken } from '../services';
import BirthdayCelebration from '../components/profile/BirthdayCelebration';
import LevelUpCelebration from '../components/profile/LevelUpCelebration';
import ActivityFeedModal from '../components/social/ActivityFeedModal';
diff --git a/front/src/screens/Auth/AuthScreen.js b/front/src/screens/Auth/AuthScreen.js
deleted file mode 100644
index e4a3609..0000000
--- a/front/src/screens/Auth/AuthScreen.js
+++ /dev/null
@@ -1,396 +0,0 @@
-import React, { useState, useRef, useEffect } from 'react';
-import {
- View, Text, StyleSheet, TouchableOpacity, Image,
- KeyboardAvoidingView, Platform, ActivityIndicator,
- Dimensions, Animated, Easing, ScrollView, StatusBar,
-} from 'react-native';
-import { SafeAreaView } from 'react-native-safe-area-context';
-import { Ionicons } from '@expo/vector-icons';
-
-import { Colors } from '../../constants/theme';
-import AuthInput from '../../components/inputs/AuthInput';
-import { login, register } from '../../services/auth.service';
-import { useAuth } from '../../context/AuthContext';
-
-// Imports statiques des assets (chemin : src/screens/Auth/ โ ../../../assets/)
-const LOGO_ORANGE = require('../../../assets/logo-orange.png');
-const LOGO_ICON_ORANGE = require('../../../assets/minimalist-logo-orange.png');
-
-const { width: _rawW } = Dimensions.get('window');
-const W = Platform.OS === 'web' ? Math.min(430, _rawW) : _rawW;
-const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
-
-function FadeLoader() {
- const opacity = useRef(new Animated.Value(0)).current;
- useEffect(() => {
- Animated.timing(opacity, { toValue: 1, duration: 220, useNativeDriver: true }).start();
- }, []);
- return (
-
-
-
- );
-}
-
-export default function AuthScreen() {
- const { signIn } = useAuth();
- const slideAnim = useRef(new Animated.Value(0)).current;
-
- // โโ Login state โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- const [loginEmail, setLoginEmail] = useState('');
- const [loginPassword, setLoginPassword] = useState('');
- const [loginLoading, setLoginLoading] = useState(false);
- const [loginShowPwd, setLoginShowPwd] = useState(false);
- const [loginEmailErr, setLoginEmailErr] = useState('');
- const [loginGlobalErr, setLoginGlobalErr] = useState('');
-
- // โโ Register state โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- const [regName, setRegName] = useState('');
- const [regEmail, setRegEmail] = useState('');
- const [regPassword, setRegPassword] = useState('');
- const [regLoading, setRegLoading] = useState(false);
- const [regShowPwd, setRegShowPwd] = useState(false);
- const [regNameErr, setRegNameErr] = useState('');
- const [regEmailErr, setRegEmailErr] = useState('');
- const [regPwdErr, setRegPwdErr] = useState('');
- const [regGlobalErr, setRegGlobalErr] = useState('');
-
- // โโ Slide โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- const slideTo = (panel) => {
- Animated.timing(slideAnim, {
- toValue: panel === 'login' ? 0 : -W,
- duration: 400,
- easing: Easing.out(Easing.exp),
- useNativeDriver: true,
- }).start();
- };
-
- // โโ Validators โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- const validateLoginEmail = (val = loginEmail) => {
- if (!val) { setLoginEmailErr('Email requis'); return false; }
- if (!EMAIL_RE.test(val)) { setLoginEmailErr('Email invalide'); return false; }
- setLoginEmailErr(''); return true;
- };
-
- const validateRegEmail = (val = regEmail) => {
- if (!val) { setRegEmailErr('Email requis'); return false; }
- if (!EMAIL_RE.test(val)) { setRegEmailErr('Email invalide'); return false; }
- setRegEmailErr(''); return true;
- };
-
- // โโ Handlers โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- const handleLogin = async () => {
- if (!validateLoginEmail() || !loginPassword) return;
- try {
- setLoginLoading(true);
- setLoginGlobalErr('');
- const res = await login(loginEmail, loginPassword);
- if (res?.token) await signIn(res.token);
- else throw new Error();
- } catch {
- setLoginGlobalErr('Email ou mot de passe incorrect.');
- } finally {
- setLoginLoading(false);
- }
- };
-
- const handleRegister = async () => {
- let ok = true;
- if (!regName) { setRegNameErr('Nom requis'); ok = false; } else setRegNameErr('');
- if (!validateRegEmail()) ok = false;
- if (regPassword.length < 6) { setRegPwdErr('6 caractรจres minimum'); ok = false; } else setRegPwdErr('');
- if (!ok) return;
- try {
- setRegLoading(true);
- setRegGlobalErr('');
- const res = await register({ name: regName, email: regEmail, password: regPassword });
- if (res?.token) await signIn(res.token);
- else slideTo('login');
- } catch {
- setRegGlobalErr("Erreur lors de l'inscription. Email dรฉjร utilisรฉ ?");
- } finally {
- setRegLoading(false);
- }
- };
-
- return (
-
-
-
-
-
-
-
- {/* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- PANEL LOGIN
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */}
-
-
-
-
- Bienvenue
- Connectez-vous pour continuer
-
-
- { setLoginEmail(v); if (loginEmailErr) validateLoginEmail(v); }}
- onBlur={() => validateLoginEmail()}
- error={loginEmailErr}
- keyboardType="email-address"
- autoCapitalize="none"
- autoCorrect={false}
- />
-
-
- {loginGlobalErr ? {loginGlobalErr} : null}
-
-
- {loginLoading ? : Se connecter}
-
-
-
- Pas encore de compte ?
- slideTo('register')}>
- S'inscrire
-
-
-
-
- {/* โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- PANEL REGISTER
- โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */}
-
- slideTo('login')}>
-
-
-
-
-
-
- Crรฉer un compte
- Rejoignez la communautรฉ Athly
-
-
- { setRegName(v); if (regNameErr) setRegNameErr(''); }}
- error={regNameErr}
- />
- { setRegEmail(v); if (regEmailErr) validateRegEmail(v); }}
- onBlur={() => validateRegEmail()}
- error={regEmailErr}
- keyboardType="email-address"
- autoCapitalize="none"
- autoCorrect={false}
- />
- {
- setRegPassword(v);
- if (regPwdErr) setRegPwdErr(v.length >= 6 ? '' : '6 caractรจres minimum');
- }}
- isPassword
- secureTextEntry={!regShowPwd}
- showPassword={regShowPwd}
- setShowPassword={setRegShowPwd}
- error={regPwdErr}
- />
-
- {regGlobalErr ? {regGlobalErr} : null}
-
-
- {regLoading ? : Crรฉer mon compte}
-
-
-
- Dรฉjร inscrit ?
- slideTo('login')}>
- Se connecter
-
-
-
-
-
-
-
-
- );
-}
-
-const styles = StyleSheet.create({
- safeArea: {
- flex: 1,
- backgroundColor: Colors.backgroundDeep,
- },
- kav: {
- flex: 1,
- backgroundColor: Colors.backgroundDeep,
- },
- clipper: {
- flex: 1,
- overflow: 'hidden',
- backgroundColor: Colors.backgroundDeep,
- },
- slider: {
- flex: 1,
- flexDirection: 'row',
- width: W * 2,
- },
- panel: {
- width: W,
- backgroundColor: Colors.backgroundDeep,
- },
- panelContent: {
- paddingHorizontal: 28,
- paddingTop: 20,
- paddingBottom: 56,
- },
-
- // โโ Logo Login (image brute, dimensions explicites) โโโโโโ
- logoImg: {
- width: 180,
- height: 120,
- alignSelf: 'center',
- marginBottom: 30,
- marginTop: 4,
- },
- // โโ Logo Register (icรดne discrรจte) โโโโโโโโโโโโโโโโโโโโโโโ
- logoSmall: {
- width: 26,
- height: 26,
- alignSelf: 'flex-start',
- marginBottom: 20,
- opacity: 0.6,
- },
-
- // โโ Typographie โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- titleBlock: {
- marginBottom: 32,
- },
- brand: {
- color: Colors.textPrimary,
- fontSize: 30,
- fontWeight: '700',
- letterSpacing: -0.5,
- marginBottom: 6,
- },
- tagline: {
- color: Colors.textMuted,
- fontSize: 14,
- letterSpacing: 0.2,
- },
-
- // โโ Erreur globale โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- globalError: {
- color: Colors.error,
- fontSize: 13,
- textAlign: 'center',
- marginTop: 8,
- marginBottom: 4,
- },
-
- // โโ Bouton principal โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- primaryBtn: {
- backgroundColor: Colors.primary,
- height: 56,
- borderRadius: 14,
- justifyContent: 'center',
- alignItems: 'center',
- marginTop: 24,
- shadowColor: Colors.primary,
- shadowOffset: { width: 0, height: 8 },
- shadowOpacity: 0.4,
- shadowRadius: 16,
- elevation: 8,
- },
- btnText: {
- color: '#fff',
- fontSize: 16,
- fontWeight: '700',
- letterSpacing: 0.4,
- },
-
- // โโ Lien de bascule โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- switchRow: {
- flexDirection: 'row',
- justifyContent: 'center',
- marginTop: 36,
- },
- switchLabel: {
- color: Colors.textMuted,
- fontSize: 14,
- },
- linkBold: {
- color: Colors.primary,
- fontWeight: '700',
- fontSize: 14,
- },
-
- // โโ Retour โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- backBtn: {
- width: 36,
- height: 36,
- justifyContent: 'center',
- marginBottom: 8,
- },
-});
diff --git a/front/src/screens/Auth/EmailVerificationScreen.js b/front/src/screens/Auth/EmailVerificationScreen.js
index 24b85cf..8c08329 100644
--- a/front/src/screens/Auth/EmailVerificationScreen.js
+++ b/front/src/screens/Auth/EmailVerificationScreen.js
@@ -8,8 +8,8 @@ import { SafeAreaView } from 'react-native-safe-area-context';
import { Ionicons } from '@expo/vector-icons';
import { Colors } from '../../constants/theme';
-import NotificationBanner from '../../components/common/NotificationBanner';
-import { verifyEmail, resendVerificationEmail } from '../../services/auth.service';
+import { NotificationBanner } from '../../components/common';
+import { verifyEmail, resendVerificationEmail } from '../../services';
import { useAuth } from '../../context/AuthContext';
const CODE_LENGTH = 6;
diff --git a/front/src/screens/Auth/ForgotPasswordScreen.js b/front/src/screens/Auth/ForgotPasswordScreen.js
index 3057934..bd3d90a 100644
--- a/front/src/screens/Auth/ForgotPasswordScreen.js
+++ b/front/src/screens/Auth/ForgotPasswordScreen.js
@@ -9,8 +9,8 @@ import { Ionicons } from '@expo/vector-icons';
import { Colors } from '../../constants/theme';
import AuthInput from '../../components/inputs/AuthInput';
-import NotificationBanner from '../../components/common/NotificationBanner';
-import { forgotPassword, resetPassword } from '../../services/auth.service';
+import { NotificationBanner } from '../../components/common';
+import { forgotPassword, resetPassword } from '../../services';
import { useToast } from '../../context/ToastContext';
const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
@@ -33,7 +33,7 @@ function getStrength(pwd) {
function StrengthBar({ password }) {
const score = getStrength(password);
if (!password) return null;
- const color = score === 3 ? '#44FF88' : score === 2 ? '#FFA500' : '#FF4D4D';
+ const color = score === 3 ? Colors.success : score === 2 ? '#FFA500' : Colors.error;
const label = score === 3 ? 'Fort' : score === 2 ? 'Moyen' : 'Faible';
return (
@@ -134,7 +134,7 @@ function WeakPasswordModal({ visible, onImprove, onSave, loading }) {
-
+
Mot de passe simple
@@ -182,7 +182,7 @@ const wm = StyleSheet.create({
},
card: {
width: '100%',
- backgroundColor: '#13131C',
+ backgroundColor: Colors.bgDeep2,
borderRadius: 22,
borderWidth: 1,
borderColor: 'rgba(245,158,11,0.25)',
diff --git a/front/src/screens/Auth/LoginScreen.js b/front/src/screens/Auth/LoginScreen.js
index f535d91..e7b9b9f 100644
--- a/front/src/screens/Auth/LoginScreen.js
+++ b/front/src/screens/Auth/LoginScreen.js
@@ -9,10 +9,10 @@ import { Ionicons } from '@expo/vector-icons';
import { Colors } from '../../constants/theme';
import AuthInput from '../../components/inputs/AuthInput';
-import NotificationBanner from '../../components/common/NotificationBanner';
-import { login, googleLogin } from '../../services/auth.service';
+import { NotificationBanner } from '../../components/common';
+import { login, googleLogin } from '../../services';
import { useAuth } from '../../context/AuthContext';
-import { useGoogleAuth } from '../../hooks/useGoogleAuth';
+import { useGoogleAuth } from '../../hooks';
const LOGO_ORANGE = require('../../../assets/logo-orange.png');
const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
diff --git a/front/src/screens/Auth/RegisterScreen.js b/front/src/screens/Auth/RegisterScreen.js
index 04aed33..d9170a7 100644
--- a/front/src/screens/Auth/RegisterScreen.js
+++ b/front/src/screens/Auth/RegisterScreen.js
@@ -9,9 +9,9 @@ import { Ionicons } from '@expo/vector-icons';
import { Colors } from '../../constants/theme';
import AuthInput from '../../components/inputs/AuthInput';
-import NotificationBanner from '../../components/common/NotificationBanner';
-import { register } from '../../services/auth.service';
-import { haptics } from '../../services/haptics.service';
+import { NotificationBanner } from '../../components/common';
+import { register } from '../../services';
+import { haptics } from '../../services';
const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
const HAS_UPPER = /[A-Z]/;
@@ -32,7 +32,7 @@ function getStrength(pwd) {
function StrengthBar({ password }) {
const score = getStrength(password);
if (!password) return null;
- const color = score === 3 ? '#44FF88' : score === 2 ? '#FFA500' : '#FF4D4D';
+ const color = score === 3 ? Colors.success : score === 2 ? '#FFA500' : Colors.error;
const label = score === 3 ? 'Fort' : score === 2 ? 'Moyen' : 'Faible';
return (
@@ -76,7 +76,7 @@ function WeakPasswordModal({ visible, onImprove, onCreate, loading }) {
{/* Icรดne */}
-
+
Mot de passe simple
@@ -124,7 +124,7 @@ const wm = StyleSheet.create({
},
card: {
width: '100%',
- backgroundColor: '#13131C',
+ backgroundColor: Colors.bgDeep2,
borderRadius: 22,
borderWidth: 1,
borderColor: 'rgba(245,158,11,0.25)',
@@ -219,7 +219,7 @@ function PseudoRejectedModal({ visible, onClose }) {
-
+
Pseudo non autorisรฉ
@@ -251,7 +251,7 @@ const pm = StyleSheet.create({
},
card: {
width: '100%',
- backgroundColor: '#13131C',
+ backgroundColor: Colors.bgDeep2,
borderRadius: 22,
borderWidth: 1,
borderColor: 'rgba(239,68,68,0.35)',
@@ -271,11 +271,11 @@ const pm = StyleSheet.create({
},
title: { color: Colors.textPrimary, fontSize: 19, fontWeight: '800', letterSpacing: -0.3, marginBottom: 12, textAlign: 'center' },
body: { color: Colors.textSecondary, fontSize: 14, lineHeight: 21, textAlign: 'center', marginBottom: 14 },
- warning: { color: '#EF4444', fontSize: 12.5, fontWeight: '700', lineHeight: 18, textAlign: 'center', marginBottom: 24 },
+ warning: { color: Colors.destructive, fontSize: 12.5, fontWeight: '700', lineHeight: 18, textAlign: 'center', marginBottom: 24 },
closeBtn: {
width: '100%', height: 50, borderRadius: 13,
- backgroundColor: '#EF4444', justifyContent: 'center', alignItems: 'center',
- shadowColor: '#EF4444', shadowOffset: { width: 0, height: 6 }, shadowOpacity: 0.35, shadowRadius: 12, elevation: 6,
+ backgroundColor: Colors.destructive, justifyContent: 'center', alignItems: 'center',
+ shadowColor: Colors.destructive, shadowOffset: { width: 0, height: 6 }, shadowOpacity: 0.35, shadowRadius: 12, elevation: 6,
},
closeTxt: { color: '#fff', fontSize: 15, fontWeight: '700', letterSpacing: 0.2 },
});
diff --git a/front/src/screens/Home/HomeScreen.js b/front/src/screens/Home/HomeScreen.js
index a5117ff..35ce1b1 100644
--- a/front/src/screens/Home/HomeScreen.js
+++ b/front/src/screens/Home/HomeScreen.js
@@ -19,7 +19,7 @@ import {
recommendNextMuscleGroup,
aggregateGlobal,
xpToLevel,
-} from '../../services/stats.service';
+} from '../../services';
import { findMuscleGroup } from '../../constants/exerciseFilters';
import {
TEMPLATES,
@@ -35,7 +35,7 @@ import DailyQuestsCard from '../../components/home/DailyQuestsCard';
import RecoveryRitualsCard from '../../components/home/RecoveryRitualsCard';
import TutorialOverlay from '../../components/tutorial/TutorialOverlay';
import { useTutorial, useTutorialTarget } from '../../context/TutorialContext';
-import { useDevSettings } from '../../hooks/useDevSettings';
+import { useDevSettings } from '../../hooks';
import { MOCK_TUTORIAL_LOGS } from '../../data/mockTutorialStats';
// รcran d'accueil. Bascule entre EmptyHomeState (compte vierge) et รฉtat actif
@@ -189,7 +189,7 @@ export default function HomeScreen({ navigation }) {
{isTutorialDashboard && (
-
+
Donnรฉes de dรฉmonstration - disparaรฎtront ร la fin du chapitre
)}
@@ -340,7 +340,7 @@ const styles = StyleSheet.create({
borderBottomWidth: 1, borderBottomColor: 'rgba(255,215,0,0.20)',
paddingHorizontal: 16, paddingVertical: 8,
},
- mockBannerText: { color: '#FFD700', fontSize: 11, fontWeight: '600', flex: 1 },
+ mockBannerText: { color: Colors.gold, fontSize: 11, fontWeight: '600', flex: 1 },
scroll: { flex: 1 },
scrollContent: {
paddingHorizontal: 20,
diff --git a/front/src/screens/Profile/EditProfileScreen.js b/front/src/screens/Profile/EditProfileScreen.js
index 841c202..e45a0ba 100644
--- a/front/src/screens/Profile/EditProfileScreen.js
+++ b/front/src/screens/Profile/EditProfileScreen.js
@@ -10,10 +10,10 @@ import {
} from 'react-native';
import { Colors } from '../../constants/theme';
-import API from '../../api/api';
+import { updateMyProfile } from '../../services';
import { useUser } from '../../context/UserContext';
import { useToast } from '../../context/ToastContext';
-import { setBirthdate } from '../../services/reward.service';
+import { setBirthdate } from '../../services';
import BirthdatePicker from '../../components/profile/BirthdatePicker';
// โโโ EditProfileScreen โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
@@ -107,8 +107,8 @@ export default function EditProfileScreen({ navigation }) {
...(parsedRythme !== null && { rythme: parsedRythme }),
};
try {
- const res = await API.put('/users/me', payload);
- if (res.data.success) {
+ const res = await updateMyProfile(payload);
+ if (res.success) {
refetchUser(); // met ร jour le UserContext โ ProfileScreen + SettingsScreen sync instantanรฉ
showToast('Profil mis ร jour avec succรจs !', 'success');
navigation.goBack();
@@ -351,7 +351,7 @@ const GRP_BORDER = 'rgba(255,255,255,0.09)';
const SEP = 'rgba(255,255,255,0.07)';
const styles = StyleSheet.create({
- root: { flex: 1, backgroundColor: '#080910' },
+ root: { flex: 1, backgroundColor: Colors.bgAbyss },
scroll: { flex: 1 },
scrollContent: { paddingHorizontal: 16, paddingTop: 8 },
diff --git a/front/src/screens/Profile/InventoryScreen.js b/front/src/screens/Profile/InventoryScreen.js
index 6796bdc..73a2a3f 100644
--- a/front/src/screens/Profile/InventoryScreen.js
+++ b/front/src/screens/Profile/InventoryScreen.js
@@ -9,12 +9,12 @@ import { Colors } from '../../constants/theme';
import { useUser } from '../../context/UserContext';
import { useWorkoutLogs } from '../../context/WorkoutLogsContext';
import { useToast } from '../../context/ToastContext';
-import { openChest, useItem, claimUniqueItem, ITEM_CATALOG, RARITY_META } from '../../services/inventory.service';
-import { xpForLevel, xpToLevel } from '../../services/stats.service';
-import { useAvatarFrame } from '../../hooks/useAvatarFrame';
-import { useDevSettings } from '../../hooks/useDevSettings';
+import { openChest, useItem, claimUniqueItem, ITEM_CATALOG, RARITY_META } from '../../services';
+import { xpForLevel, xpToLevel } from '../../services';
+import { useAvatarFrame } from '../../hooks';
+import { useDevSettings } from '../../hooks';
import ChestOpeningModal from '../../components/inventory/ChestOpeningModal';
-import { haptics } from '../../services/haptics.service';
+import { haptics } from '../../services';
import TutorialOverlay from '../../components/tutorial/TutorialOverlay';
import { useTutorial, useTutorialTarget } from '../../context/TutorialContext';
diff --git a/front/src/screens/Profile/ProfileScreen.js b/front/src/screens/Profile/ProfileScreen.js
index 337a4b8..01f4864 100644
--- a/front/src/screens/Profile/ProfileScreen.js
+++ b/front/src/screens/Profile/ProfileScreen.js
@@ -23,16 +23,16 @@ import {
xpToLevel,
computeStreak,
getRank,
-} from '../../services/stats.service';
+} from '../../services';
import { resolveExerciseMeta } from '../../data/majorExercises';
-import { getMyRecords } from '../../services/social.service';
-import { getTitles } from '../../services/title.service';
-import { haptics } from '../../services/haptics.service';
-import { RARITY_META } from '../../services/inventory.service';
-import { syncLocalAchievements } from '../../services/reward.service';
-import { useAvatarFrame } from '../../hooks/useAvatarFrame';
-import { useDevSettings } from '../../hooks/useDevSettings';
-import { useFeaturedTrophies } from '../../hooks/useFeaturedTrophies';
+import { getMyRecords } from '../../services';
+import { getTitles } from '../../services';
+import { haptics } from '../../services';
+import { RARITY_META } from '../../services';
+import { syncLocalAchievements } from '../../services';
+import { useAvatarFrame } from '../../hooks';
+import { useDevSettings } from '../../hooks';
+import { useFeaturedTrophies } from '../../hooks';
import { getTheme } from '../../data/profileThemes';
import { evaluateTrophies, ULTIMATE_TROPHY } from '../../data/trophyCatalog';
import TutorialOverlay from '../../components/tutorial/TutorialOverlay';
@@ -296,7 +296,7 @@ export default function ProfileScreen({ navigation }) {
/>
diff --git a/front/src/screens/Profile/RankRoadmapScreen.js b/front/src/screens/Profile/RankRoadmapScreen.js
index d03c56d..2545907 100644
--- a/front/src/screens/Profile/RankRoadmapScreen.js
+++ b/front/src/screens/Profile/RankRoadmapScreen.js
@@ -10,7 +10,7 @@ import { SafeAreaView } from 'react-native-safe-area-context';
import { Ionicons } from '@expo/vector-icons';
import { LinearGradient } from 'expo-linear-gradient';
import { Colors } from '../../constants/theme';
-import { getRank, xpToLevel } from '../../services/stats.service';
+import { xpToLevel } from '../../services';
import { useWorkoutLogs } from '../../context/WorkoutLogsContext';
import { FRAME_DEFS } from '../../components/profile/AvatarFrame';
@@ -22,7 +22,7 @@ const RANKS = [
levelRange: '0 โ 10',
minLevel: 0,
maxLevel: 10,
- color: '#FE7439',
+ color: Colors.primary,
gradientColors: ['#431A08', '#7C2D12'],
frameId: 'none',
perks: [
@@ -53,7 +53,7 @@ const RANKS = [
levelRange: '31 โ 50',
minLevel: 31,
maxLevel: 50,
- color: '#22C55E',
+ color: Colors.valid,
gradientColors: ['#052E16', '#14532D'],
frameId: 'silver',
perks: [
@@ -82,7 +82,7 @@ const RANKS = [
levelRange: '71 โ 90',
minLevel: 71,
maxLevel: 90,
- color: '#6E6AF0',
+ color: Colors.secondaryAccent,
gradientColors: ['#1E1B4B', '#2E2A7A'],
frameId: 'warrior',
perks: [
@@ -96,7 +96,7 @@ const RANKS = [
levelRange: '91 โ 110',
minLevel: 91,
maxLevel: 110,
- color: '#6E6AF0',
+ color: Colors.secondaryAccent,
gradientColors: ['#0F0F1A', '#1C1C38'],
frameId: 'elite',
perks: [
@@ -111,7 +111,7 @@ const RANKS = [
levelRange: '111 โ 140',
minLevel: 111,
maxLevel: 140,
- color: '#8B5CF6',
+ color: Colors.rankViolet,
gradientColors: ['#2E1065', '#4C1D95'],
frameId: 'master',
perks: [
@@ -125,7 +125,7 @@ const RANKS = [
levelRange: '141 โ 170',
minLevel: 141,
maxLevel: 170,
- color: '#A855F7',
+ color: Colors.rankPurple,
gradientColors: ['#3B0764', '#581C87'],
frameId: 'grandmaster',
perks: [
@@ -139,7 +139,7 @@ const RANKS = [
levelRange: '171 โ 199',
minLevel: 171,
maxLevel: 199,
- color: '#C084FC',
+ color: Colors.legendAccent,
gradientColors: ['#4A044E', '#701A75'],
frameId: 'legend',
perks: [
@@ -156,7 +156,7 @@ const RANKS = [
levelRange: 'Niv. 200',
minLevel: 200,
maxLevel: 200,
- color: '#FFD700',
+ color: Colors.gold,
gradientColors: ['#431407', '#78350F'],
frameId: 'god',
perks: [
diff --git a/front/src/screens/Profile/SettingsScreen.js b/front/src/screens/Profile/SettingsScreen.js
index 4511499..d3326fa 100644
--- a/front/src/screens/Profile/SettingsScreen.js
+++ b/front/src/screens/Profile/SettingsScreen.js
@@ -6,29 +6,29 @@ import {
import { Ionicons } from '@expo/vector-icons';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { Colors } from '../../constants/theme';
-import ConfirmModal from '../../components/common/ConfirmModal';
-import InfoModal from '../../components/common/InfoModal';
+import { ConfirmModal } from '../../components/common';
+import { InfoModal } from '../../components/common';
import { useAuth } from '../../context/AuthContext';
import { useUser } from '../../context/UserContext';
import { useToast } from '../../context/ToastContext';
import { useSavedWorkouts } from '../../context/SavedWorkoutsContext';
import { useQuests } from '../../context/QuestContext';
import { useCustomExercises } from '../../context/CustomExercisesContext';
-import { deleteAccount } from '../../services/auth.service';
+import { deleteAccount } from '../../services';
import { useWorkoutLogs } from '../../context/WorkoutLogsContext';
import {
xpToLevel, xpForLevel, computeStreak,
debugAddXP, debugSetLevel, debugAddSessions,
debugSimulateReps, debugSetStreak, debugClearDebugLogs,
debugResetDailyXP,
-} from '../../services/stats.service';
+} from '../../services';
import { PROFILE_THEMES, isThemeLocked } from '../../data/profileThemes';
import { TROPHY_CATALOG, TROPHY_CATEGORIES, ULTIMATE_TROPHY, evaluateTrophies } from '../../data/trophyCatalog';
import { COLLECTION_CATEGORY, BACKEND_CATEGORY_MAP } from '../../data/backendTrophyCategories';
-import { getAchievements } from '../../services/reward.service';
+import { getAchievements } from '../../services';
import { normalizeAchievement } from '../../components/profile/AchievementShowcase';
import { TrophyIcon } from '../../components/profile/TrophySlot';
-import { useDevSettings } from '../../hooks/useDevSettings';
+import { useDevSettings } from '../../hooks';
import { useFocusEffect } from '@react-navigation/native';
import TutorialOverlay from '../../components/tutorial/TutorialOverlay';
import { useTutorial, useTutorialTarget } from '../../context/TutorialContext';
@@ -38,13 +38,13 @@ import {
fireTestNotification,
scheduleDailyReminder,
cancelDailyReminder,
-} from '../../services/notificationService';
+} from '../../services';
import {
syncBackendLevel, giveChests, generateMockSocial, giveAllItems,
simulateChestsOpened, simulateReferral, simulateBirthday,
simulateGroup, simulateActivityEvent, simulateStreakBreak, simulateShakeSelf,
simulateSearchableFriend, simulateLobbyInvite, giveAllTitles,
-} from '../../services/debug.service';
+} from '../../services';
const UNIT_WEIGHT_KEY = 'athly:unit:weight:v1';
const UNIT_DIST_KEY = 'athly:unit:distance:v1';
@@ -1208,7 +1208,7 @@ export default function SettingsScreen({ navigation }) {
{/* โโโ Suppression dรฉfinitive du compte โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ */}
setDeleteModal1(true)} activeOpacity={0.7}>
-
+
Supprimer le compte
@@ -1224,7 +1224,7 @@ export default function SettingsScreen({ navigation }) {
-
+
Bienvenue ร bord !
@@ -1254,7 +1254,7 @@ export default function SettingsScreen({ navigation }) {
-
+
รtes-vous sรปr ?
@@ -1280,7 +1280,7 @@ export default function SettingsScreen({ navigation }) {
-
+
Confirmation finale
@@ -1440,7 +1440,7 @@ function DevBtn({ label, onPress, disabled, variant = 'default', flex, fullWidth
// โโโ Styles โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
const styles = StyleSheet.create({
- root: { flex: 1, backgroundColor: '#080910' },
+ root: { flex: 1, backgroundColor: Colors.bgAbyss },
scroll: { flex: 1 },
scrollContent: { paddingHorizontal: 16, paddingTop: 8 },
@@ -1482,7 +1482,7 @@ const styles = StyleSheet.create({
themeItemSel: { backgroundColor: 'rgba(255,255,255,0.07)', borderColor: 'rgba(255,255,255,0.25)' },
themeItemLocked: { opacity: 0.35 },
themeSwatch: { width: 32, height: 32, borderRadius: 16, marginBottom: 5 },
- themeCheck: { position: 'absolute', top: 6, right: 6, width: 14, height: 14, borderRadius: 7, backgroundColor: Colors.primary, alignItems: 'center', justifyContent: 'center', borderWidth: 1.5, borderColor: '#13131C' },
+ themeCheck: { position: 'absolute', top: 6, right: 6, width: 14, height: 14, borderRadius: 7, backgroundColor: Colors.primary, alignItems: 'center', justifyContent: 'center', borderWidth: 1.5, borderColor: Colors.bgDeep2 },
themeLockOverlay:{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, alignItems: 'center', justifyContent: 'center' },
themeLabel: { color: Colors.textMuted, fontSize: 8, fontWeight: '600', textAlign: 'center' },
themeLabelLocked:{ color: Colors.textMuted },
@@ -1546,7 +1546,7 @@ const styles = StyleSheet.create({
devBtnTextDestructive: { color: Colors.error },
devBtnTextDim: { color: Colors.textMuted },
devBtnTextOrange: { color: '#FF6B00' },
- devBtnTextViolet: { color: '#8B5CF6' },
+ devBtnTextViolet: { color: Colors.rankViolet },
devBtnTextDisabled: { color: Colors.textMuted },
devHint: { color: 'rgba(255,215,0,0.40)', fontSize: 10, fontWeight: '500', lineHeight: 14, marginTop: -4 },
@@ -1605,16 +1605,16 @@ const styles = StyleSheet.create({
// โโ Welcome modal (fin tutoriel) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
welcomeCard: { borderColor: 'rgba(110,106,240,0.30)' },
welcomeIconWrap:{ width: 64, height: 64, borderRadius: 20, backgroundColor: 'rgba(110,106,240,0.12)', borderWidth: 1, borderColor: 'rgba(110,106,240,0.30)', justifyContent: 'center', alignItems: 'center', marginBottom: 18 },
- welcomeBtn: { flexDirection: 'row', alignItems: 'center', width: '100%', height: 50, borderRadius: 13, backgroundColor: '#6E6AF0', justifyContent: 'center', marginBottom: 10, shadowColor: '#6E6AF0', shadowOffset: { width: 0, height: 6 }, shadowOpacity: 0.40, shadowRadius: 12, elevation: 6 },
+ welcomeBtn: { flexDirection: 'row', alignItems: 'center', width: '100%', height: 50, borderRadius: 13, backgroundColor: Colors.secondaryAccent, justifyContent: 'center', marginBottom: 10, shadowColor: Colors.secondaryAccent, shadowOffset: { width: 0, height: 6 }, shadowOpacity: 0.40, shadowRadius: 12, elevation: 6 },
welcomeBtnTxt: { color: '#fff', fontSize: 15, fontWeight: '700', letterSpacing: 0.2 },
// โโ Delete Account button โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
deleteAccountBtn: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', marginTop: 12, paddingVertical: 14, borderRadius: 14, borderWidth: 1, borderColor: 'rgba(239,68,68,0.15)', backgroundColor: 'transparent' },
- deleteAccountText: { color: '#FF4D4D', fontSize: 13, fontWeight: '600', opacity: 0.75 },
+ deleteAccountText: { color: Colors.error, fontSize: 13, fontWeight: '600', opacity: 0.75 },
// โโ Delete Account Modals โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
dmBackdrop: { flex: 1, backgroundColor: 'rgba(0,0,0,0.82)', justifyContent: 'center', alignItems: 'center', paddingHorizontal: 24 },
- dmCard: { width: '100%', backgroundColor: '#13131C', borderRadius: 22, borderWidth: 1, borderColor: 'rgba(239,68,68,0.22)', padding: 28, alignItems: 'center', shadowColor: '#000', shadowOffset: { width: 0, height: 16 }, shadowOpacity: 0.65, shadowRadius: 32, elevation: 20 },
+ dmCard: { width: '100%', backgroundColor: Colors.bgDeep2, borderRadius: 22, borderWidth: 1, borderColor: 'rgba(239,68,68,0.22)', padding: 28, alignItems: 'center', shadowColor: '#000', shadowOffset: { width: 0, height: 16 }, shadowOpacity: 0.65, shadowRadius: 32, elevation: 20 },
dmCardFinal: { borderColor: 'rgba(220,38,38,0.35)' },
dmIconWrap: { width: 60, height: 60, borderRadius: 18, backgroundColor: 'rgba(239,68,68,0.10)', borderWidth: 1, borderColor: 'rgba(239,68,68,0.25)', justifyContent: 'center', alignItems: 'center', marginBottom: 18 },
dmIconFinalWrap:{ width: 60, height: 60, borderRadius: 18, backgroundColor: 'rgba(220,38,38,0.14)', borderWidth: 1, borderColor: 'rgba(220,38,38,0.35)', justifyContent: 'center', alignItems: 'center', marginBottom: 18 },
@@ -1623,7 +1623,7 @@ const styles = StyleSheet.create({
dmKeepBtn: { flexDirection: 'row', alignItems: 'center', width: '100%', height: 50, borderRadius: 13, backgroundColor: Colors.primary, justifyContent: 'center', marginBottom: 10, shadowColor: Colors.primary, shadowOffset: { width: 0, height: 6 }, shadowOpacity: 0.35, shadowRadius: 12, elevation: 6 },
dmKeepTxt: { color: '#fff', fontSize: 15, fontWeight: '700', letterSpacing: 0.2 },
dmContinueBtn: { width: '100%', height: 44, borderRadius: 13, justifyContent: 'center', alignItems: 'center' },
- dmContinueTxt: { color: '#EF4444', fontSize: 14, fontWeight: '600' },
+ dmContinueTxt: { color: Colors.destructive, fontSize: 14, fontWeight: '600' },
dmDestroyBtn: { flexDirection: 'row', alignItems: 'center', width: '100%', height: 50, borderRadius: 13, backgroundColor: '#DC2626', justifyContent: 'center', marginBottom: 10, shadowColor: '#DC2626', shadowOffset: { width: 0, height: 6 }, shadowOpacity: 0.45, shadowRadius: 12, elevation: 6 },
dmDestroyTxt: { color: '#fff', fontSize: 15, fontWeight: '700', letterSpacing: 0.2 },
dmBackBtn: { width: '100%', height: 44, borderRadius: 13, justifyContent: 'center', alignItems: 'center' },
diff --git a/front/src/screens/Profile/TrophyRoomScreen.js b/front/src/screens/Profile/TrophyRoomScreen.js
index 0b25209..90f73c4 100644
--- a/front/src/screens/Profile/TrophyRoomScreen.js
+++ b/front/src/screens/Profile/TrophyRoomScreen.js
@@ -17,7 +17,7 @@ import {
TROPHY_FILTER_TABS,
evaluateTrophies,
} from '../../data/trophyCatalog';
-import { useFeaturedTrophies, MAX_FEATURED } from '../../hooks/useFeaturedTrophies';
+import { useFeaturedTrophies } from '../../hooks/useFeaturedTrophies';
import { useFocusEffect } from '@react-navigation/native';
import TutorialOverlay from '../../components/tutorial/TutorialOverlay';
import { useTutorial, useTutorialTarget } from '../../context/TutorialContext';
@@ -100,7 +100,7 @@ function UltimateTile({ unlocked, unlockedCount, totalCount, onPress }) {
styles.ultimateTile,
unlocked && {
borderColor: 'rgba(255,215,0,0.6)',
- borderTopColor: '#FFD700',
+ borderTopColor: Colors.gold,
},
{ transform: [{ scale }] },
]}>
@@ -118,7 +118,7 @@ function UltimateTile({ unlocked, unlockedCount, totalCount, onPress }) {
{/* Icon */}
{unlocked ? (
-
+
{ULTIMATE_TROPHY.label}
@@ -150,7 +150,7 @@ function UltimateTile({ unlocked, unlockedCount, totalCount, onPress }) {
{unlocked && (
-
+
DIAMOND ยท ULTIME
)}
@@ -322,9 +322,9 @@ export default function TrophyRoomScreen({ navigation }) {
-
+
- Trophรฉe Ultime
+ Trophรฉe Ultime
{ultimateUnlocked ? '1/1' : '0/1'}
@@ -568,7 +568,7 @@ const styles = StyleSheet.create({
paddingHorizontal: 8, paddingVertical: 4, alignSelf: 'flex-start',
borderWidth: 1, borderColor: 'rgba(255,215,0,0.35)',
},
- ultimateBadgeText: { color: '#FFD700', fontSize: 9, fontWeight: '900', letterSpacing: 1 },
+ ultimateBadgeText: { color: Colors.gold, fontSize: 9, fontWeight: '900', letterSpacing: 1 },
ultimateHint: { color: 'rgba(255,255,255,0.2)', fontSize: 10, fontWeight: '500', textAlign: 'center', marginTop: 12 },
// โโ Tile โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
diff --git a/front/src/screens/Social/FriendProfileScreen.js b/front/src/screens/Social/FriendProfileScreen.js
index d86931d..6e738fb 100644
--- a/front/src/screens/Social/FriendProfileScreen.js
+++ b/front/src/screens/Social/FriendProfileScreen.js
@@ -8,9 +8,9 @@ import { LinearGradient } from 'expo-linear-gradient';
import { Ionicons } from '@expo/vector-icons';
import { Colors } from '../../constants/theme';
import { useToast } from '../../context/ToastContext';
-import { getRank, xpToLevel } from '../../services/stats.service';
-import { getFriendProfile, getMyGroup, shakeMember } from '../../services/social.service';
-import { RARITY_META } from '../../services/inventory.service';
+import { getRank, xpToLevel } from '../../services';
+import { getFriendProfile, getMyGroup, shakeMember } from '../../services';
+import { RARITY_META } from '../../services';
import HeroLevelCard from '../../components/profile/HeroLevelCard';
import StreakBadge from '../../components/profile/StreakBadge';
@@ -174,7 +174,7 @@ export default function FriendProfileScreen({ route, navigation }) {
/>
diff --git a/front/src/screens/Social/SocialScreen.js b/front/src/screens/Social/SocialScreen.js
index 23289c3..ac12fd4 100644
--- a/front/src/screens/Social/SocialScreen.js
+++ b/front/src/screens/Social/SocialScreen.js
@@ -9,7 +9,7 @@ import { Colors } from '../../constants/theme';
import { useToast } from '../../context/ToastContext';
import { useUser } from '../../context/UserContext';
import { useWorkoutLogs } from '../../context/WorkoutLogsContext';
-import ConfirmModal from '../../components/common/ConfirmModal';
+import { ConfirmModal } from '../../components/common';
import FriendshipLevelUpModal from '../../components/social/FriendshipLevelUpModal';
import FriendPreviewModal from '../../components/social/FriendPreviewModal';
import AddFriendModal from '../../components/social/AddFriendModal';
@@ -18,7 +18,7 @@ import {
cancelFriendRequest, removeFriend,
getFriendsList, getPendingRequests, getLeaderboard, getExerciseLeaderboard,
getMyGroup, inviteToGroup, respondToGroupInvite, shakeMember, checkGroupStreak, leaveGroup,
-} from '../../services/social.service';
+} from '../../services';
import { MAJOR_EXERCISES } from '../../data/majorExercises';
import ExercisePickerModal from '../../components/social/ExercisePickerModal';
import TutorialOverlay from '../../components/tutorial/TutorialOverlay';
@@ -26,7 +26,7 @@ import { useTutorial, useTutorialTarget } from '../../context/TutorialContext';
// Podium / classements : positions 1-3 affichรฉes en mรฉdaille colorรฉe plutรดt
// qu'en emoji ๐ฅ๐ฅ๐ฅ.
-const MEDAL_COLORS = { 1: '#FFD700', 2: '#C0C0C0', 3: '#CD7F32' };
+const MEDAL_COLORS = { 1: Colors.gold, 2: '#C0C0C0', 3: '#CD7F32' };
function MedalBadge({ position, size = 16 }) {
const color = MEDAL_COLORS[position];
@@ -439,7 +439,7 @@ function FriendsSegment({
// โโโ Segment Classement โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
-const PODIUM_COLORS = ['#FFD700', '#C0C0C0', '#CD7F32'];
+const PODIUM_COLORS = [Colors.gold, '#C0C0C0', '#CD7F32'];
function LeaderboardSegment({ leaderboard }) {
const [mode, setMode] = useState('xp'); // 'xp' | 'records'
@@ -879,7 +879,7 @@ function AnimatedRow({ index, children }) {
// dans `member.weatherStatus`. Affichรฉ uniquement quand prรฉsent (les listes
// amis/requรชtes n'en portent pas โ seuls les membres de groupe en ont un).
const WEATHER_META = {
- done: { icon: 'checkmark-circle', color: '#22C55E', label: 'Sรฉance validรฉe' },
+ done: { icon: 'checkmark-circle', color: Colors.valid, label: 'Sรฉance validรฉe' },
active: { icon: 'flash', color: '#FBBF24', label: 'Sรฉance active' },
ready: { icon: 'flame', color: Colors.primary, label: 'Prรชt' },
sleeping: { icon: 'moon', color: Colors.textMuted, label: 'En sommeil' },
diff --git a/front/src/screens/Stats/StatsScreen.js b/front/src/screens/Stats/StatsScreen.js
index 6c49d54..2f8252d 100644
--- a/front/src/screens/Stats/StatsScreen.js
+++ b/front/src/screens/Stats/StatsScreen.js
@@ -8,8 +8,8 @@ import { useFocusEffect } from '@react-navigation/native';
import { Colors } from '../../constants/theme';
import { useWorkoutLogs } from '../../context/WorkoutLogsContext';
import { useUser } from '../../context/UserContext';
-import { aggregateGlobal } from '../../services/stats.service';
-import { getWeightHistory } from '../../services/weight.service';
+import { aggregateGlobal } from '../../services';
+import { getWeightHistory } from '../../services';
import PeriodSegmentedControl from '../../components/stats/PeriodSegmentedControl';
import VolumeBarChart from '../../components/stats/VolumeBarChart';
import MuscleDistributionPieChart from '../../components/stats/MuscleDistributionPieChart';
@@ -18,9 +18,9 @@ import WorkoutCalendar from '../../components/stats/WorkoutCalendar';
import XPProgressBar from '../../components/stats/XPProgressBar';
import WorkoutHistoryList from '../../components/stats/WorkoutHistoryList';
import TutorialOverlay from '../../components/tutorial/TutorialOverlay';
-import WeightEntryModal from '../../components/common/WeightEntryModal';
-import ConfirmModal from '../../components/common/ConfirmModal';
-import InfoModal from '../../components/common/InfoModal';
+import { WeightEntryModal } from '../../components/common';
+import { ConfirmModal } from '../../components/common';
+import { InfoModal } from '../../components/common';
import { useTutorial, useTutorialTarget } from '../../context/TutorialContext';
import { MOCK_TUTORIAL_LOGS } from '../../data/mockTutorialStats';
@@ -189,7 +189,7 @@ export default function StatsScreen({ navigation }) {
{/* Bandeau mock data */}
{activeChapterId === 'stats' && (
-
+
Donnรฉes de dรฉmonstration - disparaรฎtront ร la fin du chapitre
)}
@@ -368,7 +368,7 @@ const styles = StyleSheet.create({
borderBottomWidth: 1, borderBottomColor: 'rgba(255,215,0,0.25)',
paddingHorizontal: 16, paddingVertical: 8,
},
- mockBannerText: { color: '#FFD700', fontSize: 11, fontWeight: '600', flex: 1 },
+ mockBannerText: { color: Colors.gold, fontSize: 11, fontWeight: '600', flex: 1 },
header: { marginBottom: 16 },
title: { color: Colors.textPrimary, fontSize: 26, fontWeight: '900', letterSpacing: -0.5 },
diff --git a/front/src/screens/Workouts/CustomExercisesScreen.js b/front/src/screens/Workouts/CustomExercisesScreen.js
index 6ab409c..0e163b5 100644
--- a/front/src/screens/Workouts/CustomExercisesScreen.js
+++ b/front/src/screens/Workouts/CustomExercisesScreen.js
@@ -16,8 +16,8 @@ import {
secondaryMusclesLabels,
primaryEquipmentLabel,
} from '../../constants/exerciseFilters';
-import ConfirmModal from '../../components/common/ConfirmModal';
-import InfoModal from '../../components/common/InfoModal';
+import { ConfirmModal } from '../../components/common';
+import { InfoModal } from '../../components/common';
export default function CustomExercisesScreen({ navigation }) {
const { items, loading, remove } = useCustomExercises();
@@ -149,7 +149,7 @@ const styles = StyleSheet.create({
paddingTop: 40,
paddingBottom: 14,
borderBottomWidth: StyleSheet.hairlineWidth,
- borderBottomColor: '#1f1f27',
+ borderBottomColor: Colors.borderSubtle,
},
headerTitle: {
color: Colors.textPrimary,
@@ -174,7 +174,7 @@ const styles = StyleSheet.create({
width: 50,
height: 50,
borderRadius: 12,
- backgroundColor: '#0e0e12',
+ backgroundColor: Colors.cardInner,
alignItems: 'center',
justifyContent: 'center',
marginRight: 12,
diff --git a/front/src/screens/Workouts/EditExerciseScreen.js b/front/src/screens/Workouts/EditExerciseScreen.js
index bd0e057..e519796 100644
--- a/front/src/screens/Workouts/EditExerciseScreen.js
+++ b/front/src/screens/Workouts/EditExerciseScreen.js
@@ -17,8 +17,8 @@ import { EQUIPMENTS, LEVELS } from '../../constants/exerciseFilters';
import SelectableChip from '../../components/workouts/SelectableChip';
import MuscleHierarchyPicker from '../../components/workouts/MuscleHierarchyPicker';
import { useCustomExercises } from '../../context/CustomExercisesContext';
-import ConfirmModal from '../../components/common/ConfirmModal';
-import InfoModal from '../../components/common/InfoModal';
+import { ConfirmModal } from '../../components/common';
+import { InfoModal } from '../../components/common';
// Form add/edit d'un exercice perso. Alignรฉ sur la structure du catalogue (sous-muscles
// prรฉcis), donc directement compatible avec le Builder et l'algo de tri.
@@ -303,7 +303,7 @@ const styles = StyleSheet.create({
paddingTop: 40,
paddingBottom: 14,
borderBottomWidth: StyleSheet.hairlineWidth,
- borderBottomColor: '#1f1f27',
+ borderBottomColor: Colors.borderSubtle,
},
headerTitle: {
color: Colors.textPrimary,
diff --git a/front/src/screens/Workouts/ExerciseDetailScreen.js b/front/src/screens/Workouts/ExerciseDetailScreen.js
index 6aaee82..b8490e0 100644
--- a/front/src/screens/Workouts/ExerciseDetailScreen.js
+++ b/front/src/screens/Workouts/ExerciseDetailScreen.js
@@ -18,7 +18,7 @@ import {
primaryMuscleLabel,
secondaryMusclesLabels,
} from '../../constants/exerciseFilters';
-import useEffortTimer from '../../hooks/useEffortTimer';
+import { useEffortTimer } from '../../hooks';
import { useWorkoutInProgress } from '../../context/WorkoutInProgressContext';
import ExerciseHeader from '../../components/workouts/ExerciseHeader';
import SetTable from '../../components/workouts/SetTable';
diff --git a/front/src/screens/Workouts/ExerciseStatsScreen.js b/front/src/screens/Workouts/ExerciseStatsScreen.js
index c08cd4c..d2f51d4 100644
--- a/front/src/screens/Workouts/ExerciseStatsScreen.js
+++ b/front/src/screens/Workouts/ExerciseStatsScreen.js
@@ -10,7 +10,7 @@ import { SafeAreaView } from 'react-native-safe-area-context';
import { Ionicons } from '@expo/vector-icons';
import { Colors } from '../../constants/theme';
import { useWorkoutLogs } from '../../context/WorkoutLogsContext';
-import { aggregateExercise } from '../../services/stats.service';
+import { aggregateExercise } from '../../services';
import ExerciseStatsChart from '../../components/stats/ExerciseStatsChart';
import PRCard from '../../components/stats/PRCard';
diff --git a/front/src/screens/Workouts/ManualWorkoutCreatorScreen.js b/front/src/screens/Workouts/ManualWorkoutCreatorScreen.js
index a4ea4f2..1375d82 100644
--- a/front/src/screens/Workouts/ManualWorkoutCreatorScreen.js
+++ b/front/src/screens/Workouts/ManualWorkoutCreatorScreen.js
@@ -327,7 +327,7 @@ const styles = StyleSheet.create({
paddingTop: 40,
paddingBottom: 14,
borderBottomWidth: StyleSheet.hairlineWidth,
- borderBottomColor: '#1f1f27',
+ borderBottomColor: Colors.borderSubtle,
},
headerTitle: {
color: Colors.textPrimary,
@@ -403,7 +403,7 @@ const styles = StyleSheet.create({
width: 44,
height: 44,
borderRadius: 10,
- backgroundColor: '#0e0e12',
+ backgroundColor: Colors.cardInner,
alignItems: 'center',
justifyContent: 'center',
marginRight: 12,
@@ -457,7 +457,7 @@ const styles = StyleSheet.create({
stepper: {
flexDirection: 'row',
alignItems: 'center',
- backgroundColor: '#0e0e12',
+ backgroundColor: Colors.cardInner,
borderRadius: 10,
paddingHorizontal: 4,
},
@@ -475,7 +475,7 @@ const styles = StyleSheet.create({
textAlign: 'center',
},
repsInput: {
- backgroundColor: '#0e0e12',
+ backgroundColor: Colors.cardInner,
color: Colors.textPrimary,
fontSize: 14,
fontWeight: '700',
@@ -515,7 +515,7 @@ const styles = StyleSheet.create({
paddingBottom: 26,
backgroundColor: Colors.background,
borderTopWidth: StyleSheet.hairlineWidth,
- borderTopColor: '#1f1f27',
+ borderTopColor: Colors.borderSubtle,
},
saveBtn: {
flexDirection: 'row',
diff --git a/front/src/screens/Workouts/WorkoutBuilderScreen.js b/front/src/screens/Workouts/WorkoutBuilderScreen.js
index 2495497..a22d34d 100644
--- a/front/src/screens/Workouts/WorkoutBuilderScreen.js
+++ b/front/src/screens/Workouts/WorkoutBuilderScreen.js
@@ -281,7 +281,7 @@ const styles = StyleSheet.create({
paddingTop: 40,
paddingBottom: 14,
borderBottomWidth: StyleSheet.hairlineWidth,
- borderBottomColor: '#1f1f27',
+ borderBottomColor: Colors.borderSubtle,
},
headerTitle: {
color: Colors.textPrimary,
@@ -387,7 +387,7 @@ const styles = StyleSheet.create({
width: 38,
height: 38,
borderRadius: 9,
- backgroundColor: '#0e0e12',
+ backgroundColor: Colors.cardInner,
alignItems: 'center',
justifyContent: 'center',
marginRight: 10,
@@ -418,7 +418,7 @@ const styles = StyleSheet.create({
paddingBottom: 26,
backgroundColor: Colors.background,
borderTopWidth: StyleSheet.hairlineWidth,
- borderTopColor: '#1f1f27',
+ borderTopColor: Colors.borderSubtle,
},
launchBtn: {
flexDirection: 'row',
diff --git a/front/src/screens/Workouts/WorkoutScreen.js b/front/src/screens/Workouts/WorkoutScreen.js
index 1099541..82f548c 100644
--- a/front/src/screens/Workouts/WorkoutScreen.js
+++ b/front/src/screens/Workouts/WorkoutScreen.js
@@ -10,15 +10,15 @@ import {
} from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { Ionicons } from '@expo/vector-icons';
-import { haptics } from '../../services/haptics.service';
+import { haptics } from '../../services';
import { Colors } from '../../constants/theme';
-import useExerciseSorting, { isFiltering } from '../../hooks/useExerciseSorting';
+import { useExerciseSorting, isFiltering } from '../../hooks';
import { useWorkoutInProgress } from '../../context/WorkoutInProgressContext';
import { useWorkoutLogs } from '../../context/WorkoutLogsContext';
-import { useDevSettings } from '../../hooks/useDevSettings';
+import { useDevSettings } from '../../hooks';
-import API from '../../api/api';
+import { completeWorkout } from '../../services';
import SortBar from '../../components/workouts/SortBar';
import SupersetGroup from '../../components/workouts/SupersetGroup';
@@ -27,12 +27,12 @@ import InlineExerciseBlock from '../../components/workouts/InlineExerciseBlock';
import AddExerciseSheet from '../../components/workouts/AddExerciseSheet';
import WorkoutRecapModal from '../../components/workouts/WorkoutRecapModal';
import ShortSessionWarningModal from '../../components/workouts/ShortSessionWarningModal';
-import QuestToast from '../../components/common/QuestToast';
-import ConfirmModal from '../../components/common/ConfirmModal';
+import { QuestToast } from '../../components/common';
+import { ConfirmModal } from '../../components/common';
import LobbyMembersBar from '../../components/workouts/LobbyMembersBar';
import LobbyWaitingOverlay from '../../components/workouts/LobbyWaitingOverlay';
import MultiLootModal from '../../components/workouts/MultiLootModal';
-import { getLobby, finishLobby } from '../../services/lobby.service';
+import { getLobby, finishLobby } from '../../services';
const DEFAULT_FILTERS = { muscles: [], levels: [], equipment: [] };
@@ -266,7 +266,7 @@ export default function WorkoutScreen({ route, navigation }) {
const result = await actions.finalize({ notes: state.notes, durationSeconds: elapsed, ...opts });
if (state.id) {
- API.post(`/workouts/${state.id}/complete`).catch(() => {});
+ completeWorkout(state.id).catch(() => {});
}
const builtRecapData = {
diff --git a/front/src/services/exercise.service.js b/front/src/services/exercise.service.js
deleted file mode 100644
index 39400d8..0000000
--- a/front/src/services/exercise.service.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import API from "../api/api";
-
-export function getExerciseRecords(workoutId) {
- return API.get(`/exercise/workout/${workoutId}`);
-}
-
-export function addExerciseRecord(data) {
- return API.post("/exercise", data);
-}
\ No newline at end of file
diff --git a/front/src/services/index.js b/front/src/services/index.js
new file mode 100644
index 0000000..b08f3b8
--- /dev/null
+++ b/front/src/services/index.js
@@ -0,0 +1,25 @@
+// โโโ Barrel des services โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+// Point d'entrรฉe unique : `import { getFriendsList, haptics } from '../services';`
+// Tous les services n'exportent que des fonctions/constantes nommรฉes, sans
+// collision de noms (vรฉrifiรฉ) โ l'agrรฉgation en `export *` est donc sรปre.
+// Rรจgle : ne jamais importer ce barrel DEPUIS un fichier de ce dossier
+// (import direct entre services) pour exclure tout cycle de dรฉpendances.
+
+export * from './auth.service';
+export * from './customExercises.service';
+export * from './debug.service';
+export * from './haptics.service';
+export * from './inventory.service';
+export * from './lobby.service';
+export * from './notificationService';
+export * from './onboarding.service';
+export * from './profile.service';
+export * from './quest.service';
+export * from './reward.service';
+export * from './savedWorkouts.service';
+export * from './social.service';
+export * from './stats.service';
+export * from './title.service';
+export * from './weight.service';
+export * from './workouts.service';
+export * from './xpSync.service';
diff --git a/front/src/services/profile.service.js b/front/src/services/profile.service.js
index a5314ec..422ac04 100644
--- a/front/src/services/profile.service.js
+++ b/front/src/services/profile.service.js
@@ -1,5 +1,12 @@
import API from '../api/api';
+// Met ร jour les informations du profil (nom, mensurations, objectif, rythmeโฆ).
+// Renvoie la rรฉponse brute โ l'appelant gรจre le refetch du UserContext.
+export async function updateMyProfile(payload) {
+ const res = await API.put('/users/me', payload);
+ return res.data;
+}
+
// Synchronise le cadre de profil รฉquipรฉ (forme + couleur) vers le backend,
// pour qu'il soit visible sur le profil public par les amis (useAvatarFrame
// reste la source de vรฉritรฉ locale pour l'affichage instantanรฉ cรดtรฉ soi-mรชme).
diff --git a/front/src/services/workout.service.js b/front/src/services/workout.service.js
deleted file mode 100644
index a56a6a5..0000000
--- a/front/src/services/workout.service.js
+++ /dev/null
@@ -1,28 +0,0 @@
-import API from '../api/api';
-
-export async function getWorkouts() {
- try {
- const response = await API.get('/workouts');
- return response.data.workouts;
- } catch {
- return [];
- }
-}
-
-export async function getWorkout(id) {
- try {
- const response = await API.get(`/workouts/${id}`);
- return response.data.workout;
- } catch {
- return null;
- }
-}
-
-export async function createWorkout(workout) {
- try {
- const response = await API.post('/workouts', workout);
- return response.data.workout;
- } catch {
- return null;
- }
-}
diff --git a/front/src/services/workouts.service.js b/front/src/services/workouts.service.js
new file mode 100644
index 0000000..adb00b9
--- /dev/null
+++ b/front/src/services/workouts.service.js
@@ -0,0 +1,36 @@
+import API from '../api/api';
+
+// โโโ Cycle de vie des sรฉances cรดtรฉ backend โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
+// Draft (auto-save pendant la sรฉance) โ finalize (calcul XP/records serveur)
+// โ complete (marquage lรฉger fire-and-forget). Consommรฉ par useWorkoutState
+// (draft/finalize) et WorkoutScreen (complete) โ les รฉcrans et hooks ne
+// touchent jamais l'API brute directement.
+
+// Crรฉe le brouillon serveur de la sรฉance en cours (premiรจre sauvegarde).
+export async function createWorkoutDraft({ name, exercises, notes }) {
+ const res = await API.post('/workouts/draft', {
+ name, exercises, notes, status: 'draft',
+ });
+ const data = res && res.data ? res.data : res;
+ return data && data.workout ? data.workout : null;
+}
+
+// Met ร jour le brouillon serveur existant (auto-save dรฉbouncรฉ).
+export async function updateWorkoutDraft(id, { exercises, notes, durationSeconds }) {
+ const res = await API.patch(`/workouts/${id}/draft`, {
+ exercises, notes, durationSeconds,
+ });
+ const data = res && res.data ? res.data : res;
+ return data && data.workout ? data.workout : data;
+}
+
+// Finalise la sรฉance : le backend recalcule XP, records et stats (anti-cheat).
+export async function finalizeWorkout(id, payload) {
+ const res = await API.post(`/workouts/${id}/finalize`, payload);
+ return res && res.data ? res.data : res;
+}
+
+// Marque la sรฉance comme terminรฉe โ best-effort, jamais bloquant.
+export function completeWorkout(id) {
+ return API.post(`/workouts/${id}/complete`);
+}
diff --git a/front/src/styles/global.js b/front/src/styles/global.js
deleted file mode 100644
index e4fcf5e..0000000
--- a/front/src/styles/global.js
+++ /dev/null
@@ -1,131 +0,0 @@
-import { StyleSheet } from 'react-native';
-import { Colors } from '../constants/theme';
-
-export const globalStyles = StyleSheet.create({
- // โโ Conteneurs โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- safeDeep: {
- flex: 1,
- backgroundColor: Colors.backgroundDeep,
- },
- safeAbyss: {
- flex: 1,
- backgroundColor: Colors.bgAbyss,
- },
- container: {
- flex: 1,
- backgroundColor: Colors.background,
- padding: 16,
- },
- scrollContent: {
- paddingHorizontal: 20,
- paddingBottom: 40,
- },
-
- // โโ Mise en page โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- centered: {
- justifyContent: 'center',
- alignItems: 'center',
- },
- rowBetween: {
- flexDirection: 'row',
- justifyContent: 'space-between',
- alignItems: 'center',
- },
- row: {
- flexDirection: 'row',
- alignItems: 'center',
- },
- flex1: { flex: 1 },
-
- // โโ Cards โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- card: {
- backgroundColor: Colors.cardDeep,
- borderRadius: 16,
- padding: 16,
- },
- cardLegacy: {
- backgroundColor: Colors.card,
- borderRadius: 16,
- paddingVertical: 14,
- paddingHorizontal: 14,
- },
- glassCard: {
- backgroundColor: Colors.glassBg,
- borderWidth: 1,
- borderColor: Colors.glassBorder,
- borderRadius: 18,
- overflow: 'hidden',
- },
-
- // โโ Typographie โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- title: {
- color: Colors.textPrimary,
- fontSize: 22,
- fontWeight: 'bold',
- },
- subtitle: {
- color: Colors.textSecondary,
- fontSize: 14,
- marginTop: 4,
- },
- sectionLabel: {
- color: Colors.textPrimary,
- fontSize: 12,
- fontWeight: '800',
- letterSpacing: 1.2,
- textTransform: 'uppercase',
- marginBottom: 10,
- },
- sectionLinkText: {
- color: Colors.primary,
- fontSize: 12,
- fontWeight: '700',
- },
-
- // โโ Boutons โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- primaryBtn: {
- backgroundColor: Colors.primary,
- borderRadius: 16,
- alignItems: 'center',
- justifyContent: 'center',
- paddingVertical: 15,
- shadowColor: Colors.primary,
- shadowOffset: { width: 0, height: 6 },
- shadowOpacity: 0.4,
- shadowRadius: 14,
- elevation: 8,
- },
- primaryBtnText: {
- color: '#fff',
- fontSize: 14,
- fontWeight: '800',
- },
- ghostBtn: {
- borderWidth: 1.5,
- borderColor: Colors.primary,
- borderRadius: 14,
- alignItems: 'center',
- justifyContent: 'center',
- paddingVertical: 13,
- backgroundColor: 'transparent',
- },
- ghostBtnText: {
- color: Colors.primary,
- fontWeight: '700',
- fontSize: 14,
- },
-
- // โโ รcrans vides โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
- emptyState: {
- flex: 1,
- alignItems: 'center',
- justifyContent: 'center',
- paddingHorizontal: 32,
- gap: 14,
- },
- emptyText: {
- color: Colors.textMuted,
- fontSize: 14,
- textAlign: 'center',
- },
-});