ajout bibliothèque méditation et changement de test

This commit is contained in:
2026-04-26 13:02:19 +02:00
parent f3092f2dc4
commit 5b2a2e7ddb
50 changed files with 477 additions and 778 deletions
+6
View File
@@ -39,6 +39,12 @@
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/path_provider_windows/example/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/path_provider_windows/example/.pub" />
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/path_provider_windows/example/build" />
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/permission_handler_windows/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/permission_handler_windows/.pub" />
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/permission_handler_windows/build" />
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/permission_handler_windows/example/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/permission_handler_windows/example/.pub" />
<excludeFolder url="file://$MODULE_DIR$/mobile/windows/flutter/ephemeral/.plugin_symlinks/permission_handler_windows/example/build" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
+36
View File
@@ -87,6 +87,18 @@
<option name="screenX" value="720" />
<option name="screenY" value="1600" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="30" />
<option name="brand" value="realme" />
<option name="codename" value="RMX3231" />
<option name="id" value="RMX3231" />
<option name="labId" value="google" />
<option name="manufacturer" value="realme" />
<option name="name" value="C11" />
<option name="screenDensity" value="320" />
<option name="screenX" value="720" />
<option name="screenY" value="1600" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="36" />
<option name="brand" value="samsung" />
@@ -1038,6 +1050,30 @@
<option name="screenX" value="1600" />
<option name="screenY" value="2560" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="30" />
<option name="brand" value="motorola" />
<option name="codename" value="guamna" />
<option name="id" value="guamna" />
<option name="labId" value="google" />
<option name="manufacturer" value="Motorola" />
<option name="name" value="Moto XT2093-3" />
<option name="screenDensity" value="280" />
<option name="screenX" value="720" />
<option name="screenY" value="1600" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="30" />
<option name="brand" value="motorola" />
<option name="codename" value="guamp" />
<option name="id" value="guamp" />
<option name="labId" value="google" />
<option name="manufacturer" value="Motorola" />
<option name="name" value="Moto XT2083-1" />
<option name="screenDensity" value="280" />
<option name="screenX" value="720" />
<option name="screenY" value="1600" />
</PersistentDeviceSelectionData>
<PersistentDeviceSelectionData>
<option name="api" value="34" />
<option name="brand" value="google" />
@@ -12,7 +12,22 @@ class DiagnosticController extends Controller
public function index()
{
$events = StressEvent::all();
return view('diagnostics.index', compact('events'));
$history = [];
if (Auth::check()) {
$history = \App\Models\ResultatDiag::where('id_user', Auth::id())
->orderBy('date_passage', 'desc')
->take(5)
->get();
}
return view('diagnostics.index', compact('events', 'history'));
}
public function history()
{
$history = \App\Models\ResultatDiag::where('id_user', Auth::id())
->orderBy('date_passage', 'desc')
->get();
return view('diagnostics.history', compact('history'));
}
public function store(Request $request)
@@ -23,8 +38,24 @@ class DiagnosticController extends Controller
$totalPoints = StressEvent::whereIn('id', $request->events)->sum('points');
}
// Ici on pourrait enregistrer le résultat dans une table 'diagnostic_results'
// Pour l'instant on redirige avec le score
$niveauStress = 'Faible';
if ($totalPoints >= 300) {
$niveauStress = 'Élevé';
} elseif ($totalPoints >= 150) {
$niveauStress = 'Modéré';
}
// Enregistrement si l'utilisateur est connecté
if (Auth::check()) {
\App\Models\ResultatDiag::create([
'id_user' => Auth::id(),
'score_total' => $totalPoints,
'niveau_stress' => $niveauStress,
'event_ids' => $request->has('events') ? implode(';', $request->events) : null,
'date_passage' => now(),
]);
}
return redirect()->back()->with('score', $totalPoints);
}
}
@@ -13,26 +13,13 @@ class RelaxationController extends Controller
public function index()
{
$activities = RelaxationActivity::all();
$favorites = Auth::check() ? Auth::user()->favoritedBy->pluck('id')->toArray() : [];
$favorites = [];
return view('relaxation.index', compact('activities', 'favorites'));
}
public function toggleFavorite($id)
{
$userId = Auth::id();
$favorite = Favorite::where('user_id', $userId)
->where('relaxation_activity_id', $id)
->first();
if ($favorite) {
$favorite->delete();
return response()->json(['status' => 'removed']);
} else {
Favorite::create([
'user_id' => $userId,
'relaxation_activity_id' => $id
]);
return response()->json(['status' => 'added']);
}
// Fonctionnalité désactivée pour le moment
return response()->json(['status' => 'disabled']);
}
}
+24 -1
View File
@@ -2,9 +2,32 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class ResultatDiag extends Model
{
//
use HasFactory;
protected $table = 'resultat_diags';
protected $fillable = [
'id_user',
'date_passage',
'score_total',
'niveau_stress',
'event_ids'
];
public function user()
{
return $this->belongsTo(User::class, 'id_user');
}
public function getEventsAttribute()
{
if (!$this->event_ids) return collect();
$ids = explode(';', $this->event_ids);
return StressEvent::whereIn('id', $ids)->get();
}
}
@@ -1,28 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('categories', function (Blueprint $table) {
$table->id(); // id_categorie
$table->string('libelle');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('categories');
}
};
@@ -1,28 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('roles', function (Blueprint $table) {
$table->id(); // id_role [cite: 210]
$table->string('libelle');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('roles');
}
};
@@ -1,32 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('activites', function (Blueprint $table) {
$table->id(); // id_activite
$table->string('titre');
$table->text('description');
$table->string('url_media');
$table->boolean('est_active')->default(true);
$table->foreignId('id_categorie')->constrained('categories'); // Asso_4
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('activites');
}
};
@@ -1,51 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->foreignId('id_role')->constrained('roles'); // Asso_1
$table->string('password');
$table->timestamp('date_inscription')->useCurrent();
$table->rememberToken();
$table->timestamps();
});
Schema::create('password_reset_tokens', function (Blueprint $table) {
$table->string('email')->primary();
$table->string('token');
$table->timestamp('created_at')->nullable();
});
Schema::create('sessions', function (Blueprint $table) {
$table->string('id')->primary();
$table->foreignId('user_id')->nullable()->index();
$table->string('ip_address', 45)->nullable();
$table->text('user_agent')->nullable();
$table->longText('payload');
$table->integer('last_activity')->index();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('users');
Schema::dropIfExists('password_reset_tokens');
Schema::dropIfExists('sessions');
}
};
@@ -1,35 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('cache', function (Blueprint $table) {
$table->string('key')->primary();
$table->mediumText('value');
$table->integer('expiration')->index();
});
Schema::create('cache_locks', function (Blueprint $table) {
$table->string('key')->primary();
$table->string('owner');
$table->integer('expiration')->index();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('cache');
Schema::dropIfExists('cache_locks');
}
};
@@ -1,57 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('jobs', function (Blueprint $table) {
$table->id();
$table->string('queue')->index();
$table->longText('payload');
$table->unsignedTinyInteger('attempts');
$table->unsignedInteger('reserved_at')->nullable();
$table->unsignedInteger('available_at');
$table->unsignedInteger('created_at');
});
Schema::create('job_batches', function (Blueprint $table) {
$table->string('id')->primary();
$table->string('name');
$table->integer('total_jobs');
$table->integer('pending_jobs');
$table->integer('failed_jobs');
$table->longText('failed_job_ids');
$table->mediumText('options')->nullable();
$table->integer('cancelled_at')->nullable();
$table->integer('created_at');
$table->integer('finished_at')->nullable();
});
Schema::create('failed_jobs', function (Blueprint $table) {
$table->id();
$table->string('uuid')->unique();
$table->text('connection');
$table->text('queue');
$table->longText('payload');
$table->longText('exception');
$table->timestamp('failed_at')->useCurrent();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('jobs');
Schema::dropIfExists('job_batches');
Schema::dropIfExists('failed_jobs');
}
};
@@ -0,0 +1,16 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
public function up(): void {
Schema::create('roles', function (Blueprint $table) {
$table->id();
$table->string('libelle');
$table->timestamps();
});
}
public function down(): void { Schema::dropIfExists('roles'); }
};
@@ -0,0 +1,21 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
public function up(): void {
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->foreignId('id_role')->constrained('roles');
$table->rememberToken();
$table->timestamps();
});
}
public function down(): void { Schema::dropIfExists('users'); }
};
@@ -0,0 +1,17 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
public function up(): void {
Schema::create('stress_events', function (Blueprint $table) {
$table->id();
$table->string('event_name');
$table->integer('points');
$table->timestamps();
});
}
public function down(): void { Schema::dropIfExists('stress_events'); }
};
@@ -4,29 +4,18 @@ use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
return new class extends Migration {
public function up(): void {
Schema::create('relaxation_activities', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('description');
$table->string('type'); // audio, video, article
$table->string('duration')->nullable();
$table->string('category');
$table->string('url')->nullable();
$table->text('description')->nullable();
$table->string('image_url')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('relaxation_activities');
}
public function down(): void { Schema::dropIfExists('relaxation_activities'); }
};
@@ -4,28 +4,17 @@ use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
return new class extends Migration {
public function up(): void {
Schema::create('resultat_diags', function (Blueprint $table) {
$table->id(); // id_resultat
$table->foreignId('id_user')->constrained('users'); // Asso_3
$table->id();
$table->foreignId('id_user')->constrained('users')->onDelete('cascade');
$table->timestamp('date_passage')->useCurrent();
$table->integer('score_total');
$table->string('niveau_stress');
$table->text('event_ids')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('resultat_diags');
}
public function down(): void { Schema::dropIfExists('resultat_diags'); }
};
@@ -0,0 +1,19 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration {
public function up(): void {
Schema::create('emotion_records', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained('users')->onDelete('cascade');
$table->string('emotion');
$table->integer('intensity')->default(3);
$table->text('note')->nullable();
$table->timestamps();
});
}
public function down(): void { Schema::dropIfExists('emotion_records'); }
};
@@ -1,33 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('personal_access_tokens', function (Blueprint $table) {
$table->id();
$table->morphs('tokenable');
$table->text('name');
$table->string('token', 64)->unique();
$table->text('abilities')->nullable();
$table->timestamp('last_used_at')->nullable();
$table->timestamp('expires_at')->nullable()->index();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('personal_access_tokens');
}
};
@@ -1,29 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('stress_events', function (Blueprint $table) {
$table->id();
$table->string('event_name'); // Nom de l'événement (ex: Mariage, Licenciement)
$table->integer('points'); // Valeur selon l'échelle de Holmes et Rahe
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('stress_events');
}
};
@@ -1,31 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('emotion_records', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->onDelete('cascade');
$table->string('emotion'); // joy, sad, angry, etc.
$table->integer('intensity'); // 1 to 5
$table->text('note')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('emotion_records');
}
};
@@ -1,35 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('favorites', function (Blueprint $table) {
$table->foreignId('id_user')->constrained('users')->onDelete('cascade');
// id_activite fait référence à la table activites
// C'est ici que l'erreur se produit si le nom ne correspond pas exactement
$table->foreignId('id_activite')->constrained('activites')->onDelete('cascade');
$table->timestamp('date_ajout')->useCurrent();
// Clé primaire composée pour éviter les doublons (RGPD/Qualité)
$table->primary(['id_user', 'id_activite']);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('favorites');
}
};
@@ -1,29 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('emotions', function (Blueprint $table) {
$table->id(); // id_emotion
$table->string('libelle');
$table->foreignId('id_parent')->nullable()->constrained('emotions'); // Asso_7
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('emotions');
}
};
@@ -1,29 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('evenement_stress', function (Blueprint $table) {
$table->id(); // id_event
$table->string('libelle');
$table->integer('points'); // Points associés à l'échelle [cite: 126]
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('evenement_stress');
}
};
@@ -1,31 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('exercice_respi', function (Blueprint $table) {
$table->id(); // id_respi
$table->string('nom');
$table->integer('duree_inspi'); // Cohérence cardiaque [cite: 133]
$table->integer('duree_apnee'); // [cite: 134]
$table->integer('duree_expi'); // [cite: 135]
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('exercice_respi');
}
};
@@ -1,30 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('journal_entrees', function (Blueprint $table) {
$table->id(); // id_entree
$table->foreignId('id_user')->constrained('users'); // Asso_8 [cite: 98]
$table->timestamp('date_heure')->useCurrent();
$table->text('commentaire')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('journal_entrees');
}
};
@@ -1,27 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('users_table_cesizen', function (Blueprint $table) {
$table->id();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('users_table_cesizen');
}
};
@@ -1,31 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('contenu_infos', function (Blueprint $table) {
$table->id(); // id_contenu
$table->string('titre');
$table->text('texte_contenu');
$table->timestamp('date_publication')->useCurrent();
$table->foreignId('id_auteur')->constrained('users'); // Asso_2
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('contenu_infos');
}
};
@@ -1,28 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('detail_resultats', function (Blueprint $table) {
$table->foreignId('id_resultat')->constrained('resultat_diags')->onDelete('cascade');
$table->foreignId('id_event')->constrained('evenement_stress');
$table->primary(['id_resultat', 'id_event']);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('detail_resultats');
}
};
@@ -1,29 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('emotions_ressenties', function (Blueprint $table) {
$table->foreignId('id_entree')->constrained('journal_entrees')->onDelete('cascade');
$table->foreignId('id_emotion')->constrained('emotions');
$table->integer('intensite');
$table->primary(['id_entree', 'id_emotion']);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('emotions_ressenties');
}
};
@@ -1,27 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('favoris', function (Blueprint $table) {
$table->id();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('favoris');
}
};
@@ -1,27 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('historique_respi', function (Blueprint $table) {
$table->id();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('historique_respi');
}
};
+1
View File
@@ -21,6 +21,7 @@ class DatabaseSeeder extends Seeder
$this->call([
RoleSeeder::class,
StressEventSeeder::class,
RelaxationActivitySeeder::class,
]);
$adminRole = Role::where('libelle', 'Admin')->first();
@@ -0,0 +1,67 @@
<?php
namespace Database\Seeders;
use App\Models\RelaxationActivity;
use Illuminate\Database\Seeder;
class RelaxationActivitySeeder extends Seeder
{
public function run(): void
{
$activities = [
[
'title' => 'Méditation de Pleine Conscience',
'description' => 'Une séance guidée de 10 minutes pour se reconnecter au moment présent et calmer le flux des pensées.',
'type' => 'audio',
'category' => 'Méditation',
'url' => 'https://www.youtube.com/watch?v=sz7cpV7ERsY',
'image_url' => 'https://images.unsplash.com/photo-1506126613408-eca07ce68773?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80'
],
[
'title' => 'Yoga Doux pour le Soir',
'description' => 'Étirements légers et postures relaxantes pour préparer le corps au sommeil et relâcher les tensions de la journée.',
'type' => 'video',
'category' => 'Sport',
'url' => 'https://www.youtube.com/watch?v=v7AYKMP6rOE',
'image_url' => 'https://images.unsplash.com/photo-1544367567-0f2fcb009e0b?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80'
],
[
'title' => 'Bruits de Plage et Vagues',
'description' => 'Ambiance sonore apaisante pour le travail ou la lecture. Réduit le stress environnant.',
'type' => 'audio',
'category' => 'Musique',
'url' => 'https://www.youtube.com/watch?v=0_fS_pS_B0U',
'image_url' => 'https://images.unsplash.com/photo-1507525428034-b723cf961d3e?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80'
],
[
'title' => 'Comprendre le Cycle du Stress',
'description' => 'Un article détaillé sur les mécanismes biologiques du stress et comment les réguler au quotidien.',
'type' => 'article',
'category' => 'Lecture',
'url' => 'https://www.santepubliquefrance.fr',
'image_url' => 'https://images.unsplash.com/photo-1512820790803-83ca734da794?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80'
],
[
'title' => 'Musique Classique pour la Concentration',
'description' => 'Sélection de morceaux choisis pour favoriser le focus et la clarté mentale.',
'type' => 'audio',
'category' => 'Musique',
'url' => 'https://www.youtube.com/watch?v=5Qq3m9G4_4A',
'image_url' => 'https://images.unsplash.com/photo-1514119412350-e174d90d280e?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80'
],
[
'title' => 'Techniques de Respiration Wim Hof',
'description' => 'Apprenez la méthode de respiration qui renforce le système immunitaire et réduit l\'anxiété.',
'type' => 'video',
'category' => 'Respiration',
'url' => 'https://www.youtube.com/watch?v=tybOi4hjZFQ',
'image_url' => 'https://images.unsplash.com/photo-1518199266791-5375a83190b7?ixlib=rb-4.0.3&auto=format&fit=crop&w=800&q=80'
]
];
foreach ($activities as $activity) {
RelaxationActivity::create($activity);
}
}
}

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

@@ -0,0 +1,66 @@
@extends('layouts.app')
@section('title', 'Journal des Diagnostics')
@section('header_title', 'Mon Historique de Stress')
@section('content')
<div class="max-w-4xl mx-auto space-y-8">
<div class="flex items-center justify-between">
<div>
<h2 class="text-2xl font-black text-slate-900 uppercase">Journal de bord</h2>
<p class="text-slate-500">Retrouvez l'évolution de vos bilans de stress.</p>
</div>
<a href="{{ route('diagnostics.index') }}" class="px-6 py-2 bg-[--fr-blue] text-white font-bold rounded-sm shadow-md hover:bg-blue-800 transition-all text-sm uppercase tracking-widest">
Nouveau test
</a>
</div>
<div class="bg-white border border-slate-200 shadow-sm overflow-hidden">
<table class="w-full text-left border-collapse">
<thead>
<tr class="bg-slate-50 border-b border-slate-200">
<th class="px-6 py-4 text-xs font-bold text-slate-500 uppercase tracking-widest">Date du bilan</th>
<th class="px-6 py-4 text-xs font-bold text-slate-500 uppercase tracking-widest text-center">Score</th>
<th class="px-6 py-4 text-xs font-bold text-slate-500 uppercase tracking-widest">Niveau de Risque</th>
</tr>
</thead>
<tbody class="divide-y divide-slate-100">
@forelse($history as $result)
<tr class="hover:bg-slate-50 transition-colors">
<td class="px-6 py-5">
<p class="font-bold text-slate-900">{{ \Carbon\Carbon::parse($result->date_passage)->translatedFormat('d F Y') }}</p>
<p class="text-xs text-slate-400">{{ \Carbon\Carbon::parse($result->date_passage)->format('H:i') }}</p>
</td>
<td class="px-6 py-5 text-center">
<span class="text-xl font-black text-[--fr-blue]">{{ $result->score_total }}</span>
</td>
<td class="px-6 py-5">
@if($result->niveau_stress === 'Élevé')
<span class="px-3 py-1 bg-red-50 text-red-700 text-[10px] font-black uppercase rounded-full border border-red-100">Risque Élevé</span>
@elseif($result->niveau_stress === 'Modéré')
<span class="px-3 py-1 bg-orange-50 text-orange-700 text-[10px] font-black uppercase rounded-full border border-orange-100">Risque Modéré</span>
@else
<span class="px-3 py-1 bg-teal-50 text-teal-700 text-[10px] font-black uppercase rounded-full border border-teal-100">Risque Faible</span>
@endif
<div class="mt-2 flex flex-wrap gap-1">
@foreach($result->events as $event)
<span class="text-[9px] bg-slate-100 text-slate-500 px-1.5 py-0.5 rounded-sm border border-slate-200" title="{{ $event->event_name }}">
{{ Str::limit($event->event_name, 20) }}
</span>
@endforeach
</div>
</td>
</tr>
@empty
<tr>
<td colspan="4" class="px-6 py-10 text-center text-slate-400 italic">
Vous n'avez pas encore effectué de bilan de stress.
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
@endsection
@@ -25,9 +25,17 @@
</div>
@else
<div class="bg-white p-8 border border-slate-200 shadow-sm">
<div class="mb-8">
<h2 class="text-xl font-bold mb-2">Quels événements avez-vous vécus ces 12 derniers mois ?</h2>
<p class="text-sm text-slate-500 italic">Cochez toutes les cases qui correspondent à votre situation actuelle.</p>
<div class="flex items-center justify-between mb-8">
<div>
<h2 class="text-xl font-bold mb-2">Quels événements avez-vous vécus ces 12 derniers mois ?</h2>
<p class="text-sm text-slate-500 italic">Cochez toutes les cases qui correspondent à votre situation actuelle.</p>
</div>
@auth
<a href="{{ route('diagnostics.history') }}" class="text-sm font-bold text-[--fr-blue] hover:underline flex items-center gap-1">
<x-heroicon-o-clock class="w-4 h-4" />
Mon Historique
</a>
@endauth
</div>
<form action="{{ route('diagnostics.store') }}" method="POST" class="space-y-4">
@@ -49,6 +57,34 @@
</div>
</form>
</div>
@auth
@if(count($history) > 0)
<div class="bg-white p-8 border border-slate-200 shadow-sm">
<h3 class="text-lg font-bold mb-6 flex items-center gap-2">
<x-heroicon-o-clipboard-document-list class="w-5 h-5 text-[--fr-blue]" />
Dernières entrées du journal
</h3>
<div class="space-y-4">
@foreach($history as $item)
<div class="flex items-center justify-between p-4 bg-slate-50 rounded-sm border border-slate-100">
<div>
<p class="text-sm font-bold text-slate-900">{{ \Carbon\Carbon::parse($item->date_passage)->translatedFormat('d M Y') }}</p>
<p class="text-xs text-slate-500">{{ $item->niveau_stress }}</p>
</div>
<div class="text-right">
<span class="text-lg font-black text-[--fr-blue]">{{ $item->score_total }}</span>
<p class="text-[10px] uppercase font-bold text-slate-400">points</p>
</div>
</div>
@endforeach
</div>
<div class="mt-6 text-center">
<a href="{{ route('diagnostics.history') }}" class="text-xs font-bold text-slate-400 hover:text-[--fr-blue] uppercase tracking-widest transition-colors">Voir l'historique complet</a>
</div>
</div>
@endif
@endauth
@endif
</div>
@endsection
+1 -1
View File
@@ -40,7 +40,7 @@
<textarea name="note" rows="3" class="w-full border-slate-200 rounded-lg focus:ring-[--fr-blue] focus:border-[--fr-blue]" placeholder="Décrivez votre état..."></textarea>
</div>
<button type="submit" class="w-full py-4 bg-[--fr-blue] text-white font-bold rounded-sm hover:bg-blue-800 shadow-md transition-all">
<button type="submit" class="w-full py-4 bg-[#000091] text-white font-bold rounded-sm hover:bg-blue-800 shadow-md transition-all uppercase tracking-widest">
Enregistrer dans mon journal
</button>
</form>
+3 -1
View File
@@ -3,8 +3,9 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>@yield('title', 'CESIZen') | Ministère de la Santé</title>
<title>@yield('title', 'CESIZen') | Ministère de la Santé et de la Prévention</title>
@vite(['resources/css/app.css', 'resources/js/app.js'])
<link rel="icon" href="./img/CesiZen_logo.png" type="image/x-icon">
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
<style>
:root {
@@ -35,4 +36,5 @@
</main>
</div>
</body>
@include('layouts.footer')
</html>
+3 -3
View File
@@ -2,12 +2,12 @@
<div class="container mx-auto px-4 lg:px-8 py-3 flex items-center justify-between">
<div class="flex items-start justify-start gap-8">
<div class="flex flex-col border-r-2 border-slate-200 pr-8" aria-hidden="true">
<img src="img/ministere.png" alt="Logo du Ministère de la Santé et de la Prévention" class="w-24 h-auto">
<img src="{{ asset('img/ministere.png') }}" alt="Logo du Ministère de la Santé et de la Prévention" class="w-24 h-auto">
</div>
<div class="flex flex-col">
<h1 class="text-2xl font-black text-[--fr-blue] tracking-tight">
<a href="{{ route('home') }}"><img src="img/CesiZen.png" alt="Logo CesiZen" class="w-32 h-auto"></a>
<a href="{{ route('home') }}"><img src="{{ asset('img/CesiZen.png') }}" alt="Logo CesiZen" class="w-32 h-auto"></a>
</h1>
</div>
</div>
@@ -38,4 +38,4 @@
@endauth
</nav>
</div>
</header>
</header>
@@ -31,7 +31,7 @@
</a>
<!-- Exercices de Respiration -->
<a href="{{ route('relaxation.index') }}"
<a href="{{ route('relaxation.index') }}#breathing-app"
class="flex items-center gap-4 px-4 py-3 rounded-sm transition-all group {{ request()->routeIs('relaxation.*') ? 'text-white bg-[#000091] font-bold shadow-md' : 'text-slate-700 hover:bg-slate-50 font-medium border-b border-slate-100/50' }}"
{!! request()->routeIs('relaxation.*') ? 'aria-current="page"' : '' !!}>
<div class="w-8 h-8 rounded-sm flex items-center justify-center transition-colors {{ request()->routeIs('relaxation.*') ? 'bg-white/20 text-white' : 'bg-indigo-50 text-indigo-700' }}" aria-hidden="true">
@@ -6,7 +6,7 @@
<div class="max-w-6xl mx-auto space-y-12">
<!-- Module de Respiration Guidée -->
<div x-data="breathingApp()" class="bg-white p-8 border border-slate-300 shadow-sm relative overflow-hidden">
<div id="breathing-app" x-data="breathingApp()" class="bg-white p-8 border border-slate-300 shadow-sm relative overflow-hidden">
<div class="flex flex-col lg:flex-row gap-12 items-center">
<!-- Contrôles et Choix du mode -->
+5 -61
View File
@@ -1,69 +1,13 @@
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>CESIZen | Ministère de la Santé et de la Prévention</title>
@extends('layouts.app')
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.bunny.net">
<link href="https://fonts.bunny.net/css?family=instrument-sans:400,500,600,700" rel="stylesheet" />
@section('title', 'CesiZen')
<!-- Scripts -->
@vite(['resources/css/app.css', 'resources/js/app.js'])
@section('content')
<style>
:root {
/* Official French Government Colors (DSFR) */
--fr-blue: #000091;
--fr-blue-hover: #1212ff;
--fr-red: #E1000F;
--fr-text: #161616;
--fr-background: #F6F6F6;
/* CESIZen Adapted for RGAA (High Contrast) */
--cz-navy: #000074; /* Darker than 000080 for better contrast */
--cz-mustard: #715200; /* Darkened mustard for text on white (Contrast > 7:1) */
--cz-mustard-bg: #F8B803; /* Original mustard for backgrounds only */
--cz-jade: #00635B; /* Darkened jade for text (Contrast > 4.5:1) */
--cz-jade-bg: #26A69A; /* Original jade for backgrounds */
--cz-indigo: #303F9F;
--cz-coral: #C62828;
}
/* Focus state for RGAA */
:focus-visible {
outline: 3px solid var(--fr-blue);
outline-offset: 2px;
}
.skip-link {
position: absolute;
top: -40px;
left: 0;
background: var(--fr-blue);
color: white;
padding: 8px;
z-index: 100;
transition: top 0.2s;
}
.skip-link:focus {
top: 0;
}
</style>
</head>
<body class="bg-[#F6F6F6] text-[#161616] min-h-screen font-sans antialiased overflow-x-hidden">
<a href="#main-content" class="skip-link">Aller au contenu principal</a>
<!-- Government Official Header -->
@include('layouts.header')
<div class="flex min-h-[calc(100vh-80px)]">
<!-- Institutional Sidebar -->
@include('layouts.sidebar')
<!-- Main Workspace -->
<main id="main-content" class="flex-1 flex flex-col bg-[#F6F6F6] overflow-y-auto">
@@ -164,6 +108,6 @@
</main>
</div>
@include('layouts.footer')
</body>
</html>
@endsection
+5 -4
View File
@@ -21,7 +21,7 @@ Route::get('/register', [AuthController::class, 'showRegister'])->name('register
Route::post('/register', [AuthController::class, 'register']);
Route::post('/logout', [AuthController::class, 'logout'])->name('logout');
// Protected Routes
// Routes protégé par authentifications
Route::middleware('auth')->group(function () {
// Profile
Route::get('/profile', [ProfileController::class, 'index'])->name('profile.index');
@@ -29,13 +29,14 @@ Route::middleware('auth')->group(function () {
Route::put('/profile/password', [ProfileController::class, 'updatePassword'])->name('profile.password');
Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy');
// Tracker Émotionnel (Personal data)
// Tracker Émotionnel
Route::get('/emotions', [EmotionController::class, 'index'])->name('emotions.index');
Route::post('/emotions', [EmotionController::class, 'store'])->name('emotions.store');
// Diagnostic Saving (if applicable)
// Diagnostic Stress
Route::get('/diagnostics/history', [DiagnosticController::class, 'history'])->name('diagnostics.history');
Route::post('/diagnostics', [DiagnosticController::class, 'store'])->name('diagnostics.store');
// Relaxation Actions
// Pages Relaxation
Route::post('/relaxation/{id}/favorite', [RelaxationController::class, 'toggleFavorite'])->name('relaxation.favorite');
});
+24
View File
@@ -0,0 +1,24 @@
<?php
namespace Tests\Feature;
use App\Models\EvenementStress;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;
use PHPUnit\Framework\Attributes\Test;
class BusinessLogicTest extends TestCase
{
#[Test]
public function la_liste_des_evenements_de_stress_est_accessible()
{
// On crée un événement simulé
EvenementStress::create(['libelle' => 'Mariage', 'points' => 50]);
$response = $this->getJson('/api/stress-events');
$response->assertStatus(200)
->assertJsonFragment(['libelle' => 'Mariage']);
}
}
+41
View File
@@ -0,0 +1,41 @@
<?php
namespace Tests\Unit;
use Tests\TestCase;
use App\Models\User;
use App\Models\StressEvent;
use App\Models\ResultatDiag;
use Illuminate\Foundation.Testing.RefreshDatabase;
class DiagnosticTest extends TestCase
{
use RefreshDatabase;
public function test_diagnostic_score_calculation_and_storage()
{
// Créer un utilisateur
$user = User::factory()->create();
// Créer des événements de stress
$event1 = StressEvent::create(['event_name' => 'Event 1', 'points' => 100]);
$event2 = StressEvent::create(['event_name' => 'Event 2', 'points' => 60]);
// Simuler une requête de diagnostic
$response = $this->actingAs($user)
->post(route('diagnostics.store'), [
'events' => [$event1->id, $event2->id]
]);
// Vérifier la redirection et le score en session
$response->assertStatus(302);
$response->assertSessionHas('score', 160);
// Vérifier l'enregistrement en base de données
$this->assertDatabaseHas('resultat_diags', [
'id_user' => $user->id,
'score_total' => 160,
'niveau_stress' => 'Modéré'
]);
}
}
-1
View File
@@ -1,4 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
+2
View File
@@ -48,6 +48,7 @@ dependencies {
implementation(libs.compose.material3)
implementation(libs.compose.foundation)
implementation(libs.compose.ui.tooling)
implementation(libs.wear)
implementation(libs.wear.tooling.preview)
implementation(libs.activity.compose)
implementation(libs.core.splashscreen)
@@ -55,4 +56,5 @@ dependencies {
androidTestImplementation(libs.ui.test.junit4)
debugImplementation(libs.ui.tooling)
debugImplementation(libs.ui.test.manifest)
// Pour la gestion du réseau simplifiée
}
+4
View File
@@ -2,6 +2,9 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-feature android:name="android.hardware.type.watch" />
@@ -18,6 +21,7 @@
android:name="wear-sdk"
android:required="false" />
<!--
Set to true if your app is Standalone, that is, it does not require the handheld
app to run.
@@ -1,5 +1,6 @@
package com.cesizen.app
import android.content.Context
import android.os.Bundle
import android.util.Log
import android.widget.Toast
@@ -29,9 +30,14 @@ import org.json.JSONObject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkRequest
class MainActivity : ComponentActivity(), DataClient.OnDataChangedListener {
private var userName by mutableStateOf<String?>(null)
private var currentMood by mutableStateOf<String?>(null)
private var isRefreshing by mutableStateOf(false)
@@ -42,6 +48,21 @@ class MainActivity : ComponentActivity(), DataClient.OnDataChangedListener {
Wearable.getDataClient(this).addListener(this)
refreshData()
val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val request = NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI) // Priorité Wi-Fi
.addTransportType(NetworkCapabilities.TRANSPORT_BLUETOOTH) // Secours Bluetooth
.build()
connectivityManager.requestNetwork(request, object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
// Le système a trouvé soit du Wi-Fi, soit le pont Bluetooth du téléphone
connectivityManager.bindProcessToNetwork(network)
}
})
setContent {
WearApp(
userName = userName,
+4
View File
@@ -6,6 +6,8 @@ composeBom = "2024.09.00"
composeMaterial3 = "1.5.6"
composeFoundation = "1.5.6"
composeUiTooling = "1.5.6"
playServicesWearableVersion = "18.1.0"
wear = "1.4.0"
wearToolingPreview = "1.0.0"
activityCompose = "1.13.0"
coreSplashscreen = "1.2.0"
@@ -13,6 +15,7 @@ coreSplashscreen = "1.2.0"
[libraries]
play-services-wearable = { group = "com.google.android.gms", name = "play-services-wearable", version.ref = "playServicesWearable" }
compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
play-services-wearable-v1810 = { module = "com.google.android.gms:play-services-wearable", version.ref = "playServicesWearableVersion" }
ui = { group = "androidx.compose.ui", name = "ui" }
ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
@@ -22,6 +25,7 @@ ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
compose-material3 = { group = "androidx.wear.compose", name = "compose-material3", version.ref = "composeMaterial3" }
compose-foundation = { group = "androidx.wear.compose", name = "compose-foundation", version.ref = "composeFoundation" }
compose-ui-tooling = { group = "androidx.wear.compose", name = "compose-ui-tooling", version.ref = "composeUiTooling" }
wear = { module = "androidx.wear:wear", version.ref = "wear" }
wear-tooling-preview = { group = "androidx.wear", name = "wear-tooling-preview", version.ref = "wearToolingPreview" }
activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
core-splashscreen = { group = "androidx.core", name = "core-splashscreen", version.ref = "coreSplashscreen" }