From dee280550499f5e39a7839a69fe27a8472c673e0 Mon Sep 17 00:00:00 2001 From: reihanrere Date: Thu, 15 May 2025 10:02:57 +0700 Subject: [PATCH] add report student --- app/Filament/Pages/StudentReport.php | 223 ++++++++++++++++++ .../Resources/AcademicYearResource.php | 105 +++++++++ .../Pages/CreateAcademicYear.php | 33 +++ .../Pages/EditAcademicYear.php | 42 ++++ .../Pages/ListAcademicYears.php | 19 ++ app/Filament/Resources/AssessmentResource.php | 29 ++- .../Pages/ListAssessments.php | 2 +- .../Pages/MultipleAssessments.php | 112 ++++++--- .../Resources/AttendancesResource.php | 21 +- .../Pages/EditAttendances.php | 23 ++ .../Pages/MultipleAttendances.php | 89 +++++-- app/Filament/Resources/ClassRoomResource.php | 36 +-- .../Resources/ClassStudentResource.php | 112 +++++++++ .../Pages/CreateClassStudent.php | 35 +++ .../Pages/EditClassStudent.php | 41 ++++ .../Pages/ListClassStudents.php | 19 ++ .../Resources/ClassSubjectResource.php | 83 +++++++ .../Pages/CreateClassSubject.php | 34 +++ .../Pages/EditClassSubject.php | 19 ++ .../Pages/ListClassSubjects.php | 19 ++ ....php => CompetencyAchievementResource.php} | 67 ++++-- .../Pages/CreateCompetencyAchievement.php | 12 + .../Pages/EditCompetencyAchievement.php | 19 ++ .../Pages/ListCompetencyAchievements.php | 19 ++ .../Resources/ExtracurricularResource.php | 11 +- .../Resources/HomeRoomTeacherResource.php | 109 +++++++++ .../Pages/CreateHomeRoomTeacher.php | 35 +++ .../Pages/EditHomeRoomTeacher.php | 42 ++++ .../Pages/ListHomeRoomTeachers.php | 19 ++ app/Filament/Resources/StudentResource.php | 39 +-- app/Filament/Resources/SubjectResource.php | 18 ++ .../Pages/CreateSubjectScope.php | 12 - .../Pages/EditSubjectScope.php | 19 -- .../Pages/ListSubjectScopes.php | 19 -- .../Resources/TeacherSubjectResource.php | 10 +- .../Pages/CreateTeacherSubject.php | 2 +- app/Filament/Resources/UserResource.php | 5 +- app/Models/AcademicYear.php | 34 +++ app/Models/Assessment.php | 1 + app/Models/ClassRoom.php | 26 +- app/Models/ClassStudent.php | 28 +++ app/Models/ClassSubject.php | 27 +++ app/Models/CompetencyAchievement.php | 31 +++ app/Models/HomeRoomTeacher.php | 28 +++ app/Models/Student.php | 7 +- app/Models/Subject.php | 16 +- app/Models/SubjectScope.php | 15 -- app/Models/TeacherSubject.php | 33 +-- app/Models/User.php | 5 + app/Policies/SubjectScopePolicy.php | 14 +- app/Policies/TeacherSubjectPolicy.php | 14 +- ..._04_10_000425_create_class_rooms_table.php | 5 +- ...025_04_10_000426_create_students_table.php | 5 +- ...10_000427_create_academic_years_table.php} | 15 +- ...025_04_10_151020_create_subjects_table.php | 3 + ...3_010342_create_teacher_subjects_table.php | 4 +- ..._04_30_125442_create_assessments_table.php | 3 +- ...5_create_competency_achievements_table.php | 34 +++ ...120638_create_home_room_teachers_table.php | 33 +++ ..._04_121938_create_class_students_table.php | 33 +++ ..._06_041554_create_class_subjects_table.php | 33 +++ database/seeders/AcademicYearSeeder.php | 36 +++ database/seeders/AttendanceSeeder.php | 17 -- database/seeders/ClassRoomSeeder.php | 9 +- database/seeders/ClassStudentSeeder.php | 40 ++++ database/seeders/ClassSubjectSeeder.php | 41 ++++ database/seeders/DatabaseSeeder.php | 9 +- ...peSeeder.php => HomeRoomTeacherSeeder.php} | 7 +- database/seeders/StudentSeeder.php | 43 ++-- database/seeders/SubjectSeeder.php | 35 ++- .../filament/pages/student-report.blade.php | 77 ++++++ .../pages/multiple-assessment.blade.php | 4 +- 72 files changed, 1965 insertions(+), 353 deletions(-) create mode 100644 app/Filament/Pages/StudentReport.php create mode 100644 app/Filament/Resources/AcademicYearResource.php create mode 100644 app/Filament/Resources/AcademicYearResource/Pages/CreateAcademicYear.php create mode 100644 app/Filament/Resources/AcademicYearResource/Pages/EditAcademicYear.php create mode 100644 app/Filament/Resources/AcademicYearResource/Pages/ListAcademicYears.php create mode 100644 app/Filament/Resources/ClassStudentResource.php create mode 100644 app/Filament/Resources/ClassStudentResource/Pages/CreateClassStudent.php create mode 100644 app/Filament/Resources/ClassStudentResource/Pages/EditClassStudent.php create mode 100644 app/Filament/Resources/ClassStudentResource/Pages/ListClassStudents.php create mode 100644 app/Filament/Resources/ClassSubjectResource.php create mode 100644 app/Filament/Resources/ClassSubjectResource/Pages/CreateClassSubject.php create mode 100644 app/Filament/Resources/ClassSubjectResource/Pages/EditClassSubject.php create mode 100644 app/Filament/Resources/ClassSubjectResource/Pages/ListClassSubjects.php rename app/Filament/Resources/{SubjectScopeResource.php => CompetencyAchievementResource.php} (50%) create mode 100644 app/Filament/Resources/CompetencyAchievementResource/Pages/CreateCompetencyAchievement.php create mode 100644 app/Filament/Resources/CompetencyAchievementResource/Pages/EditCompetencyAchievement.php create mode 100644 app/Filament/Resources/CompetencyAchievementResource/Pages/ListCompetencyAchievements.php create mode 100644 app/Filament/Resources/HomeRoomTeacherResource.php create mode 100644 app/Filament/Resources/HomeRoomTeacherResource/Pages/CreateHomeRoomTeacher.php create mode 100644 app/Filament/Resources/HomeRoomTeacherResource/Pages/EditHomeRoomTeacher.php create mode 100644 app/Filament/Resources/HomeRoomTeacherResource/Pages/ListHomeRoomTeachers.php delete mode 100644 app/Filament/Resources/SubjectScopeResource/Pages/CreateSubjectScope.php delete mode 100644 app/Filament/Resources/SubjectScopeResource/Pages/EditSubjectScope.php delete mode 100644 app/Filament/Resources/SubjectScopeResource/Pages/ListSubjectScopes.php create mode 100644 app/Models/AcademicYear.php create mode 100644 app/Models/ClassStudent.php create mode 100644 app/Models/ClassSubject.php create mode 100644 app/Models/CompetencyAchievement.php create mode 100644 app/Models/HomeRoomTeacher.php delete mode 100644 app/Models/SubjectScope.php rename database/migrations/{2025_04_10_151152_create_subject_scopes_table.php => 2025_04_10_000427_create_academic_years_table.php} (51%) create mode 100644 database/migrations/2025_05_03_091105_create_competency_achievements_table.php create mode 100644 database/migrations/2025_05_04_120638_create_home_room_teachers_table.php create mode 100644 database/migrations/2025_05_04_121938_create_class_students_table.php create mode 100644 database/migrations/2025_05_06_041554_create_class_subjects_table.php create mode 100644 database/seeders/AcademicYearSeeder.php delete mode 100644 database/seeders/AttendanceSeeder.php create mode 100644 database/seeders/ClassStudentSeeder.php create mode 100644 database/seeders/ClassSubjectSeeder.php rename database/seeders/{SubjectScopeSeeder.php => HomeRoomTeacherSeeder.php} (59%) create mode 100644 resources/views/filament/pages/student-report.blade.php diff --git a/app/Filament/Pages/StudentReport.php b/app/Filament/Pages/StudentReport.php new file mode 100644 index 0000000..7fd64c6 --- /dev/null +++ b/app/Filament/Pages/StudentReport.php @@ -0,0 +1,223 @@ +user()->hasAnyRole(['super_admin']); + } + + public static function shouldRegisterNavigation(): bool + { + return auth()->user()->hasAnyRole(['super_admin']); + } + + public function mount(): void + { + $this->class = null; + $this->academicYear = null; + $this->semester = null; + $this->data = []; + } + + public function form(Form $form): Form + { + return $form->schema([ + Select::make('class_id') + ->label('Class') + ->required() + ->options(ClassRoom::pluck('class_name', 'id')->toArray()) + ->searchable() + ->reactive() + ->afterStateUpdated(function ($state) { + $this->class_id = $state; + $this->data['class_id'] = $state; // Update data array + $this->loadData(); + }), + + Select::make('academic_year') + ->label('Academic Year') + ->required() + ->options(AcademicYear::pluck('name', 'id')->toArray()) + ->searchable() + ->reactive() + ->afterStateUpdated(function ($state) { + $this->academic_year = $state; + $this->data['academic_year'] = $state; // Update data array + $this->loadData(); + }), + + Select::make('semester') + ->label('Semester') + ->required() + ->options([ + 'first' => 'First Semester', + 'second' => 'Second Semester' + ]) + ->reactive() + ->afterStateUpdated(function ($state) { + $this->semester = $state; + $this->data['semester'] = $state; + $this->loadData(); + }), + ])->columns(3); + } + + protected function loadData(): void + { + if (count($this->data) < 3) { + $this->list = []; + return; + } + + $groupedAssessment = []; + + $assessments = Assessment::where('semester', $this->semester) + ->whereHas('teacherSubject', function($query) { + $query->where('academic_year_id', $this->academic_year) + ->where('class_id', $this->class_id); + }) + ->with('teacherSubject', 'student') + ->get() + ->toArray(); + + $classSubjects = ClassSubject::with(['subject', 'class', 'academicYear']) + ->where('class_room_id', $this->class_id) + ->where('academic_year_id', $this->academic_year) + ->get() + ->sortByDesc(function ($item) { + return $item->subject->name; + }) + ->toArray(); + + $header = []; + + foreach ($classSubjects as $classSubject) { + $category = strtolower($classSubject['subject']['category']); + $subjectName = $classSubject['subject']['name']; + $subjectId = $classSubject['subject']['id']; + $header[$category][$subjectId] = $subjectName; + } + + $students = ClassStudent::with(['class', 'academicYear', 'student']) + ->where('class_room_id', $this->class_id) + ->where('academic_year_id', $this->academic_year) + ->get() + ->map(function($student) { + return $student['student']; + }) + ->toArray(); + + $finals = []; + foreach ($students as $student) { + $studentData = [ + 'student_id' => $student['id'], + 'name' => $student['full_name'], + ]; + + foreach ($header as $category => $subjects) { + foreach ($subjects as $subjectId => $subjectName) { + $matchingAssessment = collect($assessments)->first(function ($a) use ($student, $subjectId) { + return $a['student_id'] == $student['id'] && $a['teacher_subject']['subject_id'] == $subjectId; + }); + + $studentData[$category][$subjectId] = $matchingAssessment['score'] ?? '-'; // atau null + } + } + + $finals[] = $studentData; + } + + $result = []; + + foreach ($finals as $final => $fnl) { + $existStudent = Student::where('id', $fnl['student_id'])->firstOrFail(); + + $studentData = [ + 'name' => $fnl['name'], + ]; + + $mapel = $fnl['umum'] ?? null; + + if ($mapel) { + foreach ($mapel as $key => $value) { + $existSubject = Subject::where('id', $key)->firstOrFail(); + + if ($existSubject->is_religious) { + $studentReligion = strtolower($existStudent->religion); // contoh: "islam" + $subjectName = strtolower($existSubject->name); // contoh: "pendidikan agama islam" + + if (str_contains($subjectName, $studentReligion)) { + // Hanya mapel agama yang sesuai dengan agama siswa dimasukkan ke umum[0] + $studentData['umum'][0] = $value; + } + // Mapel agama lain tidak dimasukkan sama sekali + } else { + $studentData['umum'][$key] = $value; + } + } + } + + $studentData['muatan lokal'] = $fnl['muatan lokal'] ?? null; + + $result[] = $studentData; + } + + $groupedAssessment["data"] = $result; + + $groupedSubjectsHeader = []; + + $groupedSubjectsHeader['name'] = "Nama"; + + $religionAdded = false; + + foreach ($classSubjects as $classSubject) { + $category = strtolower($classSubject['subject']['category']); + $subjectName = $classSubject['subject']['name']; + $isReligion = $classSubject['subject']['is_religious']; + $subjectId = $classSubject['subject']['id']; + + if ($isReligion) { + if (!$religionAdded) { + $groupedSubjectsHeader['umum'][0] = 'Pendidikan Agama'; + $religionAdded = true; + } + continue; + } + + $groupedSubjectsHeader[$category][$subjectId] = $subjectName; + } + + $groupedAssessment["header"] = $groupedSubjectsHeader; + + $this->list = $groupedAssessment; + } +} diff --git a/app/Filament/Resources/AcademicYearResource.php b/app/Filament/Resources/AcademicYearResource.php new file mode 100644 index 0000000..3971a1d --- /dev/null +++ b/app/Filament/Resources/AcademicYearResource.php @@ -0,0 +1,105 @@ +schema([ + Forms\Components\TextInput::make('name') + ->required() + ->placeholder("ex: 2024/2025") + ->maxLength(255), + Forms\Components\Toggle::make('is_active') + ->required(), + Forms\Components\DatePicker::make('start_date'), + Forms\Components\DatePicker::make('end_date'), + ]); + } + + public static function table(Table $table): Table + { + return $table + ->columns([ + Tables\Columns\TextColumn::make('name') + ->searchable(), + Tables\Columns\IconColumn::make('is_active') + ->boolean(), + Tables\Columns\TextColumn::make('start_date') + ->date() + ->sortable(), + Tables\Columns\TextColumn::make('end_date') + ->date() + ->sortable(), + Tables\Columns\TextColumn::make('created_at') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + Tables\Columns\TextColumn::make('updated_at') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + Tables\Columns\TextColumn::make('deleted_at') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + ]) + ->filters([ + Tables\Filters\TrashedFilter::make(), + ]) + ->actions([ + Tables\Actions\EditAction::make(), + ]) + ->bulkActions([ + Tables\Actions\BulkActionGroup::make([ + Tables\Actions\DeleteBulkAction::make(), + Tables\Actions\ForceDeleteBulkAction::make(), + Tables\Actions\RestoreBulkAction::make(), + ]), + ]) + ->emptyStateActions([ + Tables\Actions\CreateAction::make(), + ]); + } + + public static function getRelations(): array + { + return [ + // + ]; + } + + public static function getPages(): array + { + return [ + 'index' => Pages\ListAcademicYears::route('/'), + 'create' => Pages\CreateAcademicYear::route('/create'), + 'edit' => Pages\EditAcademicYear::route('/{record}/edit'), + ]; + } + + public static function getEloquentQuery(): Builder + { + return parent::getEloquentQuery() + ->withoutGlobalScopes([ + SoftDeletingScope::class, + ]); + } +} diff --git a/app/Filament/Resources/AcademicYearResource/Pages/CreateAcademicYear.php b/app/Filament/Resources/AcademicYearResource/Pages/CreateAcademicYear.php new file mode 100644 index 0000000..476eb31 --- /dev/null +++ b/app/Filament/Resources/AcademicYearResource/Pages/CreateAcademicYear.php @@ -0,0 +1,33 @@ +exists(); + + if ($exists) { + Notification::make() + ->title('Failed to save') + ->body('A record already exists.') + ->danger() + ->send(); + + $this->halt(); // Stop the save process + } + + return $data; + } +} diff --git a/app/Filament/Resources/AcademicYearResource/Pages/EditAcademicYear.php b/app/Filament/Resources/AcademicYearResource/Pages/EditAcademicYear.php new file mode 100644 index 0000000..aa85232 --- /dev/null +++ b/app/Filament/Resources/AcademicYearResource/Pages/EditAcademicYear.php @@ -0,0 +1,42 @@ +exists(); + + if ($exists) { + Notification::make() + ->title('Failed to save') + ->body('A record already exists.') + ->danger() + ->send(); + + $this->halt(); // Stop the save process + } + + return $data; + } + + protected function getHeaderActions(): array + { + return [ + Actions\DeleteAction::make(), + Actions\ForceDeleteAction::make(), + Actions\RestoreAction::make(), + ]; + } +} diff --git a/app/Filament/Resources/AcademicYearResource/Pages/ListAcademicYears.php b/app/Filament/Resources/AcademicYearResource/Pages/ListAcademicYears.php new file mode 100644 index 0000000..5b44ba1 --- /dev/null +++ b/app/Filament/Resources/AcademicYearResource/Pages/ListAcademicYears.php @@ -0,0 +1,19 @@ +required() ->relationship('teacherSubject', 'id') ->getOptionLabelFromRecordUsing(fn (TeacherSubject $record) => - $record->teacher->name . ' - ' . $record->subject->name . ' - ' . $record->class->class_name . ' - ' . $record->academic_year) + $record->teacher->name . ' - ' . $record->subject->name . ' - ' . $record->class->class_name . ' - ' . $record->academicYear->name) ->searchable() ->preload() ->afterStateUpdated(function (callable $set, $state) { - // Filter siswa berdasarkan kelas yang dipilih dalam teacher_subject_id if ($state) { $teacherSubject = TeacherSubject::find($state); if ($teacherSubject) { - $set('student_id', null); // Reset student_id jika teacher_subject_id berubah + $set('student_id', null); } } }) @@ -51,14 +51,15 @@ class AssessmentResource extends Resource ->label('Student') ->required() ->searchable() - // Filter opsi siswa berdasarkan kelas yang terkait dengan teacher_subject_id ->options(function ($get) { $teacherSubjectId = $get('teacher_subject_id'); if ($teacherSubjectId) { $teacherSubject = TeacherSubject::find($teacherSubjectId); - // Ambil siswa yang memiliki kelas yang sesuai dengan teacher_subject_id - $students = Student::where('class_id', $teacherSubject->class_id) - ->pluck('full_name', 'id') + $students = ClassStudent::where('class_room_id', $teacherSubject->class_id) + ->where('academic_year_id', $teacherSubject->academic_year_id) + ->with('student') + ->get() + ->pluck('student.full_name', 'id') ->toArray(); return $students; @@ -68,8 +69,15 @@ class AssessmentResource extends Resource ->getOptionLabelUsing(function ($value) { $student = Student::find($value); return $student ? $student->full_name . ' (' . $student->nis . ')' : null; - }) - ->required(), + }), + + Forms\Components\Select::make('semester') + ->label('Semester') + ->required() + ->options([ + 'first' => 'First Semester', + 'second' => 'Second Semester' + ]), Forms\Components\TextInput::make('score') ->label('Score') @@ -122,6 +130,9 @@ class AssessmentResource extends Resource Tables\Actions\BulkActionGroup::make([ Tables\Actions\DeleteBulkAction::make(), ]), + ]) + ->emptyStateActions([ + Tables\Actions\CreateAction::make(), ]); } diff --git a/app/Filament/Resources/AssessmentResource/Pages/ListAssessments.php b/app/Filament/Resources/AssessmentResource/Pages/ListAssessments.php index 150f9d9..55d1e12 100644 --- a/app/Filament/Resources/AssessmentResource/Pages/ListAssessments.php +++ b/app/Filament/Resources/AssessmentResource/Pages/ListAssessments.php @@ -15,7 +15,7 @@ class ListAssessments extends ListRecords return [ Actions\CreateAction::make(), Actions\Action::make('multiple') - ->label('Multiple Attendance') + ->label('Multiple Assessments') ->url('assessments/multiple') ->icon('heroicon-o-user-group'), ]; diff --git a/app/Filament/Resources/AssessmentResource/Pages/MultipleAssessments.php b/app/Filament/Resources/AssessmentResource/Pages/MultipleAssessments.php index ca44098..3e60223 100644 --- a/app/Filament/Resources/AssessmentResource/Pages/MultipleAssessments.php +++ b/app/Filament/Resources/AssessmentResource/Pages/MultipleAssessments.php @@ -5,13 +5,16 @@ namespace App\Filament\Resources\AssessmentResource\Pages; use App\Filament\Resources\AssessmentResource; use App\Models\Assessment; use App\Models\Attendances; +use App\Models\ClassStudent; use Filament\Actions; +use Filament\Forms\Components\Select; use Filament\Resources\Pages\page; use App\Models\Student; use App\Models\TeacherSubject; use Filament\Forms; use Filament\Notifications\Notification; use Illuminate\Support\Collection; +use function Symfony\Component\Translation\t; class MultipleAssessments extends page { @@ -21,10 +24,15 @@ class MultipleAssessments extends page use Forms\Concerns\InteractsWithForms; + public ?array $data = []; public ?int $teacherSubjectId = null; + public $teacherSubject; + public array $students = []; + public $semester = 'first'; + public function mount(): void { $this->form->fill(); @@ -33,18 +41,38 @@ class MultipleAssessments extends page protected function getFormSchema(): array { return [ - Forms\Components\Select::make('teacherSubjectId') - ->label('Teacher Subject') - ->options( - TeacherSubject::with(['teacher', 'subject', 'class'])->get()->mapWithKeys(function ($item) { - return [ - $item->id => "{$item->teacher->name} - {$item->subject->name} - {$item->class->class_name}" - ]; - })->toArray() - ) - ->searchable() - ->reactive() - ->afterStateUpdated(fn ($state) => $this->loadStudents()), + Forms\Components\Grid::make(2) + ->schema([ + Forms\Components\Select::make('teacherSubjectId') + ->label('Teacher Subject') + ->options( + TeacherSubject::with(['teacher', 'subject', 'class', 'academicYear'])->get()->mapWithKeys(function ($item) { + return [ + $item->id => "{$item->teacher->name} - {$item->subject->name} - {$item->class->class_name} - {$item->academicYear->name}" + ]; + })->toArray() + ) + ->searchable() + ->reactive() + ->afterStateUpdated(function ($state) { + $this->teacherSubjectId = $state; + $this->loadStudents(); + }), + + Forms\Components\Select::make('semester') + ->label('Semester') + ->required() + ->default('first') + ->options([ + 'first' => 'First Semester', + 'second' => 'Second Semester' + ]) + ->live() + ->afterStateUpdated(function ($state) { + $this->semester = $state; + $this->loadStudents(); + }), + ]), ]; } @@ -55,34 +83,47 @@ class MultipleAssessments extends page return; } - $teacherSubject = TeacherSubject::find($this->teacherSubjectId); + $this->teacherSubject = TeacherSubject::with(['subject','class','academicYear','teacher']) + ->where('id', $this->teacherSubjectId) + ->firstOrFail(); - if (!$teacherSubject) { - $this->students = []; + $classStudents = ClassStudent::where('class_room_id', $this->teacherSubject->class_id) + ->where('academic_year_id', $this->teacherSubject->academic_year_id) + ->with('student') + ->get(); - Notification::make() - ->title('Not Found') - ->body('Selected teacher subject not found.') - ->danger() - ->send(); + $isSubjectReligion = $this->teacherSubject->subject->is_religious; + $subjectName = strtolower($this->teacherSubject->subject->name); - return; + $result = []; + + foreach ($classStudents as $classStudent) { + $student = $classStudent->student; + + if (!$student) continue; // Jaga-jaga kalau relasi student-nya null + + $existingAssessment = Assessment::where('student_id', $student->id) + ->where('teacher_subject_id', $this->teacherSubjectId) + ->where('semester', $this->semester) + ->first(); + + if ($isSubjectReligion) { + $studentReligion = strtolower($student->religion); + + if (!str_contains($subjectName, $studentReligion)) { + continue; // Skip kalau agama tidak cocok + } + } + + $result[] = [ + 'id' => $student->id, + 'name' => $student->full_name, + 'nis' => $student->nis, + 'score' => $existingAssessment ? $existingAssessment->score : null, + ]; } - $this->students = Student::where('class_id', $teacherSubject->class_id) - ->get() - ->map(function ($student) { - $existingAssessment = Assessment::where('student_id', $student->id) - ->where('teacher_subject_id', $this->teacherSubjectId) - ->first(); - - return [ - 'id' => $student->id, - 'name' => $student->full_name, - 'nis' => $student->nis, - 'score' => $existingAssessment ? $existingAssessment->score : null, - ]; - })->toArray(); + $this->students = $result; } public function submit(): void @@ -106,6 +147,7 @@ class MultipleAssessments extends page ], [ 'score' => $student['score'], + 'semester' => $this->semester, ] ); } diff --git a/app/Filament/Resources/AttendancesResource.php b/app/Filament/Resources/AttendancesResource.php index 266486e..af8bf6b 100644 --- a/app/Filament/Resources/AttendancesResource.php +++ b/app/Filament/Resources/AttendancesResource.php @@ -5,6 +5,7 @@ namespace App\Filament\Resources; use App\Filament\Resources\AttendancesResource\Pages; use App\Models\Attendances; use App\Models\ClassRoom; +use App\Models\ClassStudent; use App\Models\Student; use App\Models\Subject; use App\Models\TeacherSubject; @@ -26,22 +27,21 @@ class AttendancesResource extends Resource { return $form ->schema([ - Forms\Components\Section::make('Informasi Absensi') + Forms\Components\Section::make('Data Absensi') ->schema([ Forms\Components\Select::make('teacher_subject_id') ->label('Teacher Subject') ->required() ->relationship('teacherSubject', 'id') ->getOptionLabelFromRecordUsing(fn (TeacherSubject $record) => - $record->teacher->name . ' - ' . $record->subject->name . ' - ' . $record->class->class_name . ' - ' . $record->academic_year) + $record->teacher->name . ' - ' . $record->subject->name . ' - ' . $record->class->class_name . ' - ' . $record->academicYear->name) ->searchable() ->preload() ->afterStateUpdated(function (callable $set, $state) { - // Filter siswa berdasarkan kelas yang dipilih dalam teacher_subject_id if ($state) { $teacherSubject = TeacherSubject::find($state); if ($teacherSubject) { - $set('student_id', null); // Reset student_id jika teacher_subject_id berubah + $set('student_id', null); } } }) @@ -51,20 +51,21 @@ class AttendancesResource extends Resource ->label('Date') ->required() ->default(now()) - ->maxDate(now()), + ->live(), Forms\Components\Select::make('student_id') ->label('Student') ->required() ->searchable() - // Filter opsi siswa berdasarkan kelas yang terkait dengan teacher_subject_id ->options(function ($get) { $teacherSubjectId = $get('teacher_subject_id'); if ($teacherSubjectId) { $teacherSubject = TeacherSubject::find($teacherSubjectId); - // Ambil siswa yang memiliki kelas yang sesuai dengan teacher_subject_id - $students = Student::where('class_id', $teacherSubject->class_id) - ->pluck('full_name', 'id') + $students = ClassStudent::where('class_room_id', $teacherSubject->class_id) + ->where('academic_year_id', $teacherSubject->academic_year_id) + ->with('student') + ->get() + ->pluck('student.full_name', 'id') ->toArray(); return $students; @@ -153,7 +154,7 @@ class AttendancesResource extends Resource 'second' => 'Second Semester' }), - Tables\Columns\TextColumn::make('teacherSubject.academic_year') + Tables\Columns\TextColumn::make('teacherSubject.academicYear.name') ->label('Academic Year') ->searchable(), diff --git a/app/Filament/Resources/AttendancesResource/Pages/EditAttendances.php b/app/Filament/Resources/AttendancesResource/Pages/EditAttendances.php index 5a812a8..cd4df18 100644 --- a/app/Filament/Resources/AttendancesResource/Pages/EditAttendances.php +++ b/app/Filament/Resources/AttendancesResource/Pages/EditAttendances.php @@ -3,13 +3,36 @@ namespace App\Filament\Resources\AttendancesResource\Pages; use App\Filament\Resources\AttendancesResource; +use App\Models\Assessment; +use App\Models\Attendances; use Filament\Actions; +use Filament\Notifications\Notification; use Filament\Resources\Pages\EditRecord; class EditAttendances extends EditRecord { protected static string $resource = AttendancesResource::class; + protected function mutateFormDataBeforeSave(array $data): array + { + $exists = Attendances::where('teacher_subject_id', $data['teacher_subject_id']) + ->where('student_id', $data['student_id']) + ->where('id', '!=', $this->record->id) // ignore current record + ->exists(); + + if ($exists) { + Notification::make() + ->title('Failed to save') + ->body('An assessment for this teacher, subject, and student combination already exists.') + ->danger() + ->send(); + + $this->halt(); + } + + return $data; + } + protected function getHeaderActions(): array { return [ diff --git a/app/Filament/Resources/AttendancesResource/Pages/MultipleAttendances.php b/app/Filament/Resources/AttendancesResource/Pages/MultipleAttendances.php index ad36c6a..90058d0 100644 --- a/app/Filament/Resources/AttendancesResource/Pages/MultipleAttendances.php +++ b/app/Filament/Resources/AttendancesResource/Pages/MultipleAttendances.php @@ -3,8 +3,10 @@ namespace App\Filament\Resources\AttendancesResource\Pages; use App\Filament\Resources\AttendancesResource; +use App\Models\Assessment; use App\Models\Attendances; use App\Models\ClassRoom; +use App\Models\ClassStudent; use App\Models\Student; use App\Models\TeacherSubject; use Filament\Actions; @@ -26,7 +28,8 @@ class MultipleAttendances extends Page public $teacherSubject; public $attendanceDate; - public $semester = 'first'; // Default semester + public $semester = 'first'; + public $students = []; public function mount(): void @@ -48,7 +51,7 @@ class MultipleAttendances extends Page ->options(TeacherSubject::with(['teacher', 'subject', 'class']) ->get() ->mapWithKeys(fn ($item) => [ - $item->id => $item->teacher->name . ' - ' . $item->subject->name . ' - ' . $item->class->class_name + $item->id => $item->teacher->name . ' - ' . $item->subject->name . ' - ' . $item->class->class_name . ' - ' . $item->academicYear->name ])) ->searchable() ->live() @@ -61,7 +64,6 @@ class MultipleAttendances extends Page ->label('Attendance Date') ->required() ->default(now()) - ->maxDate(now()) ->live() ->afterStateUpdated(function ($state) { $this->attendanceDate = $state; @@ -81,7 +83,8 @@ class MultipleAttendances extends Page $this->loadStudents(); }), ]) - ->statePath('data'); + ->statePath('data') + ->columns(3); } protected function loadStudents(): void @@ -93,25 +96,67 @@ class MultipleAttendances extends Page $this->teacherSubject = TeacherSubject::where('id', $this->teacherSubjectId)->firstOrFail(); - $this->students = Student::where('class_id', $this->teacherSubject->class_id) - ->orderBy('full_name') - ->get() - ->map(function ($student) { - $existingAttendance = Attendances::where('student_id', $student->id) - ->where('teacher_subject_id', $this->teacherSubjectId) - ->whereDate('date', $this->attendanceDate) - ->where('semester', $this->semester) - ->first(); + $classStudents = ClassStudent::where('class_room_id', $this->teacherSubject->class_id) + ->where('academic_year_id', $this->teacherSubject->academic_year_id) + ->with('student') + ->get(); - return [ - 'id' => $student->id, - 'name' => $student->full_name, - 'nis' => $student->nis, - 'status' => $existingAttendance ? $existingAttendance->status : null, - 'attendance_id' => $existingAttendance ? $existingAttendance->id : null, - ]; - }) - ->toArray(); + $isSubjectReligion = $this->teacherSubject->subject->is_religious; + $subjectName = strtolower($this->teacherSubject->subject->name); + + $result = []; + + foreach ($classStudents as $classStudent) { + $student = $classStudent->student; + + if (!$student) continue; + + if ($isSubjectReligion) { + $studentReligion = strtolower($student->religion ?? ''); + + if (!str_contains($subjectName, $studentReligion)) { + continue; + } + } + + $existingAttendance = Attendances::where('student_id', $student->id) + ->where('teacher_subject_id', $this->teacherSubjectId) + ->whereDate('date', $this->attendanceDate) + ->where('semester', $this->semester) + ->first(); + + $result[] = [ + 'id' => $student->id, + 'name' => $student->full_name, + 'nis' => $student->nis, + 'status' => $existingAttendance ? $existingAttendance->status : null, + 'attendance_id' => $existingAttendance ? $existingAttendance->id : null, + ]; + } + + $this->students = $result; + +// $this->students = ClassStudent::where('class_room_id', $this->teacherSubjectId) +// ->where('academic_year_id', $this->teacherSubject->academic_year_id) +// ->with('student') +// ->get() +// ->map(function ($student) { +// $existingAttendance = Attendances::where('student_id', $student->student_id) +// ->where('teacher_subject_id', $this->teacherSubjectId) +// ->whereDate('date', $this->attendanceDate) +// ->where('semester', $this->semester) +// ->first(); +// +// return [ +// 'id' => $student->student->id, +// 'name' => $student->student->full_name, +// 'nis' => $student->student->nis, +// 'status' => $existingAttendance ? $existingAttendance->status : null, +// 'attendance_id' => $existingAttendance ? $existingAttendance->id : null, +// ]; +// }) +// ->values() +// ->toArray(); } public function markAll($status): void diff --git a/app/Filament/Resources/ClassRoomResource.php b/app/Filament/Resources/ClassRoomResource.php index b1f13e2..194b4de 100644 --- a/app/Filament/Resources/ClassRoomResource.php +++ b/app/Filament/Resources/ClassRoomResource.php @@ -43,24 +43,6 @@ class ClassRoomResource extends Resource '5' => 'Class 5', '6' => 'Class 6', ]), - - Forms\Components\Select::make('homeroom_teacher_id') - ->label('Homeroom Teacher') - ->relationship( - name: 'homeroomTeacher', - titleAttribute: 'name', - modifyQueryUsing: fn ($query) => $query->whereHas('roles', function ($q) { - $q->where('name', 'teacher'); - }), - ) - ->searchable() - ->preload() - ->required(), - Forms\Components\TextInput::make('academic_year') - ->label('Academic Year') - ->required() - ->placeholder('Example: 2023/2024') - ->maxLength(9), ])->columns(2) ]); } @@ -73,10 +55,6 @@ class ClassRoomResource extends Resource ->searchable(), Tables\Columns\TextColumn::make('class_level') ->searchable(), - Tables\Columns\TextColumn::make('homeroomTeacher.name') - ->label('Homeroom Teacher'), - Tables\Columns\TextColumn::make('academic_year') - ->searchable(), Tables\Columns\TextColumn::make('created_at') ->dateTime() ->sortable() @@ -87,16 +65,28 @@ class ClassRoomResource extends Resource ->toggleable(isToggledHiddenByDefault: true), ]) ->filters([ - // + Tables\Filters\TrashedFilter::make(), ]) ->actions([ Tables\Actions\EditAction::make(), Tables\Actions\DeleteAction::make(), + Tables\Actions\RestoreAction::make(), ]) ->bulkActions([ Tables\Actions\BulkActionGroup::make([ Tables\Actions\DeleteBulkAction::make(), ]), + ]) + ->emptyStateActions([ + Tables\Actions\CreateAction::make(), + ]); + } + + public static function getEloquentQuery(): Builder + { + return parent::getEloquentQuery() + ->withoutGlobalScopes([ + SoftDeletingScope::class, ]); } diff --git a/app/Filament/Resources/ClassStudentResource.php b/app/Filament/Resources/ClassStudentResource.php new file mode 100644 index 0000000..127fde1 --- /dev/null +++ b/app/Filament/Resources/ClassStudentResource.php @@ -0,0 +1,112 @@ +schema([ + Forms\Components\Select::make('class_room_id') + ->label('Class Room') + ->required() + ->options(ClassRoom::pluck('class_name', 'id')->toArray()) + ->searchable() + ->native(false), + + Forms\Components\Select::make('student_id') + ->label('Student') + ->required() + ->options(Student::pluck('full_name', 'id')->toArray()) + ->searchable() + ->native(false), + + Forms\Components\Select::make('academic_year_id') + ->label('Academic Year') + ->required() + ->options(AcademicYear::pluck('name', 'id')->toArray()) + ->searchable() + ->native(false), + ]); + } + + public static function table(Table $table): Table + { + return $table + ->columns([ + Tables\Columns\TextColumn::make('class.class_name') + ->label('Class Room') + ->numeric() + ->sortable(), + Tables\Columns\TextColumn::make('student.full_name') + ->label('Student') + ->numeric() + ->sortable(), + Tables\Columns\TextColumn::make('academicYear.name') + ->label('Academic Year') + ->numeric() + ->sortable(), + Tables\Columns\TextColumn::make('created_at') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + Tables\Columns\TextColumn::make('updated_at') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + Tables\Columns\TextColumn::make('deleted_at') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + ]) + ->filters([ + // + ]) + ->actions([ + Tables\Actions\EditAction::make(), + ]) + ->bulkActions([ + Tables\Actions\BulkActionGroup::make([ + Tables\Actions\DeleteBulkAction::make(), + ]), + ]) + ->emptyStateActions([ + Tables\Actions\CreateAction::make(), + ]); + } + + public static function getRelations(): array + { + return [ + // + ]; + } + + public static function getPages(): array + { + return [ + 'index' => Pages\ListClassStudents::route('/'), + 'create' => Pages\CreateClassStudent::route('/create'), + 'edit' => Pages\EditClassStudent::route('/{record}/edit'), + ]; + } +} diff --git a/app/Filament/Resources/ClassStudentResource/Pages/CreateClassStudent.php b/app/Filament/Resources/ClassStudentResource/Pages/CreateClassStudent.php new file mode 100644 index 0000000..e60ba80 --- /dev/null +++ b/app/Filament/Resources/ClassStudentResource/Pages/CreateClassStudent.php @@ -0,0 +1,35 @@ +where('student_id', $data['student_id']) + ->where('academic_year_id', $data['academic_year_id']) + ->exists(); + + if ($exists) { + Notification::make() + ->title('Failed to save') + ->body('A record for this class room, student, and academic year combination already exists.') + ->danger() + ->send(); + + $this->halt(); // Stop the save process + } + + return $data; + } +} diff --git a/app/Filament/Resources/ClassStudentResource/Pages/EditClassStudent.php b/app/Filament/Resources/ClassStudentResource/Pages/EditClassStudent.php new file mode 100644 index 0000000..02fd510 --- /dev/null +++ b/app/Filament/Resources/ClassStudentResource/Pages/EditClassStudent.php @@ -0,0 +1,41 @@ +where('student_id', $data['student_id']) + ->where('academic_year_id', $data['academic_year_id']) + ->exists(); + + if ($exists) { + Notification::make() + ->title('Failed to save') + ->body('A record for this class room, student, and academic year combination already exists.') + ->danger() + ->send(); + + $this->halt(); // Stop the save process + } + + return $data; + } + + protected function getHeaderActions(): array + { + return [ + Actions\DeleteAction::make(), + ]; + } +} diff --git a/app/Filament/Resources/ClassStudentResource/Pages/ListClassStudents.php b/app/Filament/Resources/ClassStudentResource/Pages/ListClassStudents.php new file mode 100644 index 0000000..7c0daba --- /dev/null +++ b/app/Filament/Resources/ClassStudentResource/Pages/ListClassStudents.php @@ -0,0 +1,19 @@ +schema([ + Select::make('class_room_id') + ->label('Kelas') + ->relationship('class', 'class_name') + ->required(), + + Select::make('subject_id') + ->label('Mata Pelajaran') + ->relationship('subject', 'name') + ->required(), + + Select::make('academic_year_id') + ->label('Tahun Ajaran') + ->relationship('academicYear', 'name') // ganti "name" jika kolomnya berbeda + ->required(), + ]); + } + + public static function table(Table $table): Table + { + return $table + ->columns([ + TextColumn::make('class.class_name')->label('Kelas')->sortable()->searchable(), + TextColumn::make('subject.name')->label('Mata Pelajaran')->sortable()->searchable(), + TextColumn::make('academicYear.name')->label('Tahun Ajaran')->sortable(), + ]) + ->filters([ + // + ]) + ->actions([ + Tables\Actions\EditAction::make(), + ]) + ->bulkActions([ + Tables\Actions\BulkActionGroup::make([ + Tables\Actions\DeleteBulkAction::make(), + ]), + ]); + } + + public static function getRelations(): array + { + return [ + // + ]; + } + + public static function getPages(): array + { + return [ + 'index' => Pages\ListClassSubjects::route('/'), + 'create' => Pages\CreateClassSubject::route('/create'), + 'edit' => Pages\EditClassSubject::route('/{record}/edit'), + ]; + } +} diff --git a/app/Filament/Resources/ClassSubjectResource/Pages/CreateClassSubject.php b/app/Filament/Resources/ClassSubjectResource/Pages/CreateClassSubject.php new file mode 100644 index 0000000..289780d --- /dev/null +++ b/app/Filament/Resources/ClassSubjectResource/Pages/CreateClassSubject.php @@ -0,0 +1,34 @@ +where('subject_id', $data['subject_id']) + ->where('academic_year_id', $data['academic_year_id']) + ->exists(); + + if ($exists) { + Notification::make() + ->title('Failed to save') + ->body('A record for this class, subject, and academic year combination already exists.') + ->danger() + ->send(); + + $this->halt(); // Stop the save process + } + + return $data; + } +} diff --git a/app/Filament/Resources/ClassSubjectResource/Pages/EditClassSubject.php b/app/Filament/Resources/ClassSubjectResource/Pages/EditClassSubject.php new file mode 100644 index 0000000..27b66e9 --- /dev/null +++ b/app/Filament/Resources/ClassSubjectResource/Pages/EditClassSubject.php @@ -0,0 +1,19 @@ +schema([ Forms\Components\Select::make('subject_id') ->label('Subject') - ->options( - fn () => \App\Models\Subject::pluck('name', 'id')->toArray() - ) - ->searchable() - ->required(), - Forms\Components\TextInput::make('religion') - ->maxLength(255), - Forms\Components\TextInput::make('learning_goal') ->required() - ->maxLength(255), - Forms\Components\Textarea::make('scope') + ->options(Subject::pluck('name', 'id')->toArray()) + ->searchable() + ->native(false), + + Forms\Components\Select::make('class_room_id') + ->label('Class') + ->required() + ->options(ClassRoom::pluck('class_name', 'id')->toArray()) + ->searchable() + ->native(false), + + Forms\Components\TextInput::make('min_score') + ->required() + ->numeric() + ->default(0), + Forms\Components\TextInput::make('max_score') + ->required() + ->numeric() + ->default(0), + Forms\Components\Textarea::make('description') ->required() ->columnSpanFull(), ]); @@ -50,10 +60,15 @@ class SubjectScopeResource extends Resource Tables\Columns\TextColumn::make('subject.name') ->numeric() ->sortable(), - Tables\Columns\TextColumn::make('religion') - ->searchable(), - Tables\Columns\TextColumn::make('learning_goal') - ->searchable(), + Tables\Columns\TextColumn::make('classRoom.class_name') + ->numeric() + ->sortable(), + Tables\Columns\TextColumn::make('min_score') + ->numeric() + ->sortable(), + Tables\Columns\TextColumn::make('max_score') + ->numeric() + ->sortable(), Tables\Columns\TextColumn::make('created_at') ->dateTime() ->sortable() @@ -68,11 +83,15 @@ class SubjectScopeResource extends Resource ]) ->actions([ Tables\Actions\EditAction::make(), + Tables\Actions\ViewAction::make(), ]) ->bulkActions([ Tables\Actions\BulkActionGroup::make([ Tables\Actions\DeleteBulkAction::make(), ]), + ]) + ->emptyStateActions([ + Tables\Actions\CreateAction::make(), ]); } @@ -86,9 +105,9 @@ class SubjectScopeResource extends Resource public static function getPages(): array { return [ - 'index' => Pages\ListSubjectScopes::route('/'), - 'create' => Pages\CreateSubjectScope::route('/create'), - 'edit' => Pages\EditSubjectScope::route('/{record}/edit'), + 'index' => Pages\ListCompetencyAchievements::route('/'), + 'create' => Pages\CreateCompetencyAchievement::route('/create'), + 'edit' => Pages\EditCompetencyAchievement::route('/{record}/edit'), ]; } } diff --git a/app/Filament/Resources/CompetencyAchievementResource/Pages/CreateCompetencyAchievement.php b/app/Filament/Resources/CompetencyAchievementResource/Pages/CreateCompetencyAchievement.php new file mode 100644 index 0000000..86c6919 --- /dev/null +++ b/app/Filament/Resources/CompetencyAchievementResource/Pages/CreateCompetencyAchievement.php @@ -0,0 +1,12 @@ +actions([ - Tables\Actions\EditAction::make()->label('Edit'), - Tables\Actions\DeleteAction::make()->label('Hapus'), + Tables\Actions\EditAction::make(), + Tables\Actions\DeleteAction::make(), ]) ->bulkActions([ - Tables\Actions\DeleteBulkAction::make()->label('Hapus Massal'), + Tables\Actions\DeleteBulkAction::make(), + ]) + ->emptyStateActions([ + Tables\Actions\CreateAction::make(), ]); } public static function getRelations(): array { return [ - // Tambahkan RelationManager jika ada + // ]; } diff --git a/app/Filament/Resources/HomeRoomTeacherResource.php b/app/Filament/Resources/HomeRoomTeacherResource.php new file mode 100644 index 0000000..a94a57e --- /dev/null +++ b/app/Filament/Resources/HomeRoomTeacherResource.php @@ -0,0 +1,109 @@ +schema([ + Forms\Components\Select::make('class_room_id') + ->label('Class Room') + ->required() + ->options(ClassRoom::pluck('class_name', 'id')->toArray()) + ->searchable() + ->native(false), + + Forms\Components\Select::make('teacher_id') + ->label('Teacher') + ->required() + ->options(User::role('teacher')->pluck('name', 'id')->toArray()) + ->searchable() + ->native(false), + + Forms\Components\Select::make('academic_year_id') + ->label('Academic Year') + ->required() + ->options(AcademicYear::pluck('name', 'id')->toArray()) + ->searchable() + ->native(false), + ]); + } + + public static function table(Table $table): Table + { + return $table + ->columns([ + Tables\Columns\TextColumn::make('classRoom.class_name') + ->numeric() + ->sortable(), + Tables\Columns\TextColumn::make('teacher.name') + ->numeric() + ->sortable(), + Tables\Columns\TextColumn::make('academicYear.name') + ->numeric() + ->sortable(), + Tables\Columns\TextColumn::make('created_at') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + Tables\Columns\TextColumn::make('updated_at') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + Tables\Columns\TextColumn::make('deleted_at') + ->dateTime() + ->sortable() + ->toggleable(isToggledHiddenByDefault: true), + ]) + ->filters([ + // + ]) + ->actions([ + Tables\Actions\EditAction::make(), + ]) + ->bulkActions([ + Tables\Actions\BulkActionGroup::make([ + Tables\Actions\DeleteBulkAction::make(), + ]), + ]) + ->emptyStateActions([ + Tables\Actions\CreateAction::make(), + ]); + } + + public static function getRelations(): array + { + return [ + // + ]; + } + + public static function getPages(): array + { + return [ + 'index' => Pages\ListHomeRoomTeachers::route('/'), + 'create' => Pages\CreateHomeRoomTeacher::route('/create'), + 'edit' => Pages\EditHomeRoomTeacher::route('/{record}/edit'), + ]; + } +} diff --git a/app/Filament/Resources/HomeRoomTeacherResource/Pages/CreateHomeRoomTeacher.php b/app/Filament/Resources/HomeRoomTeacherResource/Pages/CreateHomeRoomTeacher.php new file mode 100644 index 0000000..bc8e363 --- /dev/null +++ b/app/Filament/Resources/HomeRoomTeacherResource/Pages/CreateHomeRoomTeacher.php @@ -0,0 +1,35 @@ +where('teacher_id', $data['teacher_id']) + ->where('academic_year_id', $data['academic_year_id']) + ->exists(); + + if ($exists) { + Notification::make() + ->title('Failed to save') + ->body('A record for this class room, teacher, and academic year combination already exists.') + ->danger() + ->send(); + + $this->halt(); // Stop the save process + } + + return $data; + } +} diff --git a/app/Filament/Resources/HomeRoomTeacherResource/Pages/EditHomeRoomTeacher.php b/app/Filament/Resources/HomeRoomTeacherResource/Pages/EditHomeRoomTeacher.php new file mode 100644 index 0000000..e0b9da7 --- /dev/null +++ b/app/Filament/Resources/HomeRoomTeacherResource/Pages/EditHomeRoomTeacher.php @@ -0,0 +1,42 @@ +where('teacher_id', $data['teacher_id']) + ->where('academic_year_id', $data['academic_year_id']) + ->exists(); + + if ($exists) { + Notification::make() + ->title('Failed to save') + ->body('A record for this class room, teacher, and academic year combination already exists.') + ->danger() + ->send(); + + $this->halt(); // Stop the save process + } + + return $data; + } + + protected function getHeaderActions(): array + { + return [ + Actions\DeleteAction::make(), + ]; + } +} diff --git a/app/Filament/Resources/HomeRoomTeacherResource/Pages/ListHomeRoomTeachers.php b/app/Filament/Resources/HomeRoomTeacherResource/Pages/ListHomeRoomTeachers.php new file mode 100644 index 0000000..7b5be4d --- /dev/null +++ b/app/Filament/Resources/HomeRoomTeacherResource/Pages/ListHomeRoomTeachers.php @@ -0,0 +1,19 @@ +schema([ - Forms\Components\Section::make('Informasi Siswa') + Forms\Components\Section::make('Data Siswa') ->schema([ + Forms\Components\TextInput::make('nisn') + ->label('NISN') + ->maxLength(20), + Forms\Components\TextInput::make('nis') ->label('NIS') ->required() @@ -62,14 +66,6 @@ class StudentResource extends Resource ->email() ->maxLength(100), - Forms\Components\Select::make('class_id') - ->label('Class') - ->options( - fn () => \App\Models\ClassRoom::pluck('class_name', 'id')->toArray() - ) - ->searchable() - ->required(), - Forms\Components\TextInput::make('parent_name') ->label("Parent's Name") ->required() @@ -79,9 +75,19 @@ class StudentResource extends Resource ->label("Parent's Phone") ->maxLength(15), + Forms\Components\Select::make('religion') + ->label('Religion') + ->options([ + 'islam' => 'Islam', + 'hindu' => 'Hindu', + 'katolik' => 'Katolik', + 'kristen' => 'Kristen', + 'buddha' => 'Buddha', + ]), + Forms\Components\Textarea::make('address') ->label('Address') - ->rows(3), + ->rows(1), ]) ->columns(2), @@ -107,12 +113,12 @@ class StudentResource extends Resource Tables\Columns\TextColumn::make('birth_date') ->label('Birth Date') ->date(), - Tables\Columns\TextColumn::make('classRoom.class_name') - ->label('Class') - ->sortable() - ->searchable(), Tables\Columns\TextColumn::make('parent_name') ->label("Parent's Name"), + Tables\Columns\TextColumn::make('religion') + ->label("Religion") + ->formatStateUsing(fn (string $state): string => strtoupper($state)) + ->badge(), Tables\Columns\TextColumn::make('created_at') ->dateTime() ->sortable() @@ -129,6 +135,9 @@ class StudentResource extends Resource Tables\Actions\BulkActionGroup::make([ Tables\Actions\DeleteBulkAction::make(), ]), + ]) + ->emptyStateActions([ + Tables\Actions\CreateAction::make(), ]); } diff --git a/app/Filament/Resources/SubjectResource.php b/app/Filament/Resources/SubjectResource.php index e7c475f..0eed1a2 100644 --- a/app/Filament/Resources/SubjectResource.php +++ b/app/Filament/Resources/SubjectResource.php @@ -28,8 +28,18 @@ class SubjectResource extends Resource Forms\Components\TextInput::make('name') ->required() ->maxLength(255), + Forms\Components\Toggle::make('is_religious') ->required(), + + Forms\Components\Select::make('category') + ->options([ + 'umum' => 'Umum', + 'muatan lokal' => 'Muatan Lokal', + 'seni' => 'Seni', + ]) + ->required(), + ]); } @@ -38,9 +48,14 @@ class SubjectResource extends Resource return $table ->columns([ Tables\Columns\TextColumn::make('name') + ->label("Name") ->searchable(), Tables\Columns\IconColumn::make('is_religious') + ->label("Is Religious") ->boolean(), + Tables\Columns\TextColumn::make('category') + ->label("Category") + ->sortable(), Tables\Columns\TextColumn::make('created_at') ->dateTime() ->sortable() @@ -60,6 +75,9 @@ class SubjectResource extends Resource Tables\Actions\BulkActionGroup::make([ Tables\Actions\DeleteBulkAction::make(), ]), + ]) + ->emptyStateActions([ + Tables\Actions\CreateAction::make(), ]); } diff --git a/app/Filament/Resources/SubjectScopeResource/Pages/CreateSubjectScope.php b/app/Filament/Resources/SubjectScopeResource/Pages/CreateSubjectScope.php deleted file mode 100644 index ad960bb..0000000 --- a/app/Filament/Resources/SubjectScopeResource/Pages/CreateSubjectScope.php +++ /dev/null @@ -1,12 +0,0 @@ -searchable() ->native(false), - Forms\Components\TextInput::make('academic_year') + Forms\Components\Select::make('academic_year_id') ->label('Academic Year') ->required() - ->placeholder('e.g. 2023/2024') - ->maxLength(9), + ->options(AcademicYear::pluck('name', 'id')->toArray()) + ->searchable() + ->native(false), ]) ->columns(2), @@ -80,7 +82,7 @@ class TeacherSubjectResource extends Resource ->searchable() ->sortable(), - Tables\Columns\TextColumn::make('academic_year') + Tables\Columns\TextColumn::make('academicYear.name') ->label('Academic Year') ->searchable() ->sortable(), diff --git a/app/Filament/Resources/TeacherSubjectResource/Pages/CreateTeacherSubject.php b/app/Filament/Resources/TeacherSubjectResource/Pages/CreateTeacherSubject.php index ea8ec15..74f1182 100644 --- a/app/Filament/Resources/TeacherSubjectResource/Pages/CreateTeacherSubject.php +++ b/app/Filament/Resources/TeacherSubjectResource/Pages/CreateTeacherSubject.php @@ -17,7 +17,7 @@ class CreateTeacherSubject extends CreateRecord $exists = TeacherSubject::where('teacher_id', $data['teacher_id']) ->where('subject_id', $data['subject_id']) ->where('class_id', $data['class_id']) - ->where('academic_year', $data['academic_year']) + ->where('academic_year_id', $data['academic_year_id']) ->exists(); if ($exists) { diff --git a/app/Filament/Resources/UserResource.php b/app/Filament/Resources/UserResource.php index 481321d..4fdd054 100644 --- a/app/Filament/Resources/UserResource.php +++ b/app/Filament/Resources/UserResource.php @@ -73,8 +73,11 @@ class UserResource extends Resource ]) ->bulkActions([ Tables\Actions\BulkActionGroup::make([ - Tables\Actions\DeleteBulkAction::make(), +// Tables\Actions\DeleteBulkAction::make(), ]), + ]) + ->emptyStateActions([ + Tables\Actions\CreateAction::make(), ]); } diff --git a/app/Models/AcademicYear.php b/app/Models/AcademicYear.php new file mode 100644 index 0000000..6102f97 --- /dev/null +++ b/app/Models/AcademicYear.php @@ -0,0 +1,34 @@ +hasMany(HomeRoomTeacher::class); + } + + public function teacherSubject(): HasMany + { + return $this->hasMany(TeacherSubject::class, 'academic_year_id'); + } + + public function classStudents() + { + return $this->hasMany(ClassStudent::class); + } + + public function classSubjects() + { + return $this->hasMany(ClassStudent::class); + } +} diff --git a/app/Models/Assessment.php b/app/Models/Assessment.php index d2712be..416e738 100644 --- a/app/Models/Assessment.php +++ b/app/Models/Assessment.php @@ -10,6 +10,7 @@ class Assessment extends Model 'teacher_subject_id', 'student_id', 'score', + 'semester' ]; public function teacherSubject() diff --git a/app/Models/ClassRoom.php b/app/Models/ClassRoom.php index f619c36..ac91530 100644 --- a/app/Models/ClassRoom.php +++ b/app/Models/ClassRoom.php @@ -4,28 +4,44 @@ namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Database\Eloquent\SoftDeletes; class ClassRoom extends Model { + use SoftDeletes; + protected $fillable = [ 'class_name', 'class_level', - 'homeroom_teacher_id', - 'academic_year' ]; public function homeroomTeacher() { - return $this->belongsTo(User::class, 'homeroom_teacher_id'); + return $this->hasMany(HomeRoomTeacher::class); } public function students() { - return $this->hasMany(Student::class, 'class_id'); + return $this->hasMany(Student::class); } public function teacherAssignments(): HasMany { - return $this->hasMany(TeacherSubject::class, 'class_id'); + return $this->hasMany(TeacherSubject::class); + } + + public function competencyAchievements() + { + return $this->hasMany(CompetencyAchievement::class); + } + + public function classStudents() + { + return $this->hasMany(ClassStudent::class); + } + + public function classSubjects(): HasMany + { + return $this->hasMany(ClassSubject::class); } } diff --git a/app/Models/ClassStudent.php b/app/Models/ClassStudent.php new file mode 100644 index 0000000..8a007e8 --- /dev/null +++ b/app/Models/ClassStudent.php @@ -0,0 +1,28 @@ +belongsTo(ClassRoom::class, 'class_room_id'); + } + + public function student() + { + return $this->belongsTo(Student::class, 'student_id'); + } + + public function academicYear() + { + return $this->belongsTo(AcademicYear::class, 'academic_year_id'); + } +} diff --git a/app/Models/ClassSubject.php b/app/Models/ClassSubject.php new file mode 100644 index 0000000..76d4525 --- /dev/null +++ b/app/Models/ClassSubject.php @@ -0,0 +1,27 @@ +belongsTo(Subject::class, 'subject_id'); + } + + public function class(): BelongsTo + { + return $this->belongsTo(ClassRoom::class, 'class_room_id'); + } + + public function academicYear(): BelongsTo + { + return $this->belongsTo(AcademicYear::class, 'academic_year_id'); + } +} diff --git a/app/Models/CompetencyAchievement.php b/app/Models/CompetencyAchievement.php new file mode 100644 index 0000000..29c13e4 --- /dev/null +++ b/app/Models/CompetencyAchievement.php @@ -0,0 +1,31 @@ +belongsTo(Subject::class, 'subject_id'); + } + + // Relasi ke ClassRoom + public function classRoom() + { + return $this->belongsTo(ClassRoom::class, 'class_room_id'); + } +} diff --git a/app/Models/HomeRoomTeacher.php b/app/Models/HomeRoomTeacher.php new file mode 100644 index 0000000..fa75b47 --- /dev/null +++ b/app/Models/HomeRoomTeacher.php @@ -0,0 +1,28 @@ +belongsTo(ClassRoom::class, 'class_room_id'); + } + + public function teacher() + { + return $this->belongsTo(User::class, 'teacher_id'); + } + + public function academicYear() + { + return $this->belongsTo(AcademicYear::class, 'academic_year_id'); + } +} diff --git a/app/Models/Student.php b/app/Models/Student.php index 0ef2d6e..17295fb 100644 --- a/app/Models/Student.php +++ b/app/Models/Student.php @@ -17,18 +17,17 @@ class Student extends Model 'religion', 'phone', 'email', - 'class_id', 'parent_name', 'parent_phone', + 'is_active', ]; protected $casts = [ 'birth_date' => 'date', ]; - // Relasi dengan kelas - public function classRoom() + public function classStudents() { - return $this->belongsTo(ClassRoom::class, 'class_id'); + return $this->hasMany(ClassStudent::class); } } diff --git a/app/Models/Subject.php b/app/Models/Subject.php index 31bd242..e44b863 100644 --- a/app/Models/Subject.php +++ b/app/Models/Subject.php @@ -7,7 +7,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany; class Subject extends Model { - protected $fillable = ['name', 'is_religious']; + protected $fillable = ['name', 'is_religious', 'category']; public function scopes() { @@ -16,12 +16,16 @@ class Subject extends Model public function teacherAssignments(): HasMany { - return $this->hasMany(TeacherSubject::class, 'subject_id'); + return $this->hasMany(TeacherSubject::class); } + public function competencyAchievements() + { + return $this->hasMany(CompetencyAchievement::class); + } -// public function grades() -// { -// return $this->hasMany(Grade::class); -// } + public function classSubjects(): HasMany + { + return $this->hasMany(ClassSubject::class); + } } diff --git a/app/Models/SubjectScope.php b/app/Models/SubjectScope.php deleted file mode 100644 index 4811af9..0000000 --- a/app/Models/SubjectScope.php +++ /dev/null @@ -1,15 +0,0 @@ -belongsTo(Subject::class); - } -} diff --git a/app/Models/TeacherSubject.php b/app/Models/TeacherSubject.php index 72b5f88..4f828b3 100644 --- a/app/Models/TeacherSubject.php +++ b/app/Models/TeacherSubject.php @@ -4,53 +4,34 @@ namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\Relations\HasMany; class TeacherSubject extends Model { - protected $fillable = ['teacher_id', 'subject_id', 'class_id', 'academic_year']; + protected $fillable = ['teacher_id', 'subject_id', 'class_id', 'academic_year_id']; - /** - * Get the teacher associated with this assignment - */ public function teacher(): BelongsTo { return $this->belongsTo(User::class, 'teacher_id'); } - /** - * Get the subject associated with this assignment - */ public function subject(): BelongsTo { return $this->belongsTo(Subject::class, 'subject_id'); } - /** - * Get the class associated with this assignment - */ public function class(): BelongsTo { return $this->belongsTo(ClassRoom::class, 'class_id'); } - /** - * Scope for current academic year - */ - public function scopeCurrentYear($query) - { - return $query->where('academic_year', now()->format('Y')); - } - - /** - * Scope for specific academic year - */ - public function scopeForYear($query, $year) - { - return $query->where('academic_year', $year); - } - public function attendances() { return $this->hasMany(Attendances::class); } + + public function academicYear() : BelongsTo + { + return $this->belongsTo(AcademicYear::class, 'academic_year_id'); + } } diff --git a/app/Models/User.php b/app/Models/User.php index 7f5ed38..c56ea0c 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -57,4 +57,9 @@ class User extends Authenticatable { return $this->hasMany(TeacherSubject::class, 'teacher_id'); } + + public function homeRoomTeacher(): HasMany + { + return $this->hasMany(HomeRoomTeacher::class, 'teacher_id'); + } } diff --git a/app/Policies/SubjectScopePolicy.php b/app/Policies/SubjectScopePolicy.php index fbad942..1a95c02 100644 --- a/app/Policies/SubjectScopePolicy.php +++ b/app/Policies/SubjectScopePolicy.php @@ -12,7 +12,7 @@ class SubjectScopePolicy */ public function viewAny(User $user): bool { - return $user->can('view_any_subject_scope'); + return $user->can('view_any_subject::scope'); } /** @@ -20,7 +20,7 @@ class SubjectScopePolicy */ public function view(User $user, SubjectScope $subjectScope): bool { - return $user->can('view_subject_scope'); + return $user->can('view_subject::scope'); } /** @@ -28,7 +28,7 @@ class SubjectScopePolicy */ public function create(User $user): bool { - return $user->can('create_subject_scope'); + return $user->can('create_subject::scope'); } /** @@ -36,7 +36,7 @@ class SubjectScopePolicy */ public function update(User $user, SubjectScope $subjectScope): bool { - return $user->can('update_subject_scope'); + return $user->can('update_subject::scope'); } /** @@ -44,7 +44,7 @@ class SubjectScopePolicy */ public function delete(User $user, SubjectScope $subjectScope): bool { - return $user->can('delete_subject_scope'); + return $user->can('delete_subject::scope'); } /** @@ -52,7 +52,7 @@ class SubjectScopePolicy */ public function restore(User $user, SubjectScope $subjectScope): bool { - return $user->can('restore_subject_scope'); + return $user->can('restore_subject::scope'); } /** @@ -60,6 +60,6 @@ class SubjectScopePolicy */ public function forceDelete(User $user, SubjectScope $subjectScope): bool { - return $user->can('force_delete_subject_scope'); + return $user->can('force_delete_subject::scope'); } } diff --git a/app/Policies/TeacherSubjectPolicy.php b/app/Policies/TeacherSubjectPolicy.php index 4ed4761..f842648 100644 --- a/app/Policies/TeacherSubjectPolicy.php +++ b/app/Policies/TeacherSubjectPolicy.php @@ -12,7 +12,7 @@ class TeacherSubjectPolicy */ public function viewAny(User $user): bool { - return $user->can('view_any_teacher_subject'); + return $user->can('view_any_teacher::subject'); } /** @@ -20,7 +20,7 @@ class TeacherSubjectPolicy */ public function view(User $user, TeacherSubject $teacherSubject): bool { - return $user->can('view_teacher_subject'); + return $user->can('view_teacher::subject'); } /** @@ -28,7 +28,7 @@ class TeacherSubjectPolicy */ public function create(User $user): bool { - return $user->can('create_teacher_subject'); + return $user->can('create_teacher::subject'); } /** @@ -36,7 +36,7 @@ class TeacherSubjectPolicy */ public function update(User $user, TeacherSubject $teacherSubject): bool { - return $user->can('update_teacher_subject'); + return $user->can('update_teacher::subject'); } /** @@ -44,7 +44,7 @@ class TeacherSubjectPolicy */ public function delete(User $user, TeacherSubject $teacherSubject): bool { - return $user->can('delete_teacher_subject'); + return $user->can('delete_teacher::subject'); } /** @@ -52,7 +52,7 @@ class TeacherSubjectPolicy */ public function restore(User $user, TeacherSubject $teacherSubject): bool { - return $user->can('restore_teacher_subject'); + return $user->can('restore_teacher::subject'); } /** @@ -60,6 +60,6 @@ class TeacherSubjectPolicy */ public function forceDelete(User $user, TeacherSubject $teacherSubject): bool { - return $user->can('force_delete_teacher_subject'); + return $user->can('force_delete_teacher::subject'); } } diff --git a/database/migrations/2025_04_10_000425_create_class_rooms_table.php b/database/migrations/2025_04_10_000425_create_class_rooms_table.php index d2efcf2..40b6e1e 100644 --- a/database/migrations/2025_04_10_000425_create_class_rooms_table.php +++ b/database/migrations/2025_04_10_000425_create_class_rooms_table.php @@ -15,9 +15,10 @@ return new class extends Migration $table->id(); $table->string('class_name'); $table->string('class_level'); - $table->foreignId('homeroom_teacher_id')->nullable()->constrained('users'); - $table->string('academic_year'); $table->timestamps(); + + $table->softDeletes(); + $table->unique(['class_name', 'class_level']); }); } diff --git a/database/migrations/2025_04_10_000426_create_students_table.php b/database/migrations/2025_04_10_000426_create_students_table.php index df567bb..28ab720 100644 --- a/database/migrations/2025_04_10_000426_create_students_table.php +++ b/database/migrations/2025_04_10_000426_create_students_table.php @@ -23,10 +23,13 @@ return new class extends Migration $table->text('religion')->nullable(); $table->string('phone')->nullable(); $table->string('email')->nullable(); - $table->foreignId('class_id')->nullable()->constrained('class_rooms'); $table->string('parent_name')->nullable(); $table->string('parent_phone')->nullable(); + $table->boolean('is_active')->default(true); $table->timestamps(); + + $table->softDeletes(); + $table->unique(['nis', 'nisn']); }); } diff --git a/database/migrations/2025_04_10_151152_create_subject_scopes_table.php b/database/migrations/2025_04_10_000427_create_academic_years_table.php similarity index 51% rename from database/migrations/2025_04_10_151152_create_subject_scopes_table.php rename to database/migrations/2025_04_10_000427_create_academic_years_table.php index f70d7a5..d407b0a 100644 --- a/database/migrations/2025_04_10_151152_create_subject_scopes_table.php +++ b/database/migrations/2025_04_10_000427_create_academic_years_table.php @@ -11,13 +11,16 @@ return new class extends Migration */ public function up(): void { - Schema::create('subject_scopes', function (Blueprint $table) { + Schema::create('academic_years', function (Blueprint $table) { $table->id(); - $table->foreignId('subject_id')->constrained()->onDelete('cascade'); - $table->string('religion')->nullable(); // null jika bukan mapel agama - $table->string('learning_goal'); - $table->text('scope'); + $table->string('name'); // contoh: 2024/2025 + $table->boolean('is_active')->default(false); + $table->date('start_date')->nullable(); + $table->date('end_date')->nullable(); $table->timestamps(); + + $table->softDeletes(); + $table->unique(['name']); }); } @@ -26,6 +29,6 @@ return new class extends Migration */ public function down(): void { - Schema::dropIfExists('subject_scopes'); + Schema::dropIfExists('academic_years'); } }; diff --git a/database/migrations/2025_04_10_151020_create_subjects_table.php b/database/migrations/2025_04_10_151020_create_subjects_table.php index 5772701..a4f58fc 100644 --- a/database/migrations/2025_04_10_151020_create_subjects_table.php +++ b/database/migrations/2025_04_10_151020_create_subjects_table.php @@ -15,7 +15,10 @@ return new class extends Migration $table->id(); $table->string('name'); $table->boolean('is_religious')->default(false); + $table->enum('category', ['umum', 'muatan lokal', 'seni']); $table->timestamps(); + + $table->unique(['name', 'is_religious', 'category']); }); } diff --git a/database/migrations/2025_04_13_010342_create_teacher_subjects_table.php b/database/migrations/2025_04_13_010342_create_teacher_subjects_table.php index 16d4b6a..0a515e6 100644 --- a/database/migrations/2025_04_13_010342_create_teacher_subjects_table.php +++ b/database/migrations/2025_04_13_010342_create_teacher_subjects_table.php @@ -16,10 +16,10 @@ return new class extends Migration $table->foreignId('teacher_id')->constrained('users'); $table->foreignId('subject_id')->constrained('subjects'); $table->foreignId('class_id')->constrained('class_rooms'); - $table->string('academic_year'); + $table->foreignId('academic_year_id')->constrained('academic_years'); $table->timestamps(); - $table->unique(['teacher_id', 'subject_id', 'class_id', 'academic_year']); + $table->unique(['teacher_id', 'subject_id', 'class_id', 'academic_year_id']); }); } diff --git a/database/migrations/2025_04_30_125442_create_assessments_table.php b/database/migrations/2025_04_30_125442_create_assessments_table.php index 51a040c..123f8a2 100644 --- a/database/migrations/2025_04_30_125442_create_assessments_table.php +++ b/database/migrations/2025_04_30_125442_create_assessments_table.php @@ -16,9 +16,10 @@ return new class extends Migration $table->foreignId('teacher_subject_id')->constrained('teacher_subjects'); $table->foreignId('student_id')->constrained('students'); $table->float('score'); + $table->string('semester'); $table->timestamps(); - $table->unique(['teacher_subject_id', 'student_id']); + $table->unique(['teacher_subject_id', 'student_id', 'semester']); }); } diff --git a/database/migrations/2025_05_03_091105_create_competency_achievements_table.php b/database/migrations/2025_05_03_091105_create_competency_achievements_table.php new file mode 100644 index 0000000..bb23304 --- /dev/null +++ b/database/migrations/2025_05_03_091105_create_competency_achievements_table.php @@ -0,0 +1,34 @@ +id(); + $table->foreignId('subject_id')->constrained('subjects'); + $table->foreignId('class_room_id')->constrained('class_rooms'); + $table->float("min_score")->default(0); + $table->float("max_score")->default(0); + $table->text("description"); + $table->timestamps(); + + $table->softDeletes(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('competency_achievements'); + } +}; diff --git a/database/migrations/2025_05_04_120638_create_home_room_teachers_table.php b/database/migrations/2025_05_04_120638_create_home_room_teachers_table.php new file mode 100644 index 0000000..2208625 --- /dev/null +++ b/database/migrations/2025_05_04_120638_create_home_room_teachers_table.php @@ -0,0 +1,33 @@ +id(); + $table->foreignId('class_room_id')->constrained('class_rooms'); + $table->foreignId('teacher_id')->constrained('users'); + $table->foreignId('academic_year_id')->constrained('academic_years'); + $table->timestamps(); + + $table->unique(['class_room_id', 'teacher_id', 'academic_year_id']); + $table->softDeletes(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('home_room_teachers'); + } +}; diff --git a/database/migrations/2025_05_04_121938_create_class_students_table.php b/database/migrations/2025_05_04_121938_create_class_students_table.php new file mode 100644 index 0000000..b0665fc --- /dev/null +++ b/database/migrations/2025_05_04_121938_create_class_students_table.php @@ -0,0 +1,33 @@ +id(); + $table->foreignId('class_room_id')->constrained('class_rooms'); + $table->foreignId('student_id')->constrained('students'); + $table->foreignId('academic_year_id')->constrained('academic_years'); + $table->timestamps(); + + $table->unique(['class_room_id', 'student_id', 'academic_year_id']); + $table->softDeletes(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('class_students'); + } +}; diff --git a/database/migrations/2025_05_06_041554_create_class_subjects_table.php b/database/migrations/2025_05_06_041554_create_class_subjects_table.php new file mode 100644 index 0000000..85bd9c0 --- /dev/null +++ b/database/migrations/2025_05_06_041554_create_class_subjects_table.php @@ -0,0 +1,33 @@ +id(); + $table->foreignId('class_room_id')->constrained('class_rooms'); + $table->foreignId('subject_id')->constrained('subjects'); + $table->foreignId('academic_year_id')->constrained('academic_years'); + $table->timestamps(); + + $table->softDeletes(); + $table->unique(['class_room_id', 'subject_id', 'academic_year_id']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('class_subjects'); + } +}; diff --git a/database/seeders/AcademicYearSeeder.php b/database/seeders/AcademicYearSeeder.php new file mode 100644 index 0000000..a4682d4 --- /dev/null +++ b/database/seeders/AcademicYearSeeder.php @@ -0,0 +1,36 @@ +insert([ + [ + 'name' => '2023/2024', + 'is_active' => false, + 'start_date' => Carbon::create(2023, 7, 1), + 'end_date' => Carbon::create(2024, 6, 30), + 'created_at' => now(), + 'updated_at' => now(), + ], + [ + 'name' => '2024/2025', + 'is_active' => true, + 'start_date' => Carbon::create(2024, 7, 1), + 'end_date' => Carbon::create(2025, 6, 30), + 'created_at' => now(), + 'updated_at' => now(), + ], + ]); + } +} diff --git a/database/seeders/AttendanceSeeder.php b/database/seeders/AttendanceSeeder.php deleted file mode 100644 index 2605c7b..0000000 --- a/database/seeders/AttendanceSeeder.php +++ /dev/null @@ -1,17 +0,0 @@ -get(); $classes = [ - ['class_name' => '1 A', 'class_level' => '1', 'academic_year' => '2024/2025'], - ['class_name' => '1 B', 'class_level' => '1', 'academic_year' => '2024/2025'], - ['class_name' => '2 A', 'class_level' => '2', 'academic_year' => '2024/2025'], - ['class_name' => '2 B', 'class_level' => '2', 'academic_year' => '2024/2025'], + ['class_name' => '1 A', 'class_level' => '1'], + ['class_name' => '1 B', 'class_level' => '1'], + ['class_name' => '2 A', 'class_level' => '2'], + ['class_name' => '2 B', 'class_level' => '2'], ]; foreach ($classes as $index => $class) { - $class['homeroom_teacher_id'] = $teachers[$index % count($teachers)]->id; ClassRoom::create($class); } } diff --git a/database/seeders/ClassStudentSeeder.php b/database/seeders/ClassStudentSeeder.php new file mode 100644 index 0000000..f5f11d9 --- /dev/null +++ b/database/seeders/ClassStudentSeeder.php @@ -0,0 +1,40 @@ +isNotEmpty() && $students->isNotEmpty() && $academicYears->isNotEmpty()) { + foreach ($classRooms as $classRoom) { + foreach ($students as $student) { + foreach ($academicYears as $academicYear) { + // Buat ClassStudent baru untuk setiap kombinasi + ClassStudent::create([ + 'class_room_id' => $classRoom->id, + 'student_id' => $student->id, + 'academic_year_id' => $academicYear->id, + ]); + } + } + } + } + } +} diff --git a/database/seeders/ClassSubjectSeeder.php b/database/seeders/ClassSubjectSeeder.php new file mode 100644 index 0000000..671dd6c --- /dev/null +++ b/database/seeders/ClassSubjectSeeder.php @@ -0,0 +1,41 @@ +first(); + + if (!$academicYear) { + $this->command->error('Tahun ajaran aktif tidak ditemukan.'); + return; + } + + foreach ($classRooms as $classRoom) { + // Contoh: ambil 3 sampai 5 mapel random untuk tiap kelas + $assignedSubjects = $subjects->random(rand(3, 5)); + + foreach ($assignedSubjects as $subject) { + ClassSubject::firstOrCreate([ + 'class_room_id' => $classRoom->id, + 'subject_id' => $subject->id, + 'academic_year_id' => $academicYear->id, + ]); + } + } + } +} diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index edf7859..a466e15 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -2,11 +2,7 @@ namespace Database\Seeders; -use App\Models\ClassRoom; -use App\Models\User; use Illuminate\Database\Seeder; -use Spatie\Permission\Models\Permission; -use Spatie\Permission\Models\Role; class DatabaseSeeder extends Seeder { @@ -19,9 +15,12 @@ class DatabaseSeeder extends Seeder UserSeeder::class, SubjectSeeder::class, ClassRoomSeeder::class, + AcademicYearSeeder::class, StudentSeeder::class, ExtracurricularSeeder::class, - AttendanceSeeder::class, + ClassSubjectSeeder::class, + ClassStudentSeeder::class, + HomeRoomTeacherSeeder::class, ]); } } diff --git a/database/seeders/SubjectScopeSeeder.php b/database/seeders/HomeRoomTeacherSeeder.php similarity index 59% rename from database/seeders/SubjectScopeSeeder.php rename to database/seeders/HomeRoomTeacherSeeder.php index 5e13ebb..4b4470c 100644 --- a/database/seeders/SubjectScopeSeeder.php +++ b/database/seeders/HomeRoomTeacherSeeder.php @@ -2,10 +2,14 @@ namespace Database\Seeders; +use App\Models\AcademicYear; +use App\Models\ClassRoom; +use App\Models\HomeRoomTeacher; +use App\Models\User; use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Illuminate\Database\Seeder; -class SubjectScopeSeeder extends Seeder +class HomeRoomTeacherSeeder extends Seeder { /** * Run the database seeds. @@ -14,4 +18,5 @@ class SubjectScopeSeeder extends Seeder { } + } diff --git a/database/seeders/StudentSeeder.php b/database/seeders/StudentSeeder.php index 5c9e5b1..fc38ee2 100644 --- a/database/seeders/StudentSeeder.php +++ b/database/seeders/StudentSeeder.php @@ -4,6 +4,7 @@ namespace Database\Seeders; use App\Models\ClassRoom; use App\Models\Student; +use Carbon\Carbon; use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Illuminate\Database\Seeder; @@ -14,31 +15,29 @@ class StudentSeeder extends Seeder */ public function run(): void { - $classrooms = ClassRoom::all(); $religions = ['islam', 'kristen', 'katolik', 'hindu', 'buddha']; - foreach ($classrooms as $classroom) { - for ($i = 1; $i <= 20; $i++) { - $gender = $i % 2 == 0 ? 'L' : 'P'; - $religion = $religions[array_rand($religions)]; - $nis = $classroom->class_level . str_pad($i, 3, '0', STR_PAD_LEFT); + for ($i = 1; $i <= 20; $i++) { + $gender = $i % 2 == 0 ? 'L' : 'P'; + $religion = $religions[array_rand($religions)]; + $nis = '24' . str_pad($i, 4, '0', STR_PAD_LEFT); // NIS unik, misal 240001 - Student::firstOrCreate( - ['nis' => $nis], // Cek berdasarkan NIS - [ - 'full_name' => 'Siswa ' . $classroom->class_name . ' ' . $i, - 'gender' => $gender, - 'birth_date' => now()->subYears(rand(15, 18))->subMonths(rand(1, 12)), - 'birth_place' => 'Kota ' . chr(65 + rand(0, 25)), - 'address' => 'Jl. Contoh No.' . $i, - 'phone' => '0812' . rand(1000000, 9999999), - 'class_id' => $classroom->id, - 'parent_name' => 'Orang Tua Siswa ' . $i, - 'religion' => $religion, - 'parent_phone' => '0813' . rand(1000000, 9999999), - ] - ); - } + Student::firstOrCreate( + ['nis' => $nis], // Cek berdasarkan NIS + [ + 'full_name' => 'Siswa ' . $i, + 'gender' => $gender, + 'birth_date' => Carbon::now()->subYears(rand(15, 18))->subMonths(rand(1, 12)), + 'birth_place' => 'Kota ' . chr(65 + rand(0, 25)), + 'address' => 'Jl. Contoh No.' . $i, + 'phone' => '0812' . rand(1000000, 9999999), + 'parent_name' => 'Orang Tua Siswa ' . $i, + 'religion' => $religion, + 'parent_phone' => '0813' . rand(1000000, 9999999), + 'created_at' => now(), + 'updated_at' => now(), + ] + ); } } } diff --git a/database/seeders/SubjectSeeder.php b/database/seeders/SubjectSeeder.php index f332b43..de1b19c 100644 --- a/database/seeders/SubjectSeeder.php +++ b/database/seeders/SubjectSeeder.php @@ -3,7 +3,6 @@ namespace Database\Seeders; use App\Models\Subject; -use App\Models\SubjectScope; use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Illuminate\Database\Seeder; @@ -15,16 +14,21 @@ class SubjectSeeder extends Seeder public function run(): void { $subjects = [ - ['name' => 'Matematika', 'is_religious' => false], - ['name' => 'Bahasa Indonesia', 'is_religious' => false], - ['name' => 'Bahasa Inggris', 'is_religious' => false], - ['name' => 'IPA', 'is_religious' => false], - ['name' => 'IPS', 'is_religious' => false], - ['name' => 'Pendidikan Agama Islam', 'is_religious' => true], - ['name' => 'Pendidikan Agama Kristen', 'is_religious' => true], - ['name' => 'Pendidikan Agama Katolik', 'is_religious' => true], - ['name' => 'Pendidikan Agama Hindu', 'is_religious' => true], - ['name' => 'Pendidikan Agama Buddha', 'is_religious' => true], + ['name' => 'Matematika', 'is_religious' => false, 'category' => 'umum'], + ['name' => 'Bahasa Indonesia', 'is_religious' => false, 'category' => 'umum'], + ['name' => 'Bahasa Inggris', 'is_religious' => false, 'category' => 'umum'], + ['name' => 'IPA', 'is_religious' => false, 'category' => 'umum'], + ['name' => 'IPS', 'is_religious' => false, 'category' => 'umum'], + ['name' => 'Seni Rupa', 'is_religious' => false, 'category' => 'seni'], + ['name' => 'Seni Musik', 'is_religious' => false, 'category' => 'seni'], + ['name' => 'Seni Tari', 'is_religious' => false, 'category' => 'seni'], + ['name' => 'Seni Teater', 'is_religious' => false, 'category' => 'seni'], + ['name' => 'Bahasa Sunda', 'is_religious' => false, 'category' => 'muatan lokal'], + ['name' => 'Pendidikan Agama Islam', 'is_religious' => true, 'category' => 'umum'], + ['name' => 'Pendidikan Agama Kristen', 'is_religious' => true, 'category' => 'umum'], + ['name' => 'Pendidikan Agama Katolik', 'is_religious' => true, 'category' => 'umum'], + ['name' => 'Pendidikan Agama Hindu', 'is_religious' => true, 'category' => 'umum'], + ['name' => 'Pendidikan Agama Buddha', 'is_religious' => true, 'category' => 'umum'], ]; foreach ($subjects as $subject) { @@ -40,15 +44,6 @@ class SubjectSeeder extends Seeder str_contains($createdSubject->name, 'Buddha') => 'buddha', default => null }; - - if ($religion) { - SubjectScope::create([ - 'subject_id' => $createdSubject->id, - 'religion' => $religion, - 'learning_goal' => 'Memahami prinsip dasar agama ' . $religion, - 'scope' => 'Tingkat dasar' - ]); - } } } } diff --git a/resources/views/filament/pages/student-report.blade.php b/resources/views/filament/pages/student-report.blade.php new file mode 100644 index 0000000..a6abd90 --- /dev/null +++ b/resources/views/filament/pages/student-report.blade.php @@ -0,0 +1,77 @@ + + {{ $this->form }} + + @if(!empty($this->list)) +
+
+ + + + + + + @foreach(['umum', 'muatan lokal'] as $category) + @if(!empty($this->list['header'][$category])) + + @endif + @endforeach + + + + + @foreach(['umum', 'muatan lokal'] as $category) + @if(!empty($this->list['header'][$category])) + @foreach($this->list['header'][$category] as $subjectName) + + @endforeach + @endif + @endforeach + + + + + @foreach($this->list['data'] as $student) + + + + + + @foreach(['umum', 'muatan lokal'] as $category) + @if(!empty($this->list['header'][$category])) + @foreach($this->list['header'][$category] as $subjectId => $subjectName) + + @endforeach + @endif + @endforeach + + @endforeach + +
+ + {{ $this->list['header']['name'] }} + + + + {{ ucfirst($category) }} + +
+ + {{ $subjectName }} + +
+ + {{ $student['name'] }} + + + + {{ $student[$category][$subjectId] ?? '-' }} + +
+
+
+ @else +
+ Pilih Kelas, Tahun Akademik, dan Semester untuk melihat laporan siswa. +
+ @endif +
diff --git a/resources/views/filament/resources/assessment-resource/pages/multiple-assessment.blade.php b/resources/views/filament/resources/assessment-resource/pages/multiple-assessment.blade.php index 9e05cef..cfd5c97 100644 --- a/resources/views/filament/resources/assessment-resource/pages/multiple-assessment.blade.php +++ b/resources/views/filament/resources/assessment-resource/pages/multiple-assessment.blade.php @@ -10,8 +10,8 @@ @foreach($this->students as $index => $student)
-

{{ $student['name'] }}

-

{{ $student['nis'] }}

+

{{ $student['name'] ?? "" }}

+

{{ $student['nis'] ?? "" }}