add learning objective

This commit is contained in:
reihanrere 2025-05-31 18:40:39 +07:00
parent ee84781f3a
commit a5754a3eca
79 changed files with 1473 additions and 197 deletions

View File

@ -4,6 +4,7 @@ namespace App\Filament\Pages;
use App\Models\AcademicYear; use App\Models\AcademicYear;
use App\Models\Assessment; use App\Models\Assessment;
use App\Models\AssessmentLearningObjective;
use App\Models\ClassRoom; use App\Models\ClassRoom;
use App\Models\ClassSubject; use App\Models\ClassSubject;
use App\Models\CompetencyAchievement; use App\Models\CompetencyAchievement;
@ -16,6 +17,7 @@ use App\Models\TeacherSubject;
use Filament\Notifications\Notification; use Filament\Notifications\Notification;
use Filament\Pages\Page; use Filament\Pages\Page;
use Carbon\Carbon; use Carbon\Carbon;
use Illuminate\Contracts\Support\Htmlable;
use function PHPUnit\Framework\isEmpty; use function PHPUnit\Framework\isEmpty;
use function Symfony\Component\String\s; use function Symfony\Component\String\s;
use Barryvdh\DomPDF\Facade\Pdf; use Barryvdh\DomPDF\Facade\Pdf;
@ -47,10 +49,8 @@ class ReportPreview extends Page
public function mount() public function mount()
{ {
$this->loadData(); $this->loadData();
$this->loadAssessment();
} }
public function saveAttendance() public function saveAttendance()
{ {
$this->validate([ $this->validate([
@ -67,13 +67,16 @@ class ReportPreview extends Page
protected function loadData() : void protected function loadData() : void
{ {
$this->student = Student::find(request()->query('studentId')); $this->student = Student::find(request()->query('studentId')) ?? 1;
$this->class = ClassRoom::find(request()->query('classId'));
$this->academic_year = AcademicYear::find(request()->query('yearId')); $this->class = ClassRoom::find(request()->query('classId')) ?? 1;
$this->semester = request()->query('semester'); $this->academic_year = AcademicYear::find(request()->query('yearId')) ?? 1;
$this->semester = request()->query('semester') ?? "first";
$this->school_information = SchoolInformation::first(); $this->school_information = SchoolInformation::first();
$this->class_name = $this->toRoman($this->class->class_level) . ' ' . $this->extractClassLetter($this->class->class_name); $this->class_name = $this->toRoman($this->class->class_level) . ' ' . $this->extractClassLetter($this->class->class_name);
$this->loadAssessment();
} }
protected function loadAssessment(): void protected function loadAssessment(): void
@ -87,8 +90,6 @@ class ReportPreview extends Page
->body("The parameter '{$param}' is required.") ->body("The parameter '{$param}' is required.")
->danger() ->danger()
->send(); ->send();
return;
} }
} }
@ -113,17 +114,32 @@ class ReportPreview extends Page
return null; return null;
} }
$competencyAchievements = CompetencyAchievement::where('class_room_id', $class_id) $assesmentLearningObjectivesLower = AssessmentLearningObjective::with('learningObjective')
->where('subject_id', $subject->id) ->where('teacher_subject_id', $as->teacherSubject->id)
->where('min_score', '<=', $as->score) ->where('student_id', $this->student->id)
->where('max_score', '>=', $as->score) ->where('type', 'lower')
->first(); ->get()
->pluck('learningObjective.description')
->toArray();
$joinedStringLower = $as->student->full_name . ' perlu peningkatan dalam ' . implode(', ', $assesmentLearningObjectivesLower);
$assesmentLearningObjectivesHighest = AssessmentLearningObjective::with('learningObjective')
->where('teacher_subject_id', $as->teacherSubject->id)
->where('student_id', $this->student->id)
->where('type', 'highest')
->get()
->pluck('learningObjective.description')
->toArray();
$joinedStringHighest = $as->student->full_name . ' menunjukan pemahaman dalam ' . implode(', ', $assesmentLearningObjectivesHighest);
return [ return [
"score" => $as->score, "score" => $as->score,
"subject" => $subject->name, "subject" => $subject->name,
"category" => $subject->category, "category" => $subject->category,
"competency_achievement" => $competencyAchievements ? $this->student->full_name . '' . $competencyAchievements->description : '-', "lower" => $assesmentLearningObjectivesLower ? $joinedStringLower : null,
"highest" => $assesmentLearningObjectivesHighest ? $joinedStringHighest : null,
]; ];
}) })
->filter() // Hapus null jika ada ->filter() // Hapus null jika ada
@ -162,16 +178,16 @@ class ReportPreview extends Page
]; ];
}) })
->toArray(); ->toArray();
$this->table["extracurricular"] = $extracurricular; $this->table["extracurricular"] = $extracurricular;
$homeRoom = HomeRoomTeacher::with(['teacher']) $homeRoom = HomeRoomTeacher::with(['teacher'])
->where('class_room_id', $this->class->id) ->where('class_room_id', $this->class->id)
->where('academic_year_id', $this->academic_year->id) ->where('academic_year_id', $this->academic_year->id)
->firstOrFail() ->first();
->toArray();
$this->home_room_teacher = $homeRoom ?? []; if ($homeRoom) {
$this->home_room_teacher = $homeRoom ?? [];
}
} }
public function extractClassLetter($className) public function extractClassLetter($className)
@ -216,4 +232,24 @@ class ReportPreview extends Page
{ {
return false; return false;
} }
public function getTitle(): string | Htmlable
{
return 'Pratinjau Rapor Siswa'; // Contoh jika nama laporan dinamis
}
public static function getNavigationLabel(): string
{
return 'Pratinjau Rapor Siswa';
}
public static function getBreadcrumb(): string
{
return 'Pratinjau Rapor Siswa';
}
public static function getPluralModelLabel(): string
{
return 'Pratinjau Rapor Siswa';
}
} }

View File

@ -12,6 +12,7 @@ use Filament\Forms\Contracts\HasForms;
use Filament\Forms\Concerns\InteractsWithForms; use Filament\Forms\Concerns\InteractsWithForms;
use App\Models\SchoolInformation as SchoolInformationModel; use App\Models\SchoolInformation as SchoolInformationModel;
use Illuminate\Contracts\Support\Htmlable;
class SchoolInformation extends Page implements HasForms class SchoolInformation extends Page implements HasForms
{ {
@ -19,8 +20,8 @@ class SchoolInformation extends Page implements HasForms
protected static ?string $navigationIcon = 'heroicon-o-document-text'; protected static ?string $navigationIcon = 'heroicon-o-document-text';
protected static string $view = 'filament.pages.school-information'; protected static string $view = 'filament.pages.school-information';
protected static ?string $navigationGroup = 'Settings'; protected static ?string $navigationGroup = 'Pengaturan';
protected static ?int $navigationSort = 999; // Biar muncul paling bawah protected static ?int $navigationSort = 999;
public static function canAccess(): bool public static function canAccess(): bool
{ {
@ -102,4 +103,24 @@ class SchoolInformation extends Page implements HasForms
->success() ->success()
->send(); ->send();
} }
public function getTitle(): string | Htmlable
{
return 'Informasi Sekolah'; // Contoh jika nama laporan dinamis
}
public static function getNavigationLabel(): string
{
return 'Informasi Sekolah';
}
public static function getBreadcrumb(): string
{
return 'Informasi Sekolah';
}
public static function getPluralModelLabel(): string
{
return 'Informasi Sekolah';
}
} }

View File

@ -14,6 +14,7 @@ use App\Models\TeacherSubject;
use Filament\Forms\Components\Select; use Filament\Forms\Components\Select;
use Filament\Forms\Form; use Filament\Forms\Form;
use Filament\Pages\Page; use Filament\Pages\Page;
use Illuminate\Contracts\Support\Htmlable;
class StudentReport extends Page class StudentReport extends Page
{ {
@ -71,7 +72,7 @@ class StudentReport extends Page
{ {
return $form->schema([ return $form->schema([
Select::make('class_id') Select::make('class_id')
->label('Class') ->label('Kelas')
->required() ->required()
->options(function () { ->options(function () {
$query = ClassRoom::query(); $query = ClassRoom::query();
@ -107,7 +108,7 @@ class StudentReport extends Page
}), }),
Select::make('academic_year') Select::make('academic_year')
->label('Academic Year') ->label('Tahun Ajaran')
->required() ->required()
->options(AcademicYear::pluck('name', 'id')->toArray()) ->options(AcademicYear::pluck('name', 'id')->toArray())
->searchable() ->searchable()
@ -122,8 +123,8 @@ class StudentReport extends Page
->label('Semester') ->label('Semester')
->required() ->required()
->options([ ->options([
'first' => 'First Semester', 'first' => 'Ganjil',
'second' => 'Second Semester' 'second' => 'Genap'
]) ])
->reactive() ->reactive()
->afterStateUpdated(function ($state) { ->afterStateUpdated(function ($state) {
@ -281,4 +282,24 @@ class StudentReport extends Page
$this->list = $groupedAssessment; $this->list = $groupedAssessment;
} }
public function getTitle(): string | Htmlable
{
return 'Rapor Siswa'; // Contoh jika nama laporan dinamis
}
public static function getNavigationLabel(): string
{
return 'Rapor Siswa';
}
public static function getBreadcrumb(): string
{
return 'Rapor Siswa';
}
public static function getPluralModelLabel(): string
{
return 'Rapor Siswa';
}
} }

View File

@ -123,4 +123,19 @@ class AcademicYearResource extends Resource
SoftDeletingScope::class, SoftDeletingScope::class,
]); ]);
} }
public static function getNavigationLabel(): string
{
return 'Tahun Ajaran';
}
public static function getBreadcrumb(): string
{
return 'Tahun Ajaran';
}
public static function getPluralModelLabel(): string
{
return 'Tahun Ajaran';
}
} }

View File

@ -13,6 +13,11 @@ class CreateAcademicYear extends CreateRecord
{ {
protected static string $resource = AcademicYearResource::class; protected static string $resource = AcademicYearResource::class;
public function getTitle(): string
{
return 'Tambah Tahun Ajaran Baru';
}
protected function mutateFormDataBeforeCreate(array $data): array protected function mutateFormDataBeforeCreate(array $data): array
{ {
$exists = AcademicYear::where('name', $data['name']) $exists = AcademicYear::where('name', $data['name'])
@ -21,7 +26,7 @@ class CreateAcademicYear extends CreateRecord
if ($exists) { if ($exists) {
Notification::make() Notification::make()
->title('Failed to save') ->title('Failed to save')
->body('A record already exists.') ->body('Data sudah ada')
->danger() ->danger()
->send(); ->send();

View File

@ -13,6 +13,11 @@ class EditAcademicYear extends EditRecord
{ {
protected static string $resource = AcademicYearResource::class; protected static string $resource = AcademicYearResource::class;
public function getTitle(): string
{
return 'Edit Tahun Ajaran';
}
protected function mutateFormDataBeforeSave(array $data): array protected function mutateFormDataBeforeSave(array $data): array
{ {
$exists = AcademicYear::where('name', $data['name']) $exists = AcademicYear::where('name', $data['name'])
@ -21,7 +26,7 @@ class EditAcademicYear extends EditRecord
if ($exists) { if ($exists) {
Notification::make() Notification::make()
->title('Failed to save') ->title('Failed to save')
->body('A record already exists.') ->body('Data sudah ada')
->danger() ->danger()
->send(); ->send();

View File

@ -13,7 +13,13 @@ class ListAcademicYears extends ListRecords
protected function getHeaderActions(): array protected function getHeaderActions(): array
{ {
return [ return [
Actions\CreateAction::make(), Actions\CreateAction::make()
->label('Tambah Tahun Ajaran Baru'), // Custom label untuk tombol create
]; ];
} }
public function getTitle(): string
{
return 'Daftar Tahun Ajaran';
}
} }

View File

@ -164,7 +164,21 @@ class AssessmentResource extends Resource
->toggleable(isToggledHiddenByDefault: true), ->toggleable(isToggledHiddenByDefault: true),
]) ])
->filters([ ->filters([
//
Tables\Filters\SelectFilter::make('semester')
->label('Semester')
->options([
'first' => 'Ganjil',
'second' => 'Genap'
]),
Tables\Filters\SelectFilter::make('teacher_subject_id')
->label('Guru per-Mapel')
->relationship('teacherSubject', 'id')
->searchable()
->getOptionLabelFromRecordUsing(fn (TeacherSubject $record) =>
$record->teacher->name . ' - ' . $record->subject->name)
->preload(),
]) ])
->actions([ ->actions([
Tables\Actions\EditAction::make(), Tables\Actions\EditAction::make(),
@ -173,9 +187,6 @@ class AssessmentResource extends Resource
Tables\Actions\BulkActionGroup::make([ Tables\Actions\BulkActionGroup::make([
Tables\Actions\DeleteBulkAction::make()->visible(fn () => auth()->user()->hasRole('admin') || auth()->user()->hasRole('teacher')), Tables\Actions\DeleteBulkAction::make()->visible(fn () => auth()->user()->hasRole('admin') || auth()->user()->hasRole('teacher')),
]), ]),
])
->emptyStateActions([
Tables\Actions\CreateAction::make(),
]); ]);
} }
@ -195,4 +206,19 @@ class AssessmentResource extends Resource
'multiple' => Pages\MultipleAssessments::route('/multiple'), 'multiple' => Pages\MultipleAssessments::route('/multiple'),
]; ];
} }
public static function getNavigationLabel(): string
{
return 'Penilaian';
}
public static function getBreadcrumb(): string
{
return 'Penilaian';
}
public static function getPluralModelLabel(): string
{
return 'Penilaian';
}
} }

View File

@ -12,6 +12,10 @@ class CreateAssessment extends CreateRecord
{ {
protected static string $resource = AssessmentResource::class; protected static string $resource = AssessmentResource::class;
public function getTitle(): string
{
return 'Tambah Penilaian';
}
protected function mutateFormDataBeforeCreate(array $data): array protected function mutateFormDataBeforeCreate(array $data): array
{ {
$exists = Assessment::where('teacher_subject_id', $data['teacher_subject_id']) $exists = Assessment::where('teacher_subject_id', $data['teacher_subject_id'])
@ -20,8 +24,8 @@ class CreateAssessment extends CreateRecord
if ($exists) { if ($exists) {
Notification::make() Notification::make()
->title('Failed to save') ->title('Gagal menyimpan data')
->body('A record for this teacher, subject, and student combination already exists.') ->body('Data untuk kombinasi guru, mata pelajaran, dan siswa ini sudah ada.')
->danger() ->danger()
->send(); ->send();

View File

@ -22,7 +22,7 @@ class EditAssessment extends EditRecord
if ($exists) { if ($exists) {
Notification::make() Notification::make()
->title('Failed to save') ->title('Failed to save')
->body('An assessment for this teacher, subject, and student combination already exists.') ->body('Data untuk kombinasi guru, mata pelajaran, dan siswa ini sudah ada.')
->danger() ->danger()
->send(); ->send();
@ -38,4 +38,9 @@ class EditAssessment extends EditRecord
Actions\DeleteAction::make(), Actions\DeleteAction::make(),
]; ];
} }
public function getTitle(): string
{
return 'Edit Penilaian';
}
} }

View File

@ -13,6 +13,10 @@ class ListAssessments extends ListRecords
{ {
protected static string $resource = AssessmentResource::class; protected static string $resource = AssessmentResource::class;
public function getTitle(): string
{
return 'Daftar Penilaian';
}
protected function getTableQuery(): Builder protected function getTableQuery(): Builder
{ {
$user = auth()->user(); $user = auth()->user();
@ -34,9 +38,9 @@ class ListAssessments extends ListRecords
protected function getHeaderActions(): array protected function getHeaderActions(): array
{ {
return [ return [
Actions\CreateAction::make()->visible(fn () => auth()->user()->hasRole('admin')), // Actions\CreateAction::make()->visible(fn () => auth()->user()->hasRole('admin')),
Actions\Action::make('multiple') Actions\Action::make('multiple')
->label('Multiple Assessments') ->label('Tambah Penilaian')
->url('assessments/multiple') ->url('assessments/multiple')
->icon('heroicon-o-user-group') ->icon('heroicon-o-user-group')
->visible(fn () => auth()->user()->hasRole('admin') || auth()->user()->hasRole('teacher')), ->visible(fn () => auth()->user()->hasRole('admin') || auth()->user()->hasRole('teacher')),

View File

@ -4,18 +4,18 @@ namespace App\Filament\Resources\AssessmentResource\Pages;
use App\Filament\Resources\AssessmentResource; use App\Filament\Resources\AssessmentResource;
use App\Models\Assessment; use App\Models\Assessment;
use App\Models\AssessmentLearningObjective;
use App\Models\Attendances; use App\Models\Attendances;
use App\Models\ClassStudent; use App\Models\ClassStudent;
use App\Models\HomeRoomTeacher; use App\Models\HomeRoomTeacher;
use App\Models\LearningObjective;
use Filament\Actions; use Filament\Actions;
use Filament\Forms\Components\Select; use Filament\Forms\Components\Select;
use Filament\Resources\Pages\page; use Filament\Resources\Pages\page;
use App\Models\Student;
use App\Models\TeacherSubject; use App\Models\TeacherSubject;
use Filament\Forms; use Filament\Forms;
use Filament\Notifications\Notification; use Filament\Notifications\Notification;
use Illuminate\Support\Collection; use Illuminate\Contracts\Support\Htmlable;
use function Symfony\Component\Translation\t;
class MultipleAssessments extends page class MultipleAssessments extends page
{ {
@ -23,6 +23,11 @@ class MultipleAssessments extends page
protected static string $view = 'filament.resources.assessment-resource.pages.multiple-assessment'; protected static string $view = 'filament.resources.assessment-resource.pages.multiple-assessment';
public function getTitle(): string | Htmlable
{
return __('Tambah Penilaian');
}
use Forms\Concerns\InteractsWithForms; use Forms\Concerns\InteractsWithForms;
public ?array $data = []; public ?array $data = [];
@ -34,34 +39,31 @@ class MultipleAssessments extends page
public $semester = 'first'; public $semester = 'first';
public $learningObjectivesList = [];
public function mount(): void public function mount(): void
{ {
$this->form->fill(); $this->form->fill();
} }
public function loadData($subject, $class): void
{
$this->learningObjectivesList = LearningObjective::with(["subject", "class"])
->where('subject_id', $subject)
->where('class_room_id', $class)
->get()
->pluck("description", "id")
->toArray();
}
protected function getFormSchema(): array protected function getFormSchema(): array
{ {
return [ return [
Forms\Components\Grid::make(2) Forms\Components\Grid::make(2)
->schema([ ->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();
// }),
Select::make('teacherSubjectId') Select::make('teacherSubjectId')
->label('Teacher Subject') ->label('Guru per-Mapel')
->required() ->required()
->options(function () { ->options(function () {
$user = auth()->user(); $user = auth()->user();
@ -99,8 +101,8 @@ class MultipleAssessments extends page
->required() ->required()
->default('first') ->default('first')
->options([ ->options([
'first' => 'First Semester', 'first' => 'Ganjil',
'second' => 'Second Semester' 'second' => 'Genap'
]) ])
->live() ->live()
->afterStateUpdated(function ($state) { ->afterStateUpdated(function ($state) {
@ -122,6 +124,8 @@ class MultipleAssessments extends page
->where('id', $this->teacherSubjectId) ->where('id', $this->teacherSubjectId)
->firstOrFail(); ->firstOrFail();
$this->loadData($this->teacherSubject->subject->id, $this->teacherSubject->class->id);
$classStudents = ClassStudent::where('class_room_id', $this->teacherSubject->class_id) $classStudents = ClassStudent::where('class_room_id', $this->teacherSubject->class_id)
->where('academic_year_id', $this->teacherSubject->academic_year_id) ->where('academic_year_id', $this->teacherSubject->academic_year_id)
->with('student') ->with('student')
@ -150,57 +154,156 @@ class MultipleAssessments extends page
} }
} }
// Ambil tujuan pembelajaran tertinggi dari tabel assessment_learning_objectives
$highestObjectives = AssessmentLearningObjective::where('student_id', $student->id)
->where('teacher_subject_id', $this->teacherSubjectId)
->where('type', 'highest')
->pluck('learning_objective_id')
->toArray();
// Ambil tujuan pembelajaran lebih rendah dari tabel assessment_learning_objectives
$lowerObjectives = AssessmentLearningObjective::where('student_id', $student->id)
->where('teacher_subject_id', $this->teacherSubjectId)
->where('type', 'lower')
->pluck('learning_objective_id')
->toArray();
$result[] = [ $result[] = [
'id' => $student->id, 'id' => $student->id,
'name' => $student->full_name, 'name' => $student->full_name,
'nis' => $student->nis, 'nis' => $student->nis,
'score' => $existingAssessment ? $existingAssessment->score : null, 'nisn' => $student->nisn,
'gender' => $student->gender,
'birth_date' => $student->birth_date,
'score' => $existingAssessment ? $existingAssessment->score : 0,
// 'learning_objectives' => $existingAssessment && is_array($existingAssessment->learning_objectives)
// ? $existingAssessment->learning_objectives
// : [],
// 'learning_objectives_lower' => $existingAssessment && is_array($existingAssessment->learning_objectives_lower)
// ? $existingAssessment->learning_objectives_lower
// : [],
'learning_objectives' => $highestObjectives,
'learning_objectives_lower' => $lowerObjectives,
]; ];
} }
$this->students = $result; $this->students = $result;
} }
public function addLearningObjective(int $studentIndex, string $type = "highest"): void
{
if($type === "lower") {
if (!isset($this->students[$studentIndex]['learning_objectives_lower'])) {
$this->students[$studentIndex]['learning_objectives_lower'] = [];
}
$this->students[$studentIndex]['learning_objectives_lower'][] = null;
} else {
if (!isset($this->students[$studentIndex]['learning_objectives'])) {
$this->students[$studentIndex]['learning_objectives'] = [];
}
$this->students[$studentIndex]['learning_objectives'][] = null;
}
}
public function removeLearningObjective(int $studentIndex, int $objectiveIndex, string $type = "highest"): void
{
if ($type === "lower") {
if (isset($this->students[$studentIndex]['learning_objectives_lower']) && is_array($this->students[$studentIndex]['learning_objectives_lower'])) {
// Check if the specific objectiveIndex exists within the array
// This will be true for null values too, which is what we want for removal
if (array_key_exists($objectiveIndex, $this->students[$studentIndex]['learning_objectives_lower'])) {
unset($this->students[$studentIndex]['learning_objectives_lower'][$objectiveIndex]);
// Reindex the array to prevent gaps in keys, which can cause issues with Livewire
$this->students[$studentIndex]['learning_objectives_lower'] = array_values($this->students[$studentIndex]['learning_objectives_lower']);
}
}
} else {
if (isset($this->students[$studentIndex]['learning_objectives']) && is_array($this->students[$studentIndex]['learning_objectives'])) {
// Check if the specific objectiveIndex exists within the array
// This will be true for null values too, which is what we want for removal
if (array_key_exists($objectiveIndex, $this->students[$studentIndex]['learning_objectives'])) {
unset($this->students[$studentIndex]['learning_objectives'][$objectiveIndex]);
// Reindex the array to prevent gaps in keys, which can cause issues with Livewire
$this->students[$studentIndex]['learning_objectives'] = array_values($this->students[$studentIndex]['learning_objectives']);
}
}
}
}
public function submit(): void public function submit(): void
{ {
if (!$this->teacherSubjectId || empty($this->students)) { if (!$this->teacherSubjectId || empty($this->students)) {
Notification::make() Notification::make()
->title('Error') ->title('Error')
->body('Please select a teacher subject and enter student scores.') ->body('Pilih guru per-mapel dan masukkan nilai siswa.')
->danger() ->danger()
->send(); ->send();
return; return;
} }
foreach ($this->students as $student) { foreach ($this->students as $student) {
if (!isset($student['score'])) continue; if (isset($student['score']) || !empty(array_filter($student['learning_objectives'] ?? [])) || !empty(array_filter($student['learning_objectives_lower'] ?? []))) {
Assessment::updateOrCreate(
[
'student_id' => $student['id'],
'teacher_subject_id' => $this->teacherSubjectId,
'semester' => $this->semester,
],
[
'score' => $student['score'] ?? 0,
]
);
}
Assessment::updateOrCreate( AssessmentLearningObjective::where('student_id', $student['id'])
[ ->where('teacher_subject_id', $this->teacherSubjectId)
'student_id' => $student['id'], ->where('type', 'highest')
'teacher_subject_id' => $this->teacherSubjectId, ->delete();
],
[ if (!empty($student['learning_objectives'])) {
'score' => $student['score'], foreach (array_filter($student['learning_objectives']) as $loId) {
'semester' => $this->semester, AssessmentLearningObjective::create([
] 'student_id' => $student['id'],
); 'teacher_subject_id' => $this->teacherSubjectId,
'learning_objective_id' => $loId,
'type' => 'highest',
]);
}
}
AssessmentLearningObjective::where('student_id', $student['id'])
->where('teacher_subject_id', $this->teacherSubjectId)
->where('type', 'lower')
->delete();
if (!empty($student['learning_objectives_lower'])) {
foreach (array_filter($student['learning_objectives_lower']) as $loId) { // Filter nulls
AssessmentLearningObjective::create([
'student_id' => $student['id'],
'teacher_subject_id' => $this->teacherSubjectId,
'learning_objective_id' => $loId,
'type' => 'lower',
]);
}
}
} }
Notification::make() Notification::make()
->title('Success') ->title('Berhasil')
->body('Assessments saved successfully.') ->body('Penilaian dan Tujuan Pembelajaran berhasil disimpan.')
->success() ->success()
->send(); ->send();
} }
protected function getHeaderActions(): array protected function getHeaderActions(): array
{ {
return [ return [
Actions\Action::make('back') Actions\Action::make('back')
->label('Back to Assessments') ->label('Kembali ke halaman assessment')
->url(static::getResource()::getUrl('index')), ->url(static::getResource()::getUrl('index')),
]; ];
} }
} }

View File

@ -137,34 +137,34 @@ class AttendancesResource extends Resource
return $table return $table
->columns([ ->columns([
Tables\Columns\TextColumn::make('student.full_name') Tables\Columns\TextColumn::make('student.full_name')
->label('Student') ->label('Siswa')
->searchable() ->searchable()
->sortable(), ->sortable(),
Tables\Columns\TextColumn::make('teacherSubject.class.class_name') Tables\Columns\TextColumn::make('teacherSubject.class.class_name')
->label('Class') ->label('Kelas')
->searchable(), ->searchable(),
Tables\Columns\TextColumn::make('teacherSubject.subject.name') Tables\Columns\TextColumn::make('teacherSubject.subject.name')
->label('Subject') ->label('Mapel')
->searchable(), ->searchable(),
Tables\Columns\TextColumn::make('teacherSubject.teacher.name') Tables\Columns\TextColumn::make('teacherSubject.teacher.name')
->label('Teacher') ->label('Guru')
->searchable(), ->searchable(),
Tables\Columns\TextColumn::make('date') Tables\Columns\TextColumn::make('date')
->label('Date') ->label('Tanggal')
->date('d/m/Y') ->date('d/m/Y')
->sortable(), ->sortable(),
Tables\Columns\BadgeColumn::make('status') Tables\Columns\BadgeColumn::make('status')
->label('Status') ->label('Status')
->formatStateUsing(fn (string $state): string => match ($state) { ->formatStateUsing(fn (string $state): string => match ($state) {
'present' => 'Present', 'present' => 'Hadir',
'absent' => 'Absent', 'absent' => 'Tidak Hadir',
'permission' => 'Permission', 'permission' => 'Izin',
'sick' => 'Sick' 'sick' => 'Sakit'
}) })
->color(fn (string $state): string => match ($state) { ->color(fn (string $state): string => match ($state) {
'present' => 'success', 'present' => 'success',
@ -176,12 +176,12 @@ class AttendancesResource extends Resource
Tables\Columns\TextColumn::make('semester') Tables\Columns\TextColumn::make('semester')
->label('Semester') ->label('Semester')
->formatStateUsing(fn (string $state): string => match ($state) { ->formatStateUsing(fn (string $state): string => match ($state) {
'first' => 'First Semester', 'first' => 'Ganjil',
'second' => 'Second Semester' 'second' => 'Genap'
}), }),
Tables\Columns\TextColumn::make('teacherSubject.academicYear.name') Tables\Columns\TextColumn::make('teacherSubject.academicYear.name')
->label('Academic Year') ->label('Tahun Ajaran')
->searchable(), ->searchable(),
Tables\Columns\TextColumn::make('recorder.name') Tables\Columns\TextColumn::make('recorder.name')
@ -192,20 +192,21 @@ class AttendancesResource extends Resource
Tables\Filters\SelectFilter::make('status') Tables\Filters\SelectFilter::make('status')
->label('Status') ->label('Status')
->options([ ->options([
'present' => 'Present', 'present' => 'Hadir',
'absent' => 'Absent', 'absent' => 'Tidak Hadir',
'permission' => 'Permission', 'permission' => 'Izin',
'sick' => 'Sick' 'sick' => 'Sakit'
]), ]),
Tables\Filters\SelectFilter::make('semester') Tables\Filters\SelectFilter::make('semester')
->label('Semester') ->label('Semester')
->options([ ->options([
'first' => 'First Semester', 'first' => 'Ganjil',
'second' => 'Second Semester' 'second' => 'Genap'
]), ]),
Tables\Filters\Filter::make('date') Tables\Filters\Filter::make('date')
->label('Tanggal')
->form([ ->form([
Forms\Components\DatePicker::make('date_from') Forms\Components\DatePicker::make('date_from')
->label('From'), ->label('From'),
@ -225,7 +226,7 @@ class AttendancesResource extends Resource
}), }),
Tables\Filters\SelectFilter::make('teacher_subject_id') Tables\Filters\SelectFilter::make('teacher_subject_id')
->label('Teacher Subject') ->label('Guru per-Mapel')
->relationship('teacherSubject', 'id') ->relationship('teacherSubject', 'id')
->searchable() ->searchable()
->getOptionLabelFromRecordUsing(fn (TeacherSubject $record) => ->getOptionLabelFromRecordUsing(fn (TeacherSubject $record) =>
@ -243,17 +244,13 @@ class AttendancesResource extends Resource
Tables\Actions\DeleteBulkAction::make()->visible(fn () => auth()->user()->hasRole('admin') || auth()->user()->hasRole('teacher')), Tables\Actions\DeleteBulkAction::make()->visible(fn () => auth()->user()->hasRole('admin') || auth()->user()->hasRole('teacher')),
]), ]),
]) ])
->defaultSort('date', 'desc') // ->defaultSort('teacherSubject.academicYear.name', 'desc')
->groups([ ->groups([
Tables\Grouping\Group::make('date')
->label('Date')
->date()
->collapsible(),
Tables\Grouping\Group::make('teacherSubject.subject.name') Tables\Grouping\Group::make('teacherSubject.subject.name')
->label('Subjects') ->label('Mapel')
->collapsible(), ->collapsible(),
Tables\Grouping\Group::make('semester') Tables\Grouping\Group::make('teacherSubject.class.class_name')
->label('Semester') ->label('Kelas')
->collapsible(), ->collapsible(),
]); ]);
} }
@ -274,4 +271,19 @@ class AttendancesResource extends Resource
'multiple' => Pages\MultipleAttendances::route('/multiple'), 'multiple' => Pages\MultipleAttendances::route('/multiple'),
]; ];
} }
public static function getNavigationLabel(): string
{
return 'Absensi';
}
public static function getBreadcrumb(): string
{
return 'Absensi';
}
public static function getPluralModelLabel(): string
{
return 'Absensi';
}
} }

View File

@ -12,6 +12,11 @@ class CreateAttendances extends CreateRecord
{ {
protected static string $resource = AttendancesResource::class; protected static string $resource = AttendancesResource::class;
public function getTitle(): string
{
return 'Tambah Absensi';
}
protected function mutateFormDataBeforeCreate(array $data): array protected function mutateFormDataBeforeCreate(array $data): array
{ {
$exists = Assessment::where('teacher_subject_id', $data['teacher_subject_id']) $exists = Assessment::where('teacher_subject_id', $data['teacher_subject_id'])

View File

@ -13,6 +13,11 @@ class EditAttendances extends EditRecord
{ {
protected static string $resource = AttendancesResource::class; protected static string $resource = AttendancesResource::class;
public function getTitle(): string
{
return 'Edit Absensi';
}
protected function mutateFormDataBeforeSave(array $data): array protected function mutateFormDataBeforeSave(array $data): array
{ {
$exists = Attendances::where('teacher_subject_id', $data['teacher_subject_id']) $exists = Attendances::where('teacher_subject_id', $data['teacher_subject_id'])

View File

@ -13,6 +13,11 @@ class ListAttendances extends ListRecords
{ {
protected static string $resource = AttendancesResource::class; protected static string $resource = AttendancesResource::class;
public function getTitle(): string
{
return 'Daftar Absensi';
}
protected function getTableQuery(): ?Builder protected function getTableQuery(): ?Builder
{ {
$user = auth()->user(); $user = auth()->user();
@ -34,9 +39,9 @@ class ListAttendances extends ListRecords
protected function getHeaderActions(): array protected function getHeaderActions(): array
{ {
return [ return [
Actions\CreateAction::make()->visible(fn () => auth()->user()->hasRole('admin')), // Actions\CreateAction::make()->visible(fn () => auth()->user()->hasRole('admin')),
Actions\Action::make('multiple') Actions\Action::make('multiple')
->label('Multiple Attendance') ->label('Tambah Absensi')
->url('attendances/multiple') ->url('attendances/multiple')
->icon('heroicon-o-user-group') ->icon('heroicon-o-user-group')
->visible(fn () => auth()->user()->hasRole('admin') || auth()->user()->hasRole('teacher')), ->visible(fn () => auth()->user()->hasRole('admin') || auth()->user()->hasRole('teacher')),

View File

@ -47,7 +47,7 @@ class MultipleAttendances extends Page
return $form return $form
->schema([ ->schema([
Select::make('teacher_subject_id') Select::make('teacher_subject_id')
->label('Teacher Subject') ->label('Guru per-Mapel')
->required() ->required()
->options(function () { ->options(function () {
$user = auth()->user(); $user = auth()->user();
@ -84,7 +84,7 @@ class MultipleAttendances extends Page
}), }),
DatePicker::make('date') DatePicker::make('date')
->label('Attendance Date') ->label('Tanggal Absensi')
->required() ->required()
->default(now()) ->default(now())
->live() ->live()
@ -97,8 +97,8 @@ class MultipleAttendances extends Page
->label('Semester') ->label('Semester')
->required() ->required()
->options([ ->options([
'first' => 'First Semester', 'first' => 'Ganjil',
'second' => 'Second Semester' 'second' => 'Genap'
]) ])
->live() ->live()
->afterStateUpdated(function ($state) { ->afterStateUpdated(function ($state) {
@ -195,7 +195,7 @@ class MultipleAttendances extends Page
}); });
Notification::make() Notification::make()
->title('Attendance saved successfully ' . $this->teacherSubject->id) ->title('Absensi berhasil disimpan!')
->success() ->success()
->send(); ->send();
@ -206,7 +206,7 @@ class MultipleAttendances extends Page
{ {
return [ return [
Actions\Action::make('back') Actions\Action::make('back')
->label('Back to Attendances') ->label('Kembali ke halaman absensi')
->url(static::getResource()::getUrl('index')), ->url(static::getResource()::getUrl('index')),
]; ];
} }

View File

@ -105,4 +105,19 @@ class ClassRoomResource extends Resource
'edit' => Pages\EditClassRoom::route('/{record}/edit'), 'edit' => Pages\EditClassRoom::route('/{record}/edit'),
]; ];
} }
public static function getNavigationLabel(): string
{
return 'Kelas';
}
public static function getBreadcrumb(): string
{
return 'Kelas';
}
public static function getPluralModelLabel(): string
{
return 'Kelas';
}
} }

View File

@ -9,4 +9,9 @@ use Filament\Resources\Pages\CreateRecord;
class CreateClassRoom extends CreateRecord class CreateClassRoom extends CreateRecord
{ {
protected static string $resource = ClassRoomResource::class; protected static string $resource = ClassRoomResource::class;
public function getTitle(): string
{
return 'Tambah Kelas';
}
} }

View File

@ -10,6 +10,10 @@ class EditClassRoom extends EditRecord
{ {
protected static string $resource = ClassRoomResource::class; protected static string $resource = ClassRoomResource::class;
public function getTitle(): string
{
return 'Edit Kelas';
}
protected function getHeaderActions(): array protected function getHeaderActions(): array
{ {
return [ return [

View File

@ -10,6 +10,11 @@ class ListClassRooms extends ListRecords
{ {
protected static string $resource = ClassRoomResource::class; protected static string $resource = ClassRoomResource::class;
public function getTitle(): string
{
return 'Daftar Kelas';
}
protected function getHeaderActions(): array protected function getHeaderActions(): array
{ {
return [ return [

View File

@ -111,4 +111,19 @@ class ClassStudentResource extends Resource
'edit' => Pages\EditClassStudent::route('/{record}/edit'), 'edit' => Pages\EditClassStudent::route('/{record}/edit'),
]; ];
} }
public static function getNavigationLabel(): string
{
return 'Kelas per-Siswa';
}
public static function getBreadcrumb(): string
{
return 'Kelas per-Siswa';
}
public static function getPluralModelLabel(): string
{
return 'Kelas per-Siswa';
}
} }

View File

@ -13,6 +13,11 @@ class CreateClassStudent extends CreateRecord
{ {
protected static string $resource = ClassStudentResource::class; protected static string $resource = ClassStudentResource::class;
public function getTitle(): string
{
return 'Tambah Kelas per-Siswa';
}
protected function mutateFormDataBeforeCreate(array $data): array protected function mutateFormDataBeforeCreate(array $data): array
{ {
$exists = ClassStudent::where('class_room_id', $data['class_room_id']) $exists = ClassStudent::where('class_room_id', $data['class_room_id'])

View File

@ -12,6 +12,11 @@ class EditClassStudent extends EditRecord
{ {
protected static string $resource = ClassStudentResource::class; protected static string $resource = ClassStudentResource::class;
public function getTitle(): string
{
return 'Edit Kelas per-Siswa';
}
protected function mutateFormDataBeforeSave(array $data): array protected function mutateFormDataBeforeSave(array $data): array
{ {
$exists = ClassStudent::where('class_room_id', $data['class_room_id']) $exists = ClassStudent::where('class_room_id', $data['class_room_id'])

View File

@ -10,6 +10,11 @@ class ListClassStudents extends ListRecords
{ {
protected static string $resource = ClassStudentResource::class; protected static string $resource = ClassStudentResource::class;
public function getTitle(): string
{
return 'Daftar Kelas per-Siswa';
}
protected function getHeaderActions(): array protected function getHeaderActions(): array
{ {
return [ return [

View File

@ -80,4 +80,19 @@ class ClassSubjectResource extends Resource
'edit' => Pages\EditClassSubject::route('/{record}/edit'), 'edit' => Pages\EditClassSubject::route('/{record}/edit'),
]; ];
} }
public static function getNavigationLabel(): string
{
return 'Mata Pelajaran per-Kelas';
}
public static function getBreadcrumb(): string
{
return 'Mata Pelajaran per-Kelas';
}
public static function getPluralModelLabel(): string
{
return 'Mata Pelajaran per-Kelas';
}
} }

View File

@ -12,6 +12,11 @@ class CreateClassSubject extends CreateRecord
{ {
protected static string $resource = ClassSubjectResource::class; protected static string $resource = ClassSubjectResource::class;
public function getTitle(): string
{
return 'Tambah Mata Pelajaran per-Kelas';
}
protected function mutateFormDataBeforeCreate(array $data): array protected function mutateFormDataBeforeCreate(array $data): array
{ {
$exists = ClassSubject::where('class_room_id', $data['class_room_id']) $exists = ClassSubject::where('class_room_id', $data['class_room_id'])

View File

@ -10,6 +10,11 @@ class EditClassSubject extends EditRecord
{ {
protected static string $resource = ClassSubjectResource::class; protected static string $resource = ClassSubjectResource::class;
public function getTitle(): string
{
return 'Edit Mata Pelajaran per-Kelas';
}
protected function getHeaderActions(): array protected function getHeaderActions(): array
{ {
return [ return [

View File

@ -10,6 +10,11 @@ class ListClassSubjects extends ListRecords
{ {
protected static string $resource = ClassSubjectResource::class; protected static string $resource = ClassSubjectResource::class;
public function getTitle(): string
{
return 'Daftar Mata Pelajaran per-Kelas';
}
protected function getHeaderActions(): array protected function getHeaderActions(): array
{ {
return [ return [

View File

@ -124,4 +124,9 @@ class CompetencyAchievementResource extends Resource
'edit' => Pages\EditCompetencyAchievement::route('/{record}/edit'), 'edit' => Pages\EditCompetencyAchievement::route('/{record}/edit'),
]; ];
} }
public static function shouldRegisterNavigation(): bool
{
return false;
}
} }

View File

@ -148,4 +148,47 @@ class ExtracurricularAssessmentResource extends Resource
'edit' => Pages\EditExtracurricularAssessment::route('/{record}/edit'), 'edit' => Pages\EditExtracurricularAssessment::route('/{record}/edit'),
]; ];
} }
public static function canAccess(): bool
{
$user = auth()->user();
if ($user->hasRole('parent')) {
return true;
}
$isHomeRoomTeacher = HomeRoomTeacher::where('teacher_id', $user->id)->exists();
return $user->can('page_SchoolInformation') || $isHomeRoomTeacher;
}
public static function shouldRegisterNavigation(): bool
{
$user = auth()->user();
if ($user->hasRole('parent')) {
return true;
}
// Untuk teacher (wali kelas)
$isHomeRoomTeacher = HomeRoomTeacher::where('teacher_id', $user->id)->exists();
// Atau punya permission khusus
return $user->can('page_SchoolInformation') || $isHomeRoomTeacher;
}
public static function getNavigationLabel(): string
{
return 'Penilaian Ektrakurikuler';
}
public static function getBreadcrumb(): string
{
return 'Penilaian Ektrakurikuler';
}
public static function getPluralModelLabel(): string
{
return 'Penilaian Ektrakurikuler';
}
} }

View File

@ -9,4 +9,9 @@ use Filament\Resources\Pages\CreateRecord;
class CreateExtracurricularAssessment extends CreateRecord class CreateExtracurricularAssessment extends CreateRecord
{ {
protected static string $resource = ExtracurricularAssessmentResource::class; protected static string $resource = ExtracurricularAssessmentResource::class;
public function getTitle(): string
{
return 'Tambah Penilaian Ektrakurikuler';
}
} }

View File

@ -10,6 +10,11 @@ class EditExtracurricularAssessment extends EditRecord
{ {
protected static string $resource = ExtracurricularAssessmentResource::class; protected static string $resource = ExtracurricularAssessmentResource::class;
public function getTitle(): string
{
return 'Edit Penilaian Ektrakurikuler';
}
protected function getHeaderActions(): array protected function getHeaderActions(): array
{ {
return [ return [

View File

@ -13,6 +13,11 @@ class ListExtracurricularAssessments extends ListRecords
{ {
protected static string $resource = ExtracurricularAssessmentResource::class; protected static string $resource = ExtracurricularAssessmentResource::class;
public function getTitle(): string
{
return 'Daftar Penilaian Ektrakurikuler';
}
protected function getTableQuery() : Builder protected function getTableQuery() : Builder
{ {
$user = auth()->user(); $user = auth()->user();

View File

@ -24,12 +24,12 @@ class ExtracurricularResource extends Resource
return $form return $form
->schema([ ->schema([
Forms\Components\TextInput::make('name') Forms\Components\TextInput::make('name')
->label('Name') ->label('Nama')
->required() ->required()
->maxLength(255), ->maxLength(255),
Forms\Components\Textarea::make('description') Forms\Components\Textarea::make('description')
->label('Description') ->label('Deskripsi')
->maxLength(1000) ->maxLength(1000)
->rows(4), ->rows(4),
]); ]);
@ -40,12 +40,12 @@ class ExtracurricularResource extends Resource
return $table return $table
->columns([ ->columns([
Tables\Columns\TextColumn::make('name') Tables\Columns\TextColumn::make('name')
->label('Name') ->label('Nama')
->searchable() ->searchable()
->sortable(), ->sortable(),
Tables\Columns\TextColumn::make('description') Tables\Columns\TextColumn::make('description')
->label('Description') ->label('Deskripsi')
->limit(50), ->limit(50),
Tables\Columns\TextColumn::make('created_at') Tables\Columns\TextColumn::make('created_at')
@ -53,7 +53,6 @@ class ExtracurricularResource extends Resource
->dateTime('d M Y'), ->dateTime('d M Y'),
]) ])
->filters([ ->filters([
// Tambahkan filter jika perlu
]) ])
->actions([ ->actions([
Tables\Actions\EditAction::make(), Tables\Actions\EditAction::make(),
@ -82,4 +81,19 @@ class ExtracurricularResource extends Resource
'edit' => Pages\EditExtracurricular::route('/{record}/edit'), 'edit' => Pages\EditExtracurricular::route('/{record}/edit'),
]; ];
} }
public static function getNavigationLabel(): string
{
return 'Ektrakurikuler';
}
public static function getBreadcrumb(): string
{
return 'Ektrakurikuler';
}
public static function getPluralModelLabel(): string
{
return 'Ektrakurikuler';
}
} }

View File

@ -9,4 +9,9 @@ use Filament\Resources\Pages\CreateRecord;
class CreateExtracurricular extends CreateRecord class CreateExtracurricular extends CreateRecord
{ {
protected static string $resource = ExtracurricularResource::class; protected static string $resource = ExtracurricularResource::class;
public function getTitle(): string
{
return 'Tambah Ektrakuriler';
}
} }

View File

@ -10,6 +10,11 @@ class EditExtracurricular extends EditRecord
{ {
protected static string $resource = ExtracurricularResource::class; protected static string $resource = ExtracurricularResource::class;
public function getTitle(): string
{
return 'Edit Ektrakuriler';
}
protected function getHeaderActions(): array protected function getHeaderActions(): array
{ {
return [ return [

View File

@ -10,10 +10,15 @@ class ListExtracurriculars extends ListRecords
{ {
protected static string $resource = ExtracurricularResource::class; protected static string $resource = ExtracurricularResource::class;
public function getTitle(): string
{
return 'Daftar Ektrakuriler';
}
protected function getHeaderActions(): array protected function getHeaderActions(): array
{ {
return [ return [
Actions\CreateAction::make(), Actions\CreateAction::make()->label('Tambah Ekstrakurikuler'),
]; ];
} }
} }

View File

@ -108,4 +108,19 @@ class HomeRoomTeacherResource extends Resource
'edit' => Pages\EditHomeRoomTeacher::route('/{record}/edit'), 'edit' => Pages\EditHomeRoomTeacher::route('/{record}/edit'),
]; ];
} }
public static function getNavigationLabel(): string
{
return 'Guru Wali Kelas';
}
public static function getBreadcrumb(): string
{
return 'Guru Wali Kelas';
}
public static function getPluralModelLabel(): string
{
return 'Guru Wali Kelas';
}
} }

View File

@ -13,6 +13,11 @@ class CreateHomeRoomTeacher extends CreateRecord
{ {
protected static string $resource = HomeRoomTeacherResource::class; protected static string $resource = HomeRoomTeacherResource::class;
public function getTitle(): string
{
return 'Tambah Guru Wali Kelas';
}
protected function mutateFormDataBeforeCreate(array $data): array protected function mutateFormDataBeforeCreate(array $data): array
{ {
$exists = HomeRoomTeacher::where('class_room_id', $data['class_room_id']) $exists = HomeRoomTeacher::where('class_room_id', $data['class_room_id'])

View File

@ -13,6 +13,12 @@ class EditHomeRoomTeacher extends EditRecord
{ {
protected static string $resource = HomeRoomTeacherResource::class; protected static string $resource = HomeRoomTeacherResource::class;
public function getTitle(): string
{
return 'Edit Guru Wali Kelas';
}
protected function mutateFormDataBeforeSave(array $data): array protected function mutateFormDataBeforeSave(array $data): array
{ {
$exists = HomeRoomTeacher::where('class_room_id', $data['class_room_id']) $exists = HomeRoomTeacher::where('class_room_id', $data['class_room_id'])

View File

@ -10,6 +10,11 @@ class ListHomeRoomTeachers extends ListRecords
{ {
protected static string $resource = HomeRoomTeacherResource::class; protected static string $resource = HomeRoomTeacherResource::class;
public function getTitle(): string
{
return 'Daftar Guru Wali Kelas';
}
protected function getHeaderActions(): array protected function getHeaderActions(): array
{ {
return [ return [

View File

@ -0,0 +1,148 @@
<?php
namespace App\Filament\Resources;
use App\Filament\Resources\LearningObjectiveResource\Pages;
use App\Filament\Resources\LearningObjectiveResource\RelationManagers;
use App\Models\ClassRoom; // Import ClassRoom model
use App\Models\LearningObjective;
use App\Models\Subject; // Import Subject model
use Filament\Forms;
use Filament\Forms\Components\Select;
use Filament\Forms\Form;
use Filament\Resources\Resource;
use Filament\Tables;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\SoftDeletingScope;
class LearningObjectiveResource extends Resource
{
protected static ?string $model = LearningObjective::class;
protected static ?string $navigationIcon = 'heroicon-o-document-text';
protected static ?string $navigationGroup = 'Academic Setup';
public static function form(Form $form): Form
{
return $form
->schema([
Select::make('class_room_id')
->label('Class Room')
->required()
->options(ClassRoom::pluck('class_name', 'id')->toArray())
->searchable()
->native(false),
Select::make('subject_id')
->label('Mata Pelajaran')
->relationship('subject', 'name')
->required(),
// Textarea for description
Forms\Components\Textarea::make('description')
->rows(5)
->cols(10)
->required()
->maxLength(65535), // Max length for TEXT type in database
]);
}
public static function table(Table $table): Table
{
return $table
->columns([
// Display related Class Room name
Tables\Columns\TextColumn::make('class.class_name')
->label('Kelas')
->searchable()
->sortable(),
// Display related Subject name
Tables\Columns\TextColumn::make('subject.name')
->label('Mapel')
->searchable()
->sortable(),
// Display description, truncate for brevity in table
Tables\Columns\TextColumn::make('description')
->label('Deskripsi')
->searchable()
->limit(50) // Limit text length in table
->tooltip(fn (LearningObjective $record): string => $record->description) // Show full text on hover
->sortable(),
// Standard timestamp columns
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), // Show deleted_at if soft deletes are used
])
->filters([
// TrashedFilter works with SoftDeletes trait on the model
Tables\Filters\TrashedFilter::make(),
])
->actions([
Tables\Actions\EditAction::make(),
Tables\Actions\DeleteAction::make(), // For soft deleting records
Tables\Actions\RestoreAction::make(), // For restoring soft-deleted records
Tables\Actions\ForceDeleteAction::make(), // For permanently deleting records
])
->bulkActions([
Tables\Actions\BulkActionGroup::make([
Tables\Actions\DeleteBulkAction::make(),
Tables\Actions\ForceDeleteBulkAction::make(),
Tables\Actions\RestoreBulkAction::make(),
]),
]);
}
public static function getRelations(): array
{
return [
// Define any relation managers here if needed
];
}
public static function getPages(): array
{
return [
'index' => Pages\ListLearningObjectives::route('/'),
'create' => Pages\CreateLearningObjective::route('/create'),
'edit' => Pages\EditLearningObjective::route('/{record}/edit'),
];
}
public static function getEloquentQuery(): Builder
{
return parent::getEloquentQuery()
->withoutGlobalScopes([
SoftDeletingScope::class,
]);
}
public static function getNavigationLabel(): string
{
return 'Tujuan Pembelajaran';
}
public static function getBreadcrumb(): string
{
return 'Tujuan Pembelajaran';
}
public static function getPluralModelLabel(): string
{
return 'Tujuan Pembelajaran';
}
}

View File

@ -0,0 +1,17 @@
<?php
namespace App\Filament\Resources\LearningObjectiveResource\Pages;
use App\Filament\Resources\LearningObjectiveResource;
use Filament\Actions;
use Filament\Resources\Pages\CreateRecord;
class CreateLearningObjective extends CreateRecord
{
protected static string $resource = LearningObjectiveResource::class;
public function getTitle(): string
{
return 'Tambah Tujuan Pembelajaran Baru';
}
}

View File

@ -0,0 +1,26 @@
<?php
namespace App\Filament\Resources\LearningObjectiveResource\Pages;
use App\Filament\Resources\LearningObjectiveResource;
use Filament\Actions;
use Filament\Resources\Pages\EditRecord;
class EditLearningObjective extends EditRecord
{
protected static string $resource = LearningObjectiveResource::class;
public function getTitle(): string
{
return 'Edit Tujuan Pembelajaran';
}
protected function getHeaderActions(): array
{
return [
Actions\DeleteAction::make(),
Actions\ForceDeleteAction::make(),
Actions\RestoreAction::make(),
];
}
}

View File

@ -0,0 +1,46 @@
<?php
namespace App\Filament\Resources\LearningObjectiveResource\Pages;
use App\Filament\Resources\LearningObjectiveResource;
use App\Models\Attendances;
use App\Models\HomeRoomTeacher;
use App\Models\LearningObjective;
use App\Models\TeacherSubject;
use Filament\Actions;
use Filament\Resources\Pages\ListRecords;
use Illuminate\Database\Eloquent\Builder;
class ListLearningObjectives extends ListRecords
{
protected static string $resource = LearningObjectiveResource::class;
protected function getTableQuery(): ?Builder
{
$user = auth()->user();
if ($user->hasRole('teacher')) {
$homeRoomClassIds = HomeRoomTeacher::where('teacher_id', $user->id)
->pluck('class_room_id')
->toArray();
return LearningObjective::with(["subject", "class"])
->whereIn('class_room_id', $homeRoomClassIds);
}
return LearningObjective::query();
}
protected function getHeaderActions(): array
{
return [
Actions\CreateAction::make()->label('Tambah Tujuan Pembelajaran Baru'),
];
}
public function getTitle(): string
{
return 'Daftar Tujuan Pembelajaran';
}
}

View File

@ -36,12 +36,12 @@ class StudentResource extends Resource
->maxLength(20), ->maxLength(20),
Forms\Components\TextInput::make('full_name') Forms\Components\TextInput::make('full_name')
->label('Full Name') ->label('Nama Lengkap')
->required() ->required()
->maxLength(100), ->maxLength(100),
Forms\Components\Select::make('gender') Forms\Components\Select::make('gender')
->label('Gender') ->label('Jenis Kelamin')
->options([ ->options([
'L' => 'L', 'L' => 'L',
'P' => 'P', 'P' => 'P',
@ -49,36 +49,36 @@ class StudentResource extends Resource
->required(), ->required(),
Forms\Components\DatePicker::make('birth_date') Forms\Components\DatePicker::make('birth_date')
->label('Birth Date') ->label('Tanggal Lahir')
->required(), ->required(),
Forms\Components\TextInput::make('birth_place') Forms\Components\TextInput::make('birth_place')
->label('Birth Place') ->label('Tempat Lahir')
->required() ->required()
->maxLength(100), ->maxLength(100),
Forms\Components\TextInput::make('phone') Forms\Components\TextInput::make('phone')
->label('Phone') ->label('Nomor Telepon')
->maxLength(15), ->maxLength(15),
Forms\Components\TextInput::make('email') Forms\Components\TextInput::make('email')
->label("Parent's Email") ->label("Email Orang Tua")
->email() ->email()
->required() ->required()
->maxLength(100), ->maxLength(100),
Forms\Components\TextInput::make('parent_name') Forms\Components\TextInput::make('parent_name')
->label("Parent's Name") ->label("Nama Orang Tua")
->required() ->required()
->maxLength(100), ->maxLength(100),
Forms\Components\TextInput::make('parent_phone') Forms\Components\TextInput::make('parent_phone')
->label("Parent's Phone") ->label("Nomor Telepon Orang Tua")
->required() ->required()
->maxLength(15), ->maxLength(15),
Forms\Components\Select::make('religion') Forms\Components\Select::make('religion')
->label('Religion') ->label('Agama')
->options([ ->options([
'islam' => 'Islam', 'islam' => 'Islam',
'hindu' => 'Hindu', 'hindu' => 'Hindu',
@ -88,7 +88,7 @@ class StudentResource extends Resource
]), ]),
Forms\Components\Textarea::make('address') Forms\Components\Textarea::make('address')
->label('Address') ->label('Alamat')
->rows(1), ->rows(1),
]) ])
@ -104,21 +104,21 @@ class StudentResource extends Resource
->label('NIS') ->label('NIS')
->searchable(), ->searchable(),
Tables\Columns\TextColumn::make('full_name') Tables\Columns\TextColumn::make('full_name')
->label('Name') ->label('Nama')
->searchable() ->searchable()
->sortable(), ->sortable(),
Tables\Columns\TextColumn::make('gender') Tables\Columns\TextColumn::make('gender')
->label('Gender') ->label('Jenis Kelamin')
->sortable(), ->sortable(),
Tables\Columns\TextColumn::make('birth_place') Tables\Columns\TextColumn::make('birth_place')
->label('Birth Place'), ->label('Tempat Lahir'),
Tables\Columns\TextColumn::make('birth_date') Tables\Columns\TextColumn::make('birth_date')
->label('Birth Date') ->label('Tanggal Lahir')
->date(), ->date(),
Tables\Columns\TextColumn::make('parent_name') Tables\Columns\TextColumn::make('parent_name')
->label("Parent's Name"), ->label("Nama Orang Tua"),
Tables\Columns\TextColumn::make('religion') Tables\Columns\TextColumn::make('religion')
->label("Religion") ->label("Agama")
->formatStateUsing(fn (string $state): string => strtoupper($state)) ->formatStateUsing(fn (string $state): string => strtoupper($state))
->badge(), ->badge(),
Tables\Columns\TextColumn::make('created_at') Tables\Columns\TextColumn::make('created_at')
@ -158,4 +158,19 @@ class StudentResource extends Resource
'edit' => Pages\EditStudent::route('/{record}/edit'), 'edit' => Pages\EditStudent::route('/{record}/edit'),
]; ];
} }
public static function getNavigationLabel(): string
{
return 'Siswa';
}
public static function getBreadcrumb(): string
{
return 'Siswa';
}
public static function getPluralModelLabel(): string
{
return 'Siswa';
}
} }

View File

@ -15,6 +15,11 @@ class CreateStudent extends CreateRecord
{ {
protected static string $resource = StudentResource::class; protected static string $resource = StudentResource::class;
public function getTitle(): string
{
return 'Tambah Siswa';
}
protected function mutateFormDataBeforeCreate(array $data): array protected function mutateFormDataBeforeCreate(array $data): array
{ {
$parentRole = Role::where('name', 'parent')->first(); $parentRole = Role::where('name', 'parent')->first();

View File

@ -4,6 +4,7 @@ namespace App\Filament\Resources\StudentResource\Pages;
use App\Filament\Resources\StudentResource; use App\Filament\Resources\StudentResource;
use App\Models\User; use App\Models\User;
use Carbon\Carbon;
use Filament\Actions; use Filament\Actions;
use Filament\Notifications\Notification; use Filament\Notifications\Notification;
use Filament\Resources\Pages\EditRecord; use Filament\Resources\Pages\EditRecord;
@ -13,16 +14,31 @@ class EditStudent extends EditRecord
{ {
protected static string $resource = StudentResource::class; protected static string $resource = StudentResource::class;
public function getTitle(): string
{
return 'Edit Siswa';
}
protected function mutateFormDataBeforeSave(array $data): array protected function mutateFormDataBeforeSave(array $data): array
{ {
$parentRole = Role::where('name', 'parent')->first(); $parentRole = Role::where('name', 'parent')->first();
$birthDate = Carbon::parse($data['birth_date']);
$password = $birthDate->format('dmY') . $data['nis'];
$user = User::where('email', $data['email'])->first();
$dataSubmit = [
'name' => $data['parent_name'],
'phone' => $data['parent_phone'],
];
if (!$user) {
$dataSubmit['password'] = bcrypt($password);
}
$parentUser = User::updateOrCreate( $parentUser = User::updateOrCreate(
['email' => $data['email']], ['email' => $data['email']],
[ $dataSubmit,
'name' => $data['parent_name'],
'phone' => $data['parent_phone'],
]
); );
// Pastikan user memiliki role parent // Pastikan user memiliki role parent

View File

@ -11,6 +11,10 @@ class ListStudents extends ListRecords
{ {
protected static string $resource = StudentResource::class; protected static string $resource = StudentResource::class;
public function getTitle(): string
{
return 'Daftar Siswa';
}
protected function getHeaderActions(): array protected function getHeaderActions(): array
{ {
return [ return [

View File

@ -26,13 +26,16 @@ class SubjectResource extends Resource
return $form return $form
->schema([ ->schema([
Forms\Components\TextInput::make('name') Forms\Components\TextInput::make('name')
->label('Nama Mapel')
->required() ->required()
->maxLength(255), ->maxLength(255),
Forms\Components\Toggle::make('is_religious') Forms\Components\Toggle::make('is_religious')
->label('Religious')
->required(), ->required(),
Forms\Components\Select::make('category') Forms\Components\Select::make('category')
->label('Kategori')
->options([ ->options([
'umum' => 'Umum', 'umum' => 'Umum',
'muatan lokal' => 'Muatan Lokal', 'muatan lokal' => 'Muatan Lokal',
@ -48,13 +51,13 @@ class SubjectResource extends Resource
return $table return $table
->columns([ ->columns([
Tables\Columns\TextColumn::make('name') Tables\Columns\TextColumn::make('name')
->label("Name") ->label("Nama")
->searchable(), ->searchable(),
Tables\Columns\IconColumn::make('is_religious') Tables\Columns\IconColumn::make('is_religious')
->label("Is Religious") ->label("Is Religious")
->boolean(), ->boolean(),
Tables\Columns\TextColumn::make('category') Tables\Columns\TextColumn::make('category')
->label("Category") ->label("Kategori")
->sortable(), ->sortable(),
Tables\Columns\TextColumn::make('created_at') Tables\Columns\TextColumn::make('created_at')
->dateTime() ->dateTime()
@ -96,4 +99,19 @@ class SubjectResource extends Resource
'edit' => Pages\EditSubject::route('/{record}/edit'), 'edit' => Pages\EditSubject::route('/{record}/edit'),
]; ];
} }
public static function getNavigationLabel(): string
{
return 'Mata Pelajaran';
}
public static function getBreadcrumb(): string
{
return 'Mata Pelajaran';
}
public static function getPluralModelLabel(): string
{
return 'Mata Pelajaran';
}
} }

View File

@ -9,4 +9,10 @@ use Filament\Resources\Pages\CreateRecord;
class CreateSubject extends CreateRecord class CreateSubject extends CreateRecord
{ {
protected static string $resource = SubjectResource::class; protected static string $resource = SubjectResource::class;
public function getTitle(): string
{
return 'Tambah Mata Pelajaran';
}
} }

View File

@ -10,6 +10,11 @@ class EditSubject extends EditRecord
{ {
protected static string $resource = SubjectResource::class; protected static string $resource = SubjectResource::class;
public function getTitle(): string
{
return 'Edit Mata Pelajaran';
}
protected function getHeaderActions(): array protected function getHeaderActions(): array
{ {
return [ return [

View File

@ -10,10 +10,15 @@ class ListSubjects extends ListRecords
{ {
protected static string $resource = SubjectResource::class; protected static string $resource = SubjectResource::class;
public function getTitle(): string
{
return 'Daftar Mata Pelajaran';
}
protected function getHeaderActions(): array protected function getHeaderActions(): array
{ {
return [ return [
Actions\CreateAction::make(), Actions\CreateAction::make()->label('Tambah Mata Pelajaran'),
]; ];
} }
} }

View File

@ -143,4 +143,19 @@ class TeacherSubjectResource extends Resource
'edit' => Pages\EditTeacherSubject::route('/{record}/edit'), 'edit' => Pages\EditTeacherSubject::route('/{record}/edit'),
]; ];
} }
public static function getNavigationLabel(): string
{
return 'Guru per-Mapel';
}
public static function getBreadcrumb(): string
{
return 'Guru per-Mapel';
}
public static function getPluralModelLabel(): string
{
return 'Guru per-Mapel';
}
} }

View File

@ -12,6 +12,10 @@ class CreateTeacherSubject extends CreateRecord
{ {
protected static string $resource = TeacherSubjectResource::class; protected static string $resource = TeacherSubjectResource::class;
public function getTitle(): string
{
return 'Tambah Guru per-Mapel';
}
protected function mutateFormDataBeforeCreate(array $data): array protected function mutateFormDataBeforeCreate(array $data): array
{ {
$exists = TeacherSubject::where('teacher_id', $data['teacher_id']) $exists = TeacherSubject::where('teacher_id', $data['teacher_id'])

View File

@ -10,6 +10,11 @@ class EditTeacherSubject extends EditRecord
{ {
protected static string $resource = TeacherSubjectResource::class; protected static string $resource = TeacherSubjectResource::class;
public function getTitle(): string
{
return 'Edit Guru per-Mapel';
}
protected function getHeaderActions(): array protected function getHeaderActions(): array
{ {
return [ return [

View File

@ -10,6 +10,11 @@ class ListTeacherSubjects extends ListRecords
{ {
protected static string $resource = TeacherSubjectResource::class; protected static string $resource = TeacherSubjectResource::class;
public function getTitle(): string
{
return 'Daftar Guru per-Mapel';
}
protected function getHeaderActions(): array protected function getHeaderActions(): array
{ {
return [ return [

View File

@ -27,10 +27,12 @@ class UserResource extends Resource
return $form return $form
->schema([ ->schema([
Forms\Components\TextInput::make('name') Forms\Components\TextInput::make('name')
->label('Nama Pengguna')
->required() ->required()
->maxLength(255), ->maxLength(255),
Forms\Components\TextInput::make('email') Forms\Components\TextInput::make('email')
->label('Email')
->email() ->email()
->required() ->required()
->maxLength(255), ->maxLength(255),
@ -43,6 +45,7 @@ class UserResource extends Resource
->maxLength(255), ->maxLength(255),
Forms\Components\Select::make('role') Forms\Components\Select::make('role')
->label('Role')
->relationship('roles', 'name') ->relationship('roles', 'name')
->preload() ->preload()
->searchable() ->searchable()
@ -53,18 +56,22 @@ class UserResource extends Resource
->maxLength(255), ->maxLength(255),
Forms\Components\Select::make('gender') Forms\Components\Select::make('gender')
->label('Jenis Kelamin')
->options([ ->options([
'L' => 'L', 'L' => 'L',
'P' => 'P', 'P' => 'P',
]), ]),
Forms\Components\DatePicker::make('birth_date'), Forms\Components\DatePicker::make('birth_date')
->label('Tanggal Lahir'),
Forms\Components\TextInput::make('phone') Forms\Components\TextInput::make('phone')
->label('Nomor Telepon')
->tel() ->tel()
->maxLength(255), ->maxLength(255),
Forms\Components\Textarea::make('address') Forms\Components\Textarea::make('address')
->label('Alamat')
->columnSpanFull(), ->columnSpanFull(),
]); ]);
} }
@ -74,17 +81,22 @@ class UserResource extends Resource
return $table return $table
->columns([ ->columns([
Tables\Columns\TextColumn::make('name') Tables\Columns\TextColumn::make('name')
->label('Nama')
->searchable(), ->searchable(),
Tables\Columns\TextColumn::make('email') Tables\Columns\TextColumn::make('email')
->label('Email')
->searchable(), ->searchable(),
Tables\Columns\TextColumn::make('nip') Tables\Columns\TextColumn::make('nip')
->label('NIP') ->label('NIP')
->searchable(), ->searchable(),
Tables\Columns\TextColumn::make('gender') Tables\Columns\TextColumn::make('gender')
->label('Jenis Kelamin')
->formatStateUsing(fn (string $state): string => ucfirst($state)), ->formatStateUsing(fn (string $state): string => ucfirst($state)),
Tables\Columns\TextColumn::make('birth_date') Tables\Columns\TextColumn::make('birth_date')
->label('Tanggal Lahir')
->date(), ->date(),
Tables\Columns\TextColumn::make('phone') Tables\Columns\TextColumn::make('phone')
->label('Nomor Telepon')
->searchable(), ->searchable(),
Tables\Columns\TextColumn::make('roles.name') Tables\Columns\TextColumn::make('roles.name')
->searchable() ->searchable()
@ -129,4 +141,19 @@ class UserResource extends Resource
'edit' => Pages\EditUser::route('/{record}/edit'), 'edit' => Pages\EditUser::route('/{record}/edit'),
]; ];
} }
public static function getNavigationLabel(): string
{
return 'Pengguna';
}
public static function getBreadcrumb(): string
{
return 'Pengguna';
}
public static function getPluralModelLabel(): string
{
return 'Pengguna';
}
} }

View File

@ -9,4 +9,9 @@ use Filament\Resources\Pages\CreateRecord;
class CreateUser extends CreateRecord class CreateUser extends CreateRecord
{ {
protected static string $resource = UserResource::class; protected static string $resource = UserResource::class;
public function getTitle(): string
{
return 'Tambah Penilaian';
}
} }

View File

@ -16,4 +16,9 @@ class EditUser extends EditRecord
Actions\DeleteAction::make(), Actions\DeleteAction::make(),
]; ];
} }
public function getTitle(): string
{
return 'Edit Pengguna';
}
} }

View File

@ -10,10 +10,15 @@ class ListUsers extends ListRecords
{ {
protected static string $resource = UserResource::class; protected static string $resource = UserResource::class;
public function getTitle(): string
{
return 'Daftar Pengguna';
}
protected function getHeaderActions(): array protected function getHeaderActions(): array
{ {
return [ return [
Actions\CreateAction::make(), Actions\CreateAction::make()->label('Tambah Pengguna'),
]; ];
} }
} }

View File

@ -4,6 +4,7 @@ namespace App\Http\Controllers;
use App\Models\AcademicYear; use App\Models\AcademicYear;
use App\Models\Assessment; use App\Models\Assessment;
use App\Models\AssessmentLearningObjective;
use App\Models\ClassRoom; use App\Models\ClassRoom;
use App\Models\CompetencyAchievement; use App\Models\CompetencyAchievement;
use App\Models\ExtracurricularAssessment; use App\Models\ExtracurricularAssessment;
@ -105,17 +106,32 @@ class ReportPDFController extends Controller
return null; return null;
} }
$competencyAchievements = CompetencyAchievement::where('class_room_id', $class_id) $assesmentLearningObjectivesLower = AssessmentLearningObjective::with('learningObjective')
->where('subject_id', $subject->id) ->where('teacher_subject_id', $as->teacherSubject->id)
->where('min_score', '<=', $as->score) ->where('student_id', $this->student->id)
->where('max_score', '>=', $as->score) ->where('type', 'lower')
->first(); ->get()
->pluck('learningObjective.description')
->toArray();
$joinedStringLower = $as->student->full_name . ' perlu peningkatan dalam ' . implode(', ', $assesmentLearningObjectivesLower);
$assesmentLearningObjectivesHighest = AssessmentLearningObjective::with('learningObjective')
->where('teacher_subject_id', $as->teacherSubject->id)
->where('student_id', $this->student->id)
->where('type', 'highest')
->get()
->pluck('learningObjective.description')
->toArray();
$joinedStringHighest = $as->student->full_name . ' menunjukan pemahaman dalam ' . implode(', ', $assesmentLearningObjectivesHighest);
return [ return [
"score" => $as->score, "score" => $as->score,
"subject" => $subject->name, "subject" => $subject->name,
"category" => $subject->category, "category" => $subject->category,
"competency_achievement" => $competencyAchievements ? $this->student->full_name . ' ' . $competencyAchievements->description : '-', "lower" => $assesmentLearningObjectivesLower ? $joinedStringLower : null,
"highest" => $assesmentLearningObjectivesHighest ? $joinedStringHighest : null,
]; ];
}) })
->filter() // Hapus null jika ada ->filter() // Hapus null jika ada
@ -160,10 +176,11 @@ class ReportPDFController extends Controller
$homeRoom = HomeRoomTeacher::with(['teacher']) $homeRoom = HomeRoomTeacher::with(['teacher'])
->where('class_room_id', $this->class->id) ->where('class_room_id', $this->class->id)
->where('academic_year_id', $this->academic_year->id) ->where('academic_year_id', $this->academic_year->id)
->firstOrFail() ->first();
->toArray();
$this->home_room_teacher = $homeRoom ?? []; if ($homeRoom) {
$this->home_room_teacher = $homeRoom ?? [];
}
} }
protected function validateParameters(array $params, array $allowedParams): void protected function validateParameters(array $params, array $allowedParams): void

View File

@ -0,0 +1,31 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class AssessmentLearningObjective extends Model
{
protected $fillable = [
'teacher_subject_id',
'student_id',
'learning_objective_id',
'type',
];
public function teacherSubject(): BelongsTo
{
return $this->belongsTo(TeacherSubject::class);
}
public function student(): BelongsTo
{
return $this->belongsTo(Student::class);
}
public function learningObjective(): BelongsTo
{
return $this->belongsTo(LearningObjective::class);
}
}

View File

@ -25,6 +25,11 @@ class ClassRoom extends Model
return $this->hasMany(Student::class); return $this->hasMany(Student::class);
} }
public function learningObjectives()
{
return $this->hasMany(LearningObjective::class);
}
public function teacherAssignments(): HasMany public function teacherAssignments(): HasMany
{ {
return $this->hasMany(TeacherSubject::class); return $this->hasMany(TeacherSubject::class);

View File

@ -0,0 +1,30 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\SoftDeletes;
class LearningObjective extends Model
{
use SoftDeletes;
protected $fillable = ['class_room_id', 'subject_id', 'description'];
public function subject(): BelongsTo
{
return $this->belongsTo(Subject::class, 'subject_id');
}
public function class(): BelongsTo
{
return $this->belongsTo(ClassRoom::class, 'class_room_id');
}
public function assessmentLearningObjective()
{
return $this->hasMany(AssessmentLearningObjective::class);
}
}

View File

@ -30,4 +30,9 @@ class Student extends Model
{ {
return $this->hasMany(ClassStudent::class); return $this->hasMany(ClassStudent::class);
} }
public function assessmentLearningObjective()
{
return $this->hasMany(AssessmentLearningObjective::class);
}
} }

View File

@ -14,6 +14,11 @@ class Subject extends Model
return $this->hasMany(SubjectScope::class); return $this->hasMany(SubjectScope::class);
} }
public function learningObjectives()
{
return $this->hasMany(LearningObjective::class);
}
public function teacherAssignments(): HasMany public function teacherAssignments(): HasMany
{ {
return $this->hasMany(TeacherSubject::class); return $this->hasMany(TeacherSubject::class);

View File

@ -30,6 +30,11 @@ class TeacherSubject extends Model
return $this->hasMany(Attendances::class); return $this->hasMany(Attendances::class);
} }
public function assessmentLearningObjective()
{
return $this->hasMany(AssessmentLearningObjective::class);
}
public function academicYear() : BelongsTo public function academicYear() : BelongsTo
{ {
return $this->belongsTo(AcademicYear::class, 'academic_year_id'); return $this->belongsTo(AcademicYear::class, 'academic_year_id');

View File

@ -0,0 +1,66 @@
<?php
namespace App\Policies;
use App\Models\LearningObjective;
use App\Models\User;
use Illuminate\Auth\Access\Response;
class LearningObjectivePolicy
{
/**
* Determine whether the user can view any models.
*/
public function viewAny(User $user): bool
{
return $user->can('view_any_learning::objective');
}
/**
* Determine whether the user can view the model.
*/
public function view(User $user, LearningObjective $learningObjective): bool
{
return $user->can('view_learning::objective');
}
/**
* Determine whether the user can create models.
*/
public function create(User $user): bool
{
return $user->can('create_learning::objective');
}
/**
* Determine whether the user can update the model.
*/
public function update(User $user, LearningObjective $learningObjective): bool
{
return $user->can('update_learning::objective');
}
/**
* Determine whether the user can delete the model.
*/
public function delete(User $user, LearningObjective $learningObjective): bool
{
return $user->can('delete_learning::objective');
}
/**
* Determine whether the user can restore the model.
*/
public function restore(User $user, LearningObjective $learningObjective): bool
{
return $user->can('restore_learning::objective');
}
/**
* Determine whether the user can permanently delete the model.
*/
public function forceDelete(User $user, LearningObjective $learningObjective): bool
{
return $user->can('force_delete_learning::objective');
}
}

View File

@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('learning_objectives', function (Blueprint $table) {
$table->id();
$table->foreignId('class_room_id')->constrained('class_rooms')->cascadeOnDelete();
$table->foreignId('subject_id')->constrained('subjects')->cascadeOnDelete();
$table->text('description');
$table->timestamps();
$table->softDeletes();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('learning_objectives');
}
};

View File

@ -0,0 +1,31 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('assessment_learning_objectives', function (Blueprint $table) {
$table->id();
$table->foreignId('teacher_subject_id')->constrained('teacher_subjects')->onDelete('cascade');
$table->foreignId('student_id')->constrained('students')->onDelete('cascade');
$table->foreignId('learning_objective_id')->constrained('learning_objectives')->onDelete('cascade');
$table->enum('type', ['highest', 'lower']);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('assessment_learning_objectives');
}
};

View File

@ -16,11 +16,11 @@ class DatabaseSeeder extends Seeder
SubjectSeeder::class, SubjectSeeder::class,
ClassRoomSeeder::class, ClassRoomSeeder::class,
AcademicYearSeeder::class, AcademicYearSeeder::class,
StudentSeeder::class, // StudentSeeder::class,
ExtracurricularSeeder::class, ExtracurricularSeeder::class,
ClassSubjectSeeder::class, // ClassSubjectSeeder::class,
ClassStudentSeeder::class, // ClassStudentSeeder::class,
HomeRoomTeacherSeeder::class, // HomeRoomTeacherSeeder::class,
SchoolInformationSeeder::class, SchoolInformationSeeder::class,
]); ]);
} }

File diff suppressed because one or more lines are too long

View File

@ -47,10 +47,21 @@
@if(!empty($this->table['assessments']['umum'])) @if(!empty($this->table['assessments']['umum']))
@foreach($this->table['assessments']['umum'] as $subjects => $subject) @foreach($this->table['assessments']['umum'] as $subjects => $subject)
<tr> <tr>
<td class="border border-black text-center">{{ $i }}</td> <td class="border border-black text-center" rowspan="2">{{ $i }}</td>
<td class="border border-black">{{ $subject["subject"] }}</td> <td class="border border-black" rowspan="2">{{ $subject["subject"] }}</td>
<td class="border border-black text-center">{{ $subject["score"] }}</td> <td class="border border-black text-center" rowspan="2">{{ $subject["score"] }}</td>
<td class="border border-black">{{ $subject["competency_achievement"] }}</td> <td class="border border-black p-2">
@if(!empty($subject["highest"]))
{{ $subject["highest"] }}
@endif
</td>
</tr>
<tr>
<td class="border border-black p-2">
@if(!empty($subject["lower"]))
{{ $subject["lower"] }}
@endif
</td>
</tr> </tr>
@php @php
$i++; $i++;
@ -67,7 +78,18 @@
<td class="border border-black text-center">a</td> <td class="border border-black text-center">a</td>
<td class="border border-black">{{ $subject["subject"] }}</td> <td class="border border-black">{{ $subject["subject"] }}</td>
<td class="border border-black text-center">{{ $subject["score"] }}</td> <td class="border border-black text-center">{{ $subject["score"] }}</td>
<td class="border border-black">{{ $subject["competency_achievement"] }}</td> <td class="border border-black">
@if(!empty($subject["highest"]))
<div class="border border-black p-2">
{{ $subject["highest"] }}
</div>
@endif
@if(!empty($subject["lower"]))
<div class="border border-black p-2">
{{ $subject["lower"] }}
</div>
@endif
</td>
</tr> </tr>
@php @php
$i++; $i++;
@ -96,7 +118,18 @@
<td class="border border-black text-center">{{ $i }}</td> <td class="border border-black text-center">{{ $i }}</td>
<td class="border border-black">{{ $subject["subject"] }}</td> <td class="border border-black">{{ $subject["subject"] }}</td>
<td class="border border-black text-center">{{ $subject["score"] }}</td> <td class="border border-black text-center">{{ $subject["score"] }}</td>
<td class="border border-black">{{ $subject["competency_achievement"] }}</td> <td class="border border-black">
@if(!empty($subject["highest"]))
<div class="border border-black p-2">
{{ $subject["highest"] }}
</div>
@endif
@if(!empty($subject["lower"]))
<div class="border border-black p-2">
{{ $subject["lower"] }}
</div>
@endif
</td>
</tr> </tr>
@php @php
$i++; $i++;

View File

@ -7,7 +7,7 @@
type="submit" type="submit"
class="inline-flex items-center px-4 py-2 bg-primary-600 text-white rounded-md hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-primary-500" class="inline-flex items-center px-4 py-2 bg-primary-600 text-white rounded-md hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-primary-500"
> >
Save Simpan
</button> </button>
</div> </div>
</form> </form>

View File

@ -4,24 +4,123 @@
@if($this->teacherSubjectId) @if($this->teacherSubjectId)
<div class="mt-6 space-y-4"> <div class="mt-6 space-y-4">
<h3 class="text-lg font-medium">Multiple Assessment</h3>
<div class="space-y-2"> <div class="space-y-2">
@foreach($this->students as $index => $student) @foreach($this->students as $index => $student)
<div class="flex items-center p-3 border rounded-lg"> {{-- Each student card is now an accordion item --}}
<div class="flex-1"> <div x-data="{ open: false }" class="border rounded-lg overflow-hidden shadow-sm">
<p class="font-medium">{{ $student['name'] ?? "" }}</p> {{-- Accordion Header: Displays essential student info and acts as toggle --}}
<p class="text-sm text-gray-500">{{ $student['nis'] ?? "" }}</p> <div @click="open = !open" class="flex items-center p-3 cursor-pointer bg-gray-50 hover:bg-gray-100 transition duration-150 ease-in-out">
<div class="w-full flex">
<div class="flex-1 grid grid-cols-1 xl:grid-cols-4 gap-4">
<div class="flex flex-col">
<p class="text-xs text-gray-500">NISN</p>
<p class="font-medium text-sm">{{ $student['nisn'] ?? "" }}</p>
</div>
<div class="flex flex-col">
<p class="text-xs text-gray-500">NIS</p>
<p class="font-medium text-sm">{{ $student['nis'] ?? "" }}</p>
</div>
<div class="flex flex-col col-span-1">
<p class="text-xs text-gray-500">Nama</p>
<p class="font-medium text-sm">{{ $student['name'] ?? "" }}</p>
</div>
<div class="flex flex-col">
<p class="text-xs text-gray-500">Jenis Kelamin</p>
<p class="font-medium text-sm">{{ $student['gender'] === "L" ? "Laki-laki" : "Perempuan" ?? "" }}</p>
</div>
</div>
<div class="w-32"> {{-- Make input full width on small screens, then fixed width --}}
<x-filament::input
type="number"
:value="$student['score'] ?? null"
wire:model.defer="students.{{ $index }}.score"
placeholder="Nilai"
min="0"
max="100"
class="text-base" {{-- Adjust input text size --}}
/>
</div>
</div>
{{-- Chevron icon to indicate expand/collapse state --}}
<x-heroicon-s-chevron-down class="w-5 h-5 text-gray-500 transform transition-transform duration-200" x-bind:class="{ 'rotate-180': open }" />
</div> </div>
<div class="w-32">
<x-filament::input {{-- Accordion Body: Contains the score input field --}}
type="number" <div x-show="open" x-collapse.duration.300ms class="p-3 bg-white border-t border-gray-200">
:value="$student['score'] ?? null" <div class="w-full flex flex-col gap-2">
wire:model.defer="students.{{ $index }}.score" <span class="text-lg font-bold">Input Penilaian Sumatif</span>
placeholder="Score" <div class="w-full flex flex-col gap-2">
min="0" <div class="w-full flex justify-between items-center">
max="100" <span class="text-sm">Tujuan Pembelajaran Tertinggi</span>
/> <x-filament::button type="button" color="primary" wire:click="addLearningObjective({{ $index }})">
Add
</x-filament::button>
</div>
@foreach($student['learning_objectives'] as $objectiveIndex => $objectiveId)
<div class="w-full flex gap-8 mt-3 items-center">
<x-filament::input.wrapper class="w-full">
<x-filament::input.select wire:model="students.{{ $index }}.learning_objectives.{{ $objectiveIndex }}">
<option value="">Pilih Tujuan Pembelajaran</option>
@foreach($this->learningObjectivesList as $idLearningObjective => $description)
{{-- Check if this option is already selected by ANY of the student's learning objective inputs --}}
@php
$isAlreadySelected = in_array($idLearningObjective, $student['learning_objectives']);
$isCurrentOption = ($objectiveId == $idLearningObjective);
@endphp
<option
value="{{ $idLearningObjective }}"
@if($isCurrentOption) selected @endif
@if($isAlreadySelected && !$isCurrentOption) disabled @endif
>
{{ $description }}
</option>
@endforeach
</x-filament::input.select>
</x-filament::input.wrapper>
<x-filament::button type="button" color="danger" wire:click="removeLearningObjective({{ $index }}, {{ $objectiveIndex }})">
Delete
</x-filament::button>
</div>
@endforeach
</div>
<div class="w-full flex flex-col gap-2 mt-3">
<div class="w-full flex justify-between items-center">
<span class="text-sm">Tujuan Pembelajaran Terendah</span>
<x-filament::button type="button" color="primary" wire:click="addLearningObjective({{ $index }}, 'lower')">
Add
</x-filament::button>
</div>
@foreach($student['learning_objectives_lower'] as $objectiveIndex => $objectiveId)
<div class="w-full flex gap-8 mt-3 items-center">
<x-filament::input.wrapper class="w-full">
<x-filament::input.select wire:model="students.{{ $index }}.learning_objectives_lower.{{ $objectiveIndex }}">
<option value="">Pilih Tujuan Pembelajaran</option>
@foreach($this->learningObjectivesList as $idLearningObjective => $description)
{{-- Check if this option is already selected by ANY of the student's learning objective inputs --}}
@php
$isAlreadySelected = in_array($idLearningObjective, $student['learning_objectives_lower']);
$isCurrentOption = ($objectiveId == $idLearningObjective);
@endphp
<option
value="{{ $idLearningObjective }}"
@if($isCurrentOption) selected @endif
@if($isAlreadySelected && !$isCurrentOption) disabled @endif
>
{{ $description }}
</option>
@endforeach
</x-filament::input.select>
</x-filament::input.wrapper>
<x-filament::button type="button" color="danger" wire:click="removeLearningObjective({{ $index }}, {{ $objectiveIndex }}, 'lower')">
Delete
</x-filament::button>
</div>
@endforeach
</div>
</div>
</div> </div>
</div> </div>
@endforeach @endforeach
@ -29,7 +128,7 @@
<div class="mt-4"> <div class="mt-4">
<x-filament::button type="submit" color="primary"> <x-filament::button type="submit" color="primary">
Save Assessments Simpan
</x-filament::button> </x-filament::button>
</div> </div>
</div> </div>

View File

@ -4,20 +4,19 @@
@if($this->attendanceDate && $this->teacherSubjectId) @if($this->attendanceDate && $this->teacherSubjectId)
<div class="mt-6 space-y-4"> <div class="mt-6 space-y-4">
<div class="flex items-center justify-between"> <div class="flex items-center justify-end">
<h3 class="text-lg font-medium">Student Attendance</h3>
<div class="flex gap-2 space-x-2"> <div class="flex gap-2 space-x-2">
<x-filament::button type="button" wire:click="markAll('present')" color="success"> <x-filament::button type="button" wire:click="markAll('present')" color="success">
Mark All Present Tandai Semua Hadir
</x-filament::button> </x-filament::button>
<x-filament::button type="button" wire:click="markAll('absent')" color="danger"> <x-filament::button type="button" wire:click="markAll('absent')" color="danger">
Mark All Absent Tandai Semua Tidak Hadir
</x-filament::button> </x-filament::button>
<x-filament::button type="button" wire:click="markAll('sick')" color="warning"> <x-filament::button type="button" wire:click="markAll('sick')" color="warning">
Mark All Sick Tandai Semua Sakit
</x-filament::button> </x-filament::button>
<x-filament::button type="button" wire:click="markAll('permission')" color="warning"> <x-filament::button type="button" wire:click="markAll('permission')" color="info">
Mark All Permission Tandai Semua Izin
</x-filament::button> </x-filament::button>
</div> </div>
</div> </div>
@ -36,7 +35,7 @@
color="{{ $student['status'] === 'present' ? 'success' : 'gray' }}" color="{{ $student['status'] === 'present' ? 'success' : 'gray' }}"
size="sm" size="sm"
> >
Present Hadir
</x-filament::button> </x-filament::button>
<x-filament::button <x-filament::button
type="button" type="button"
@ -44,7 +43,7 @@
color="{{ $student['status'] === 'absent' ? 'danger' : 'gray' }}" color="{{ $student['status'] === 'absent' ? 'danger' : 'gray' }}"
size="sm" size="sm"
> >
Absent Tidak Hadir
</x-filament::button> </x-filament::button>
<x-filament::button <x-filament::button
type="button" type="button"
@ -52,7 +51,7 @@
color="{{ $student['status'] === 'sick' ? 'warning' : 'gray' }}" color="{{ $student['status'] === 'sick' ? 'warning' : 'gray' }}"
size="sm" size="sm"
> >
Sick Sakit
</x-filament::button> </x-filament::button>
<x-filament::button <x-filament::button
type="button" type="button"
@ -60,7 +59,7 @@
color="{{ $student['status'] === 'permission' ? 'warning' : 'gray' }}" color="{{ $student['status'] === 'permission' ? 'warning' : 'gray' }}"
size="sm" size="sm"
> >
Permission Izin
</x-filament::button> </x-filament::button>
</div> </div>
</div> </div>
@ -68,7 +67,7 @@
</div> </div>
<x-filament::button type="submit" class="w-full"> <x-filament::button type="submit" class="w-full">
Save Attendance Simpan Absensi
</x-filament::button> </x-filament::button>
</div> </div>
@endif @endif

View File

@ -34,12 +34,15 @@
border: 1px solid #000; border: 1px solid #000;
} }
td, th { td:not(.custom-cell), th {
padding: 6px; padding: 6px;
vertical-align: top; vertical-align: top;
} }
td.custom-cell {
padding: 0;
}
.no-border { .no-border {
border: none !important; border: none !important;
text-align: left; text-align: left;
@ -76,6 +79,14 @@
width: 200px; width: 200px;
margin-top: 30px; margin-top: 30px;
} }
.p-2 {
padding:.5rem
}
.capaian-kompetensi-cell div:not(:last-child) {
margin-bottom: 0.5em; /* Memberi sedikit jarak antar baris */
}
</style> </style>
</head> </head>
<body> <body>
@ -125,10 +136,23 @@
@if(!empty($table['assessments']['umum'])) @if(!empty($table['assessments']['umum']))
@foreach($table['assessments']['umum'] as $subjects => $subject) @foreach($table['assessments']['umum'] as $subjects => $subject)
<tr> <tr>
<td>{{ $i }}</td> <td rowspan="2">{{ $i }}</td>
<td>{{ $subject["subject"] }}</td> <td rowspan="2">{{ $subject["subject"] }}</td>
<td class="text-center">{{ $subject["score"] }}</td> <td rowspan="2" class="text-center">{{ $subject["score"] }}</td>
<td>{{ $subject["competency_achievement"] }}</td> <td class="">
@if(!empty($subject["highest"]))
<div class="p-2">
{{ $subject["highest"] }}
</div>
@endif
</td>
</tr>
<tr>
@if(!empty($subject["lower"]))
<div class="p-2">
{{ $subject["lower"] }}
</div>
@endif
</tr> </tr>
@php @php
$i++; $i++;
@ -147,7 +171,18 @@
<td>{{ chr(97 + $charIndex) }}</td> <td>{{ chr(97 + $charIndex) }}</td>
<td>{{ $subject["subject"] }}</td> <td>{{ $subject["subject"] }}</td>
<td class="text-center">{{ $subject["score"] }}</td> <td class="text-center">{{ $subject["score"] }}</td>
<td>{{ $subject["competency_achievement"] }}</td> <td class="custom-cell">
@if(!empty($subject["highest"]))
<div class="p-2">
{{ $subject["highest"] }}
</div>
@endif
@if(!empty($subject["lower"]))
<div class="p-2">
{{ $subject["lower"] }}
</div>
@endif
</td>
</tr> </tr>
@php @php
$i++; $i++;
@ -171,7 +206,18 @@
<td>{{ $i }}</td> <td>{{ $i }}</td>
<td>{{ $subject["subject"] }}</td> <td>{{ $subject["subject"] }}</td>
<td class="text-center">{{ $subject["score"] }}</td> <td class="text-center">{{ $subject["score"] }}</td>
<td>{{ $subject["competency_achievement"] }}</td> <td class="">
@if(!empty($subject["highest"]))
<div class="p-2">
{{ $subject["highest"] }}
</div>
@endif
@if(!empty($subject["lower"]))
<div class="p-2">
{{ $subject["lower"] }}
</div>
@endif
</td>
</tr> </tr>
@php @php
$i++; $i++;