From fb79697fded2157ceefe49f00b1d670eefd1f682 Mon Sep 17 00:00:00 2001 From: DimGiagias Date: Sun, 25 May 2025 00:55:41 +0300 Subject: [PATCH 1/5] fix: fixed seeding issue --- .../0001_01_01_000000_create_users_table.php | 13 ++++- ...025_04_17_141934_create_lessons_table.php} | 3 +- ...025_04_17_141934_create_modules_table.php} | 0 ...025_04_17_142012_create_courses_table.php} | 1 + ...025_04_22_192107_create_quizzes_table.php} | 0 ...5_04_22_192108_create_questions_table.php} | 3 ++ ...4_22_192108_create_quiz_answers_table.php} | 5 +- ..._22_192108_create_quiz_attempts_table.php} | 3 +- ...04_22_192603_create_lesson_quiz_table.php} | 2 +- ..._24_114041_create_user_progress_table.php} | 5 +- ...17_165334_create_learning_paths_table.php} | 0 ...453_create_learning_path_course_table.php} | 6 +-- ...15956_create_external_resources_table.php} | 0 database/seeders/ExternalResourceSeeder.php | 49 +++++++++++++++++ database/seeders/LearningPathCourseSeeder.php | 52 +++++++++++++++++++ database/seeders/LearningPathSeeder.php | 41 +++++++++++++++ 16 files changed, 171 insertions(+), 12 deletions(-) rename database/migrations/{2025_04_27_192057_create_lessons_table.php => 2025_04_17_141934_create_lessons_table.php} (84%) rename database/migrations/{2025_04_27_192057_create_modules_table.php => 2025_04_17_141934_create_modules_table.php} (100%) rename database/migrations/{2025_04_27_192057_create_courses_table.php => 2025_04_17_142012_create_courses_table.php} (89%) rename database/migrations/{2025_04_28_131921_create_quizzes_table.php => 2025_04_22_192107_create_quizzes_table.php} (100%) rename database/migrations/{2025_04_28_131922_create_questions_table.php => 2025_04_22_192108_create_questions_table.php} (85%) rename database/migrations/{2025_04_28_131922_create_quiz_answers_table.php => 2025_04_22_192108_create_quiz_answers_table.php} (79%) rename database/migrations/{2025_04_28_131922_create_quiz_attempts_table.php => 2025_04_22_192108_create_quiz_attempts_table.php} (85%) rename database/migrations/{2025_04_28_131923_create_lesson_quiz_table.php => 2025_04_22_192603_create_lesson_quiz_table.php} (91%) rename database/migrations/{2025_05_21_181615_create_user_progress_table.php => 2025_04_24_114041_create_user_progress_table.php} (78%) rename database/migrations/{2025_05_22_184048_create_learning_paths_table.php => 2025_05_17_165334_create_learning_paths_table.php} (100%) rename database/migrations/{2025_05_22_184102_create_learning_path_course_table.php => 2025_05_17_170453_create_learning_path_course_table.php} (72%) rename database/migrations/{2025_05_22_184123_create_external_resources_table.php => 2025_05_20_115956_create_external_resources_table.php} (100%) create mode 100644 database/seeders/ExternalResourceSeeder.php create mode 100644 database/seeders/LearningPathCourseSeeder.php create mode 100644 database/seeders/LearningPathSeeder.php diff --git a/database/migrations/0001_01_01_000000_create_users_table.php b/database/migrations/0001_01_01_000000_create_users_table.php index 18fb540..af7e70d 100644 --- a/database/migrations/0001_01_01_000000_create_users_table.php +++ b/database/migrations/0001_01_01_000000_create_users_table.php @@ -14,13 +14,22 @@ 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->rememberToken(); - $table->enum('preferred_learning_style', ['reading', 'visual'])->default('reading')->nullable()->after('password'); - $table->foreignId('learning_path_id')->nullable()->after('preferred_learning_style')->constrained('learning_paths')->onDelete('set null'); + $table->enum('preferred_learning_style', ['reading', 'visual', 'balanced']) + ->default('balanced') + ->nullable() // Allow null if user hasn't set it + ->after('password'); // Place it after password + + $table->foreignId('learning_path_id') // New column name + ->nullable() + ->after('preferred_learning_style') + ->constrained('learning_paths') // Foreign key to learning_paths table + ->onDelete('set null'); $table->timestamps(); }); diff --git a/database/migrations/2025_04_27_192057_create_lessons_table.php b/database/migrations/2025_04_17_141934_create_lessons_table.php similarity index 84% rename from database/migrations/2025_04_27_192057_create_lessons_table.php rename to database/migrations/2025_04_17_141934_create_lessons_table.php index 5571829..350c883 100644 --- a/database/migrations/2025_04_27_192057_create_lessons_table.php +++ b/database/migrations/2025_04_17_141934_create_lessons_table.php @@ -22,7 +22,8 @@ public function up(): void $table->text('video_embed_html')->nullable()->after('content'); $table->text('assignment')->nullable()->comment('Assignment description'); $table->text('initial_code')->nullable()->comment('Starting code for editor'); - $table->text('expected_output')->nullable(); + // $table->text('solution')->nullable()->comment('Optional solution code/explanation'); + $table->text('expected_output')->nullable()->comment('For simple stdout checks'); // Example addition $table->unsignedSmallInteger('order')->default(0); $table->timestamps(); diff --git a/database/migrations/2025_04_27_192057_create_modules_table.php b/database/migrations/2025_04_17_141934_create_modules_table.php similarity index 100% rename from database/migrations/2025_04_27_192057_create_modules_table.php rename to database/migrations/2025_04_17_141934_create_modules_table.php diff --git a/database/migrations/2025_04_27_192057_create_courses_table.php b/database/migrations/2025_04_17_142012_create_courses_table.php similarity index 89% rename from database/migrations/2025_04_27_192057_create_courses_table.php rename to database/migrations/2025_04_17_142012_create_courses_table.php index 7f6e0f8..edc1ec2 100644 --- a/database/migrations/2025_04_27_192057_create_courses_table.php +++ b/database/migrations/2025_04_17_142012_create_courses_table.php @@ -19,6 +19,7 @@ public function up(): void $table->string('slug')->unique(); $table->text('description')->nullable(); $table->boolean('is_published')->default(false); + // Set null on delete: if the quiz is deleted, the course doesn't break, just loses its assessment link. $table->foreignId('assessment_quiz_id')->nullable()->after('is_published')->constrained('quizzes')->onDelete('set null'); $table->foreignId('final_review_quiz_id')->nullable()->after('assessment_quiz_id')->constrained('quizzes')->onDelete('set null'); $table->timestamps(); diff --git a/database/migrations/2025_04_28_131921_create_quizzes_table.php b/database/migrations/2025_04_22_192107_create_quizzes_table.php similarity index 100% rename from database/migrations/2025_04_28_131921_create_quizzes_table.php rename to database/migrations/2025_04_22_192107_create_quizzes_table.php diff --git a/database/migrations/2025_04_28_131922_create_questions_table.php b/database/migrations/2025_04_22_192108_create_questions_table.php similarity index 85% rename from database/migrations/2025_04_28_131922_create_questions_table.php rename to database/migrations/2025_04_22_192108_create_questions_table.php index 87f3be1..caa6325 100644 --- a/database/migrations/2025_04_28_131922_create_questions_table.php +++ b/database/migrations/2025_04_22_192108_create_questions_table.php @@ -16,7 +16,10 @@ public function up(): void Schema::create('questions', function (Blueprint $table) { $table->id(); $table->foreignId('quiz_id')->constrained()->onDelete('cascade'); + // Crucial link for suggesting review topics! + // Can be null if a question is general, or set null if lesson deleted. $table->foreignId('lesson_id')->nullable()->constrained()->onDelete('set null'); + // Start with multiple choice, add more later if needed $table->enum('type', ['multiple_choice', 'true_false', 'fill_blank'])->default('multiple_choice'); $table->text('text')->comment('The question text'); $table->json('options')->nullable(); diff --git a/database/migrations/2025_04_28_131922_create_quiz_answers_table.php b/database/migrations/2025_04_22_192108_create_quiz_answers_table.php similarity index 79% rename from database/migrations/2025_04_28_131922_create_quiz_answers_table.php rename to database/migrations/2025_04_22_192108_create_quiz_answers_table.php index b749613..439336f 100644 --- a/database/migrations/2025_04_28_131922_create_quiz_answers_table.php +++ b/database/migrations/2025_04_22_192108_create_quiz_answers_table.php @@ -17,11 +17,12 @@ public function up(): void $table->id(); $table->foreignId('quiz_attempt_id')->constrained()->onDelete('cascade'); $table->foreignId('question_id')->constrained()->onDelete('cascade'); + // Store the user's selected option ID (e.g., "a", "b") $table->string('user_answer')->nullable(); - $table->boolean('is_correct')->nullable(); + $table->boolean('is_correct')->nullable()->comment('Graded result'); $table->timestamps(); - // User can answer each question once per attempt + // User should only answer each question once per attempt $table->unique(['quiz_attempt_id', 'question_id']); }); } diff --git a/database/migrations/2025_04_28_131922_create_quiz_attempts_table.php b/database/migrations/2025_04_22_192108_create_quiz_attempts_table.php similarity index 85% rename from database/migrations/2025_04_28_131922_create_quiz_attempts_table.php rename to database/migrations/2025_04_22_192108_create_quiz_attempts_table.php index 6683832..7c0f5c5 100644 --- a/database/migrations/2025_04_28_131922_create_quiz_attempts_table.php +++ b/database/migrations/2025_04_22_192108_create_quiz_attempts_table.php @@ -18,9 +18,10 @@ public function up(): void $table->foreignId('user_id')->constrained()->onDelete('cascade'); $table->foreignId('quiz_id')->nullable()->constrained()->onDelete('cascade'); $table->string('type')->default('standard')->after('quiz_id'); + // Score as percentage (0-100), calculated after submission $table->unsignedTinyInteger('score')->nullable(); $table->timestamp('started_at')->useCurrent(); - $table->timestamp('completed_at')->nullable(); + $table->timestamp('completed_at')->nullable(); // Set when submitted $table->timestamps(); }); } diff --git a/database/migrations/2025_04_28_131923_create_lesson_quiz_table.php b/database/migrations/2025_04_22_192603_create_lesson_quiz_table.php similarity index 91% rename from database/migrations/2025_04_28_131923_create_lesson_quiz_table.php rename to database/migrations/2025_04_22_192603_create_lesson_quiz_table.php index a251c24..2152708 100644 --- a/database/migrations/2025_04_28_131923_create_lesson_quiz_table.php +++ b/database/migrations/2025_04_22_192603_create_lesson_quiz_table.php @@ -17,7 +17,7 @@ public function up(): void $table->id(); $table->foreignId('lesson_id')->constrained()->onDelete('cascade'); $table->foreignId('quiz_id')->constrained()->onDelete('cascade'); - $table->timestamps(); + $table->timestamps(); // Optional, but good practice $table->unique(['lesson_id', 'quiz_id']); }); diff --git a/database/migrations/2025_05_21_181615_create_user_progress_table.php b/database/migrations/2025_04_24_114041_create_user_progress_table.php similarity index 78% rename from database/migrations/2025_05_21_181615_create_user_progress_table.php rename to database/migrations/2025_04_24_114041_create_user_progress_table.php index ade2eda..d3182a9 100644 --- a/database/migrations/2025_05_21_181615_create_user_progress_table.php +++ b/database/migrations/2025_04_24_114041_create_user_progress_table.php @@ -15,12 +15,13 @@ public function up(): void { Schema::create('user_progress', function (Blueprint $table) { $table->id(); + // Foreign keys linking to users and lessons $table->foreignId('user_id')->constrained()->onDelete('cascade'); $table->foreignId('lesson_id')->constrained()->onDelete('cascade'); - $table->timestamp('completed_at')->useCurrent(); + $table->timestamp('completed_at')->useCurrent(); // Record when completed $table->timestamps(); - // A user can complete a specific lesson only once. + // IMPORTANT: A user can complete a specific lesson only once $table->unique(['user_id', 'lesson_id']); }); } diff --git a/database/migrations/2025_05_22_184048_create_learning_paths_table.php b/database/migrations/2025_05_17_165334_create_learning_paths_table.php similarity index 100% rename from database/migrations/2025_05_22_184048_create_learning_paths_table.php rename to database/migrations/2025_05_17_165334_create_learning_paths_table.php diff --git a/database/migrations/2025_05_22_184102_create_learning_path_course_table.php b/database/migrations/2025_05_17_170453_create_learning_path_course_table.php similarity index 72% rename from database/migrations/2025_05_22_184102_create_learning_path_course_table.php rename to database/migrations/2025_05_17_170453_create_learning_path_course_table.php index b9a5376..6206d5d 100644 --- a/database/migrations/2025_05_22_184102_create_learning_path_course_table.php +++ b/database/migrations/2025_05_17_170453_create_learning_path_course_table.php @@ -17,10 +17,10 @@ public function up(): void $table->id(); $table->foreignId('learning_path_id')->constrained()->onDelete('cascade'); $table->foreignId('course_id')->constrained()->onDelete('cascade'); - $table->unsignedSmallInteger('order')->default(0); - $table->timestamps(); + $table->unsignedSmallInteger('order')->default(0); // Order of the course in the path + $table->timestamps(); // Usually not needed for simple pivot unless tracking when added - $table->unique(['learning_path_id', 'course_id']); + $table->unique(['learning_path_id', 'course_id']); // A course appears once per path $table->index(['learning_path_id', 'order']); }); } diff --git a/database/migrations/2025_05_22_184123_create_external_resources_table.php b/database/migrations/2025_05_20_115956_create_external_resources_table.php similarity index 100% rename from database/migrations/2025_05_22_184123_create_external_resources_table.php rename to database/migrations/2025_05_20_115956_create_external_resources_table.php diff --git a/database/seeders/ExternalResourceSeeder.php b/database/seeders/ExternalResourceSeeder.php new file mode 100644 index 0000000..08e9d06 --- /dev/null +++ b/database/seeders/ExternalResourceSeeder.php @@ -0,0 +1,49 @@ +first(); + if ($lessonDeclare) { + ExternalResource::create([ + 'lesson_id' => $lessonDeclare->id, + 'title' => 'MDN: let keyword', + 'url' => 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let', + 'type' => 'documentation', + 'description' => 'Detailed documentation on the `let` keyword from Mozilla Developer Network.', + ]); + ExternalResource::create([ + 'lesson_id' => $lessonDeclare->id, + 'title' => 'Understanding var, let, and const in JavaScript (Video)', + 'url' => 'https://www.youtube.com/watch?v=s-hLgT_t3uA', // Example video + 'type' => 'video', + 'description' => 'A video explaining the differences and use cases for variable declarations.', + ]); + } + + $lessonTypes = Lesson::where('slug', 'primitive-data-types')->first(); + if ($lessonTypes) { + ExternalResource::create([ + 'lesson_id' => $lessonTypes->id, + 'title' => 'MDN: JavaScript data types and data structures', + 'url' => 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures', + 'type' => 'documentation', + ]); + } + // Add more resources for other lessons... + } +} diff --git a/database/seeders/LearningPathCourseSeeder.php b/database/seeders/LearningPathCourseSeeder.php new file mode 100644 index 0000000..5fe8586 --- /dev/null +++ b/database/seeders/LearningPathCourseSeeder.php @@ -0,0 +1,52 @@ +truncate(); + + $pathFrontend = LearningPath::where('slug', 'frontend-developer-path')->first(); + $pathBackend = LearningPath::where('slug', 'backend-javascript-developer-path')->first(); + $pathFullstack = LearningPath::where('slug', 'full-stack-javascript-path')->first(); + + $courseFundamentals = Course::where('slug', 'javascript-fundamentals')->first(); + $courseIntermediate = Course::where('slug', 'intermediate-web-dev-js')->first(); + $courseAdvanced = Course::where('slug', 'advanced-js-nodejs')->first(); + + if ($pathFrontend && $courseFundamentals) { + $pathFrontend->courses()->attach($courseFundamentals->id, ['order' => 1]); + } + if ($pathFrontend && $courseIntermediate) { + $pathFrontend->courses()->attach($courseIntermediate->id, ['order' => 2]); + } + + if ($pathBackend && $courseFundamentals) { + $pathBackend->courses()->attach($courseFundamentals->id, ['order' => 1]); + } + if ($pathBackend && $courseAdvanced) { + $pathBackend->courses()->attach($courseAdvanced->id, ['order' => 2]); + } + + if ($pathFullstack && $courseFundamentals) { + $pathFullstack->courses()->attach($courseFundamentals->id, ['order' => 1]); + } + if ($pathFullstack && $courseIntermediate) { + $pathFullstack->courses()->attach($courseIntermediate->id, ['order' => 2]); + } + if ($pathFullstack && $courseAdvanced) { + $pathFullstack->courses()->attach($courseAdvanced->id, ['order' => 3]); + } + } +} diff --git a/database/seeders/LearningPathSeeder.php b/database/seeders/LearningPathSeeder.php new file mode 100644 index 0000000..6187b85 --- /dev/null +++ b/database/seeders/LearningPathSeeder.php @@ -0,0 +1,41 @@ + 'Frontend Developer Path', + 'slug' => Str::slug('Frontend Developer Path'), + 'description' => 'Learn the essentials of frontend web development, focusing on JavaScript, HTML, CSS, and modern frameworks.', + 'is_active' => true, + ]); + + LearningPath::create([ + 'name' => 'Backend JavaScript Developer Path', + 'slug' => Str::slug('Backend JavaScript Developer Path'), + 'description' => 'Master server-side JavaScript with Node.js, Express, and databases to build robust APIs and web applications.', + 'is_active' => true, + ]); + + LearningPath::create([ + 'name' => 'Full-Stack JavaScript Path', + 'slug' => Str::slug('Full-Stack JavaScript Path'), + 'description' => 'Become proficient in both frontend and backend JavaScript technologies for a comprehensive skill set.', + 'is_active' => true, + ]); + } +} From 933b422b0c0038e1622ee837cd919b05346a9fcf Mon Sep 17 00:00:00 2001 From: DimGiagias Date: Sun, 25 May 2025 00:58:46 +0300 Subject: [PATCH 2/5] fix: fix lesson completion tracking issue --- app/Models/Lesson.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/app/Models/Lesson.php b/app/Models/Lesson.php index 4b8f7a4..1d531db 100644 --- a/app/Models/Lesson.php +++ b/app/Models/Lesson.php @@ -10,6 +10,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Support\Facades\Auth; final class Lesson extends Model { @@ -57,6 +58,21 @@ public function userProgress(): HasMany return $this->hasMany(UserProgress::class); } + /** + * Accessor: Dynamically check if the *currently authenticated* user + * has completed this lesson. + */ + public function getIsCompletedAttribute(): bool + { + // If no user is logged in, it's not completed by them + if (!Auth::check()) { + return false; + } + + // Check if a progress record exists for the logged-in user and this lesson + return $this->userProgress()->where('user_id', Auth::id())->exists(); + } + // A Lesson has many external resources. public function externalResources(): HasMany { From 8a82ffd0e216d6ec30348bd9d19a2f42e9b97920 Mon Sep 17 00:00:00 2001 From: DimGiagias Date: Sun, 25 May 2025 01:12:19 +0300 Subject: [PATCH 3/5] feat: finalized mvp ui for all teh features --- resources/js/pages/Dashboard.vue | 175 ++++++++-- resources/js/pages/RandomQuiz/Show.vue | 252 +++++++-------- resources/js/pages/Welcome.vue | 48 +-- resources/js/pages/auth/ForgotPassword.vue | 2 +- resources/js/pages/auth/Login.vue | 6 +- resources/js/pages/auth/Register.vue | 2 +- resources/js/pages/courses/Index.vue | 54 ++-- resources/js/pages/courses/Show.vue | 153 ++++----- .../js/pages/courses/assessment/Result.vue | 61 ++++ .../js/pages/courses/assessment/Show.vue | 173 ++++++++++ resources/js/pages/courses/review/Show.vue | 162 ++++++++++ resources/js/pages/lessons/Show.vue | 298 +++++++++--------- resources/js/pages/quizzes/Result.vue | 181 ++++++----- resources/js/pages/quizzes/Show.vue | 159 +++++++--- resources/js/pages/settings/Profile.vue | 2 +- resources/js/pages/stats/Show.vue | 175 ++++++++++ 16 files changed, 1337 insertions(+), 566 deletions(-) create mode 100644 resources/js/pages/courses/assessment/Result.vue create mode 100644 resources/js/pages/courses/assessment/Show.vue create mode 100644 resources/js/pages/courses/review/Show.vue create mode 100644 resources/js/pages/stats/Show.vue diff --git a/resources/js/pages/Dashboard.vue b/resources/js/pages/Dashboard.vue index 78af1b4..720b342 100644 --- a/resources/js/pages/Dashboard.vue +++ b/resources/js/pages/Dashboard.vue @@ -1,7 +1,8 @@ + diff --git a/resources/js/pages/RandomQuiz/Show.vue b/resources/js/pages/RandomQuiz/Show.vue index 1632a6e..ba481b3 100644 --- a/resources/js/pages/RandomQuiz/Show.vue +++ b/resources/js/pages/RandomQuiz/Show.vue @@ -1,180 +1,170 @@ - + diff --git a/resources/js/pages/Welcome.vue b/resources/js/pages/Welcome.vue index ba9f930..3560aa5 100644 --- a/resources/js/pages/Welcome.vue +++ b/resources/js/pages/Welcome.vue @@ -7,8 +7,8 @@ import { Head, Link } from '@inertiajs/vue3'; -
-
+
+
-
+

Let's get started

@@ -44,7 +44,7 @@ import { Head, Link } from '@inertiajs/vue3';

- + - +
- +
diff --git a/resources/js/pages/auth/ForgotPassword.vue b/resources/js/pages/auth/ForgotPassword.vue index e70fbad..e992a14 100644 --- a/resources/js/pages/auth/ForgotPassword.vue +++ b/resources/js/pages/auth/ForgotPassword.vue @@ -45,7 +45,7 @@ const submit = () => {
-
+
Or, return to log in
diff --git a/resources/js/pages/auth/Login.vue b/resources/js/pages/auth/Login.vue index f11f406..166caf9 100644 --- a/resources/js/pages/auth/Login.vue +++ b/resources/js/pages/auth/Login.vue @@ -71,9 +71,9 @@ const submit = () => {
-
+
@@ -84,7 +84,7 @@ const submit = () => {
-
+
Don't have an account? Sign up
diff --git a/resources/js/pages/auth/Register.vue b/resources/js/pages/auth/Register.vue index 3ad9e38..c0a757d 100644 --- a/resources/js/pages/auth/Register.vue +++ b/resources/js/pages/auth/Register.vue @@ -74,7 +74,7 @@ const submit = () => {
-
+
Already have an account? Log in
diff --git a/resources/js/pages/courses/Index.vue b/resources/js/pages/courses/Index.vue index ca8064b..04f3417 100644 --- a/resources/js/pages/courses/Index.vue +++ b/resources/js/pages/courses/Index.vue @@ -1,42 +1,46 @@ + + diff --git a/resources/js/pages/courses/Show.vue b/resources/js/pages/courses/Show.vue index c770f4e..cd160c8 100644 --- a/resources/js/pages/courses/Show.vue +++ b/resources/js/pages/courses/Show.vue @@ -1,97 +1,98 @@ + + diff --git a/resources/js/pages/courses/assessment/Result.vue b/resources/js/pages/courses/assessment/Result.vue new file mode 100644 index 0000000..100435a --- /dev/null +++ b/resources/js/pages/courses/assessment/Result.vue @@ -0,0 +1,61 @@ + + + diff --git a/resources/js/pages/courses/assessment/Show.vue b/resources/js/pages/courses/assessment/Show.vue new file mode 100644 index 0000000..f065951 --- /dev/null +++ b/resources/js/pages/courses/assessment/Show.vue @@ -0,0 +1,173 @@ + + + + + diff --git a/resources/js/pages/courses/review/Show.vue b/resources/js/pages/courses/review/Show.vue new file mode 100644 index 0000000..253065c --- /dev/null +++ b/resources/js/pages/courses/review/Show.vue @@ -0,0 +1,162 @@ + + + + + diff --git a/resources/js/pages/lessons/Show.vue b/resources/js/pages/lessons/Show.vue index 8b99489..f828d04 100644 --- a/resources/js/pages/lessons/Show.vue +++ b/resources/js/pages/lessons/Show.vue @@ -1,55 +1,62 @@ diff --git a/resources/js/pages/quizzes/Result.vue b/resources/js/pages/quizzes/Result.vue index 474a5d3..473a091 100644 --- a/resources/js/pages/quizzes/Result.vue +++ b/resources/js/pages/quizzes/Result.vue @@ -1,23 +1,30 @@ + + diff --git a/resources/js/pages/quizzes/Show.vue b/resources/js/pages/quizzes/Show.vue index 8bd3a3e..1e4933e 100644 --- a/resources/js/pages/quizzes/Show.vue +++ b/resources/js/pages/quizzes/Show.vue @@ -1,113 +1,172 @@ + + diff --git a/resources/js/pages/settings/Profile.vue b/resources/js/pages/settings/Profile.vue index f8f8f37..a1028f6 100644 --- a/resources/js/pages/settings/Profile.vue +++ b/resources/js/pages/settings/Profile.vue @@ -70,7 +70,7 @@ const submit = () => {
-

+

Your email address is unverified. +import AppLayout from '@/layouts/app/AppSidebarLayout.vue'; +import { Head } from '@inertiajs/vue3'; +import { Pie, Bar } from 'vue-chartjs'; +import { + Chart as ChartJS, + Title, + Tooltip, + Legend, + ArcElement, + CategoryScale, + LinearScale, + BarElement, +} from 'chart.js'; + +ChartJS.register( + Title, + Tooltip, + Legend, + ArcElement, + CategoryScale, + LinearScale, + BarElement +); + +const props = defineProps({ + totalQuizzesAttempted: Number, + learningStreak: Number, + quizScoreDistribution: Object, + lessonContributionData: Array, +}); + +// --- Pie Chart Data & Options --- +const pieChartData = { + labels: ['Excellent (80-100%)', 'Good (50-79%)', 'Needs Improvement (<50%)'], + datasets: [ + { + backgroundColor: ['#4CAF50', '#FFC107', '#F44336'], // Green, Yellow, Red + data: [ + props.quizScoreDistribution.green || 0, + props.quizScoreDistribution.yellow || 0, + props.quizScoreDistribution.red || 0, + ], + }, + ], +}; + +const pieChartOptions = { + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { + position: 'top', + }, + title: { + display: true, + text: 'Quiz Score Distribution', + }, + }, +}; + + +// --- Contribution Graph Data & Options (Bar Chart for now) --- +const processContributionDataForBarChart = () => { + const monthlyCompletions = {}; + props.lessonContributionData?.forEach(item => { + const monthYear = item.date.substring(0, 7); // YYYY-MM + if (!monthlyCompletions[monthYear]) { + monthlyCompletions[monthYear] = 0; + } + monthlyCompletions[monthYear] += item.count; + }); + + const labels = Object.keys(monthlyCompletions).sort(); + const data = labels.map(label => monthlyCompletions[label]); + + return { + labels: labels.map(label => new Date(label + '-01').toLocaleString('default', { month: 'short', year: 'numeric' })), // Format for display + datasets: [ + { + label: 'Lessons Completed', + backgroundColor: '#3B82F6', + data: data, + }, + ], + }; +}; + +const contributionBarChartData = processContributionDataForBarChart(); + +const contributionBarChartOptions = { + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { + display: false, + }, + title: { + display: true, + text: 'Monthly Lesson Completions (Last Year)', + }, + }, + scales: { + y: { + beginAtZero: true, + ticks: { + precision: 0, + } + }, + }, +}; + + + + From ab011ae6c0369d60c0d680fdd19c3386896488c4 Mon Sep 17 00:00:00 2001 From: DimGiagias Date: Sun, 25 May 2025 01:16:31 +0300 Subject: [PATCH 4/5] chore: fix linting --- app/Models/Lesson.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Models/Lesson.php b/app/Models/Lesson.php index 1d531db..b8e1031 100644 --- a/app/Models/Lesson.php +++ b/app/Models/Lesson.php @@ -65,7 +65,7 @@ public function userProgress(): HasMany public function getIsCompletedAttribute(): bool { // If no user is logged in, it's not completed by them - if (!Auth::check()) { + if (! Auth::check()) { return false; } From b0d5508315760a06a0b7dcd42e3eafaff6b8c8a8 Mon Sep 17 00:00:00 2001 From: DimGiagias Date: Sun, 25 May 2025 01:23:52 +0300 Subject: [PATCH 5/5] feat: finalize mvp ui for all the features --- resources/js/pages/Dashboard.vue | 111 ++++---- resources/js/pages/RandomQuiz/Show.vue | 251 +++++++++--------- resources/js/pages/Welcome.vue | 48 ++-- resources/js/pages/auth/ForgotPassword.vue | 2 +- resources/js/pages/auth/Login.vue | 2 +- resources/js/pages/auth/Register.vue | 2 +- resources/js/pages/courses/Index.vue | 24 +- resources/js/pages/courses/Show.vue | 65 ++--- .../js/pages/courses/assessment/Result.vue | 57 ++-- .../js/pages/courses/assessment/Show.vue | 132 +++++---- resources/js/pages/courses/review/Show.vue | 222 ++++++++++------ resources/js/pages/lessons/Show.vue | 218 ++++++++------- resources/js/pages/quizzes/Result.vue | 197 ++++++-------- resources/js/pages/quizzes/Show.vue | 216 ++++++++------- resources/js/pages/settings/Profile.vue | 2 +- resources/js/pages/stats/Show.vue | 132 ++++----- 16 files changed, 851 insertions(+), 830 deletions(-) diff --git a/resources/js/pages/Dashboard.vue b/resources/js/pages/Dashboard.vue index 720b342..ca14801 100644 --- a/resources/js/pages/Dashboard.vue +++ b/resources/js/pages/Dashboard.vue @@ -34,14 +34,14 @@ const submitPreferences = () => { // OR preferencesForm.reset(); // if want to clear form after successful save }, onError: (errors) => { - console.error("Preference update errors:", errors); - } + console.error('Preference update errors:', errors); + }, }); }; const formatDate = (dateString) => { if (!dateString) return 'N/A'; return new Date(dateString).toLocaleString(); -} +}; const learningStyleOptions = [ { value: 'reading', label: 'Reading Preferred' }, @@ -50,35 +50,34 @@ const learningStyleOptions = [ const currentLearningPathName = computed(() => { if (page.props.auth.user?.learning_path_id && props.learningPaths) { - const path = props.learningPaths.find(p => p.id === page.props.auth.user.learning_path_id); + const path = props.learningPaths.find((p) => p.id === page.props.auth.user.learning_path_id); return path ? path.name : 'Not Set'; } return 'Not Set'; }); - - diff --git a/resources/js/pages/RandomQuiz/Show.vue b/resources/js/pages/RandomQuiz/Show.vue index ba481b3..7b94706 100644 --- a/resources/js/pages/RandomQuiz/Show.vue +++ b/resources/js/pages/RandomQuiz/Show.vue @@ -1,170 +1,177 @@ - - diff --git a/resources/js/pages/Welcome.vue b/resources/js/pages/Welcome.vue index 3560aa5..ba9f930 100644 --- a/resources/js/pages/Welcome.vue +++ b/resources/js/pages/Welcome.vue @@ -7,8 +7,8 @@ import { Head, Link } from '@inertiajs/vue3'; -

-
+
+
-
+

Let's get started

@@ -44,7 +44,7 @@ import { Head, Link } from '@inertiajs/vue3';

- + - +
- +
diff --git a/resources/js/pages/auth/ForgotPassword.vue b/resources/js/pages/auth/ForgotPassword.vue index e992a14..e70fbad 100644 --- a/resources/js/pages/auth/ForgotPassword.vue +++ b/resources/js/pages/auth/ForgotPassword.vue @@ -45,7 +45,7 @@ const submit = () => {
-
+
Or, return to log in
diff --git a/resources/js/pages/auth/Login.vue b/resources/js/pages/auth/Login.vue index 166caf9..8234094 100644 --- a/resources/js/pages/auth/Login.vue +++ b/resources/js/pages/auth/Login.vue @@ -84,7 +84,7 @@ const submit = () => {
-
+
Don't have an account? Sign up
diff --git a/resources/js/pages/auth/Register.vue b/resources/js/pages/auth/Register.vue index c0a757d..3ad9e38 100644 --- a/resources/js/pages/auth/Register.vue +++ b/resources/js/pages/auth/Register.vue @@ -74,7 +74,7 @@ const submit = () => {
-
+
Already have an account? Log in
diff --git a/resources/js/pages/courses/Index.vue b/resources/js/pages/courses/Index.vue index 04f3417..997bae9 100644 --- a/resources/js/pages/courses/Index.vue +++ b/resources/js/pages/courses/Index.vue @@ -1,30 +1,30 @@ - + diff --git a/resources/js/pages/courses/Show.vue b/resources/js/pages/courses/Show.vue index cd160c8..42b2464 100644 --- a/resources/js/pages/courses/Show.vue +++ b/resources/js/pages/courses/Show.vue @@ -1,11 +1,9 @@