Compare commits

..

79 Commits

Author SHA1 Message Date
antoine.perederii a1ff3cd005 add pdf previsionnal GANTT
1 year ago
antoine.perederii e4189e6409 Merge branch 'merged' of https://codefirst.iut.uca.fr/git/HeartDev/Web into merged
1 year ago
antoine.perederii 70490199b7 update diagrammes
1 year ago
Antoine PEREDERII 5e04cfb2a5 📝 Update GANTT pdf
1 year ago
antoine.perederii e7f0d51ddf git
1 year ago
antoine.perederii 7bbedee03c Merge branch 'merged' of https://codefirst.iut.uca.fr/git/HeartDev/Web into merged
1 year ago
Antoine PEREDERII e86b3f7b00 🚧 🏗️ 💥 🧪 WORK IN PROGRESS ! Wait for WORK
2 years ago
Antoine PEREDERII 2efb321f6a update some parts with database
2 years ago
Antoine PEREDERII d3d6dbb12f 🚀 introduce charts
2 years ago
Antoine PEREDERII 114dd33a4f Merge remote-tracking branch 'origin/merged_PLE' into merging_APE
2 years ago
Antoine PEREDERII 6786eea1c4 add heartRate Graph
2 years ago
Antoine PEREDERII 59de72856f merge merged with merged_PLE
2 years ago
Antoine PEREDERII 9e57169ae6 test to add chart but not conclude
2 years ago
Antoine PEREDERII 90c43b08f2 Merge branch 'merged_PLE' of codefirst.iut.uca.fr:HeartDev/Web into merged_PLE
2 years ago
Antoine PEREDERII a691e2a952 add activity analyze
2 years ago
Paul LEVRAULT 23ccabbb71 Test ajout graphique
2 years ago
Antoine PEREDERII 1b18825afa update merged, add .env file to resolve this
continuous-integration/drone/push Build is failing Details
2 years ago
Antoine PEREDERII 1b4448fecd update activities
2 years ago
Paul LEVRAULT f844bfaca2 Merge branch 'merged_PLE' of https://codefirst.iut.uca.fr/git/HeartDev/Web into merged_PLE
2 years ago
Antoine PEREDERII 6585a92eb6 Merge branch 'merged_PLE' of codefirst.iut.uca.fr:HeartDev/Web into merged_PLE
2 years ago
Antoine PEREDERII 98eddfc9f7 update controllers
2 years ago
Paul LEVRAULT 07b223c633 correction de trucs
2 years ago
Paul LEVRAULT d092f7c318 Ajout de notif dans la BD
2 years ago
Paul LEVRAULT 39f5026f99 Correction AthleteMapper
2 years ago
Paul LEVRAULT ca3e2591f0 Correction AthleteMapper
2 years ago
Antoine PEREDERII 0093f21b81 update vues with database
2 years ago
Antoine PEREDERII 6e935ce864 add database on athlete page
2 years ago
Antoine PEREDERII 8473b52a96 Update '.drone.yml'
2 years ago
Antoine PEREDERII 0b4e76e66f add Training Entity and gateway
continuous-integration/drone/push Build is failing Details
2 years ago
David D'ALMEIDA 9eb7074df5 push
continuous-integration/drone/push Build is failing Details
2 years ago
Paul LEVRAULT de5f390af1 Merge
continuous-integration/drone/push Build is failing Details
2 years ago
Paul LEVRAULT 76c6b4b8d1 Ok il me faut une base de données
2 years ago
Paul LEVRAULT 16ee9a131e Ca marche pas et ca commence a m'enerver
2 years ago
antoine.perederii 90496b376b update Gestion
continuous-integration/drone/push Build is failing Details
2 years ago
antoine.perederii 320443cce1 update Gestion de Projet
continuous-integration/drone/push Build is failing Details
2 years ago
Antoine PEREDERII 3b67119b09 update CI
continuous-integration/drone/push Build is failing Details
2 years ago
Antoine PEREDERII 12d9d6d0b5 Merge branch 'merged' of codefirst.iut.uca.fr:HeartDev/Web into merged
continuous-integration/drone/push Build is failing Details
2 years ago
Antoine PEREDERII bb3af1ab28 update CI
2 years ago
Antoine PEREDERII eabd88e959 Update '.drone.yml'
continuous-integration/drone/push Build is failing Details
2 years ago
Antoine PEREDERII e09629c810 update CI
continuous-integration/drone/push Build encountered an error Details
2 years ago
Antoine PEREDERII 15b97837f7 update paul's branch with database link on search user
continuous-integration/drone/push Build is failing Details
2 years ago
Kevin MONTEIRO 0734320c4a Modif Script -> Fonctionnel sur BDD pour les TP
continuous-integration/drone/push Build is failing Details
2 years ago
Kevin MONTEIRO 89aa392301 Merge branch 'merged' of https://codefirst.iut.uca.fr/git/HeartDev/Web into merged
continuous-integration/drone/push Build is failing Details
2 years ago
Kevin MONTEIRO 983245ad0f Modif MLD + Modif Script BDD suivant les modification à faire
2 years ago
Paul LEVRAULT d4ee62ce18 test
continuous-integration/drone/push Build is failing Details
2 years ago
David D'ALMEIDA adb9584cba work with APE
continuous-integration/drone/push Build is failing Details
2 years ago
Paul LEVRAULT fe2dbded3b J'en ai marre
continuous-integration/drone/push Build is failing Details
2 years ago
Paul LEVRAULT ae9744cde6 Merge remote-tracking branch 'origin/WORK-KMO-Gateway' into merged_PLE
2 years ago
Paul LEVRAULT f0f7e3ad8c resol de bug
continuous-integration/drone/push Build is failing Details
2 years ago
Paul LEVRAULT 18bb092a71 Merge remote-tracking branch 'origin/merged_PLE' into merged
continuous-integration/drone/push Build is failing Details
2 years ago
Paul LEVRAULT 0ac615539b Merge remote-tracking branch 'origin/merged' into merged_PLE
2 years ago
Paul LEVRAULT 7c1824f072 Controlleur athlete quasi terminée
2 years ago
Paul LEVRAULT aa49d0960a Un autre truc
continuous-integration/drone/push Build is failing Details
2 years ago
Paul LEVRAULT 9ee8e90065 Truc de test
continuous-integration/drone/push Build is failing Details
2 years ago
Paul LEVRAULT 661b9c80cf athlete controller quasi fini
continuous-integration/drone/push Build is failing Details
2 years ago
Antoine PINAGOT d0d1d4c23d auth Ctrl fini
continuous-integration/drone/push Build is failing Details
2 years ago
Paul LEVRAULT 24919e6ba5 La recherche de users fonctionne
continuous-integration/drone/push Build is failing Details
2 years ago
Kevin MONTEIRO fe12112ce6 Finition gateway Athlete Coach et Activite a tester mais normalement fonctionnel
continuous-integration/drone/push Build is failing Details
2 years ago
Kevin MONTEIRO aba0924f64 Gateways, Mappers et Entity pour Athlete et Coach a tester mais fonctionnel normalement
continuous-integration/drone/push Build is failing Details
2 years ago
David D'ALMEIDA f804820e85 exemple
continuous-integration/drone/push Build is failing Details
2 years ago
antoine.perederii c311825459 Merge branch 'merged' of https://codefirst.iut.uca.fr/git/HeartDev/Web into merged
continuous-integration/drone/push Build is failing Details
2 years ago
antoine.perederii 8488240e9c update gestion de projet
2 years ago
Paul LEVRAULT 1aa8dcc9fa test
2 years ago
Kevin MONTEIRO af9821709c Grosse Amélioration Gateways, Mappers, Entity Athlète et Coach
continuous-integration/drone/push Build is failing Details
2 years ago
Antoine PINAGOT 09dafc90c0 merge terminé
continuous-integration/drone/push Build is failing Details
2 years ago
Antoine PINAGOT 64ecdf87f2 Merge remote-tracking branch 'origin/issue_032_models' into merged
2 years ago
Antoine PINAGOT c201ee9bff Finition de link route/controller
continuous-integration/drone/push Build is failing Details
2 years ago
Antoine PINAGOT 35cb501e47 Il reste le AuthController, et le Athlete
continuous-integration/drone/push Build is failing Details
2 years ago
Antoine PINAGOT 637c522959 Ajout route dans les bons controller (pas terminé)
continuous-integration/drone/push Build is failing Details
2 years ago
antoine.perederii 3a2b0dd405 Merge branch 'merged' of https://codefirst.iut.uca.fr/git/HeartDev/Web into merged
continuous-integration/drone/push Build is failing Details
2 years ago
antoine.perederii 591bbe67c1 update GANTT Project
2 years ago
Antoine PEREDERII 4d67c66080 update oral diaporama
continuous-integration/drone/push Build is failing Details
2 years ago
antoine.perederii a9c5c28143 Merge branch 'merged' of https://codefirst.iut.uca.fr/git/HeartDev/Web into merged
continuous-integration/drone/push Build is failing Details
2 years ago
antoine.perederii 3f17b380ee update GANTT project
2 years ago
Kevin MONTEIRO acf783143f Début Diapo Oral 2
continuous-integration/drone/push Build is failing Details
2 years ago
antoine.perederii f15e45e197 update real GANTT
continuous-integration/drone/push Build is failing Details
2 years ago
Antoine PEREDERII 1918025c2f update msProject
continuous-integration/drone/push Build is failing Details
2 years ago
Antoine PINAGOT c0c7d6560f final page
continuous-integration/drone/push Build is failing Details
2 years ago
Antoine PEREDERII 637085afce update app and diagrams
continuous-integration/drone/push Build is failing Details
2 years ago

@ -1,90 +0,0 @@
kind: pipeline
type: docker
name: HeartWave
trigger:
event:
- push
steps:
# Test ✔️
- name: test
image: composer:2.6
commands:
- cd Sources
- rm -r vendor
- rm composer.lock
# Installe les dépendances PHP si nécessaire
- php composer.phar install --no-interaction
- ./vendor/bin/phpunit tests
# Sonar static code analisis deployment
# TODO : use an image that already have unzip
- name: code-analysis
image: php:8.1-cli
environment:
SONAR_TOKEN:
from_secret: SONAR_TOKEN
commands:
- apt-get update && apt-get install -y curl unzip
- export SONAR_SCANNER_VERSION=4.7.0.2747
- export SONAR_SCANNER_HOME=$HOME/.sonar/sonar-scanner-$SONAR_SCANNER_VERSION-linux
- curl --create-dirs -sSLo $HOME/.sonar/sonar-scanner.zip https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-$SONAR_SCANNER_VERSION-linux.zip
- unzip -o $HOME/.sonar/sonar-scanner.zip -d $HOME/.sonar/
- export PATH=$SONAR_SCANNER_HOME/bin:$PATH
- export SONAR_SCANNER_OPTS="-server"
- cd Sources
- sonar-scanner -D sonar.projectKey=HeartTrack -D sonar.host.url=https://codefirst.iut.uca.fr/sonar
depends_on: [ test ]
# build image and push on the registry ✔️
- name: rewrite-urls
image: 'busybox:latest'
commands:
- cd Sources
- ls
- >-
find . -type f -exec sed -i -r
"s@(href|src)=\"/@\1=\"$${PLUGIN_CONTAINER_PATH}@g" {} +
settings:
container_path: https://codefirst.iut.uca.fr/containers/HeartDev-web/
- name: docker-build-and-push
image: plugins/docker
settings:
commands: ls
dockerfile: Sources/config/Dockerfile
context: Sources
registry: hub.codefirst.iut.uca.fr
repo: hub.codefirst.iut.uca.fr/david.d_almeida/web
mirror: https://proxy.iut.uca.fr:8443
username:
from_secret: SECRET_REGISTRY_USERNAME
password:
from_secret: SECRET_REGISTRY_PASSWORD
depends_on:
- rewrite-urls
- name: deploy-container
image: >-
hub.codefirst.iut.uca.fr/thomas.bellembois/codefirst-dockerproxy-clientdrone:latest
environment:
IMAGENAME: 'hub.codefirst.iut.uca.fr/david.d_almeida/web:latest'
CONTAINERNAME: web
COMMAND: create
OVERWRITE: true
ADMINS: david.d_almeida
depends_on:
- docker-build-and-push
- name: notify
image: ruby:2.1
when:
status: [ success ]
ref:
include:
- refs/tags/*-demo
commands:
- sh ./notifymail.sh
depends_on: [ docker-build-and-push ]

@ -11,6 +11,7 @@ entity "Athlète" as athlete {
poids
motDePasse
dateNaissance
isCoach
}
entity "Amitié" as friendship{
@ -28,11 +29,7 @@ entity "Notification" as notif {
#athleteId
}
entity "Coach" as coach {
{static} idCoach
// attributs spécifiques au coach
#athleteId
}
entity "Statistique" as stats {
{static} idStatistique
@ -52,7 +49,7 @@ entity "Entraînement" as training {
latitude
longitude
feedback
#coachId
#athleteId
}
entity "Participe" as takepart {
@ -101,13 +98,12 @@ entity "FréquenceCardiaque" as fc {
activity --> athlete
activity --> source
activity <-- fc
coach --> athlete
athlete <-- source
stats --> athlete
takepart --> athlete
takepart --> training
friendship --> athlete
notif --> athlete
coach <-- training
athlete <-- training
athlete <-- friendship
@enduml

@ -4,56 +4,6 @@
# BDD
## Modèle Logique de Données (MLD)
Le MLD représente la structure de données de l'application, décrivant les entités et les relations entre elles. Voici un aperçu des principales entités du MLD :
### Athlète (Athlete)
L'entité principale représentant un athlète avec ces informations propre à lui telles que l'identifiant, le nom, le prénom, l'email, etc. Les athlètes peuvent être coach avec le boolean idCoach et être liés par des amitiés, ou par un coaching via la table `Amitie`.
### Amitié (Friendship)
Une entité qui modélise les relations d'amitié entre les athlètes et de coaching entre les athlètes et les coachs. Elle stocke les identifiants des deux utilisateurs impliqués.
### Notification (Notification)
L'entité qui stocke les notifications destinées aux athlètes, avec des détails tels que le message, la date, le statut, et le degré d'urgence.
### Envoi de Notification (SendNotification)
Une entité de liaison entre les athlètes et les notifications, indiquant quel athlète ou coach a envoyé quelle notification. Cela peut-être utile lors d'une notification d'ajout d'amie par exemple.
### Statistique (Statistic)
Les statistiques relatives à un athlètes, y compris le poids, la fréquence cardiaque moyenne, la fréquence cardiaque maximale, etc.
### Entraînement (Training)
Détails sur les sessions d'entraînement planifiés par un coach pour ses athlètes, comprenant la date, la description, la localisation, etc. Les athlètes peuvent participer à des entraînements et donner leur feedback sur l'entrainement donné.
### Participation (Participate)
Une entité de liaison entre les athlètes et les entraînements, indiquant quels athlètes participent à quels entraînements.
### Don (GiveParticipation)
Une entité de liaison entre les coachs et les entraînements, indiquant quels coachs ont attribué quels entraînements.
### Source de Données (DataSource)
L'entité représentant la source des données des enregistrements sportif, telle que le type, le modèle, la précision, etc., utilisée par les athlètes pour enregistrer une ou des activités.
### Activité (Activity)
Les détails des activités des athlètes, y compris le type, la date, les heures de début et de fin, l'effort ressenti, etc.
### Fréquence Cardiaque (HeartRate)
Les données de fréquence cardiaque enregistrées pendant les activités, avec des informations telles que l'altitude, la température, etc.
Ce MLD forme la base de données sous-jacente pour l'application, offrant une structure organisée pour stocker et récupérer les informations relatives aux athlètes et à leurs activités.
```plantuml
@startuml
skinparam classAttributeIconSize 0
@ -179,10 +129,9 @@ athlete <-- friendship
```plantuml
@startuml
skinparam classAttributeIconSize 0
package MCD{
entity "Athlete" as athlete {
{static} idAthlete
class Athlete {
idAthlete
username
nom
prenom
@ -195,45 +144,65 @@ entity "Athlete" as athlete {
isCoach
}
entity "Notification" as notif {
{static} idNotif
class Amitie {
idAthlete1
idAthlete2
}
class Notification {
idNotif
message
date
statut
urgence
#athleteId
athleteId
}
entity "Statistique" as stats {
{static} idStatistique
class Envoi {
idAthlete
idNotif
}
class Statistique {
idStatistique
poids
fcMoyenne
fcMax
caloriesBruleesMoy
date
#athleteId
athleteId
}
entity "Entrainement" as training {
{static} idEntrainement
class Entrainement {
idEntrainement
date
description
latitude
longitude
feedback
#athleteId
athleteId
}
entity "SourceDonnee" as source {
{static} idSource
class Participe {
athleteId
entrainementId
}
class Donne {
coachId
entrainementId
}
class SourceDonnee {
idSource
type
modele
precision
#athleteId
athleteId
}
entity "Activite" as activity {
{static} idActivité
class Activite {
idActivite
type
date
heureDeDebut
@ -246,31 +215,29 @@ entity "Activite" as activity {
maximum
minimum
temperatureMoyenne
#athleteId
#sourceId
athleteId
sourceId
}
entity "FréquenceCardiaque" as fc {
{static} idFc
class FrequenceCardiaque {
idFc
altitude
temps : time
température
temps
temperature
bpm
longitude
latitude
#activitéId
activiteId
}
}
activity "0..n" --- "1..1" athlete : réalise
activity "1..n" --- "1..1" source : possede
activity "1..1" --- "1..n" fc : enregistre
athlete "1..n" --- "0..1" source : possede
stats "0..n" --- "1..1" athlete : possede
training "0..n" --- "1..n" athlete : participe
training "0..n" --- "1..1" athlete : donne
athlete "0..n" --- "1..n" athlete : est ami
notif "0..n" --- "1..n" athlete : recoit
notif "0..n" --- "1..1" athlete : envoie
Athlete "1" --o "0..*" Amitie
Athlete "1" --o "0..*" Notification
Athlete "1" --o "0..*" Statistique
Athlete "1" --o "0..*" Entrainement
Entrainement "0..*" --o "0..*" Athlete : Participants
Entrainement "0..*" --o "0..*" Athlete : Coachs
Athlete "1" --o "0..*" SourceDonnee
Activite "1" --o "0..*" FrequenceCardiaque
@enduml
```

@ -28,11 +28,11 @@ Bienvenue dans l'écosystème dynamique de notre plateforme de gestion d'activit
- Les Utilisateurs ont un rôle spécifique (Athlete, Coach) qui détermine leurs fonctionnalités.
- Un Athlète peut enregistrer plusieurs Activités, possède des Statistiques, et une Sources de Données qui est la plus utilisé.
- Un Athlète peut enregistrer plusieurs Activités, Statistiques, et interagir avec différentes Sources de Données.
- Les Entraînements sont liés aux Utilisateurs, permettant une planification efficace.
- Les Notifications informent les Utilisateurs des événements importants tels qu'une demande d'amis ou une notification d'avertissement de ban.
- Les Notifications informent les Utilisateurs des événements importants.
Explorez ce diagramme pour comprendre comment notre plateforme offre une expérience complète, de la gestion des utilisateurs à l'enregistrement des activités sportives et au suivi des performances.

@ -18,7 +18,7 @@ Bienvenue dans le cœur de notre système, où les données prennent vie à trav
- **NotificationRepository :** Responsable de la gestion des notifications, assurant un accès structuré et une manipulation sécurisée de ces informations cruciales.
- **RelationshipRequestRepository :** Facilite la gestion des demandes de relations entre utilisateurs (amitiés), garantissant une interaction claire et ordonnée au sein de l'application.
- **RelationshipRequestRepository :** Facilite la gestion des demandes de relations entre utilisateurs, garantissant une interaction claire et ordonnée.
- **TrainingRepository :** Permet l'accès et la manipulation des données liées aux entraînements, facilitant le suivi des performances athlétiques.
@ -26,7 +26,7 @@ Explorez ce diagramme pour découvrir la robustesse de notre architecture de ges
```plantuml
@startuml couche_acces_aux_donnees
interface IGenericRepository {
abstract class IGenericRepository {
+ getItemById(int id) : object
+ getNbItems() : int
+ getItems(int index, int count, string orderingPropertyName, bool descending) : array

@ -4,15 +4,15 @@
# Introduction au Diagramme de Classes : Statistiques pour Coach
Bienvenue dans l'univers captivant de notre système de gestion d'activités sportives avec une mise au point spéciale sur les statistiques destinées aux athlètes. Ce diagramme de classes offre une vue approfondie de la manière dont les utilisateurs, en particulier les athlètes, interagissent avec leurs statistiques.
Bienvenue dans l'univers captivant de notre système de gestion d'activités sportives avec une mise au point spéciale sur les statistiques destinées aux coaches. Ce diagramme de classes offre une vue approfondie de la manière dont les utilisateurs, en particulier les athlètes et les coaches, interagissent avec les données de performance.
**Entités Principales :**
- **Utilisateur (User) :** Représente les individus inscrits sur notre plateforme, avec des détails personnels et un rôle spécifique dans l'écosystème sportif.
- **Athlète (Athlete) :** Un type spécialisé d'utilisateur qui possède des statistiques liées à ses activités sportives.
- **Athlète (Athlete) :** Un type spécialisé d'utilisateur qui peut enregistrer des statistiques liées à ses activités sportives.
- **Coach (Coach) :** Un rôle qui s'étend à partir de la classe abstraite Role, dédié à la gestion des athlètes et de la vision de leurs statistiques.
- **Coach (Coach) :** Un rôle qui s'étend à partir de la classe abstraite Role, dédié à la gestion des athlètes et de leurs statistiques.
- **Statistique (Statistique) :** Contient des informations détaillées sur les performances sportives d'un athlète, telles que la distance totale, le poids, le temps total, la fréquence cardiaque moyenne, minimale et maximale, ainsi que les calories brûlées.
@ -22,11 +22,11 @@ Bienvenue dans l'univers captivant de notre système de gestion d'activités spo
- Un Coach peut gérer une liste d'athlètes et avoir accès à leurs statistiques.
- Un Athlète peut enregistrer plusieurs activités afin d'avoir des statistiques liées à celle-ci.
- Un Athlète peut enregistrer plusieurs statistiques liées à ses activités.
**Objectif Principal :**
- Permettre aux coachs d'accéder et de surveiller les statistiques détaillées de leurs athlètes, offrant ainsi un aperçu complet de leurs performances sportives.
- Permettre aux coaches d'accéder et de surveiller les statistiques détaillées de leurs athlètes, offrant ainsi un aperçu complet de leurs performances sportives.
Explorez ce diagramme pour découvrir comment notre application crée une synergie entre les utilisateurs, les rôles, et les statistiques, contribuant ainsi à une expérience enrichissante dans le suivi des activités sportives.

@ -4,7 +4,7 @@
# Introduction au Modèle de Données de l'Application
L'architecture de données de notre application de suivi d'activités sportives repose sur un modèle robuste, avec des entités clés pour représenter les activités, les athlètes et les coachs et la récupération de ces données au sein de notre application. Découvrez les composants principaux de notre modèle de données :
L'architecture de données de notre application de suivi d'activités sportives repose sur un modèle robuste, avec des entités clés pour représenter les activités, les athlètes et les coachs. Découvrez les composants principaux de notre modèle de données :
## Activité
L'entité Activité représente une session d'activité sportive avec des détails variés tels que le type d'activité, la date, la durée, l'effort ressenti, etc. Le `ActiviteEntity` encapsule ces données, tandis que le `ActiviteGateway` gère la communication avec la base de données pour les activités.

@ -4,12 +4,12 @@
# Diagramme de classes pour l'importation de fichiers .fit
Bienvenue dans le monde de la gestion d'activités sportives avec notre application innovante ! Ce diagramme de classe se concentre sur une fonctionnalité essentielle qui améliorera l'expérience des utilisateurs : l'importation de fichiers .fit. Nous avons conçu un diagramme de classes pour vous offrir une vision claire et structurée de la manière dont cette fonctionnalité est implémentée au sein de notre application.
Bienvenue dans le monde de la gestion d'activités sportives avec notre application innovante ! Cette user story se concentre sur une fonctionnalité essentielle qui améliorera l'expérience des utilisateurs : l'importation de fichiers .fit. Nous avons conçu un diagramme de classes pour vous offrir une vision claire et structurée de la manière dont cette fonctionnalité est implémentée au sein de notre application.
**Acteurs Principaux :**
- Utilisateur (User) : Représente un individu inscrit sur notre plateforme.
- Athlète (Athlete) : Un type spécialisé d'utilisateur, bénéficiant de fonctionnalités supplémentaires liées à la gestion d'activités sportives et avec la capacité d'importer des fichiers .fit.
- Utilisateur (User) : Représente un individu inscrit sur notre plateforme, avec la capacité d'importer des fichiers .fit.
- Athlète (Athlete) : Un type spécialisé d'utilisateur, bénéficiant de fonctionnalités supplémentaires liées à la gestion d'activités sportives.
**Entités Clés :**
@ -18,13 +18,13 @@ Bienvenue dans le monde de la gestion d'activités sportives avec notre applicat
**Fonctionnalité Clé :**
- Importation de fichiers .fit : Permet aux utilisateurs de charger des données provenant de fichiers .fit via la bibliothèque `php-fit-file-analysis`, générés par des dispositifs de suivi d'activité. Ces fichiers contiennent des informations précieuses telles que la fréquence cardiaque, la distance parcourue et d'autres données de santé importante pour nos analyses.
- Importation de fichiers .fit : Permet aux utilisateurs de charger des données provenant de fichiers .fit, générés par des dispositifs de suivi d'activité. Ces fichiers contiennent des informations précieuses telles que la fréquence cardiaque, la distance parcourue et d'autres métriques essentielles.
**Architecture :**
- AuthService (Service d'Authentification) : Gère l'authentification des utilisateurs, garantissant un accès sécurisé à la fonction d'importation.
- UserManager (Gestionnaire d'Utilisateurs) : Gère les opérations liées aux utilisateurs, y compris l'importation de fichiers .fit.
- ActivityManager (Gestionnaire d'Activités) : Responsable du stockage et de la gestion des activités importées.
ActivityManager (Gestionnaire d'Activités) : Responsable du stockage et de la gestion des activités importées.
**Objectif :**

@ -21,6 +21,3 @@
- [Cas d'utilisation pour la gestion du compte et des amitiés](CasUtilisations/README_gestionCompteAmitie.md)
- [Cas d'utilisation pour la gestion des activités et données](CasUtilisations/README_gestionActivites.md)
- [Cas d'utilisation pour la suivi d'une équipe sportive](CasUtilisations/README_coachSuiviSportif.md)
## Base de Données
- [MLD](BDD/README_BDD.md)

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 2.0 MiB

@ -71,8 +71,7 @@ Documentation et informations à propos de `HearthTrack` disponible [ici]()
### Prerequisites
* [Visual Studio code](https://code.visualstudio.com/) - exemple d'IDE gratuit
* [Git](https://git-scm.com/) - Versioning
* [XAMPP : X, Apache, MySQL, Perl, PHP](https://www.apachefriends.org/fr/index.html) - Languages
* [PostgreSQL](https://www.postgresql.org/) - Base de Donnée
* [XAMPP : X, Apache, MySQL, Perl, PHP](https://www.apachefriends.org/fr/index.html) - Langages
## Getting Started
1. Cloner le répos

@ -24,7 +24,8 @@
"Shared\\Exception\\": "src/shared/exception",
"Shared\\Attributes\\": "src/shared/attributes",
"App\\Views\\Directives\\" : "src/app/views/directives",
"Data\\Core\\": "src/data/core/"
"Data\\Core\\": "src/data/core/",
"Database\\": "src/data/core/database"
}
},
"require": {
@ -32,7 +33,12 @@
"altorouter/altorouter": "1.1.0",
"vlucas/phpdotenv": "^5.5",
"psr/container": "^2.0",
"adriangibbons/php-fit-file-analysis": "^3.2.0"
"adriangibbons/php-fit-file-analysis": "^3.2.0",
"ext-couchbase": "*",
"ext-pdo": "*"
},
"config": {
"process-timeout": 2000
},
"require-dev": {
"phpunit/phpunit": "*"

@ -1,2 +0,0 @@
AddType text/css .css
AddType application/javascript .js

@ -1,31 +1,17 @@
FROM php:8.2-apache as base
FROM php:8.2-fpm
# Installation de dépendances nécessaires pour Composer
RUN apt-get update && apt-get install -y \
git \
unzip
# Installation de Composer
# TODO : should use a image with composer install
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
RUN docker-php-ext-install pdo pdo_mysql
# Copy configs
COPY ./config/virtual-host.conf /etc/apache2/sites-available/000-default.conf
COPY ./config/httpd.conf /etc/apache2/httpd.conf
# Setup App
RUN mkdir -p /app/public && chown -R www-data:www-data /app
WORKDIR /app
COPY . /var/www/
# add sources code
COPY . /app
ENV VOLUME_PATH /app/public
WORKDIR /var/www/
RUN composer install
EXPOSE 80
CMD ["apache2-foreground"]

@ -5,18 +5,18 @@ use Shared\Log;
$dotenv = Dotenv::createUnsafeImmutable(__DIR__,'.env');
$dotenv->safeLoad();
// echo($_ENV);
// apenrently getEnv is not a good thing cause
// const DB_HOST = $_ENV['DB_HOST'] ?? 'localhost';
// const DB_DATABASE = $_ENV['DB_DATABASE'] ?? 'heartTrack';
// const DB_USER = $_ENV['DB_USER'] ?? 'toto';
// const DB_PASSWORD = $_ENV['DB_PASSWORD'] ?? 'achanger';
define("APP_ENV", 'development');
define("APP_ENV", getenv('APP_ENV'));
const DB_SERVER = 'mysql';
const DB_HOST = 'localhost';
const DB_DATABASE = 'heartTrack';
const DB_USER = 'toto';
const DB_PASSWORD = 'achanger';
const DB_DATABASE = 'sae_3';
const DB_USER = 'Perederii';
const DB_PASSWORD = '';
//const APP_ENV = 'console';
const DSN = "mysql:host=" . DB_HOST . ";dbname=" . DB_DATABASE;
const DSN = DB_SERVER . ":host=" . DB_HOST . ";dbname=" . DB_DATABASE;

@ -1 +0,0 @@
SetEnv ASSET_PREFIX containers/HeartDev-web/

@ -1,19 +1,28 @@
server {
listen 80;
index index.php index.html index.htm;
root /usr/share/nginx/html;
index index.php;
root /var/www/public;
error_page 404 /index.php;
location ~ ^/(images|javascript|js|css|flash|media|static)/ {
root /var/www/public;
}
location ~ \.php$ {
fastcgi_pass web:9000; # service name defined in docker-compose.yml file like web
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
location / {
root /usr/share/nginx/html;
try_files $uri /index.php;
try_files $uri $uri/ /index.php?$query_string;
}
}

@ -1,39 +0,0 @@
<VirtualHost *:80>
ServerName default
<Directory />
Options FollowSymLinks
AllowOverride None
Require all denied
</Directory>
<Directory ${VOLUME_PATH}>
AllowOverride All
Require all granted
</Directory>
DocumentRoot ${VOLUME_PATH}
AccessFileName .htaccess
<FilesMatch "^\.ht">
Require all denied
</FilesMatch>
LogFormat "%v:%p %h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" vhost_combined
LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%h %l %u %t \"%r\" %>s %O" common
LogFormat "%{Referer}i -> %U" referer
LogFormat "%{User-agent}i" agent
CustomLog /proc/self/fd/1 combined
<FilesMatch \.php$>
SetHandler application/x-httpd-php
</FilesMatch>
# Multiple DirectoryIndex directives within the same context will add
# to the list of resources to look for rather than replace
# https://httpd.apache.org/docs/current/mod/mod_dir.html#directoryindex
DirectoryIndex disabled
DirectoryIndex index.php index.html
</VirtualHost>

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 586 B

After

Width:  |  Height:  |  Size: 692 B

File diff suppressed because it is too large Load Diff

@ -12,7 +12,7 @@ use Manager\DataManager;
use Repository\IUserRepository;
use Shared\ArgumentControllerResolver;
use Shared\IArgumentResolver;
use Stub\AuthService;
use Network\AuthService;
use Stub\NotificationRepository;
use Stub\TrainingRepository;
use Stub\UserRepository;
@ -30,13 +30,13 @@ use Network\RelationshipService;
use Network\INotificationService;
use Stub\NotificationService;
use App\Router\Session;
use Stub\StubData;
use Twig\Environment;
use Twig\Loader\FilesystemLoader;
use Shared\IHashPassword;
use Shared\HashPassword;
use Shared\Log;
$appFactory = new AppCreator();
$appFactory->registerService(IArgumentResolver::class, ArgumentControllerResolver::class);
@ -54,7 +54,6 @@ $appFactory->registerService(IUserRepository::class, UserRepository::class);
$appFactory->registerService(\Twig\Loader\LoaderInterface::class, function() {
return new FilesystemLoader(__DIR__ . '/../src/app/views/Templates');
});
@ -65,4 +64,12 @@ $appFactory->registerService(\Twig\Environment::class,\Twig\Environment::class);
// $databaseContext = DatabaseContext::getInstance();
$appFactory->AddControllers();
$app = $appFactory->create();
if (!is_null($app)){
// Ajout des Middleware
/*$app->use(new LoggingMiddleware());*/
$app->use(new AuthMiddleware($appFactory->getDicontainer()->get(IAuthService::class) ));
$app->mapControllers();
$app->run(RequestFactory::createFromGlobals());
}

@ -0,0 +1,34 @@
/*!
* Start Bootstrap - New Age v6.0.7 (https://startbootstrap.com/theme/new-age)
* Copyright 2013-2023 Start Bootstrap
* Licensed under MIT (https://github.com/StartBootstrap/startbootstrap-new-age/blob/master/LICENSE)
*/
//
// Scripts
//
window.addEventListener('DOMContentLoaded', event => {
// Activate Bootstrap scrollspy on the main nav element
const mainNav = document.body.querySelector('#mainNav');
if (mainNav) {
new bootstrap.ScrollSpy(document.body, {
target: '#mainNav',
offset: 74,
});
};
// Collapse responsive navbar when toggler is visible
const navbarToggler = document.body.querySelector('.navbar-toggler');
const responsiveNavItems = [].slice.call(
document.querySelectorAll('#navbarResponsive .nav-link')
);
responsiveNavItems.map(function (responsiveNavItem) {
responsiveNavItem.addEventListener('click', () => {
if (window.getComputedStyle(navbarToggler).display !== 'none') {
navbarToggler.click();
}
});
});
});

@ -0,0 +1,34 @@
/*!
* Start Bootstrap - New Age v6.0.7 (https://startbootstrap.com/theme/new-age)
* Copyright 2013-2023 Start Bootstrap
* Licensed under MIT (https://github.com/StartBootstrap/startbootstrap-new-age/blob/master/LICENSE)
*/
//
// Scripts
//
window.addEventListener('DOMContentLoaded', event => {
// Activate Bootstrap scrollspy on the main nav element
const mainNav = document.body.querySelector('#mainNav');
if (mainNav) {
new bootstrap.ScrollSpy(document.body, {
target: '#mainNav',
offset: 74,
});
};
// Collapse responsive navbar when toggler is visible
const navbarToggler = document.body.querySelector('.navbar-toggler');
const responsiveNavItems = [].slice.call(
document.querySelectorAll('#navbarResponsive .nav-link')
);
responsiveNavItems.map(function (responsiveNavItem) {
responsiveNavItem.addEventListener('click', () => {
if (window.getComputedStyle(navbarToggler).display !== 'none') {
navbarToggler.click();
}
});
});
});

@ -29,9 +29,6 @@ class App
private FrontController $frontController;
private Session $session;
public function __construct(string $appName, int $version, \App\Container $diContainer)
{
$this->appName = $appName;
@ -39,7 +36,7 @@ class App
$this->container = $diContainer;
$this->router = new Router("");
$this->frontController = new FrontController($this->router,$this->container);
$this->session = Session::getInstance();
Session::getInstance();
}
public function use(IHttpMiddleware $middleware)
@ -142,7 +139,7 @@ class App
$route = $attribute->newInstance();
$this->router->addControllerRoute(
implode('|', $route->getMethods()),
$route->getMethods(),
$prefix . $route->getPath(),
$controllerClass,
$method->getName(),

@ -24,6 +24,13 @@ class AppCreator
return $this;
}
public function registerSingleton(string $serviceId, $service): self
{
$this->container->set($serviceId, $service, Container::SINGLETON);
$this->services[] = $serviceId;
return $this;
}
/**
* Create an instance or perform actions based on the current application environment.
*
@ -33,6 +40,7 @@ class AppCreator
{
// Check the application environment
switch (APP_ENV) {
case 'console':
// Load the Console.php file in case of the 'console' environment
require_once __DIR__ . '/../console/Console.php';
@ -40,7 +48,6 @@ class AppCreator
case 'development':
// Create a new instance of the App class in the 'development' environment
return new App("HeartTrack", 1, $this->container);
break;
case 'html':
// Load the index.test.php file in case of the 'html' environment
require_once __DIR__ . '/index.test.php';
@ -53,7 +60,6 @@ class AppCreator
}
function AddControllers($namespacePrefix = 'App\Controller', $pathToControllers = __DIR__ . '/controller'): self
{
$controllerFiles = glob($pathToControllers . '/*.php');
@ -71,7 +77,8 @@ class AppCreator
if ($reflectionClass->isSubclassOf(BaseController::class)) {
// Register in DI container
$this->container->set($fullClassName, function () use ($fullClassName) {
$controllerInstance = new $fullClassName();
/** @var $controllerInstance * */
$controllerInstance = $this->container->resolve($fullClassName);
$controllerInstance->setContainer($this->container);
return $controllerInstance;
});
@ -81,11 +88,15 @@ class AppCreator
return $this;
}
public function getServiceRegistered(): array
{
return $this->services;
}
public function getDicontainer()
{
return $this->container;
}
}

@ -11,31 +11,45 @@ class Container implements ContainerInterface
{
private array $entries = [];
const SINGLETON = 'singleton';
const TRANSIENT = 'transient';
public function get(string $id)
{
if ($this->has($id)) {
$entry = $this->entries[$id];
if (is_callable($entry)) {
return $entry($this);
if ($entry['lifecycle'] === self::SINGLETON) {
if ($entry['instance'] === null) {
$entry['instance'] = $this->resolve($entry['concrete']);
}
return $entry['instance'];
}
$id = $entry;
if (is_callable($entry['concrete'])) {
return $entry['concrete']($this);
}
return $this->resolve($entry['concrete']);
}
return $this->resolve($id);
}
public function has(string $id): bool
{
return isset($this->entries[$id]);
}
public function set(string $id, callable|string $concrete): void
public function set(string $id, callable|string $concrete, string $lifecycle = self::TRANSIENT): void
{
$this->entries[$id] = $concrete;
$this->entries[$id] = [
'concrete' => $concrete,
'lifecycle' => $lifecycle,
'instance' => null
];
}
public function resolve(string $id)

@ -2,6 +2,19 @@
namespace App\Controller;
use Database\ActivityGateway;
use Database\ActivityMapper;
use Database\AnalyzeGateway;
use Database\AthleteEntity;
use Database\AthleteMapper;
use Database\EntrainementGateway;
use Database\EntrainementMapper;
use Database\NotificationEntity;
use Database\NotificationGateway;
use Database\NotificationMapper;
use DateTime;
use Model\Notification;
use Shared\Validation;
use App\Container;
use App\Router\Request\IRequest;
use App\Router\Response\Response;
@ -9,87 +22,272 @@ use Shared\Attributes\Route;
use Twig\Environment;
use Data\Core\Preferences;
use Shared\Log;
use Manager\UserManager;
use Database\Connexion;
use Database\AthleteGateway;
class AthleteController extends BaseController
{
#[Route(path: '/search-ath', name: 'search-ath', methods: ['GET'])]
private Environment $twig;
protected Preferences $preference;
// private UserManager $userMgr;
public function __construct()
{
$this->preference = new Preferences();
// $this->userMgr = $manager;
}
// #[Route(path: '/search-user', name: 'search-user', methods: ['GET'])]
// public function searchUser(string $username, IRequest $req): Response
// {
// $taberror = [];
// if(!Validation::val_string($username)){
// print("Nom invalide.");
// }
// else {
// try {
// $athleteGateway = new AthleteGateway(new Connexion("mysql:host=localhost;dbname=sae_3", "Perederii", ""));
//// $listSearch=$this->container->get(UserManager::class)->searchUsersByName($username);
// $listSearch = $athleteGateway->getAthlete();
// $map = new AthleteMapper ();
// $athleteEntity = $map->athleteSqlToEntity($listSearch);
// foreach ($athleteEntity as $entity) {
// $users = $map->athleteEntityToModel($entity);
// $listUsers[$i++]=['nom' => $users->getNom(), 'prenom'=>$users->getPrenom(), 'img'=>'john_doe'];
// }
//
// $i=0;
// foreach ($users as $user){
// echo "<h1> HELLO </h1>";
// $listUsers[$i++]=['nom' => $user->getNom(), 'prenom'=>$user->getPrenom(), 'img'=>'john_doe', 'username'=>$user->getUsername()];
// }
// echo "<h1> HEllO </h1>";
// //$currentUser=$this->container->get(UserManager::class)->getCurrentUser();
// //$currentUser=$_SESSION['user'];
// $response = $this->render('./page/addfriend.html.twig',[
// 'css' => $_COOKIE['preferences'],
// 'pp' => "test",
// 'user' => 'johndoe',//$currentUser->getUsername(),
// 'role' => 'Athlete',//$currentUser->getRole(),
// 'users' => $listUsers
// ]);
// } catch (\Throwable $th) {
// throw $th;
// return $this->render("addfriend.html.twig", ['tabError' => $taberror ]);
// }
// }
// return $response;
//
// }
#[Route(path: '/search-user', name: 'search-user', methods: ['GET'])]
public function searchUser(string $username, IRequest $req): Response
{
$taberror = [];
$utiliArray = [
[
'nom' => 'John',
'prenom' => 'Doe',
'img' => 'john_doe',
'username' => 'johndoe',
],
[
'nom' => 'Alice',
'prenom' => 'Smith',
'img' => 'alice_smith',
'username' => 'alicesmith',
],
];
// if(!Validation::val_string($name)){
if (!Validation::val_string($username)) {
print("Nom invalide.");
} else {
try {
//code...
// $model->userMgr->getUser($name);
return $this->render('./page/addfriend.html.twig', [
$athleteGateway = new AthleteGateway(new Connexion(DSN, DB_USER, DB_PASSWORD));
if($username==null){
$listSearch=$athleteGateway->getAthlete();
} else {
$listSearch = $athleteGateway->getAthleteByName($username);
}
$map = new AthleteMapper();
$athleteEntity = $map->athleteSqlToEntity($listSearch);
$listUsers = [];
foreach ($athleteEntity as $entity) {
$user = $map->athleteEntityToModel($entity);
$listUsers[] = ['idathlete' => number_format($user->getId(), 1), 'nom' => $user->getNom(),
'prenom' => $user->getPrenom(),'email' => $user->getEmail(), 'sexe' => $user->getSexe(),
'taille' => $user->getTaille(), 'poids' => $user->getPoids(), 'motdepasse' => $user->getMotDePasse(),
'datenaissance' => $user->getDateNaissance(), 'iscoach' => $user->getRole(), 'img' => 'test',
'username' => $user->getUsername()];
}
$response = $this->render('./page/addfriend.html.twig',[
'css' => $_COOKIE['preferences'],
'pp' => 'test',
'user' => 'John',//$currentUser->getUsername(),
'role' => 'Athlete',//$currentUser->getRole(),
'users' => $listUsers
]);
} catch (\Throwable $th) {
throw $th;
return $this->render("addfriend.html.twig", ['tabError' => $taberror]);
}
}
return $response;
}
#[Route(path: '/analyze', name: 'analyze_details', methods: ['GET'])]
public function activityDetails(int $id): Response
{
try {
// Utilisez $id pour récupérer les détails de l'activité depuis la base de données
// $activityGateway = new ActivityGateway(new Connexion(DSN, DB_USER, DB_PASSWORD));
// $activityEntity = $activityGateway->getActivityById($id); // Assurez-vous d'avoir une méthode similaire dans votre gateway
$analyzeGateway = new AnalyzeGateway(new Connexion(DSN, DB_USER, DB_PASSWORD));
$analyzeEntity = $analyzeGateway->getFrequenceCardiaqueByIdActivity($id);
$response = $this->render('./page/analyze.html.twig', [
'css' => $this->preference->getCookie(),
'pp' => "test2",
'user' => "Doe",
'role' => "Athlète",
'user' => 'johndoe',//$currentUser->getUsername(),
'role' => 'Athlete',//$currentUser->getRole(),
'friendship' => [],
'analyzes' => [],
'mails' => [],
'users' => $utiliArray,
'infoUser' => [],
'exos' => [],
'member' => [],
'responce' => "Notification d'ajout envoyée à $username"
'activity' => [],
'analyzes' => $analyzeEntity,
]);
} catch (\Throwable $th) {
//throw $th;
// return $this->render("addfriend.html.twig", ['tabError' => $taberror ]);
throw $th;
return $this->render("./page/analyze.html.twig", ['tabError' => $taberror]);
}
return $response;
}
//
// #[Route(path: '/analyses/{id}', name: 'analyses', methods: ['GET'])]
// public function analyses(): Response
// {
// try {
// $activityGateway = new ActivityGateway(new Connexion(DSN, DB_USER, DB_PASSWORD));
// $listSearch = $activityGateway->getActivity();
// $map = new ActivityMapper();
// $activityGateway = $map->activitySqlToEntity($listSearch);
// $listActivity = [];
// foreach ($activityGateway as $entity) {
// $activity = $map->activityEntityToModel($entity);
// $listActivity[] = ['idactivity' => number_format($activity->getIdActivity(), 1), 'type' => $activity->getType(),
// 'date' => $activity->getDate()->format("D j F Y"), 'heureDebut' => $activity->getHeureDebut()->format("H\h i"), 'heureFin' => $activity->getHeureFin()->format("H\h i"),
// 'effortRessenti' => $activity->getEffortRessenti(), 'variabilite' => $activity->getVariability(), 'variance' => $activity->getVariance(),
// 'ecartType' => $activity->getStandardDeviation(), 'moyenne' => $activity->getAverage(),
// 'max' => $activity->getMaximum(), 'min' => $activity->getMinimum(), 'temperature' =>$activity->getAvrTemperature()];
// }
//
// $response = $this->render('./page/analyze.html.twig',[
// 'css' => $this->preference->getCookie(),
// 'pp' => "test2",
// 'user' => 'johndoe',//$currentUser->getUsername(),
// 'role' => 'Athlete',//$currentUser->getRole(),
// 'friendship' => [],
// 'analyzes' => $listActivity,
// 'mails' => [],
// 'users' => [],
// 'infoUser' => [],
// 'exos' => [],
// 'member' => []
// ]);
// } catch (\Throwable $th) {
// throw $th;
// return $this->render("./page/analyze.html.twig", ['tabError' => $taberror]);
// }
// return $response;
// }
#[Route(path: '/activity', name: 'activity', methods: ['GET'])]
public function activity(): Response
{
try {
$activityGateway = new ActivityGateway(new Connexion(DSN, DB_USER, DB_PASSWORD));
$listSearch = $activityGateway->getActivity();
$map = new ActivityMapper();
$activityGateway = $map->activitySqlToEntity($listSearch);
$listActivity = [];
foreach ($activityGateway as $entity) {
$activity = $map->activityEntityToModel($entity);
$listActivity[] = ['idactivity' => number_format($activity->getIdActivity()), 'type' => $activity->getType(),
'date' => $activity->getDate()->format("D j F Y"), 'heureDebut' => $activity->getHeureDebut()->format("H\h i"), 'heureFin' => $activity->getHeureFin()->format("H\h i"),
'effortRessenti' => $activity->getEffortRessenti(), 'variabilite' => $activity->getVariability(), 'variance' => $activity->getVariance(),
'ecartType' => $activity->getStandardDeviation(), 'moyenne' => $activity->getAverage(),
'max' => $activity->getMaximum(), 'min' => $activity->getMinimum(), 'temperature' => $activity->getAvrTemperature()];
}
#[Route(path: '/analyses', name: 'analyses', methods: ['GET'])]
public function analyses(): Response
{
return $this->render('./page/analyze.html.twig', [
$response = $this->render('./page/activity.html.twig', [
'css' => $this->preference->getCookie(),
'pp' => "test2",
'user' => "Doe",
'role' => "Athlète",
'user' => 'johndoe',//$currentUser->getUsername(),
'role' => 'Athlete',//$currentUser->getRole(),
'friendship' => [],
'analyzes' => [],
'analyzes' => $listActivity,
'mails' => [],
'users' => [],
'infoUser' => [],
'exos' => [],
'member' => []
]);
} catch (\Throwable $th) {
throw $th;
return $this->render("./page/analyze.html.twig", ['tabError' => $taberror]);
}
return $response;
}
#[Route(path: '/exercice', name: 'exercice', methods: ['GET'])] // 8
public function exercice(): Response
{
try {
$entrainementGateway = new EntrainementGateway(new Connexion(DSN, DB_USER, DB_PASSWORD));
$listSearch = $entrainementGateway->getEntrainements();
$map = new EntrainementMapper();
$entrainementEntity = $map->entrainementSqlToEntity($listSearch);
$listUsers = [];
foreach ($entrainementEntity as $entity) {
$training = $map->entrainementEntityToModel($entity);
$listUsers[] = ['idTraining' => number_format($training->getId(), 1), 'date' => $training->getDate()->format("D j F Y"),
'location' => $training->getLocation(), 'description' => $training->getDescription(),
'feedback' => $training->getFeedback()];
}
$response = $this->render('./page/exercice.html.twig',[
'css' => $this->preference->getCookie(),
'pp' => "test2",
'user' => 'johndoe',//$currentUser->getUsername(),
'role' => 'Athlete',//$currentUser->getRole(),
'friendship' => [],
'analyzes' => [],
'mails' => [],
'users' => [],
'infoUser' => [],
'exos' => $listUsers,
'member' => []
]);
} catch (\Throwable $th) {
throw $th;
return $this->render("addfriend.html.twig", ['tabError' => $taberror]);
}
return $response;
}
#[Route(path: '/exercices', name: 'exercices', methods: ['POST'])] // 8
public function exercices(String $type, String $intensite, String $date, IRequest $req): Response
{
$exercicesArray = [
[
'date' => $date,
'type' => $type,
'intensite' => $intensite,
'status' => 'A venir',
]
];
return $this->render('./page/exercice.html.twig',[
'css' => $this->preference->getCookie(),
'pp' => "test2",
'user' => "Doe",
'role' => "Athlète",
'user' => 'johndoe',//$currentUser->getUsername(),
'role' => 'Athlete',//$currentUser->getRole(),
'friendship' => [],
'analyzes' => [],
'mails' => [],
'users' => [],
'infoUser' => [],
'exos' => [],
'exos' => $exercicesArray,
'member' => []
]);
}
@ -97,54 +295,81 @@ class AthleteController extends BaseController
#[Route(path: '/add-friend', name: 'add-friend', methods: ['POST'])]
public function addFriend(string $username, IRequest $req): Response
{
$taberror = [];
$utiliArray = [
[
'nom' => 'John',
'prenom' => 'Doe',
'img' => 'john_doe',
'username' => 'johndoe',
],
[
'nom' => 'Alice',
'prenom' => 'Smith',
'img' => 'alice_smith',
'username' => 'alicesmith',
],
];
// if(!Validation::val_string($name)){
try {
//code...
// $model->userMgr->addFriend($name);
$athleteGateway = new AthleteGateway(new Connexion(DSN, DB_USER, DB_PASSWORD));
$map = new AthleteMapper();
$userSearched = $athleteGateway->getAthleteByFirstName("Bryan");
$athleteEntity = $map->athleteSqlToEntity($userSearched);
$users=[];
foreach ($athleteEntity as $user) {
// $this->userMgr->getCurrentUser()->addFriend($user);
// $currentUser->addFriend($user);
$users = ['nom' => $user->getNom(), 'prenom' => $user->getPrenom(), 'img' => 'test', 'username' => $user->getUsername()];
}
$notif = new NotificationGateway(new Connexion(DSN, DB_USER, DB_PASSWORD));
$mapper = new NotificationMapper();
$date = new DateTime();
$myNotif = new Notification(21, "Demande d'ami de la part de " . $users['nom'], $date, true, 7, 1);
$notif->addNotification($mapper->notificationToEntity($myNotif));
return $this->render('./page/addfriend.html.twig',[
'css' => $this->preference->getCookie(),
'pp' => "test2",
'user' => "Doe",
'role' => "Athlète",
'user' => 'johndoe',//$currentUser->getUsername(),
'role' => 'Athlete',//$currentUser->getRole(),
'friendship' => [],
'analyzes' => [],
'mails' => [],
'users' => $utiliArray,
'users' => $users,
'infoUser' => [],
'exos' => [],
'member' => [],
'responce' => "Notification d'ajout envoyée à $username"
]);
} catch (\Throwable $th) {
//throw $th;
// return $this->render("addfriend.html.twig", ['tabError' => $taberror ]);
throw $th;
return $this->render("addfriend.html.twig", ['tabError' => $taberror ]);
}
// }
}
#[Route(path: '/friend', name: 'friend', methods: ['GET'])]
public function friend(): Response
{
try {
$athleteGateway = new AthleteGateway(new Connexion(DSN, DB_USER, DB_PASSWORD));
$map=new AthleteMapper();
$friendEntity = $athleteGateway->getAthlete();
$listUserEntity = $map->athleteSqlToEntity($friendEntity);
$friendList = [];
foreach ($listUserEntity as $userEntity) {
$friendList[] = ['nom' => $userEntity->getNom(), 'prenom' => $userEntity->getPrenom(), 'img' => 'test', 'username' => 'test'];
}
$response = $this->render('./page/addfriend.html.twig',[
'css' => $this->preference->getCookie(),
'pp' => "test2",
'user' => 'John',//$currentUser->getUsername(),
'role' => 'Athlete',//$currentUser->getRole(),
'friendship' => [],
'analyzes' => [],
'mails' => [],
'users' => $friendList,
'infoUser' => [],
'exos' => [],
'member' => [],
]);
#[Route(path: '/friend', name: 'friend', methods: ['GET'])]
public function friend(): Response
} catch (\Throwable $th) {
throw $th;
return $this->render("addfriend.html.twig", ['tabError' => $taberror]);
}
return $response;
}
/*#[Route(path: '/friendlist', name: 'friendlist', methods: ['POST'])]
public function friendlist(string $username, IRequest $req): Response
{
$utiliArray = [
$friendList = [
[
'nom' => 'John',
'prenom' => 'Doe',
@ -158,19 +383,58 @@ class AthleteController extends BaseController
'username' => 'alicesmith',
],
];
return $this->render('./page/addfriend.html.twig',[
// TODO
// -> Enlever ou bloquer un utilisateur en fonction de son username
//$friendList = currentUser->getFriends();
return $this->render('./page/friend.html.twig',[
'css' => $this->preference->getCookie(),
'pp' => "test2",
'user' => 'johndoe',//$currentUser->getUsername(),
'role' => 'Athlete',//$currentUser->getRole(),
'friendship' => $friendList,
]);
}*/
#[Route(path: '/friendlist', name: 'friendlist2', methods: ['GET'])]
public function friendlist2(): Response
{
try {
$athleteGateway = new AthleteGateway(new Connexion(DSN, DB_USER, DB_PASSWORD));
$map=new AthleteMapper();
$friendEntity = $athleteGateway->getListIdFriends(1/*$currentUser->getId()*/);
$friendList = [];
$listUserEntity = [];
foreach($friendEntity as $users) {
if ($users['idathlete1']==1/*$currentUser->getId()*/){
$user=(int)$users['idathlete2'];
} else {
$user=(int)$users['idathlete1'];
}
$listUserEntity[] = $map->athleteSqlToEntity($athleteGateway->getAthleteById(1));
}
foreach ($listUserEntity as $user) {
$friendList[] = ['nom' => $user[0]->getNom(), 'prenom' => $user[0]->getPrenom(), 'img' => 'test', 'username' => 'test'];
}
$response = $this->render('./page/friend.html.twig',[
'css' => $this->preference->getCookie(),
'pp' => "test2",
'user' => "Doe",
'role' => "Athlète",
'friendship' => [],
'analyzes' => [],
'mails' => [],
'users' => $utiliArray,
'infoUser' => [],
'exos' => [],
'member' => [],
'friendship' => $friendList,
]);
} catch (\Throwable $th) {
throw $th;
return $this->render("addfriend.html.twig", ['tabError' => $taberror]);
}
return $response;
}
// #[Route(path: '/delete-friend', name: 'delete-friend', methods: ['POST'])]
}
?>

@ -4,98 +4,112 @@ namespace App\Controller;
use App\Container;
use App\Router\Request\IRequest;
use App\Router\Response\RedirectResponse;
use App\Router\Response\Response;
use App\Router\Response\IResponse;
use Couchbase\UserManager;
use App\Router\Session;
use Database\AthleteGateway;
use Database\AthleteMapper;
use Database\Connexion;
use Manager\UserManager;
use Shared\Attributes\Route;
use Shared\Validation;
use Twig\Environment;
use Data\Core\Preferences;
use Shared\Log;
use function PHPUnit\Framework\isEmpty;
class AuthController extends BaseController
{
private UserManager $userMgr;
public function __construct(UserManager $manager){
parent::__construct();
$this->userMgr = $manager;
}
#[Route('/login', name: 'login',methods: ['POST'])]
public function login(IRequest $request): IResponse {
public function login(string $email,string $password, IRequest $request): IResponse {
$error = [];
try {
$log=Validation::clean_string($request->getBody()['email']);
$mdp=Validation::clean_string($request->getBody()['password']);
} catch (\Throwable $th) {
$error = "Wrong cred";
}
if($this->container->get(UserManager::class)->login($log,$mdp)){
return $this->redirectToRoute('/');
try {
$log=$email; // should check email with verrify email
$mdp=Validation::clean_string($password);
if($this->userMgr->login($log,$mdp)){
// Log::dd($this->userMgr->getCurrentUser());
// $athleteGateway = new AthleteGateway(new Connexion(DSN, DB_USER, DB_PASSWORD));
// $user = $athleteGateway->getUserByEmail($email);
// $map = new AthleteMapper();
// $userEntity = $map->athleteSqlToEntity($user);
//
// $users = ['username' => $userEntity[0]->getUsername(), 'nom' => $userEntity[0]->getNom(),
// 'prenom' => $userEntity[0]->getPrenom(),'email' => $userEntity[0]->getEmail(), 'sexe' => $userEntity[0]->getSexe(),
// 'taille' => $userEntity[0]->getTaille(), 'poids' => $userEntity[0]->getPoids(), 'motdepasse' => $userEntity[0]->getMotDePasse(),
// 'datenaissance' => $userEntity[0]->getDateNaissance(), 'iscoach' => $userEntity[0]->getIsCoach(), 'img' => 'test'];
return $this->render('./page/home.html.twig', [
'css' => $this->preference->getCookie(),
'pp' => 'test',
// 'user' => $this->userMgr->getCurrentUser()->getUsername(),
// 'role' => $this->userMgr->getCurrentUser()->getRole()
]);
}
else{
$error [] = "Erreur de connexion. Essayez encore";
}
return $this->render('./page/login.html.twig', ['error' => $error]);
return $this->render('./page/login.html.twig',[
'css' => $this->preference->getCookie(),
'login_error'=> $error,
]);
}
#[Route('/log', name: 'baseLog',methods: ['GET'])]
public function index(IRequest $request): IResponse {
// should only cath a type of Exception not all
} catch (\Throwable $th) {
$error [] =$th->getMessage();
return $this->render('./page/login.html.twig',[
'css' => $this->preference->getCookie(),
'pp' => "test2",
'user' => "Doe",
'role' => "Athlète",
'friendship' => [],
'analyzes' => [],
'mails' => [],
'users' => [],
'infoUser' => [],
'exos' => [],
'member' => []
'login_error'=> $error,
]);
}
#[Route('/register', name: 'register' , methods:['GET'])]
public function register(IRequest $request): IResponse
{
if ($request->getMethod() == 'POST') {
$nom = $request->getBody()['nom'];
$prenom = $request->getBody()['prenom'];
$username = $request->getBody()['username'];
$email = $request->getBody()['email'];
$motDePasse = $request->getBody()['motDePasse'];
$sexe = $request->getBody()['sexe'];
$taille = $request->getBody()['taille'];
$poids = $request->getBody()['poids'];
$dateNaissanceStr = $request->getBody()['nom'];
$dateNaissance = new \DateTime($dateNaissanceStr);
}
#[Route('/login', name: 'login2',methods: ['GET'])]
public function login2(IRequest $request): IResponse {
return $this->render('./page/login.html.twig',[
'css' => $this->preference->getCookie()
]);
if (!$dateNaissance) {
throw new \Exception("Date de naissance non valide. Format attendu : YYYY-MM-DD");
}
#[Route('/register', name: 'register2' , methods:['GET'])]
public function register2(IRequest $request): IResponse{
return $this->render('./page/register.html.twig',[
'css' => $this->preference->getCookie()
]);
}
$roleName = $request->getBody()['roleName'];
#[Route('/register', name: 'register' , methods:['POST'])]
public function register(
string $nom,
string $prenom,
string $username,
string $mail,
string $motDePasse,
string $sexe,
float $taille,
float $poids,
string $dateNaissance,
string $roleName,
IRequest $request): IResponse
{
$error = [];
try {
$registrationData = [
'nom' => $nom,
'prenom' => $prenom,
'username' => $username,
'email' => $email,
'email' => $mail,
'sexe' => $sexe,
'taille' => $taille,
'poids' => $poids,
@ -103,45 +117,66 @@ class AuthController extends BaseController
'roleName' => $roleName
];
try {
if ($this->container->get(UserManager::class)->register($email, $motDePasse, $registrationData)) {
return $this->redirectToRoute('/');
if ($this->userMgr->register($mail, $motDePasse, $registrationData) ) {
return new RedirectResponse('/');
} else {
$error [] = 'L\'inscription a échoué. Veuillez réessayer.';
return $this->render('./page/register.html.twig',[
'css' => $this->preference->getCookie(),
'register_error'=> $error,
]);
}
} catch (\Exception $e) {
$error [] = 'Erreur lors de l\'inscription: ' . $e->getMessage();
}
} catch (\Throwable $e) {
$error [] =$e->getMessage();
return $this->render('./page/register.html.twig', ['css' => $this->preference->getCookie(),"register_error" => $error ]);
}
return $this->render('/register.html.twig');
}
#[Route(path: '/mdp', name: 'mdp', methods: ['POST'])]
public function mdp(string $ancienMotDePasse,string $nouveauMotDePasse,string $confirmerMotDePasse, IRequest $req): Response
//string $ancienMotDePasse,string $nouveauMotDePasse,string $confirmerMotDePasse,
#[Route(path: '/forgetPassword', name: 'forget-password2', methods: ['GET'])]
public function forgetPassword2(IRequest $request): IResponse
{
return $this->render('./page/password.html.twig',[
'css' => $this->preference->getCookie(),
]);
}
// CONFIRMER LES DONNESS !!!!! IMPORTANT
#[Route(path: '/forgetPassword', name: 'forget-password', methods: ['POST'])]
public function forgetPassword(string $mail, IRequest $request): IResponse
{
return $this->render('./page/settings.html.twig',[
return $this->render('./page/password.html.twig',[
'css' => $this->preference->getCookie(),
'pp' => "test2",
'user' => "Doe",
'role' => "Athlète",
'friendship' => [],
'analyzes' => [],
'mails' => [],
'users' => [],
'infoUser' => [],
'exos' => [],
'member' => []
]);
}
#[Route(path: '/logout', name: 'logout', methods: ['GET'])]
public function logout(IRequest $request): IResponse
{
$error = [];
try {
if($this->userMgr->deconnecter()){
return new RedirectResponse('/');
}
else{
$error [] = "Erreur de deconnexion. Essayez encore";
return new RedirectResponse('/');
}
} catch (\Throwable $th) {
$error [] =$th->getMessage();
return new RedirectResponse('/');
}
}
}
?>

@ -8,13 +8,14 @@ use App\Router\Response\RedirectResponse;
use App\Router\Response\Response;
use Psr\Container\ContainerInterface;
use Shared\Log;
abstract class BaseController
{
protected Preferences $preference;
public function __construct(){
$this->preference = new Preferences();
}
protected ContainerInterface $container;
@ -62,18 +63,5 @@ abstract class BaseController
return new RedirectResponse($url, $status);
}
protected function redirectToRoute(string $route, array $parameters = [], int $status = 302): RedirectResponse
{
return $this->redirect($this->generateUrl($route, $parameters), $status);
}
/*
* TODO : Should hanle ierror if the route is not existing
* */
protected function generateUrl(string $route, array $parameters = []): string
{
return $this->container->get(\App\Router\Router::class)->generate($route, $parameters);
}
}

@ -1,49 +1,35 @@
<?php
// namespace App\Controller;
namespace App\Controller;
// use App\Container;
// use App\Router\Request\IRequest;
// use App\Router\Response\Response;
// use App\Router\Response\IResponse;
use App\Container;
use App\Router\Request\IRequest;
use App\Router\Response\Response;
use App\Router\Response\IResponse;
// use Shared\Attributes\Route;
// use Twig\Environment;
// use Data\Core\Preferences;
// use Shared\Log;
use Manager\DataManager;
use Shared\Attributes\Route;
use Twig\Environment;
use Data\Core\Preferences;
use Shared\Log;
// #[Route(path: '/coach', name: 'coach')]
// class CoachController extends BaseController
// {
class CoachController extends BaseController
{
// private ICoachManager $coachManager;
// private $security;
// private Environment $twig;
protected Preferences $preference;
// public function __construct(DataManager $dataManager, Security $security)
// {
public function __construct()
{
// session_start();
// $this->coachManager = $dataManager->coachMgr;
// $this->security = $security;
// }
$this->preference = new Preferences();
}
// #[Route(path: '/', name: 'home', methods: ['GET'])]
// public function index(): Response
// {
// return $this->render('./page/home.html.twig',[
// 'css' => $this->preference->getCookie(),
// 'pp' => "test2",
// 'user' => "Doe",
// 'role' => "Athlète",
// 'friendship' => [],
// 'analyzes' => [],
// 'mails' => [],
// 'users' => [],
// 'infoUser' => [],
// 'exos' => [],
// 'member' => []
// ]);
// }
// #[Route('/global-stats', name: 'coach_global_stats', methods: ['GET'])]
// public function globalStats(): Response
// {
@ -51,42 +37,146 @@
// return $this->render('coach/global_stats.html.twig');
// }
// #[Route(path: '/exercice', name: 'exercice', methods: ['GET'])] // 8
// public function exercice(): Response
// {
// return $this->render('./page/exercice.html.twig',[
// 'css' => $this->preference->getCookie(),
// 'pp' => "test2",
// 'user' => "Doe",
// 'role' => "Athlète",
// 'friendship' => [],
// 'analyzes' => [],
// 'mails' => [],
// 'users' => [],
// 'infoUser' => [],
// 'exos' => [],
// 'member' => []
// ]);
#[Route(path: '/coaching', name: 'coaching', methods: ['GET'])]
public function coaching(): Response
{
return $this->render('./page/coaching.html.twig',[
'css' => $this->preference->getCookie(),
'pp' => "test2",
'user' => "Doe",
'role' => "Coach",
'friendship' => [],
'analyzes' => [],
'mails' => [],
'users' => [],
'infoUser' => [],
'exos' => [],
'member' => []
]);
}
#[Route(path: '/search-member', name: 'search-member', methods: ['GET'])]
public function searchMember(string $username, IRequest $req): Response
{
$taberror = [];
// FILTER
$utiliArray = [
[
'nom' => 'John',
'prenom' => 'Doe',
'img' => 'john_doe',
'username' => 'johndoe',
],
[
'nom' => 'Alice',
'prenom' => 'Smith',
'img' => 'alice_smith',
'username' => 'alicesmith',
],
];
// if(!Validation::val_string($name)){
try {
//code...
// $model->userMgr->addFriend($name);
return $this->render('./page/addmember.html.twig',[
'css' => $this->preference->getCookie(),
'pp' => "test2",
'user' => "Doe",
'role' => "Athlète",
'friendship' => [],
'analyzes' => [],
'mails' => [],
'users' => $utiliArray,
'infoUser' => [],
'exos' => [],
'member' => [],
'responce' => "Notification d'ajout envoyée à $username"
]);
} catch (\Throwable $th) {
//throw $th;
// return $this->render("addfriend.html.twig", ['tabError' => $taberror ]);
}
// }
// #[Route(path: '/coaching', name: 'coaching', methods: ['GET'])]
// public function coaching(): Response
// {
// return $this->render('./page/coaching.html.twig',[
// 'css' => $this->preference->getCookie(),
// 'pp' => "test2",
// 'user' => "Doe",
// 'role' => "Athlète",
// 'friendship' => [],
// 'analyzes' => [],
// 'mails' => [],
// 'users' => [],
// 'infoUser' => [],
// 'exos' => [],
// 'member' => []
// ]);
}
#[Route(path: '/add-member', name: 'add-member', methods: ['POST'])]
public function addmember(string $username, IRequest $req): Response
{
$taberror = [];
$utiliArray = [
[
'nom' => 'John',
'prenom' => 'Doe',
'img' => 'john_doe',
'username' => 'johndoe',
],
[
'nom' => 'Alice',
'prenom' => 'Smith',
'img' => 'alice_smith',
'username' => 'alicesmith',
],
];
// if(!Validation::val_string($name)){
try {
//code...
// $model->userMgr->addFriend($name);
return $this->render('./page/addmember.html.twig',[
'css' => $this->preference->getCookie(),
'pp' => "test2",
'user' => "Doe",
'role' => "Athlète",
'friendship' => [],
'analyzes' => [],
'mails' => [],
'users' => $utiliArray,
'infoUser' => [],
'exos' => [],
'member' => [],
'responce' => "Notification d'ajout envoyée à $username"
]);
} catch (\Throwable $th) {
//throw $th;
// return $this->render("addfriend.html.twig", ['tabError' => $taberror ]);
}
// }
}
#[Route(path: '/member', name: 'member', methods: ['GET'])]
public function member(): Response
{
$utiliArray = [
[
'nom' => 'John',
'prenom' => 'Doe',
'img' => 'john_doe',
'username' => 'johndoe',
],
[
'nom' => 'Alice',
'prenom' => 'Smith',
'img' => 'alice_smith',
'username' => 'alicesmith',
],
];
return $this->render('./page/addmember.html.twig',[
'css' => $this->preference->getCookie(),
'pp' => "test2",
'user' => "Doe",
'role' => "Athlète",
'friendship' => [],
'analyzes' => [],
'mails' => [],
'users' => $utiliArray,
'infoUser' => [],
'exos' => [],
'member' => [],
]);
}
// #[Route('/list-athletes', name: 'coach_list_athletes')]
// public function listAthletes(): Response
// {
@ -121,4 +211,4 @@
// // }
// }
}

@ -0,0 +1,423 @@
<?php
//
//namespace App\Controller;
//
//use App\Container;
//use App\Router\Request\IRequest;
//use App\Router\Response\Response;
//use Shared\Attributes\Route;
//use Twig\Environment;
//use Data\Core\Preferences;
//use Shared\Log;
//
//// TODO : Remove this BaseClass
//class Controller extends BaseController
//{
//
// #[Route(path: '/activity', name: 'activity', methods: ['GET'])]
// public function activity(): Response
// {
// return $this->render('./page/activity.html.twig',[
// 'css' => $this->preference->getCookie(),
// 'pp' => "test2",
// 'user' => "Doe",
// 'role' => "Athlète",
// 'friendship' => [],
// 'analyzes' => [],
// 'mails' => [],
// 'users' => [],
// 'infoUser' => [],
// 'exos' => [],
// 'member' => []
// ]);
// }
//
//
// #[Route(path: '/exercices', name: 'exercices', methods: ['POST'])] // 8
// public function exercices(String $type, String $intensite, String $date, IRequest $req): Response
// {
// $exercicesArray = [
// [
// 'date' => $date,
// 'type' => $type,
// 'intensite' => $intensite,
// 'status' => 'A venur',
// ]
// ];
// return $this->render('./page/exercice.html.twig',[
// 'css' => $this->preference->getCookie(),
// 'pp' => "test2",
// 'user' => "Doe",
// 'role' => "Athlète",
// 'friendship' => [],
// 'analyzes' => [],
// 'mails' => [],
// 'users' => [],
// 'infoUser' => [],
// 'exos' => $exercicesArray,
// 'member' => []
// ]);
// }
//
// #[Route(path: '/search-user', name: 'search-user', methods: ['GET'])]
// public function searchUser(string $username, IRequest $req): Response
// {
// $taberror = [];
// // FILTER
// $utiliArray = [
// [
// 'nom' => 'John',
// 'prenom' => 'Doe',
// 'img' => 'john_doe',
// 'username' => 'johndoe',
// ],
// [
// 'nom' => 'Alice',
// 'prenom' => 'Smith',
// 'img' => 'alice_smith',
// 'username' => 'alicesmith',
// ],
// ];
// // if(!Validation::val_string($name)){
// try {
// //code...
// // $model->userMgr->addFriend($name);
// return $this->render('./page/addfriend.html.twig',[
// 'css' => $this->preference->getCookie(),
// 'pp' => "test2",
// 'user' => "Doe",
// 'role' => "Athlète",
// 'friendship' => [],
// 'analyzes' => [],
// 'mails' => [],
// 'users' => $utiliArray,
// 'infoUser' => [],
// 'exos' => [],
// 'member' => [],
// 'responce' => "Notification d'ajout envoyée à $username"
// ]);
// } catch (\Throwable $th) {
// //throw $th;
// // return $this->render("addfriend.html.twig", ['tabError' => $taberror ]);
// }
// // }
//
// }
//
// #[Route(path: '/search-member', name: 'search-member', methods: ['GET'])]
// public function searchMember(string $username, IRequest $req): Response
// {
// $taberror = [];
// // FILTER
// $utiliArray = [
// [
// 'nom' => 'John',
// 'prenom' => 'Doe',
// 'img' => 'john_doe',
// 'username' => 'johndoe',
// ],
// [
// 'nom' => 'Alice',
// 'prenom' => 'Smith',
// 'img' => 'alice_smith',
// 'username' => 'alicesmith',
// ],
// ];
// // if(!Validation::val_string($name)){
// try {
// //code...
// // $model->userMgr->addFriend($name);
// return $this->render('./page/addmember.html.twig',[
// 'css' => $this->preference->getCookie(),
// 'pp' => "test2",
// 'user' => "Doe",
// 'role' => "Athlète",
// 'friendship' => [],
// 'analyzes' => [],
// 'mails' => [],
// 'users' => $utiliArray,
// 'infoUser' => [],
// 'exos' => [],
// 'member' => [],
// 'responce' => "Notification d'ajout envoyée à $username"
// ]);
// } catch (\Throwable $th) {
// //throw $th;
// // return $this->render("addfriend.html.twig", ['tabError' => $taberror ]);
// }
// // }
//
// }
//
// #[Route(path: '/add-member', name: 'add-member', methods: ['POST'])]
// public function addmember(string $username, IRequest $req): Response
// {
// $taberror = [];
// $utiliArray = [
// [
// 'nom' => 'John',
// 'prenom' => 'Doe',
// 'img' => 'john_doe',
// 'username' => 'johndoe',
// ],
// [
// 'nom' => 'Alice',
// 'prenom' => 'Smith',
// 'img' => 'alice_smith',
// 'username' => 'alicesmith',
// ],
// ];
// // if(!Validation::val_string($name)){
// try {
// //code...
// // $model->userMgr->addFriend($name);
// return $this->render('./page/addmember.html.twig',[
// 'css' => $this->preference->getCookie(),
// 'pp' => "test2",
// 'user' => "Doe",
// 'role' => "Athlète",
// 'friendship' => [],
// 'analyzes' => [],
// 'mails' => [],
// 'users' => $utiliArray,
// 'infoUser' => [],
// 'exos' => [],
// 'member' => [],
// 'responce' => "Notification d'ajout envoyée à $username"
// ]);
// } catch (\Throwable $th) {
// //throw $th;
// // return $this->render("addfriend.html.twig", ['tabError' => $taberror ]);
// }
// // }
//
// }
//
// #[Route(path: '/member', name: 'member', methods: ['GET'])]
// public function member(): Response
// {
// $utiliArray = [
// [
// 'nom' => 'John',
// 'prenom' => 'Doe',
// 'img' => 'john_doe',
// 'username' => 'johndoe',
// ],
// [
// 'nom' => 'Alice',
// 'prenom' => 'Smith',
// 'img' => 'alice_smith',
// 'username' => 'alicesmith',
// ],
// ];
// return $this->render('./page/addmember.html.twig',[
// 'css' => $this->preference->getCookie(),
// 'pp' => "test2",
// 'user' => "Doe",
// 'role' => "Athlète",
// 'friendship' => [],
// 'analyzes' => [],
// 'mails' => [],
// 'users' => $utiliArray,
// 'infoUser' => [],
// 'exos' => [],
// 'member' => [],
// ]);
// }
//
//
// #[Route(path: '/friendlist', name: 'friendlist', methods: ['POST'])]
// public function friendlist(string $username, IRequest $req): Response
// {
// $utiliArray = [
// [
// 'nom' => 'John',
// 'prenom' => 'Doe',
// 'img' => 'john_doe',
// 'username' => 'johndoe',
// ],
// [
// 'nom' => 'Alice',
// 'prenom' => 'Smith',
// 'img' => 'alice_smith',
// 'username' => 'alicesmith',
// ],
// ];
// /* TODO */
//
// // -> Enlever ou bloquer un utilisateur en fonction de son username
//
// return $this->render('./page/friend.html.twig',[
// 'css' => $this->preference->getCookie(),
// 'pp' => "test2",
// 'user' => "Doe",
// 'role' => "Athlète",
// 'friendship' => $utiliArray,
// 'analyzes' => [],
// 'mails' => [],
// 'users' => [],
// 'infoUser' => [],
// 'exos' => [],
// 'member' => [],
// ]);
// }
//
// #[Route(path: '/friendlist', name: 'friendlist2', methods: ['GET'])]
// public function friendlist2(): Response
// {
// $utiliArray = [
// [
// 'nom' => 'John',
// 'prenom' => 'Doe',
// 'img' => 'test',
// 'status' => 'johndoe',
// 'username' => 'jdoe',
// ],
// [
// 'nom' => 'Alice',
// 'prenom' => 'Smith',
// 'img' => 'test2',
// 'status' => 'alicesmith',
// 'username' => 'asmith',
// ],
// ];
// return $this->render('./page/friend.html.twig',[
// 'css' => $this->preference->getCookie(),
// 'pp' => "test2",
// 'user' => "Doe",
// 'role' => "Athlète",
// 'friendship' => $utiliArray,
// 'analyzes' => [],
// 'mails' => [],
// 'users' => [],
// 'infoUser' => [],
// 'exos' => [],
// 'member' => [],
// ]);
// }
//
// #[Route(path: '/coaching', name: 'coaching', methods: ['GET'])]
// public function coaching(): Response
// {
// return $this->render('./page/coaching.html.twig',[
// 'css' => $this->preference->getCookie(),
// 'pp' => "test2",
// 'user' => "Doe",
// 'role' => "Athlète",
// 'friendship' => [],
// 'analyzes' => [],
// 'mails' => [],
// 'users' => [],
// 'infoUser' => [],
// 'exos' => [],
// 'member' => []
// ]);
// }
//
// #[Route(path: '/mail', name: 'mail', methods: ['GET'])]
// public function mail(): Response
// {
// return $this->render('./page/mail.html.twig',[
// 'css' => $this->preference->getCookie(),
// 'pp' => "test2",
// 'user' => "Doe",
// 'role' => "Athlète",
// 'friendship' => [],
// 'analyzes' => [],
// 'mails' => [],
// 'users' => [],
// 'infoUser' => [],
// 'exos' => [],
// 'member' => []
// ]);
// }
//
//
// #[Route(path: '/profile', name: 'profile', methods: ['GET'])]
// public function profile(): Response
// {
// return $this->render('./page/profile.html.twig',[
// 'css' => $this->preference->getCookie(),
// 'pp' => "test2",
// 'user' => "Doe",
// 'role' => "Athlète",
// 'friendship' => [],
// 'analyzes' => [],
// 'mails' => [],
// 'users' => [],
// 'infoUser' => [],
// 'exos' => [],
// 'member' => []
// ]);
// }
//
//
// #[Route(path: '/psettings', name: 'psettings', methods: ['POST'])]
// public function psettings(string $nom,string $prenom,string $dateNaissance,string $mail,string $tel, IRequest $req): Response
// {
//
//
// return $this->render('./page/settings.html.twig',[
// 'css' => $this->preference->getCookie(),
// 'pp' => "test2",
// 'user' => $prenom,
// 'role' => "Athlète",
// 'friendship' => [],
// 'analyzes' => [],
// 'mails' => [],
// 'users' => [],
// 'infoUser' => [],
// 'exos' => [],
// 'member' => []
// ]);
// }
//
//
//
//
// #[Route(path: '/pass', name: 'pass', methods: ['GET'])]
// public function pass(): Response
// {
//
// // CONFIRMER LES DONNESS !!!!! IMPORTANT
//
// return $this->render('./page/password.html.twig',[
// 'css' => $this->preference->getCookie(),
// 'pp' => "test2",
// 'user' => "Doe",
// 'role' => "Athlète",
// 'friendship' => [],
// 'analyzes' => [],
// 'mails' => [],
// 'users' => [],
// 'infoUser' => [],
// 'exos' => [],
// 'member' => []
// ]);
// }
//
// #[Route(path: '/password', name: 'password', methods: ['POST'])]
// public function password(string $email, IRequest $req): Response
// {
//
// // CONFIRMER LES DONNESS !!!!! IMPORTANT
//
// return $this->render('./page/login.html.twig',[
// 'css' => $this->preference->getCookie(),
// 'pp' => "test2",
// 'user' => "Doe",
// 'role' => "Athlète",
// 'friendship' => [],
// 'analyzes' => [],
// 'mails' => [],
// 'users' => [],
// 'infoUser' => [],
// 'exos' => [],
// 'member' => []
// ]);
// }
//
//}
//
//
//

@ -48,6 +48,7 @@ class FrontController {
$this->handleError(404, $e->getMessage());
}
catch(\Throwable $e){
Log::dd($e->getLine() . $e->getFile() . $e->getMessage() );
$this->handleError(501, $e->getMessage());
}
}
@ -65,7 +66,7 @@ class FrontController {
// TODO : Don't work need Antoine help
private function handleError(int $statusCode, $message) : void {
if (!$this->container->has(\Twig\Environment::class)) {
throw new \LogicException('You cannot use the "renderView" method if the Twig Bundle is not available. Try running "composer require symfony/twig-bundle".');
throw new \LogicException('You cannot use the "renderView" method if the Twig Bundle is not available. Try running "composer require ".');
}
$response = new Response($this->container->get(\Twig\Environment::class)->render('./error/error.html.twig',['title'=> $message , "code" => $statusCode, "name" => $message, "descr" => $message ]),$statusCode);

@ -1,36 +1,94 @@
<?php
// namespace App\Controller;
// use App\Container;
// use App\Router\Request\IRequest;
// use App\Router\Response\Response;
// use Shared\Attributes\Route;
// use Twig\Environment;
// use Data\Core\Preferences;
// use Shared\Log;
// class HeartRateController extends BaseController
// {
// #[Route(path: '/import', name: 'import', methods: ['GET'])]
// public function import(): Response
// {
// return $this->render('./page/import.html.twig',[
// 'css' => $this->preference->getCookie(),
// 'pp' => "test2",
// 'user' => "Doe",
// 'role' => "Athlète",
// 'friendship' => [],
// 'analyzes' => [],
// 'mails' => [],
// 'users' => [],
// 'infoUser' => [],
// 'exos' => [],
// 'member' => []
// ]);
// }
// }
namespace App\Controller;
use App\Container;
use App\Router\Request\IRequest;
use App\Router\Response\IResponse;
use App\Router\Response\RedirectResponse;
use App\Router\Response\Response;
use Manager\ActivityManager;
use Shared\Attributes\Route;
use Twig\Environment;
use Data\Core\Preferences;
use Shared\Log;
class HeartRateController extends BaseController
{
private ActivityManager $activityMgr;
public function __construct(ActivityManager $manager)
{
parent::__construct();
$this->activityMgr = $manager;
}
#[Route(path: '/import', name: 'import', methods: ['GET'])]
public function import(): Response
{
return $this->render('./page/import.html.twig', [
'css' => $this->preference->getCookie(),
'pp' => "test2",
'user' => "Doe",
'role' => "Athlète",
'friendship' => [],
'analyzes' => [],
'mails' => [],
'users' => [],
'infoUser' => [],
'exos' => [],
'member' => []
]);
}
#[Route(path: '/upload', name: 'upload', methods: ['POST'])]
public function uploadFile(string $activityType, int $effort, IRequest $req): IResponse
{
$error = $this->validateRequest($effort);
if (!empty($error)) {
return $this->renderError($error);
}
$tmp_file = $_FILES['uploaded_file']['tmp_name'];
if (!$this->isValidFile($tmp_file)) {
return $this->renderError(['Failed to get file be sure that you provide the file']);
}
$content = file_get_contents($tmp_file);
try {
if ($this->activityMgr->uploadFile($activityType, 5, $content)) {
return new RedirectResponse('/home');
}
} catch (\Exception $e) {
return $this->renderError([$e->getMessage()]);
}
return $this->renderError(['Failed to save activity.']);
}
private function validateRequest(int $effort): array
{
$error = [];
if ($effort < 0 || $effort > 5) {
$error[] = 'Invalid effort level.';
}
$fileExtension = pathinfo($_FILES['uploaded_file']['name'], PATHINFO_EXTENSION);
if ($fileExtension !== 'fit') {
$error[] = 'Invalid file type. Only .fit files are allowed.';
}
return $error;
}
private function isValidFile(string $tmp_file): bool
{
return file_exists($tmp_file) && is_uploaded_file($tmp_file);
}
private function renderError(array $error): Response
{
// Consolidez la logique de rendu ici
return $this->render('./error/error.html.twig', ['title'=> "Failed" , "code" => 400, "name" => "error import", "descr" => $error[0] ], new Response('$error', 400));
}
}

@ -1,19 +1,66 @@
<?php
// namespace App\Controller;
namespace App\Controller;
// use App\Container;
// use App\Router\Request\IRequest;
// use App\Router\Response\Response;
// use App\Router\Response\IResponse;
use App\Container;
use App\Router\Request\IRequest;
use App\Router\Response\Response;
use Database\AthleteGateway;
use Database\AthleteMapper;
use Database\Connexion;
use Database\NotificationGateway;
use Database\NotificationMapper;
use Shared\Attributes\Route;
use Twig\Environment;
use Data\Core\Preferences;
use Shared\Log;
// use Shared\Attributes\Route;
// use Twig\Environment;
// use Data\Core\Preferences;
// use Shared\Log;
class SocialController extends BaseController
{
protected Preferences $preference;
public function __construct(){
$this->preference = new Preferences();
}
#[Route(path: '/notification', name: 'notification', methods: ['GET'])]
public function mail(): Response
{
try {
$notificationGateway = new NotificationGateway(new Connexion(DSN, DB_USER, DB_PASSWORD));
$listSearch = $notificationGateway->getNotifications();
$map = new NotificationMapper();
$notificationEntity = $map->notificationSqlToEntity($listSearch);
// #[Route(path: '/coach', name: 'coach')]
// class CoachController extends BaseController
// {
$listUsers = [];
foreach ($notificationEntity as $entity) {
$notification = $map->notificationEntityToModel($entity);
$listUsers[] = ['idnotif' => $notification->getId(), 'message' => $notification->getMessage(),
'date' => $notification->getDate()->format("D j F Y"),'statut' => $notification->getStatut(), 'urgence' => $notification->getUrgence(),
'idathlete' => $notification->getToUserId()];
}
$response = $this->render('./page/notification.html.twig',[
'css' => $this->preference->getCookie(),
'pp' => "test2",
'user' => "Doe",
'role' => "Athlète",
'friendship' => [],
'analyzes' => [],
'mails' => $listUsers,
'users' => [],
'infoUser' => [],
'exos' => [],
'member' => []
]);
} catch (\Throwable $th) {
throw $th;
return $this->render("notification.html.twig", ['tabError' => $taberror]);
}
return $response;
}
}

@ -4,20 +4,147 @@ namespace App\Controller;
use App\Container;
use App\Router\Request\IRequest;
use App\Router\Response\RedirectResponse;
use App\Router\Response\Response;
use App\Router\Response\IResponse;
use App\Router\Session;
use DateInterval;
use DateTime;
use Manager\UserManager;
use Shared\Attributes\Route;
use Shared\Validation;
use Twig\Environment;
use Data\Core\Preferences;
use Shared\Log;
use function PHPUnit\Framework\isEmpty;
use Database\Athletegateway;
use Database\Connexion;
class UserController extends BaseController
{
private UserManager $userMgr;
public function __construct(UserManager $manager){
parent::__construct();
$this->userMgr = $manager;
}
#[Route(path: '/', name: 'home', methods: ['GET'])]
#[Route(path: '/', name: 'index', methods: ['GET'])]
public function index(): Response
{
return $this->render('./page/index.html',[
'css' => $this->preference->getCookie(),
'pp' => "test2",
'user' => "Doe",
'role' => "Athlète",
'friendship' => [],
'analyzes' => [],
'mails' => [],
'users' => [],
'infoUser' => [],
'exos' => [],
'member' => []
]);
}
#[Route(path: '/home', name: 'home', methods: ['GET'])]
public function home(): Response
{
$athleteGateway = new AthleteGateway(new Connexion(DSN, DB_USER, DB_PASSWORD));
$activity = $athleteGateway->getListActivity('1');//$currentUser->getId()
// $charts = [];
// $i = 0;
// while ($i <= 12) {
// if ($activity[$i]['mois'] == null) {
// $activity[$i]['mois'] = $i;
// $activity[$i]['nbactivite'] = 0;
// } elseif (($indice = intval($activity[$i]['mois'])) != $i) {
// $temp = $activity[$i]; // Stocker temporairement les données actuelles
// $activity[$i]['mois'] = $i;
// $activity[$i]['nbactivite'] = 0;
// $activity[$indice]['mois'] = $indice;
// $activity[$indice]['nbactivite'] = $temp['nbactivite']; // Restaurer les données
// }
// $charts[] = ['act' => $activity[$i]['nbactivite'], 'mois' => $activity[$i]['mois']];
// $i++;
// }
$charts = [];
$monthNames = [
'Janvier', 'Fevrier', 'Mars', 'Avril', 'Mai', 'Juin',
'Juillet', 'Aout', 'Septembre', 'Octobre', 'Novembre', 'Decembre'
];
$currentDate = new DateTime();
$interval = new DateInterval('P1M'); // Période d'un mois
for ($i = 0; $i < 12; $i++) {
$currentMonth = $currentDate->format('n'); // Format numérique du mois (1 à 12)
$currentMonthName = $monthNames[$currentMonth - 1]; // Mois correspondant dans le tableau
// Recherche de l'indice du mois dans le tableau $activity
$activityIndex = null;
for ($j = 0; $j < count($activity); $j++) {
if (!empty($activity[$j]['mois']) && $activity[$j]['mois'] == $currentMonth) {
$activityIndex = $j;
break;
}
}
// Récupération du nombre d'activités et réinitialisation à 0
$nbActivity = isset($activityIndex) ? $activity[$activityIndex]['nbactivite'] : 0;
if (isset($activityIndex)) {
$activity[$activityIndex]['nbactivite'] = 0;
}
// Log::dd($currentMonth);
$charts[] = ['act' => $nbActivity, 'mois' => $currentMonth];
$currentDate->sub($interval);
}
// Inverser l'ordre des éléments si nécessaire
$charts = array_reverse($charts);
// Log::dd($charts);
//
// $charts = [];
// $monthNames = [
// 'Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin',
// 'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'
// ];
//
// $currentDate = new DateTime();
// $interval = new DateInterval('P1M'); // Période d'un mois
//
// for ($i = 0; $i < 12; $i++) {
//// $currentDate; // Soustraire un mois
//// Log::dd($currentDate);
// $currentMonth = $currentDate->format('n'); // Format numérique du mois (1 à 12)
//// Log::dd($currentMonth);
// $currentMonthName = $monthNames[$currentMonth - 1]; // Mois correspondant dans le tableau
// for($j = 12; $j > 0; $j--) {
// if(!empty($activity[$j]['mois']) || $activity[$j]['mois'] == $currentMonth) {
// $nbAct = $activity[$j]['nbactivite'];
// $activity[$j]['nbactivite'] = 0;
//// Log::dd($activity);
// break;
// }
// }
//
// $nbActivity = isset($nbAct) ? $nbAct : 0;
//// Log::dd($nbActivity);
//
// $charts[] = ['act' => $nbActivity, 'mois' => $currentMonthName];
// $currentDate->sub($interval);
// }
// Log::dd($charts);
// Inverser l'ordre des éléments si nécessaire
// $charts = array_reverse($charts);
//
// Log::dd($charts);
return $this->render('./page/home.html.twig',[
'css' => $this->preference->getCookie(),
'pp' => "test2",
@ -25,6 +152,7 @@ class UserController extends BaseController
'role' => "Athlète",
'friendship' => [],
'analyzes' => [],
'charts' => $charts,
'mails' => [],
'users' => [],
'infoUser' => [],
@ -52,10 +180,28 @@ class UserController extends BaseController
]);
}
#[Route(path: '/profile', name: 'profile', methods: ['GET'])]
public function profile(): Response
{
return $this->render('./page/profile.html.twig',[
'css' => $this->preference->getCookie(),
'pp' => "test2",
'user' => "Doe",
'role' => "Athlète",
'friendship' => [],
'analyzes' => [],
'mails' => [],
'users' => [],
'infoUser' => [],
'exos' => [],
'member' => []
]);
}
#[Route(path: '/preferences', name: 'preferences', methods: ['POST'])]
public function preferences(string $theme, IRequest $req): Response
{
/*TODO*/
// VALIDER LES DONNEES
$this->preference->majCookie($theme);
@ -75,4 +221,25 @@ class UserController extends BaseController
]);
}
// #[Route(path: '/logout', name: 'logout', methods: ['GET'])]
// public function logout(IRequest $request): IResponse
// {
// $error = [];
//
// try {
//
// if($this->userMgr->deconnecter()){
// return new RedirectResponse('/');
// }
// else{
// $error [] = "Erreur de deconnexion. Essayez encore";
// return new RedirectResponse('/');
// }
//
// } catch (\Throwable $th) {
// $error [] =$th->getMessage();
// return new RedirectResponse('/');
// }
// }
}

@ -1,12 +1,15 @@
<?php
namespace App\Router;
use App\Router\Request\IRequest;
/**
* Router class to manage a collection of routes in the application.
* It provides functionalities to add routes and check if a given URL matches any of the defined routes.
*/
class Router {
class Router
{
/**
* The base path for routing.
@ -34,7 +37,8 @@ class Router {
*
* @param string $path The base path for the router.
*/
public function __construct(string $path = "/PHP/project/index.php") {
public function __construct(string $path = "/PHP/project/index.php")
{
$this->path = $path;
$this->routes = new \AltoRouter();
}
@ -46,7 +50,8 @@ class Router {
* @param Route $route The route object.
* @throws \InvalidArgumentException If method is not supported.
*/
public function add(string $method, Route $route) {
public function add(string $method, Route $route)
{
if (!in_array($method, self::$verbs)) {
throw new \InvalidArgumentException("Method not supported");
}
@ -55,20 +60,28 @@ class Router {
/**
* Adds a route for a controller action.
*
* @param string $method The HTTP method.
* TODO : the problème is that AltoRouter is a map so i can't have mutilple Route just by doing this:(i need to find a logic to resolve this beavior) #[Route('/login', name: 'login',methods: ['POST','GET'])]
* @param string|array $methods HTTP method.
* @param string $path The path for the route.
* @param mixed $controller The controller object.
* @param string $action The action method in the controller.
* @param string $name (Optional) The name of the route.
* @throws \InvalidArgumentException If method is not supported.
*/
public function addControllerRoute(string $method, string $path, $controller, string $action, string $name = '') {
public function addControllerRoute(string|array $methods, string $path, $controller, string $action, string $name = '')
{
if (is_string($methods)) {
$methods = [$methods]; // Convert to an array if it's a string
}
foreach ($methods as $method) {
if (!in_array($method, self::$verbs)) {
throw new \InvalidArgumentException("Method not supported");
}
$this->routes->map($method, $path, [$controller, $action], $name);
}
}
// TODO: Implement the extractParams method.
// public function extractParams(string $path) {}
@ -80,7 +93,8 @@ class Router {
* @param callable $callable The callback function.
* @param string $name The name of the route.
*/
public function get(string $path, callable $callable, $name) {
public function get(string $path, callable $callable, $name)
{
$this->routes->map('GET', $path, $callable, $name);
}
@ -92,7 +106,8 @@ class Router {
* @param IRequest $request The request object.
* @return array|null The matched route or null if no match.
*/
public function match(IRequest $request): ?array {
public function match(IRequest $request): ?array
{
return $this->routes->match($request->getRequestUri(), $request->getMethod()) ?: null;
}
@ -101,7 +116,8 @@ class Router {
*
* @return array The array of routes.
*/
public function getRoutes() {
public function getRoutes()
{
return []; // TODO: Implement the actual logic to return routes.
}

@ -111,8 +111,7 @@ class Session
if ( $this->sessionState == self::SESSION_STARTED )
{
$this->sessionState = !session_destroy();
unset( $_SESSION );
session_unset(); // Clear all session variables
return !$this->sessionState;
}

@ -2,26 +2,24 @@
namespace App\Router\Middleware;
use App\Router\Session;
use Network\IAuthService;
use Shared\Log;
use App\Router\Request\IRequest;
use App\Router\Response\RedirectResponse;
class AuthMiddleware extends Middleware {
public function handle(IRequest $request, callable $next) {
// if (isset($_SESSION['user'])) {
// $resp =new RedirectResponse("/");
// $resp->send();
// exit;
// }
// La page nest pas redirigée correctement
// Firefox a détecté que le serveur redirige la demande pour cette adresse dune manière qui naboutira pas.
// La cause de ce problème peut être la désactivation ou le refus des cookies.
// if (!isset($_SESSION['user'])) {
// $resp =new RedirectResponse("/log");
// $resp->send();
// exit;
// }
private IAuthService $auth;
public function __construct(IAuthService $auth) {
$this->auth = $auth;
}
public function handle(IRequest $request, callable $next) {
$excludedUrls = ['/login', '/register','/forgetPassword', '/'];
// Log::dd($this->auth->getCurrentUser());
if ($this->auth->getCurrentUser() === null && !in_array($request->getRequestUri(), $excludedUrls)) {
$resp = new RedirectResponse("/login");
$resp->send();
exit;
}
return parent::handle($request, $next);
}
}

@ -2,6 +2,9 @@
namespace App\Router\Response;
use App\Router\Session;
use Shared\Log;
class RedirectResponse implements IResponse
{
private $content;
@ -49,6 +52,7 @@ class RedirectResponse implements IResponse
public function send(): void
{
http_response_code($this->statusCode);
foreach ($this->headers as $name => $value) {

@ -1,6 +1,8 @@
<?php
namespace App\Router\Response;
use Shared\Log;
class Response implements IResponse {
private string $content;
private int $statusCode;

@ -1,5 +1,6 @@
<!DOCTYPE html>
<html lang="en">
{#<<<<<<< HEAD#}
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
@ -34,14 +35,54 @@
<input class="form-check-input" id="inputRememberPassword" type="checkbox" value="" />
<label class="form-check-label" for="inputRememberPassword">Mémoriser le mot de passe</label>
</div>
<div class="d-flex align-items-center justify-content-between mt-4 mb-0">
<a class="small" href="password.html">Mot de passe oublié ?</a>
<a class="btn btn-primary" href="index.html">Se connecter</a>
</div>
</form>
</div>
<div class="card-footer text-center py-3">
<div class="small"><a href="register.html">Besoin d'un compte ? Inscrivez-vous !</a></div>
{#=======#}
{#<head>#}
{# <meta charset="utf-8" />#}
{# <meta http-equiv="X-UA-Compatible" content="IE=edge" />#}
{# <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />#}
{# <meta name="description" content="" />#}
{# <meta name="author" content="" />#}
{# <title>{% block title %}{% endblock %}</title>#}
{# <link href="/css/{% block css %}base_theme{% endblock %}.css" rel="stylesheet" />#}
{# <script src="https://use.fontawesome.com/releases/v6.3.0/js/all.js" crossorigin="anonymous"></script>#}
{#</head>#}
{#<body class="bg-primary">#}
{#<div id="layoutAuthentication">#}
{# <div id="layoutAuthentication_content">#}
{# <main>#}
{# {% block main %}#}
{# <div class="container">#}
{# <div class="row justify-content-center">#}
{# <div class="col-lg-5">#}
{# <div class="card shadow-lg border-0 rounded-lg mt-5">#}
{# <div class="card-header"><h3 class="text-center font-weight-light my-4">Connexion</h3></div>#}
{# <div class="card-body">#}
{# <form>#}
{# <div class="form-floating mb-3">#}
{# <input class="form-control" id="inputEmail" type="email" placeholder="nom@exemple.com" />#}
{# <label for="inputEmail">Adresse eMail</label>#}
{# </div>#}
{# <div class="form-floating mb-3">#}
{# <input class="form-control" id="inputPassword" type="password" placeholder="Mot de passe" />#}
{# <label for="inputPassword">Mot de passe</label>#}
{# </div>#}
{# <div class="form-check mb-3">#}
{# <input class="form-check-input" id="inputRememberPassword" type="checkbox" value="" />#}
{# <label class="form-check-label" for="inputRememberPassword">Mémoriser le mot de passe</label>#}
{# </div>#}
{# <div class="d-flex align-items-center justify-content-between mt-4 mb-0">#}
{# <a class="small" href="password.html">Mot de passe oublié ?</a>#}
{# <a class="btn btn-primary" href="index.html">Se connecter</a>#}
{# </div>#}
{# </form>#}
{# </div>#}
{# <div class="card-footer text-center py-3">#}
{# <div class="small"><a href="register.html">Besoin d'un compte ? Inscrivez-vous !</a></div>#}
{#>>>>>>> origin/merged_PLE#}
</div>
</div>
</div>

@ -7,9 +7,12 @@
<meta name="description" content="Accueil" />
<meta name="author" content="PINAGOT Antoine" />
<title>{% block title %}{% endblock %}</title>
<link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/simple-datatables@7.1.2/dist/style.min.css" rel="stylesheet" />
<link href="/css/{% block css %}style{% endblock %}.css" rel="stylesheet" />
<link href="/css/{% block css %}styles{% endblock %}.css" rel="stylesheet" />
<script src="https://use.fontawesome.com/releases/v6.3.0/js/all.js" crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css" />
</head>
<body class="sb-nav-fixed">
<nav class="sb-topnav navbar navbar-expand navbar-dark bg-dark">
@ -35,7 +38,7 @@
<li><a class="dropdown-item" href="/profile">Profile</a></li>
<li><a class="dropdown-item" href="/settings">Paramètres</a></li>
<li><hr class="dropdown-divider" /></li>
<li><a class="dropdown-item" href="/logout">Déconnexion</a></li>
<li><a class="dropdown-item" href="/">Déconnexion</a></li>
</ul>
</li>
</ul>
@ -46,7 +49,7 @@
<div class="sb-sidenav-menu">
<div class="nav">
<div class="sb-sidenav-menu-heading">Menu</div>
<a class="nav-link" href="/">
<a class="nav-link" href="/home">
<div class="sb-nav-link-icon"><img src="/assets/img/house.png"></div>
Accueil
</a>
@ -55,9 +58,9 @@
<div class="sb-nav-link-icon"><img src="/assets/img/sprinter.png"></div>
Exercices
</a>
<a class="nav-link" href="/analyses">
<a class="nav-link" href="/activity">
<div class="sb-nav-link-icon"><i class="fas fa-chart-area"></i></div>
Analyses
Activités
</a>
<div class="sb-sidenav-menu-heading">Social</div>
<a class="nav-link" href="/friendlist">
@ -68,7 +71,7 @@
<div class="sb-nav-link-icon"><img src="/assets/img/coaching.png"></div>
Coaching
</a>
<a class="nav-link" href="/mail">
<a class="nav-link" href="/notification">
<div class="sb-nav-link-icon"><img src="/assets/img/letter.png"></div>
Messagerie
</a>

@ -21,7 +21,7 @@
<h1 class="display-1">{% block nb %}{% endblock %}</h1>
<p class="lead">{% block name %}{% endblock %}</p>
<p>{% block descr %}{% endblock %}</p>
<a href="/">
<a href="/home">
<i class="fas fa-arrow-left me-1"></i>
Retour à l'accueil
</a>

@ -4,30 +4,84 @@
{% block css %}{{css}}{% endblock %}
{% block title %}Exercices - HearthTrack{% endblock %}
{% block title %}Analyses - HearthTrack{% endblock %}
{% block user %}{{user}} - {{role}}{% endblock %}
{% block body %}
<div class="container-fluid px-4">
<h1 class="mt-4">Exercices</h1>
<h1 class="mt-4">Analyses</h1>
<ol class="breadcrumb mb-4">
<li class="breadcrumb-item"><a href="/">Accueil</a></li>
<li class="breadcrumb-item active">Exercices</li>
<li class="breadcrumb-item"><a href="/home">Accueil</a></li>
<li class="breadcrumb-item active">Analyses</li>
</ol>
<div class="card-body">
<div class="datatable-container">
<form method="post" action="/exercices">
Type : <input type="text" name="type"/>
Intensité : <input type="text" name="intensite"/>
Date : <input type="text" name="date"/>
<button class="btn btn-primary btn-mrg" id="btnNavbarSearch" type="submit">Ajouter l'exercice</button>
</form>
<table id="datatablesSimple" class="datatable-table">
<thead>
<tr>
<th>Date</th>
<th>Heure de début</th>
<th>Heure de fin</th>
<th>Type</th>
<th>effort ressenti</th>
<th>Variabilité</th>
<th>Variance</th>
<th>Ecart type</th>
<th>Moyenne</th>
<th>Maximum</th>
<th>Minimum</th>
<th>Temperature moyenne</th>
<th></th>
</tr>
</thead>
<tfoot>
<tr>
<th>Date</th>
<th>Heure de début</th>
<th>Heure de fin</th>
<th>Type</th>
<th>effort ressenti</th>
<th>Variabilité</th>
<th>Variance</th>
<th>Ecart type</th>
<th>Moyenne</th>
<th>Maximum</th>
<th>Minimum</th>
<th>Temperature moyenne</th>
</tr><tr></tr>
</tfoot>
<tbody>
{% for analyze in analyzes %}
<tr>
<td>{{analyze.date}}</td>
<td>{{analyze.heureDebut}}</td>
<td>{{analyze.heureFin}}</td>
<td>{{analyze.type}}</td>
<td>{{analyze.effortRessenti}}</td>
<td>{{analyze.variabilite}}</td>
<td>{{analyze.variance}}</td>
<td>{{analyze.ecartType}}</td>
<td>{{analyze.moyenne}} Bpm</td>
<td>{{analyze.max}} Bpm</td>
<td>{{analyze.min}} Bpm</td>
<td>{{analyze.temperature}} °C</td>
<td><a href="/analyze?id={{ analyze.idactivity }}">En savoir plus</a></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<h1>{{responce}}</h1>
{% endblock %}
{% block script %}
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
<script src="js/scripts.js"></script>
<script src="https://cdn.jsdelivr.net/npm/simple-datatables@7.1.2/dist/umd/simple-datatables.min.js" crossorigin="anonymous"></script>
<script src="js/datatables-simple-demo.js"></script>
{% endblock %}

@ -12,7 +12,7 @@
<div class="container-fluid px-4">
<h1 class="mt-4">Liste d'ami</h1>
<ol class="breadcrumb mb-4">
<li class="breadcrumb-item"><a href="/">Accueil</a></li>
<li class="breadcrumb-item"><a href="/home">Accueil</a></li>
<li class="breadcrumb-item active">Ami</li>
</ol>

@ -12,7 +12,7 @@
<div class="container-fluid px-4">
<h1 class="mt-4">Coaching</h1>
<ol class="breadcrumb mb-4">
<li class="breadcrumb-item"><a href="/">Accueil</a></li>
<li class="breadcrumb-item"><a href="/home">Accueil</a></li>
<li class="breadcrumb-item active">Coaching</li>
</ol>

@ -4,63 +4,151 @@
{% block css %}{{css}}{% endblock %}
{% block title %}Analyses - HearthTrack{% endblock %}
{% block title %}Exercices - HearthTrack{% endblock %}
{% block user %}{{user}} - {{role}}{% endblock %}
{% block body %}
<div class="container-fluid px-4">
<h1 class="mt-4">Analyses</h1>
<h1 class="mt-4">Exercices</h1>
<ol class="breadcrumb mb-4">
<li class="breadcrumb-item"><a href="/">Accueil</a></li>
<li class="breadcrumb-item active">Analyses</li>
<li class="breadcrumb-item"><a href="/home">Accueil</a></li>
<li class="breadcrumb-item active">Exercices</li>
<script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
</ol>
<div class="card-body">
<div class="datatable-container">
<table id="datatablesSimple" class="datatable-table">
<thead>
<tr>
<th>Date</th>
<th>Type</th>
<th>BMP</th>
<th>KM/H</th>
<th>Distance</th>
<th></th>
</tr>
</thead>
<tfoot>
<tr>
<th>Date</th>
<th>Type</th>
<th>BPM</th>
<th>KM/H</th>
<th>Distance</th>
</tr><tr></tr>
{# <table id="datatablesSimple" class="datatable-table">#}
{# <thead>#}
{# <tr>#}
{# <th>id FC</th>#}
{# <th>altitude</th>#}
{# <th>temps</th>#}
{# <th>Temperature</th>#}
{# <th>bpm</th>#}
{# <th>longitude</th>#}
{# <th>latitude</th>#}
{# <th>activiteid</th>#}
{# <th></th>#}
{# </tr>#}
{# </thead>#}
{# {% for analyze in analyzes %}#}
{# <tbody>#}
{# <tr>#}
{# <td>{{analyze.idfc}}</td>#}
{# <td>{{analyze.altitude}}</td>#}
{# <td>{{analyze.temps}}</td>#}
{# <td>{{analyze.temperature}}</td>#}
{# <td>{{analyze.bpm}}</td>#}
{# <td>{{analyze.longitude}}</td>#}
{# <td>{{analyze.latitude}}</td>#}
{# <td>{{analyze.activiteid}}</td>#}
{# <td><a href="/home">Home</a></td>#}
{# </tr>#}
{# </tbody>#}
{# {% endfor %}#}
{# </table>#}
{# <div>#}
{# <canvas id="myChart" width="400" height="200">#}
{# <p>Hello Fallback World</p>#}
{# </canvas>#}
{# </div>#}
{# <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>#}
{# <script>#}
{# const ctx = document.getElementById('myChart');#}
{# new Chart(ctx, {#}
{# type: 'line',#}
{# data: {#}
{# labels: [#}
{# {% for analyze in analyzes %}#}
{# {{analyze.temps}}#}
{# {% endfor %}#}
{# ],#}
{# datasets: [{#}
{# label: 'Batement par minute',#}
{# data: [#}
{# {% for analyze in analyzes %}#}
{# {{analyze.bpm}}#}
{# {% endfor %}#}
{# ],#}
{# borderWidth: 1#}
{# }]#}
{# },#}
{# options: {#}
{# scales: {#}
{# y: {#}
{# beginAtZero: true#}
{# }#}
{# }#}
{# }#}
{# });#}
{# </script>#}
<div>
<canvas id="myChart" height="50">
<p>Hello Fallback World</p>
</canvas>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
const ctx = document.getElementById('myChart');
const labels = [
{% for analyze in analyzes %}
'{{ analyze.temps }}',
{% endfor %}
];
const data = [
{% for analyze in analyzes %}
{{ analyze.bpm }},
{% endfor %}
];
new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [{
label: 'Batement par minute',
data: data,
borderWidth: 1,
tension: 0.2,
fill: false,
borderColor: 'rgb(255, 0, 0)'
}]
},
options: {
scales: {
y: {
beginAtZero: false
}
}
}
});
</script>
<div class="card-body">
<div id="map" style="height: 500px;"></div>
<script>
document.addEventListener("DOMContentLoaded", function () {
{#var lat = {{ analyze.latitude }}#}
var map = L.map('map').setView([45.75771709151474, 3.113484980409329], 14);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
}).addTo(map);
</tfoot>
<tbody>
{% for analyze in analyzes %}
<tr>
<td>{{analyze.date}}</td>
<td>{{analyze.type}}</td>
<td>{{analyze.bpm}}</td>
<td>{{analyze.kmh}} Km/H</td>
<td>{{analyze.distance}} Km</td>
<td><a href="#about">En savoir plus</a></td>
</tr>
var marker = L.marker([{{ analyze.latitude }}, {{ analyze.longitude }}]).addTo(map);
marker.bindPopup(`
<strong>Date:</strong> {{ analyze.temps }}<br>
<strong>FC:</strong> {{ analyze.bpm }}<br>
<strong>Altitude:</strong> {{ analyze.altitude }}<br>
<strong>Temperature:</strong> {{ analyze.temperature }}
`);
{% endfor %}
</tbody>
</table>
});
</script>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block script %}
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script>
<script src="js/scripts.js"></script>
<script src="https://cdn.jsdelivr.net/npm/simple-datatables@7.1.2/dist/umd/simple-datatables.min.js" crossorigin="anonymous"></script>
<script src="js/datatables-simple-demo.js"></script>
<h1>{{responce}}</h1>
{% endblock %}

@ -13,7 +13,7 @@
<div class="container-fluid px-4">
<h1 class="mt-4">Coaching</h1>
<ol class="breadcrumb mb-4">
<li class="breadcrumb-item"><a href="/">Accueil</a></li>
<li class="breadcrumb-item"><a href="/home">Accueil</a></li>
<li class="breadcrumb-item active">Coaching</li>
</ol>
<style>.btn-mrg{margin:15px; margin-left:83%}</style>

@ -9,10 +9,11 @@
{% block user %}{{user}} - {{role}}{% endblock %}
{% block body %}
<div class="container-fluid px-4">
<h1 class="mt-4">Exercices</h1>
<ol class="breadcrumb mb-4">
<li class="breadcrumb-item"><a href="/">Accueil</a></li>
<li class="breadcrumb-item"><a href="/home">Accueil</a></li>
<li class="breadcrumb-item active">Exercices</li>
</ol>
<style>.btn-mrg{margin:15px; margin-left:85%}</style>
@ -25,18 +26,39 @@
<thead>
<tr>
<th>Date</th>
<th>Type</th>
<th>Intensité prévue</th>
<th>Status</th>
<th>Description</th>
<th>Localisation</th>
<th>FeedBack</th>
</tr>
</thead>
<tbody>
<script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
{% for exo in exos %}
<tr>
<td>{{ exo.date }}</td>
<td>{{exo.type}}</td>
<td>{{exo.intensite}}</td>
<td>{{exo.status}}</td>
<td>{{ exo.description }}</td>
<td>
<div id="map-{{ loop.index }}"
data-lat=45.758285982369415
data-lng=3.110223414416445
style="height: 300px; width: 650px;"></div>
<script>
document.addEventListener("DOMContentLoaded", function() {
var map{{ loop.index }} = L.map('map-{{ loop.index }}').setView([45.75771709151474, 3.113484980409329], 14);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
}).addTo(map{{ loop.index }});
var marker{{ loop.index }} = L.marker([45.75771709151474, 3.113484980409329]).addTo(map{{ loop.index }});
marker{{ loop.index }}.bindPopup(`
<strong>Date:</strong> {{ exo.date }}<br>
<strong>Description:</strong> {{ exo.description }}<br>
<strong>Feedback:</strong> {{ exo.feedback }}
`);
});
</script>
</td>
<td>{{ exo.feedback }}</td>
</tr>
{% endfor %}
</tbody>

@ -12,7 +12,7 @@
<div class="container-fluid px-4">
<h1 class="mt-4">Liste d'ami</h1>
<ol class="breadcrumb mb-4">
<li class="breadcrumb-item"><a href="/">Accueil</a></li>
<li class="breadcrumb-item"><a href="/home">Accueil</a></li>
<li class="breadcrumb-item active">Ami</li>
</ol>
<style>.btn-mrg{margin:15px; margin-left:87%}</style>

@ -21,8 +21,85 @@
<i class="fas fa-chart-area me-1"></i>
Stastiques globales
</div>
<div class="card-body">
{# <canvas id="myAreaChart" width="100%" height="40"></canvas> #}
<div>
<canvas id="myChart" height="50" width="100">
<p>Hello Fallback World</p>
</canvas>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
const ctx = document.getElementById('myChart');
const labels = [
{% for chart in charts %}
{{ chart.mois }},
{% endfor %}
];
const data = [
{% for chart in charts %}
{{ chart.act }},
{% endfor %}
];
new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [{
label: 'Activité du mois',
data: data,
borderWidth: 1,
tension: 0.2,
fill: false,
borderColor: 'rgb(255, 0, 0)'
}]
},
options: {
scales: {
x: {
// type: 'linear', // Utiliser une échelle linéaire pour les mois
position: 'bottom'
},
y: {
beginAtZero: true
}
}
}
});
{#const ctx = document.getElementById('myChart');#}
{#const labels = [#}
{# {% for chart in charts %}#}
{# '{{ chart.mois }}',#}
{# {% endfor %}#}
{#];#}
{#const data = [#}
{# {% for chart in charts %}#}
{# {{ chart.act }},#}
{# {% endfor %}#}
{#];#}
{#new Chart(ctx, {#}
{# type: 'line',#}
{# data: {#}
{# labels: labels,#}
{# datasets: [{#}
{# label: 'Activité du mois',#}
{# data: data,#}
{# borderWidth: 1,#}
{# tension: 0.2,#}
{# fill: false,#}
{# borderColor: 'rgb(255, 0, 0)'#}
{# }]#}
{# },#}
{# options: {#}
{# scales: {#}
{# y: {#}
{# beginAtZero: true#}
{# }#}
{# }#}
{# }#}
{#});#}
</script>
</div>
</div>
</div>

@ -12,30 +12,53 @@
<div class="container-fluid px-4">
<h1 class="mt-4">Importer</h1>
<ol class="breadcrumb mb-4">
<li class="breadcrumb-item"><a href="home.html">Accueil</a></li>
<li class="breadcrumb-item"><a href="/">Accueil</a></li>
<li class="breadcrumb-item active">Importer</li>
</ol>
<div class="row">
<div class="col">
<div class="card mb-4">
<div class="card-header">
<img src="/assets/img/bupload.png"></i>
Importer un fichier .fit
</div>
<div class="card-body">
<div style="text-align:center; margin-bottom: 15px;">
<label for="file-input">
<img src="/assets/img/uploadW.svg"/>
<div class="flex items-center justify-center">
<div class="sm:max-w-lg w-full p-10 bg-white rounded-xl z-10 ">
<div class="text-center">
<h2 class="mt-5 text-3xl font-bold text-gray-900">
Importer un fichier .fit !
</h2>
<p class="mt-2 text-sm text-gray-400">Let's go</p>
</div>
<form class="mt-8 space-y-3" action="/upload" method="POST" enctype="multipart/form-data">
<div class="grid grid-cols-1 space-y-2">
<label class="text-sm font-bold text-gray-500 tracking-wide">Veuillez renseigner le type d'activité</label>
<input class="text-base p-2 border border-gray-300 rounded-lg focus:outline-none focus:border-indigo-500" type="text" name="activityType" placeholder="Course à pied">
</div>
<div class="grid grid-cols-1 space-y-2">
<label class="text-sm font-bold text-gray-500 tracking-wide">Effort Level (0-5)</label>
<input class="text-base p-2 border border-gray-300 rounded-lg focus:outline-none focus:border-indigo-500" type="number" name="effort" min="0" max="5" placeholder="Enter effort level (0-5)">
</div>
<div class="grid grid-cols-1 space-y-2">
<label class="text-sm font-bold text-gray-500 tracking-wide">Attach Document</label>
<div class="flex items-center justify-center w-full">
<label class="flex flex-col rounded-lg border-4 border-dashed w-full h-60 p-10 group text-center cursor-pointer">
<div class="h-full w-full text-center flex flex-col items-center justify-center items-center ">
<div class="flex flex-auto max-h-48 w-2/5 mx-auto -mt-10">
<img class="has-mask h-36 object-center" src="https://img.freepik.com/free-vector/image-upload-concept-landing-page_52683-27130.jpg?size=338&ext=jpg" alt="freepik image">
</div>
<p class="pointer-none text-gray-500 "><span class="text-sm">Drag and drop</span> files here <br /> or
<span class="text-blue-600 hover:underline">select a file</span>
from your computer</p>
</div>
<input type="file" class="hidden" id="file-input" name="uploaded_file" accept=".fit">
</label>
<form action="/analyses" method="post">
<input id="file-input" type="file" name="fileToUpload" value=""style="visibility: hidden; width:0; height:0;"/>
<input type="submit" value="Soumettre le fichier" name="submit"/>
</form>
<p id="file-name-display" class="text-sm text-gray-500"></p>
</div>
</div>
<p class="text-sm text-gray-300">
<span>File type: .fit</span>
</p>
<div class="flex justify-center">
<button type="submit" class="my-5 w-3/4 flex justify-center bg-blue-500 text-gray-100 p-4 rounded-full tracking-wide
font-semibold focus:outline-none focus:shadow-outline hover:bg-blue-600 shadow-lg cursor-pointer transition ease-in duration-300">
Upload
</button>
</div>
</form>
</div>
</div>
</div>

@ -1 +1,296 @@
<a href="/log">Se connecter</button>
<!--<<<<<<< HEAD-->
<!--<a href="/log">Se connecter</button>-->
<!--=======-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<meta name="description" content="index page du site web" />
<meta name="author" content="PINAGOT Antoine" />
<title>HeartTrack - Getting Started</title>
<link rel="icon" type="image/x-icon" href="assets/favicon.ico" />
<!-- Bootstrap icons-->
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.5.0/font/bootstrap-icons.css" rel="stylesheet" />
<!-- Google fonts-->
<link rel="preconnect" href="https://fonts.gstatic.com" />
<link href="https://fonts.googleapis.com/css2?family=Newsreader:ital,wght@0,600;1,600&amp;display=swap" rel="stylesheet" />
<link href="https://fonts.googleapis.com/css2?family=Mulish:ital,wght@0,300;0,500;0,600;0,700;1,300;1,500;1,600;1,700&amp;display=swap" rel="stylesheet" />
<link href="https://fonts.googleapis.com/css2?family=Kanit:ital,wght@0,400;1,400&amp;display=swap" rel="stylesheet" />
<!-- Core theme CSS (includes Bootstrap)-->
<link href="css/styles2.css" rel="stylesheet" />
</head>
<body id="page-top">
<!-- Navigation-->
<nav class="navbar navbar-expand-lg navbar-light fixed-top shadow-sm" id="mainNav">
<div class="container px-5">
<a class="navbar-brand fw-bold" href="/">HeartTrack</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
Menu
<i class="bi-list"></i>
</button>
<div class="collapse navbar-collapse" id="navbarResponsive">
<ul class="navbar-nav ms-auto me-4 my-3 my-lg-0">
<li class="nav-item"><a class="nav-link me-lg-3" href="/login">Se connecter</a></li>
<li class="nav-item"><a class="nav-link me-lg-3" href="/register">S'inscire</a></li>
</ul>
<button class="btn btn-primary rounded-pill px-3 mb-2 mb-lg-0" data-bs-toggle="modal" data-bs-target="#feedbackModal">
<span class="d-flex align-items-center">
<i class="bi-chat-text-fill me-2"></i>
<span class="small">Donnez votre avis</span>
</span>
</button>
</div>
</div>
</nav>
<!-- Mashead header-->
<header class="masthead">
<div class="container px-5">
<div class="row gx-5 align-items-center">
<div class="col-lg-6">
<!-- Mashead text and app badges-->
<div class="mb-5 mb-lg-0 text-center text-lg-start">
<h1 class="display-1 lh-1 mb-3">HeartTrack pour mobile !</h1>
<p class="lead fw-normal text-muted mb-5">Une application pour suivre vos analyses en direct, est en cours de développement.</p>
<!-- <div class="d-flex flex-column flex-lg-row align-items-center">
<a class="me-lg-3 mb-4 mb-lg-0" href="#!"><img class="app-badge" src="assets/img/google-play-badge.svg" alt="..." /></a>
<a href="#!"><img class="app-badge" src="assets/img/app-store-badge.svg" alt="..." /></a>
</div> -->
</div>
</div>
<div class="col-lg-6">
<!-- Masthead device mockup feature-->
<div class="masthead-device-mockup">
<svg class="circle" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="circleGradient" gradientTransform="rotate(45)">
<stop class="gradient-start-color" offset="0%"></stop>
<stop class="gradient-end-color" offset="100%"></stop>
</linearGradient>
</defs>
<circle cx="50" cy="50" r="50"></circle></svg
><svg class="shape-1 d-none d-sm-block" viewBox="0 0 240.83 240.83" xmlns="http://www.w3.org/2000/svg">
<rect x="-32.54" y="78.39" width="305.92" height="84.05" rx="42.03" transform="translate(120.42 -49.88) rotate(45)"></rect>
<rect x="-32.54" y="78.39" width="305.92" height="84.05" rx="42.03" transform="translate(-49.88 120.42) rotate(-45)"></rect></svg
><svg class="shape-2 d-none d-sm-block" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="50" r="50"></circle></svg>
<div class="device-wrapper">
<div class="device" data-device="iPhoneX" data-orientation="portrait" data-color="black">
<div class="screen bg-black">
<!-- PUT CONTENTS HERE:-->
<!-- * * This can be a video, image, or just about anything else.-->
<!-- * * Set the max width of your media to 100% and the height to-->
<!-- * * 100% like the demo example below.-->
<video muted="muted" autoplay="" loop="" style="max-width: 100%; height: 100%"><source src="assets/img/demo-screen.mp4" type="video/mp4" /></video>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</header>
<!-- Quote/testimonial aside-->
<aside class="text-center bg-gradient-primary-to-secondary">
<div class="container px-5">
<div class="row gx-5 justify-content-center">
<div class="col-xl-8">
<div class="h2 fs-1 text-white mb-4">"Une solution gratuite pour analyser vos données de fréquences cardiaques !"</div>
<img src="assets/img/tnw-logo.svg" alt="..." style="height: 3rem" />
</div>
</div>
</div>
</aside>
<!-- App features section-->
<section id="features">
<div class="container px-5">
<div class="row gx-5 align-items-center">
<div class="col-lg-8 order-lg-1 mb-5 mb-lg-0">
<div class="container-fluid px-5">
<div class="row gx-5">
<div class="col-md-6 mb-5">
<!-- Feature item-->
<div class="text-center">
<i class="bi-phone icon-feature text-gradient d-block mb-3"></i>
<h3 class="font-alt">Web/Mobile</h3>
<p class="text-muted mb-0">Gardez les informations tant bien dans l'application que sur le site web avec la synchronisation des données !</p>
</div>
</div>
<div class="col-md-6 mb-5">
<!-- Feature item-->
<div class="text-center">
<i class="bi-cast icon-feature text-gradient d-block mb-3"></i>
<h3 class="font-alt">Importez vos données</h3>
<p class="text-muted mb-0">Importez vos données directement depuis un fichier .FIT !</p>
</div>
</div>
</div>
<div class="row">
<div class="col-md-6 mb-5 mb-md-0">
<!-- Feature item-->
<div class="text-center">
<i class="bi-gift icon-feature text-gradient d-block mb-3"></i>
<h3 class="font-alt">Gratuit</h3>
<p class="text-muted mb-0">Ce site et cette application sont entièrement gratuites !</p>
</div>
</div>
<div class="col-md-6">
<!-- Feature item-->
<div class="text-center">
<i class="bi-patch-check icon-feature text-gradient d-block mb-3"></i>
<h3 class="font-alt">Open Source</h3>
<p class="text-muted mb-0">Ce projet est fait en Open Source, tout le monde peut y accéder !</p>
</div>
</div>
</div>
</div>
</div>
<div class="col-lg-4 order-lg-0">
<!-- Features section device mockup-->
<div class="features-device-mockup">
<svg class="circle" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="circleGradient" gradientTransform="rotate(45)">
<stop class="gradient-start-color" offset="0%"></stop>
<stop class="gradient-end-color" offset="100%"></stop>
</linearGradient>
</defs>
<circle cx="50" cy="50" r="50"></circle></svg
><svg class="shape-1 d-none d-sm-block" viewBox="0 0 240.83 240.83" xmlns="http://www.w3.org/2000/svg">
<rect x="-32.54" y="78.39" width="305.92" height="84.05" rx="42.03" transform="translate(120.42 -49.88) rotate(45)"></rect>
<rect x="-32.54" y="78.39" width="305.92" height="84.05" rx="42.03" transform="translate(-49.88 120.42) rotate(-45)"></rect></svg
><svg class="shape-2 d-none d-sm-block" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="50" r="50"></circle></svg>
<div class="device-wrapper">
<div class="device" data-device="iPhoneX" data-orientation="portrait" data-color="black">
<div class="screen bg-black">
<!-- PUT CONTENTS HERE:-->
<!-- * * This can be a video, image, or just about anything else.-->
<!-- * * Set the max width of your media to 100% and the height to-->
<!-- * * 100% like the demo example below.-->
<video muted="muted" autoplay="" loop="" style="max-width: 100%; height: 100%"><source src="assets/img/demo-screen.mp4" type="video/mp4" /></video>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Basic features section-->
<section class="bg-light">
<div class="container px-5">
<div class="row gx-5 align-items-center justify-content-center justify-content-lg-between">
<div class="col-12 col-lg-5">
<h2 class="display-4 lh-1 mb-4">Fonctionnalité supplémentaire de notre application</h2>
<p class="lead fw-normal text-muted mb-5 mb-lg-0">Notre application propose un service de coaching personnalisé en fonction du besoin que vous avez en tant qu'athelète. Vous pouvez aussi fournir un service de coaching aux autres utilisateurs en vous inscrivant en tant que coach sportif.</p>
</div>
<div class="col-sm-8 col-md-6">
<div class="px-5 px-sm-0"><img class="img-fluid rounded-circle" src="./assets/img/bg1.png" alt="..." /></div>
</div>
</div>
</div>
</section>
<!-- Call to action section-->
<section class="cta">
<div class="cta-content">
<div class="container px-5">
<h2 class="text-white display-1 lh-1 mb-4">
Arrêtez d'attendre...
<br />
Inscrivez-vous !
</h2>
<!-- <a class="btn btn-outline-light py-3 px-4 rounded-pill" href="#" target="_blank">Download for free</a> -->
</div>
</div>
</section>
<!-- App badge section-->
<section class="bg-gradient-primary-to-secondary" id="download">
<div class="container px-5">
<h2 class="text-center text-white font-alt mb-4">Application mobile bientôt disponible !</h2>
<!-- <div class="d-flex flex-column flex-lg-row align-items-center justify-content-center">
<a class="me-lg-3 mb-4 mb-lg-0" href="#!"><img class="app-badge" src="assets/img/google-play-badge.svg" alt="..." /></a>
<a href="#!"><img class="app-badge" src="assets/img/app-store-badge.svg" alt="..." /></a>
</div> -->
</div>
</section>
<!-- Footer-->
<footer class="bg-black text-center py-5">
<div class="container px-5">
<div class="text-white-50 small">
<div class="mb-2">&copy; HeartTrack 2023. Tous droits réservé.</div>
<a href="#!">Politique de confidentialité</a>
<span class="mx-1">&middot;</span>
<a href="#!">Termes & Conditions d'utilisation</a>
</div>
</div>
</footer>
<!-- Feedback Modal-->
<div class="modal fade" id="feedbackModal" tabindex="-1" aria-labelledby="feedbackModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header bg-gradient-primary-to-secondary p-4">
<h5 class="modal-title font-alt text-white" id="feedbackModalLabel">Envoyez votre avis</h5>
<button class="btn-close btn-close-white" type="button" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body border-0 p-4">
<!-- * * * * * * * * * * * * * * *-->
<!-- * * SB Forms Contact Form * *-->
<!-- * * * * * * * * * * * * * * *-->
<!-- This form is pre-integrated with SB Forms.-->
<!-- To make this form functional, sign up at-->
<!-- https://startbootstrap.com/solution/contact-forms-->
<!-- to get an API token!-->
<form id="contactForm" data-sb-form-api-token="API_TOKEN">
<!-- Name input-->
<div class="form-floating mb-3">
<input class="form-control" id="name" type="text" placeholder="Entrez votre nom complet..." data-sb-validations="required" />
<label for="name">Nom complet</label>
<div class="invalid-feedback" data-sb-feedback="name:required">Un nom est requis.</div>
</div>
<!-- Email address input-->
<div class="form-floating mb-3">
<input class="form-control" id="email" type="email" placeholder="nom@exemple.com" data-sb-validations="required,email" />
<label for="email">Adresse email</label>
<div class="invalid-feedback" data-sb-feedback="email:required">Adresse email requis.</div>
<div class="invalid-feedback" data-sb-feedback="email:email">L'adresse email n'est pas valide.</div>
</div>
<!-- Phone number input-->
<div class="form-floating mb-3">
<input class="form-control" id="phone" type="tel" placeholder="06 12 34 56 78" data-sb-validations="required" />
<label for="phone">Numéro de téléphone</label>
<div class="invalid-feedback" data-sb-feedback="phone:required">Numéro de téléphone requis.</div>
</div>
<!-- Message input-->
<div class="form-floating mb-3">
<textarea class="form-control" id="message" type="text" placeholder="Entrez votre message..." style="height: 10rem" data-sb-validations="required"></textarea>
<label for="message">Message</label>
<div class="invalid-feedback" data-sb-feedback="message:required">Un message est requis.</div>
</div>
<!-- Submit success message-->
<!---->
<!-- This is what your users will see when the form-->
<!-- has successfully submitted-->
<div class="d-none" id="submitSuccessMessage">
<div class="text-center mb-3">
<div class="fw-bolder">Avis envoyé !</div>
</div>
</div>
<!-- Submit error message-->
<!---->
<!-- This is what your users will see when there is-->
<!-- an error submitting the form-->
<div class="d-none" id="submitErrorMessage"><div class="text-center text-danger mb-3">Erreur du formulaire !</div></div>
<!-- Submit Button-->
<div class="d-grid"><button class="btn btn-primary rounded-pill btn-lg disabled" id="submitButton" type="submit">Envoyer</button></div>
</form>
</div>
</div>
</div>
</div>
<!-- Bootstrap core JS-->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"></script>
<!-- Core theme JS-->
<script src="js/scripts2.js"></script>
<script src="https://cdn.startbootstrap.com/sb-forms-latest.js"></script>
</body>
</html>
<!--&gt;>>>>>> origin/merged_PLE-->

@ -5,33 +5,72 @@
{% block title %}Connexion - HearthTrack{% endblock %}
{% block main %}
{#<<<<<<< HEAD#}
<div class="container">
<div class="row justify-content-center">
<div class="col-lg-5">
<div class="card shadow-lg border-0 rounded-lg mt-5">
<div class="card-header"><h3 class="text-center font-weight-light my-4">Connexion</h3></div>
<div class="card-body">
<form>
{% if login_error %}
{% for value in login_error %}
<div class="alert alert-danger" role="alert">
{{ value }}
{#=======#}
{# <div class="container">#}
{# <div class="row justify-content-center">#}
{# <div class="col-lg-5">#}
{# <div class="card shadow-lg border-0 rounded-lg mt-5">#}
{# <div class="card-header"><h3 class="text-center font-weight-light my-4">Connexion</h3></div>#}
{# <div class="card-body">#}
{# <form>#}
{# <div class="form-floating mb-3">#}
{# <input class="form-control" id="inputEmail" type="email" placeholder="nom@exemple.com" />#}
{# <label for="inputEmail">Adresse eMail</label>#}
{# </div>#}
{# <div class="form-floating mb-3">#}
{# <input class="form-control" id="inputPassword" type="password" placeholder="Mot de passe" />#}
{# <label for="inputPassword">Mot de passe</label>#}
{# </div>#}
{# <div class="form-check mb-3">#}
{# <input class="form-check-input" id="inputRememberPassword" type="checkbox" value="" />#}
{# <label class="form-check-label" for="inputRememberPassword">Mémoriser le mot de passe</label>#}
{# </div>#}
{# <div class="d-flex align-items-center justify-content-between mt-4 mb-0">#}
{# <a class="small" href="/pass">Mot de passe oublié ?</a>#}
{# <a class="btn btn-primary" href="/home">Se connecter</a>#}
{# </div>#}
{# </form>#}
{# </div>#}
{# <div class="card-footer text-center py-3">#}
{# <div class="small"><a href="/regist">Besoin d'un compte ? Inscrivez-vous !</a></div>#}
{# </div>#}
{#>>>>>>> origin/merged_PLE#}
</div>
{% endfor %}
{% endif %}
<form method="post" action="/login">
<div class="form-floating mb-3">
<input class="form-control" id="inputEmail" type="email" placeholder="nom@exemple.com" />
<label for="inputEmail">Adresse eMail</label>
<input class="form-control" id="email" name="email" type="text" placeholder="Nom d'utilisateur" />
<label for="email">Nom d'utilisateur</label>
</div>
<div class="form-floating mb-3">
<input class="form-control" id="inputPassword" type="password" placeholder="Mot de passe" />
<label for="inputPassword">Mot de passe</label>
<input class="form-control" id="password" name="password" type="password" placeholder="Mot de passe" />
<label for="password">Mot de passe</label>
</div>
<div class="form-check mb-3">
<input class="form-check-input" id="inputRememberPassword" type="checkbox" value="" />
<label class="form-check-label" for="inputRememberPassword">Mémoriser le mot de passe</label>
</div>
<div class="d-flex align-items-center justify-content-between mt-4 mb-0">
<a class="small" href="password.html">Mot de passe oublié ?</a>
<a class="btn btn-primary" href="index.html">Se connecter</a>
<a class="small" href="/forgetPassword">Mot de passe oublié ?</a>
<button class="btn btn-primary" type="submit">Se connecter</button>
</div>
</form>
</div>
<div class="card-footer text-center py-3">
<div class="small"><a href="register.html">Besoin d'un compte ? Inscrivez-vous !</a></div>
<div class="small"><a href="/register">Besoin d'un compte ? Inscrivez-vous !</a></div>
</div>
</div>
</div>

@ -23,14 +23,16 @@
<th>Lu</th>
<th>De:</th>
<th>Description</th>
<th>Date</th>
</tr>
</thead>
<tbody>
{% for mail in mails %}
<tr>
<td><img src="/assets/img/verif/{{mail.lu}}.png" width="25px" height="25px"></td>
<td>{{mail.nom}} {{mail.prenom}}</td>
<td><img src="/assets/img/verif/{{mail.idnotif}}.png" width="25px" height="25px"></td>
<td>{{mail.idathlete}}</td>
<td><a href="#">{{mail.message}}</a></td>
<td>{{ mail.date }}</td>
</tr>
{% endfor %}
</tbody>

@ -12,19 +12,31 @@
<div class="card-header"><h3 class="text-center font-weight-light my-4">Récupération du mot de passe</h3></div>
<div class="card-body">
<div class="small mb-3 text-muted">Entrez votre adresse eMail pour recevoir un lien pour changer de mot de passe</div>
<form method="post" action="/password">
{#<<<<<<< HEAD#}
<form method="post" action="/">
{#=======#}
{# <form method="post" action="/password">#}
{#>>>>>>> origin/merged_PLE#}
<div class="form-floating mb-3">
<input class="form-control" id="email" name="email" type="email" placeholder="name@example.com" />
<label for="email">Adresse eMail</label>
</div>
<div class="d-flex align-items-center justify-content-between mt-4 mb-0">
<a class="small" href="/log">Retour à la connexion</a>
<button class="btn btn-primary" type="submit">Réinitialiser votre mot de passe</a>
{#<<<<<<< HEAD#}
<a class="small" href="/login">Retour à la connexion</a>
{#=======#}
{# <a class="small" href="/log">Retour à la connexion</a>#}
{#>>>>>>> origin/merged_PLE#}
<button class="btn btn-primary" type="submit">Réinitialiser votre mot de passe</button>
</div>
</form>
</div>
<div class="card-footer text-center py-3">
<div class="small"><a href="/regist">Besoin d'un compte ? Inscrivez-vous !</a></div>
{#<<<<<<< HEAD#}
<div class="small"><a href="/register">Besoin d'un compte ? Inscrivez-vous !</a></div>
{#=======#}
{# <div class="small"><a href="/regist">Besoin d'un compte ? Inscrivez-vous !</a></div>#}
{#>>>>>>> origin/merged_PLE#}
</div>
</div>
</div>

@ -12,7 +12,7 @@
<div class="container-fluid px-4">
<h1 class="mt-4">Profile</h1>
<ol class="breadcrumb mb-4">
<li class="breadcrumb-item"><a href="/">Accueil</a></li>
<li class="breadcrumb-item"><a href="/home">Accueil</a></li>
<li class="breadcrumb-item active">Profile</li>
</ol>
<div class="row">

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save