feat: breathing exercise & profile

This commit is contained in:
2025-08-24 19:50:16 +03:30
parent 0e49229621
commit abb53ed969
6 changed files with 133 additions and 1 deletions
@@ -0,0 +1,50 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\BreathingExercise;
class BreathingExerciseController extends Controller
{
public function store(Request $request)
{
$data = $request->validate([
'subject' => 'required|string',
'duration' => 'required|integer',
'created_at' => 'nullable|date',
]);
$user = auth()->user();
$exercise = BreathingExercise::create([
'user_id' => $user->id,
'subject' => $data['subject'],
'duration' => $data['duration'],
'created_at' => $data['created_at'] ?? now(),
]);
// Give XP (e.g., +10 per session)
$user->increment('xp', 10);
return response()->json([
'message' => 'Exercise saved',
'exercise' => $exercise,
'xp' => $user->xp
]);
}
public function profile()
{
$user = auth()->user();
$user->load('breathingExercises');
return response()->json([
'email' => $user->email,
'name' => $user->first_name . ' ' . $user->last_name,
'xp' => $user->xp,
'breathing_exercises' => $user->breathingExercises
]);
}
}
+16
View File
@@ -0,0 +1,16 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class BreathingExercise extends Model
{
public $timestamps = false; // we handle created_at manually
protected $fillable = ['user_id', 'subject', 'duration', 'created_at'];
public function user()
{
return $this->belongsTo(User::class);
}
}
+5
View File
@@ -98,4 +98,9 @@ public function response()
return $this->hasOne(UserResponse::class);
}
public function breathingExercises()
{
return $this->hasMany(BreathingExercise::class);
}
}
@@ -0,0 +1,30 @@
<?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()
{
Schema::create('breathing_exercises', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->onDelete('cascade');
$table->string('subject');
$table->integer('duration'); // duration in seconds or minutes
$table->timestamp('created_at')->nullable(); // if null = now
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('breathing_exercises');
}
};
@@ -0,0 +1,29 @@
<?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()
{
Schema::table('users', function (Blueprint $table) {
$table->integer('xp')->default(0);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
//
});
}
};
+3 -1
View File
@@ -3,7 +3,7 @@
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use Laravel\Socialite\Facades\Socialite;
use App\Http\Controllers\BreathingExerciseController;
use App\Http\Controllers\PackageNameController;
use App\Http\Controllers\ProductController;
@@ -37,5 +37,7 @@
Route::put('/{name}/products/{id}/subscribe', [ProductController::class, 'subscribe']);
});
Route::post('breathing-exercises', [BreathingExerciseController::class, 'store']);
Route::get('profile', [BreathingExerciseController::class, 'profile']);
});