From fd2027894841f57862d6e78cb3b3acb1ada41784 Mon Sep 17 00:00:00 2001 From: Jonas Pfalzgraf Date: Mon, 16 Dec 2024 09:00:57 +0100 Subject: [PATCH] feat: Add user level and experience attributes, and implement reward, task, and quest models with corresponding controllers and migrations --- .../app/Http/Controllers/AuthController.php | 60 +++ .../app/Http/Controllers/QuestController.php | 70 +++ .../app/Http/Controllers/RewardController.php | 22 + .../app/Http/Controllers/TaskController.php | 57 +++ .../app/Http/Controllers/UserController.php | 25 + backend/app/Models/Quest.php | 31 ++ backend/app/Models/Reward.php | 22 + backend/app/Models/Task.php | 25 + backend/app/Models/User.php | 2 + backend/config/app.php | 8 +- .../2024_12_16_084121_create_users_table.php | 30 ++ .../2024_12_16_084321_create_tasks_table.php | 33 ++ .../2024_12_16_084521_create_quest_table.php | 36 ++ ...2024_12_16_084621_create_rewards_table.php | 41 ++ backend/resources/views/welcome.blade.php | 104 ---- backend/routes/api.php | 29 +- swagger.yml | 480 ++++++++++++++++++ 17 files changed, 963 insertions(+), 112 deletions(-) create mode 100644 backend/app/Http/Controllers/AuthController.php create mode 100644 backend/app/Http/Controllers/QuestController.php create mode 100644 backend/app/Http/Controllers/RewardController.php create mode 100644 backend/app/Http/Controllers/TaskController.php create mode 100644 backend/app/Http/Controllers/UserController.php create mode 100644 backend/app/Models/Quest.php create mode 100644 backend/app/Models/Reward.php create mode 100644 backend/app/Models/Task.php create mode 100644 backend/database/migrations/2024_12_16_084121_create_users_table.php create mode 100644 backend/database/migrations/2024_12_16_084321_create_tasks_table.php create mode 100644 backend/database/migrations/2024_12_16_084521_create_quest_table.php create mode 100644 backend/database/migrations/2024_12_16_084621_create_rewards_table.php create mode 100644 swagger.yml diff --git a/backend/app/Http/Controllers/AuthController.php b/backend/app/Http/Controllers/AuthController.php new file mode 100644 index 0000000..2459fb2 --- /dev/null +++ b/backend/app/Http/Controllers/AuthController.php @@ -0,0 +1,60 @@ +validate([ + 'name' => 'required', + 'email' => 'required|email|unique:users', + 'password' => 'required|min:6' + ]); + + $user = User::create([ + 'name' => $request->name, + 'email' => $request->email, + 'password' => Hash::make($request->password), + 'level' => 1, + 'xp' => 0 + ]); + + $token = $user->createToken('api_token')->plainTextToken; + + return response()->json([ + 'user' => $user, + 'token' => $token + ], 201); + } + + public function login(Request $request) + { + $request->validate([ + 'email' => 'required|email', + 'password' => 'required' + ]); + + $user = User::where('email', $request->email)->first(); + + if (!$user || !Hash::check($request->password, $user->password)) { + return response()->json(['message' => 'Invalid credentials'], 401); + } + + $token = $user->createToken('api_token')->plainTextToken; + + return response()->json([ + 'token' => $token, + 'user' => $user + ]); + } + + public function me(Request $request) + { + return response()->json(['user' => $request->user()]); + } +} diff --git a/backend/app/Http/Controllers/QuestController.php b/backend/app/Http/Controllers/QuestController.php new file mode 100644 index 0000000..5aff741 --- /dev/null +++ b/backend/app/Http/Controllers/QuestController.php @@ -0,0 +1,70 @@ +user()->id)->where('is_completed', false)->get(); + return response()->json($quests); + } + + public function generate(Request $request) + { + // Vereinfachte Logik: Nimmt die letzten 5 offenen Tasks und macht daraus Quests + $tasks = Task::where('user_id', $request->user()->id)->limit(5)->get(); + + $quests = []; + foreach ($tasks as $task) { + $quests[] = Quest::create([ + 'user_id' => $request->user()->id, + 'task_id' => $task->id, + 'title' => $task->title, + 'category' => $task->category, + 'due_date' => $task->due_date, + 'xp_reward' => 50, + 'is_completed' => false + ]); + } + + return response()->json($quests, 201); + } + + public function complete(Request $request, $id) + { + $quest = Quest::where('user_id', $request->user()->id)->findOrFail($id); + + if ($quest->is_completed) { + return response()->json(['message' => 'Quest already completed'], 400); + } + + // Mark as completed + $quest->update(['is_completed' => true]); + + $user = $request->user(); + $user->xp += $quest->xp_reward; + + // Level-Up Check (vereinfachte Logik) + $xpForNextLevel = $user->level * 100; + $levelUp = false; + while ($user->xp >= $xpForNextLevel) { + $user->level += 1; + $user->xp = $user->xp - $xpForNextLevel; + $xpForNextLevel = $user->level * 100; + $levelUp = true; + } + $user->save(); + + return response()->json([ + 'quest' => $quest, + 'xp_gained' => $quest->xp_reward, + 'new_level' => $user->level, + 'level_up' => $levelUp + ]); + } +} diff --git a/backend/app/Http/Controllers/RewardController.php b/backend/app/Http/Controllers/RewardController.php new file mode 100644 index 0000000..86b7eed --- /dev/null +++ b/backend/app/Http/Controllers/RewardController.php @@ -0,0 +1,22 @@ +json($rewards); + } + + public function unlocked(Request $request) + { + $user = $request->user(); + $unlocked = $user->belongsToMany(Reward::class, 'reward_user')->get(); + return response()->json($unlocked); + } +} diff --git a/backend/app/Http/Controllers/TaskController.php b/backend/app/Http/Controllers/TaskController.php new file mode 100644 index 0000000..fb61a7e --- /dev/null +++ b/backend/app/Http/Controllers/TaskController.php @@ -0,0 +1,57 @@ +user()->id)->get(); + return response()->json($tasks); + } + + public function store(Request $request) + { + $request->validate([ + 'title' => 'required', + 'category' => 'in:daily,weekend,vacation,work', + 'priority' => 'in:low,medium,high' + ]); + + $task = Task::create([ + 'user_id' => $request->user()->id, + 'title' => $request->title, + 'description' => $request->description, + 'category' => $request->category ?? 'daily', + 'priority' => $request->priority ?? 'medium', + 'due_date' => $request->due_date + ]); + + return response()->json($task, 201); + } + + public function show(Request $request, $id) + { + $task = Task::where('user_id', $request->user()->id)->findOrFail($id); + return response()->json($task); + } + + public function update(Request $request, $id) + { + $task = Task::where('user_id', $request->user()->id)->findOrFail($id); + + $task->update($request->only('title', 'description', 'category', 'priority', 'due_date')); + + return response()->json($task); + } + + public function destroy(Request $request, $id) + { + $task = Task::where('user_id', $request->user()->id)->findOrFail($id); + $task->delete(); + return response()->noContent(); + } +} diff --git a/backend/app/Http/Controllers/UserController.php b/backend/app/Http/Controllers/UserController.php new file mode 100644 index 0000000..634d9d9 --- /dev/null +++ b/backend/app/Http/Controllers/UserController.php @@ -0,0 +1,25 @@ +user(); + $xpForNextLevel = $user->level * 100; + + // Finde noch nicht freigeschaltete Rewards, die in Reichweite sind + $upcomingRewards = Reward::where('unlocked_at_level', '>', $user->level)->limit(5)->get(); + + return response()->json([ + 'level' => $user->level, + 'xp' => $user->xp, + 'xp_for_next_level' => $xpForNextLevel, + 'upcoming_rewards' => $upcomingRewards + ]); + } +} diff --git a/backend/app/Models/Quest.php b/backend/app/Models/Quest.php new file mode 100644 index 0000000..1f6815e --- /dev/null +++ b/backend/app/Models/Quest.php @@ -0,0 +1,31 @@ +belongsTo(User::class); + } + + public function task() + { + return $this->belongsTo(Task::class); + } +} diff --git a/backend/app/Models/Reward.php b/backend/app/Models/Reward.php new file mode 100644 index 0000000..a219ed3 --- /dev/null +++ b/backend/app/Models/Reward.php @@ -0,0 +1,22 @@ +belongsToMany(User::class, 'reward_user'); + } +} diff --git a/backend/app/Models/Task.php b/backend/app/Models/Task.php new file mode 100644 index 0000000..386a0a6 --- /dev/null +++ b/backend/app/Models/Task.php @@ -0,0 +1,25 @@ +belongsTo(User::class); + } +} diff --git a/backend/app/Models/User.php b/backend/app/Models/User.php index def621f..9b450b9 100644 --- a/backend/app/Models/User.php +++ b/backend/app/Models/User.php @@ -20,6 +20,8 @@ class User extends Authenticatable 'name', 'email', 'password', + 'level', + 'xp' ]; /** diff --git a/backend/config/app.php b/backend/config/app.php index f467267..63f9d3c 100644 --- a/backend/config/app.php +++ b/backend/config/app.php @@ -13,7 +13,7 @@ return [ | */ - 'name' => env('APP_NAME', 'Laravel'), + 'name' => env('APP_NAME', 'ADHD Home Quest'), /* |-------------------------------------------------------------------------- @@ -26,7 +26,7 @@ return [ | */ - 'env' => env('APP_ENV', 'production'), + 'env' => env('APP_ENV', 'development'), /* |-------------------------------------------------------------------------- @@ -39,7 +39,7 @@ return [ | */ - 'debug' => (bool) env('APP_DEBUG', false), + 'debug' => (bool) env('APP_DEBUG', true), /* |-------------------------------------------------------------------------- @@ -65,7 +65,7 @@ return [ | */ - 'timezone' => env('APP_TIMEZONE', 'UTC'), + 'timezone' => env('APP_TIMEZONE', 'CET'), /* |-------------------------------------------------------------------------- diff --git a/backend/database/migrations/2024_12_16_084121_create_users_table.php b/backend/database/migrations/2024_12_16_084121_create_users_table.php new file mode 100644 index 0000000..92d02f3 --- /dev/null +++ b/backend/database/migrations/2024_12_16_084121_create_users_table.php @@ -0,0 +1,30 @@ +id(); + $table->string('name'); + $table->string('email')->unique(); + $table->string('password'); + $table->integer('level')->default(1); + $table->integer('xp')->default(0); + $table->timestamps(); + }); + } + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('users'); + } +}; diff --git a/backend/database/migrations/2024_12_16_084321_create_tasks_table.php b/backend/database/migrations/2024_12_16_084321_create_tasks_table.php new file mode 100644 index 0000000..40a44c9 --- /dev/null +++ b/backend/database/migrations/2024_12_16_084321_create_tasks_table.php @@ -0,0 +1,33 @@ +id(); + $table->unsignedBigInteger('user_id'); + $table->string('title'); + $table->text('description')->nullable(); + $table->enum('category', ['daily', 'weekend', 'vacation', 'work'])->default('daily'); + $table->enum('priority', ['low', 'medium', 'high'])->default('medium'); + $table->dateTime('due_date')->nullable(); + $table->timestamps(); + + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + }); + } + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('tasks'); + } +}; diff --git a/backend/database/migrations/2024_12_16_084521_create_quest_table.php b/backend/database/migrations/2024_12_16_084521_create_quest_table.php new file mode 100644 index 0000000..6728852 --- /dev/null +++ b/backend/database/migrations/2024_12_16_084521_create_quest_table.php @@ -0,0 +1,36 @@ +id(); + $table->unsignedBigInteger('user_id'); + $table->unsignedBigInteger('task_id'); + $table->string('title'); + $table->string('category'); + $table->dateTime('due_date')->nullable(); + $table->integer('xp_reward')->default(50); + $table->boolean('is_completed')->default(false); + $table->timestamps(); + + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + $table->foreign('task_id')->references('id')->on('tasks')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('quests'); + } +}; diff --git a/backend/database/migrations/2024_12_16_084621_create_rewards_table.php b/backend/database/migrations/2024_12_16_084621_create_rewards_table.php new file mode 100644 index 0000000..6be9c0e --- /dev/null +++ b/backend/database/migrations/2024_12_16_084621_create_rewards_table.php @@ -0,0 +1,41 @@ +id(); + $table->string('name'); + $table->text('description')->nullable(); + $table->integer('unlocked_at_level')->default(2); + $table->timestamps(); + }); + + // Optionale Pivot-Tabelle für freigeschaltete Rewards + Schema::create('reward_user', function (Blueprint $table) { + $table->id(); + $table->unsignedBigInteger('user_id'); + $table->unsignedBigInteger('reward_id'); + $table->timestamps(); + + $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); + $table->foreign('reward_id')->references('id')->on('rewards')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('rewards'); + Schema::dropIfExists('reward_user'); + } +}; diff --git a/backend/resources/views/welcome.blade.php b/backend/resources/views/welcome.blade.php index a9898e3..73559a9 100644 --- a/backend/resources/views/welcome.blade.php +++ b/backend/resources/views/welcome.blade.php @@ -55,111 +55,7 @@
-