diff --git a/app/DTOs/StudentData.php b/app/DTOs/StudentData.php new file mode 100644 index 0000000..33eba4f --- /dev/null +++ b/app/DTOs/StudentData.php @@ -0,0 +1,47 @@ +id === $studentByIndex->id ? $student->index_number : "", + grades: self::prepareGrades($student, $gradeColumns), + ); + } + + public static function prepareGrades(Student $student, Collection $gradeColumns): Collection + { + return $gradeColumns->map(function (GradeColumn $column) use ($student): array { + /** @var Grade $grade */ + $grade = Grade::query() + ->where("grade_column_id", $column->id) + ->where("student_id", $student->id) + ->first(); + + return $grade + ? [ + "present" => $grade->status, + "value" => $grade->value, + ] + : [ + "present" => null, + "value" => null, + ]; + }); + } +} diff --git a/app/Http/Controllers/Dashboard/GradeController.php b/app/Http/Controllers/Dashboard/GradeController.php index 1a0f125..1ddbc5c 100644 --- a/app/Http/Controllers/Dashboard/GradeController.php +++ b/app/Http/Controllers/Dashboard/GradeController.php @@ -67,12 +67,21 @@ public function storeGrade(UpdateGrade $request, CourseSemester $course, Group $ public function updateGrade(UpdateGrade $request, CourseSemester $course, Group $group, GradeColumn $gradeColumn): RedirectResponse { - $gradeColumn->grades() + $grade = $gradeColumn->grades() ->where("student_id", $request->get("student_id")) - ->update($request->getData()); + ->first(); - return redirect()->back() - ->with("success", "Zaktualizowano ocenę"); + if ($grade) { + $grade->update($request->getData()); + + return redirect()->back() + ->with("success", "Zaktualizowano ocenę"); + } + + $gradeColumn->grades() + ->create($request->getData()); + + return redirect()->back(); } public function destroy(CourseSemester $course, Group $group, GradeColumn $gradeColumn): RedirectResponse diff --git a/app/Http/Controllers/Public/GradeController.php b/app/Http/Controllers/Public/GradeController.php new file mode 100644 index 0000000..f021e9e --- /dev/null +++ b/app/Http/Controllers/Public/GradeController.php @@ -0,0 +1,75 @@ +courses() + ->with("course", fn(BelongsTo $query): BelongsTo => $query->select(["name", "id"])) + ->get(); + } + + if ($course) { + $groups = $course + ->groups() + ->get(["name", "id"]); + } + + if ($group && $index) { + $studentByIndex = $group->students() + ->where("index_number", $index) + ->first(); + + if ($studentByIndex?->exists()) { + $gradeColumns = $group + ->gradeColumns() + ->where("active", true) + ->orderBy("priority") + ->get(); + $students = $group->students() + ->whereNot("index_number", $index) + ->inRandomOrder() + ->take(8) + ->get() + ->push($studentByIndex) + ->sortBy("index_number") + ->map(fn(Student $student): StudentData => StudentData::fromModels($student, $studentByIndex, $gradeColumns)); + } + } + + return inertia("Public/Grade", [ + "semesters" => Semester::query()->get(["name", "id"]), + "semester" => $semester, + "courses" => $courses, + "course" => $course, + "groups" => $groups, + "group" => $group, + "index" => $index ?? "", + "gradeColumns" => $gradeColumns, + "students" => $students, + "indexExists" => $studentByIndex?->exists(), + ]); + } +} diff --git a/app/Http/Requests/UpdateGrade.php b/app/Http/Requests/UpdateGrade.php index a03c12e..ab451dc 100644 --- a/app/Http/Requests/UpdateGrade.php +++ b/app/Http/Requests/UpdateGrade.php @@ -20,7 +20,7 @@ public function getData(): array { return [ "student_id" => $this->get("student_id"), - "status" => $this->boolean("status"), + "status" => $this->get("status") === null ? null : $this->boolean("status"), "value" => $this->get("value"), ]; } diff --git a/app/Models/Grade.php b/app/Models/Grade.php index a09ec80..9c331a3 100644 --- a/app/Models/Grade.php +++ b/app/Models/Grade.php @@ -4,6 +4,7 @@ namespace App\Models; +use App\Observers\GradeObserver; use Carbon\Carbon; use Illuminate\Database\Eloquent\Concerns\HasUlids; use Illuminate\Database\Eloquent\Factories\HasFactory; @@ -40,4 +41,9 @@ public function student(): BelongsTo { return $this->belongsTo(Student::class); } + + protected static function booted(): void + { + self::observe(GradeObserver::class); + } } diff --git a/app/Observers/GradeObserver.php b/app/Observers/GradeObserver.php new file mode 100644 index 0000000..9094d7a --- /dev/null +++ b/app/Observers/GradeObserver.php @@ -0,0 +1,17 @@ +status === true && $grade->getOriginal("status") === false) { + $grade->status = null; + } + } +} diff --git a/composer.lock b/composer.lock index bc9365a..037f525 100644 --- a/composer.lock +++ b/composer.lock @@ -7535,16 +7535,16 @@ }, { "name": "phpunit/phpunit", - "version": "11.2.6", + "version": "11.2.7", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "1dc0fedac703199e8704de085e47dd46bac0dde4" + "reference": "15c7e69dec4a8f246840859e6b430bd2abeb5039" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1dc0fedac703199e8704de085e47dd46bac0dde4", - "reference": "1dc0fedac703199e8704de085e47dd46bac0dde4", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/15c7e69dec4a8f246840859e6b430bd2abeb5039", + "reference": "15c7e69dec4a8f246840859e6b430bd2abeb5039", "shasum": "" }, "require": { @@ -7554,25 +7554,25 @@ "ext-mbstring": "*", "ext-xml": "*", "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.10.1", - "phar-io/manifest": "^2.0.3", - "phar-io/version": "^3.0.2", + "myclabs/deep-copy": "^1.12.0", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", "php": ">=8.2", - "phpunit/php-code-coverage": "^11.0", - "phpunit/php-file-iterator": "^5.0", - "phpunit/php-invoker": "^5.0", - "phpunit/php-text-template": "^4.0", - "phpunit/php-timer": "^7.0", - "sebastian/cli-parser": "^3.0", - "sebastian/code-unit": "^3.0", - "sebastian/comparator": "^6.0", - "sebastian/diff": "^6.0", - "sebastian/environment": "^7.0", - "sebastian/exporter": "^6.1.2", - "sebastian/global-state": "^7.0", - "sebastian/object-enumerator": "^6.0", - "sebastian/type": "^5.0", - "sebastian/version": "^5.0" + "phpunit/php-code-coverage": "^11.0.5", + "phpunit/php-file-iterator": "^5.0.1", + "phpunit/php-invoker": "^5.0.1", + "phpunit/php-text-template": "^4.0.1", + "phpunit/php-timer": "^7.0.1", + "sebastian/cli-parser": "^3.0.2", + "sebastian/code-unit": "^3.0.1", + "sebastian/comparator": "^6.0.1", + "sebastian/diff": "^6.0.2", + "sebastian/environment": "^7.2.0", + "sebastian/exporter": "^6.1.3", + "sebastian/global-state": "^7.0.2", + "sebastian/object-enumerator": "^6.0.1", + "sebastian/type": "^5.0.1", + "sebastian/version": "^5.0.1" }, "suggest": { "ext-soap": "To be able to generate mocks based on WSDL files" @@ -7615,7 +7615,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/11.2.6" + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.2.7" }, "funding": [ { @@ -7631,7 +7631,7 @@ "type": "tidelift" } ], - "time": "2024-07-03T05:51:49+00:00" + "time": "2024-07-10T11:50:09+00:00" }, { "name": "react/cache", diff --git a/database/factories/GradeFactory.php b/database/factories/GradeFactory.php index 110f593..6c5d477 100644 --- a/database/factories/GradeFactory.php +++ b/database/factories/GradeFactory.php @@ -13,7 +13,7 @@ class GradeFactory extends Factory public function definition(): array { return [ - "value" => fake()->numberBetween(2, 5), + "value" => fake()->boolean ? fake()->numberBetween(2, 5) : null, "status" => fake()->boolean, "student_id" => Student::factory(), "grade_column_id" => GradeColumn::factory(), diff --git a/database/factories/SemesterFactory.php b/database/factories/SemesterFactory.php index c5c3312..62348aa 100644 --- a/database/factories/SemesterFactory.php +++ b/database/factories/SemesterFactory.php @@ -11,7 +11,7 @@ class SemesterFactory extends Factory public function definition(): array { return [ - "name" => "Semestr" . fake()->numberBetween(1, 7), + "name" => "Semestr " . fake()->numberBetween(1, 7), "active" => fake()->boolean, ]; } diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index 2d3fd79..4552c2e 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -4,9 +4,14 @@ namespace Database\Seeders; +use App\Models\CourseSemester; +use App\Models\Grade; +use App\Models\GradeColumn; +use App\Models\Group; use App\Models\Section; use App\Models\SectionSettings; use App\Models\Setting; +use App\Models\Student; use App\Models\User; use Illuminate\Database\Seeder; @@ -24,5 +29,35 @@ public function run(): void "counters_enabled" => true, "contact_enabled" => true, ]); + $this->seedGrades(); + } + + protected function seedGrades(): void + { + $courses = CourseSemester::factory(5)->create(); + $students = Student::factory(100)->create(); + + foreach ($courses as $course) { + $groups = Group::factory(fake()->numberBetween(2, 6))->create([ + "course_semester_id" => $course->id, + ]); + + /** @var Group $group */ + foreach ($groups as $group) { + $group->students()->sync($students->random(15)); + $columns = GradeColumn::factory(15)->create([ + "group_id" => $group->id, + ]); + + foreach ($group->students as $student) { + foreach ($columns as $column) { + Grade::factory()->create([ + "grade_column_id" => $column->id, + "student_id" => $student->id, + ]); + } + } + } + } } } diff --git a/resources/js/Pages/Dashboard/CourseSemester/Grade/Index.vue b/resources/js/Pages/Dashboard/CourseSemester/Grade/Index.vue index d4b4692..5197bcb 100644 --- a/resources/js/Pages/Dashboard/CourseSemester/Grade/Index.vue +++ b/resources/js/Pages/Dashboard/CourseSemester/Grade/Index.vue @@ -30,7 +30,6 @@ const props = defineProps({ search: String, total: Number, }) -const showName = ref(false) const showModal = ref(false) const showEditForm = ref(false) const showCreateForm = ref(false) @@ -71,10 +70,11 @@ function updateGrade(gradeColumnId, studentId, value, status) { }) } -function createGrade(gradeColumnId, studentId, status) { +function createGrade(gradeColumnId, studentId, status, value) { Inertia.post(`/dashboard/semester-courses/${props.course.data.id}/groups/${props.group.id}/grades/${gradeColumnId}/store`, { status: status, student_id: studentId, + value: value, }, { preserveScroll: true, }) @@ -221,9 +221,11 @@ watch(searchForm, debounce(() => {