add-attendaces
This commit is contained in:
parent
946006cfe6
commit
43344cb74b
235
app/Filament/Resources/AttendancesResource.php
Normal file
235
app/Filament/Resources/AttendancesResource.php
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources;
|
||||||
|
|
||||||
|
use App\Filament\Resources\AttendancesResource\Pages;
|
||||||
|
use App\Models\Attendances;
|
||||||
|
use App\Models\ClassRoom;
|
||||||
|
use App\Models\Student;
|
||||||
|
use App\Models\Subject;
|
||||||
|
use App\Models\TeacherSubject;
|
||||||
|
use App\Models\User;
|
||||||
|
use Filament\Forms;
|
||||||
|
use Filament\Forms\Form;
|
||||||
|
use Filament\Resources\Resource;
|
||||||
|
use Filament\Tables;
|
||||||
|
use Filament\Tables\Table;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
|
||||||
|
class AttendancesResource extends Resource
|
||||||
|
{
|
||||||
|
protected static ?string $model = Attendances::class;
|
||||||
|
protected static ?string $navigationIcon = 'heroicon-o-clipboard-document-check';
|
||||||
|
protected static ?string $navigationGroup = 'Student Management';
|
||||||
|
|
||||||
|
public static function form(Form $form): Form
|
||||||
|
{
|
||||||
|
return $form
|
||||||
|
->schema([
|
||||||
|
Forms\Components\Section::make('Informasi 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)
|
||||||
|
->searchable()
|
||||||
|
->preload()
|
||||||
|
->columnSpanFull(),
|
||||||
|
|
||||||
|
Forms\Components\DatePicker::make('date')
|
||||||
|
->label('Date')
|
||||||
|
->required()
|
||||||
|
->default(now())
|
||||||
|
->maxDate(now()),
|
||||||
|
|
||||||
|
Forms\Components\Select::make('student_id')
|
||||||
|
->label('Student')
|
||||||
|
->required()
|
||||||
|
->searchable()
|
||||||
|
->options(Student::pluck('full_name', 'id')->toArray())
|
||||||
|
->getOptionLabelUsing(function ($value) {
|
||||||
|
$student = Student::find($value);
|
||||||
|
return $student ? $student->full_name . ' (' . $student->nis . ')' : null;
|
||||||
|
}),
|
||||||
|
|
||||||
|
Forms\Components\Select::make('semester')
|
||||||
|
->label('Semester')
|
||||||
|
->required()
|
||||||
|
->options([
|
||||||
|
'first' => 'First Semester',
|
||||||
|
'second' => 'Second Semester'
|
||||||
|
]),
|
||||||
|
|
||||||
|
Forms\Components\Select::make('status')
|
||||||
|
->label('Status')
|
||||||
|
->required()
|
||||||
|
->options([
|
||||||
|
'present' => 'Present',
|
||||||
|
'absent' => 'Absent',
|
||||||
|
'permission' => 'Permission',
|
||||||
|
'sick' => 'Sick'
|
||||||
|
])
|
||||||
|
->native(false),
|
||||||
|
|
||||||
|
Forms\Components\Hidden::make('recorded_by')
|
||||||
|
->default(auth()->id()),
|
||||||
|
|
||||||
|
Forms\Components\Textarea::make('notes')
|
||||||
|
->label('Notes')
|
||||||
|
->columnSpanFull()
|
||||||
|
])->columns(2)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function table(Table $table): Table
|
||||||
|
{
|
||||||
|
return $table
|
||||||
|
->columns([
|
||||||
|
Tables\Columns\TextColumn::make('student.full_name')
|
||||||
|
->label('Student')
|
||||||
|
->searchable()
|
||||||
|
->sortable(),
|
||||||
|
|
||||||
|
Tables\Columns\TextColumn::make('teacherSubject.class.class_name')
|
||||||
|
->label('Class')
|
||||||
|
->searchable(),
|
||||||
|
|
||||||
|
Tables\Columns\TextColumn::make('teacherSubject.subject.name')
|
||||||
|
->label('Subject')
|
||||||
|
->searchable(),
|
||||||
|
|
||||||
|
Tables\Columns\TextColumn::make('teacherSubject.teacher.name')
|
||||||
|
->label('Teacher')
|
||||||
|
->searchable(),
|
||||||
|
|
||||||
|
Tables\Columns\TextColumn::make('date')
|
||||||
|
->label('Date')
|
||||||
|
->date('d/m/Y')
|
||||||
|
->sortable(),
|
||||||
|
|
||||||
|
Tables\Columns\BadgeColumn::make('status')
|
||||||
|
->label('Status')
|
||||||
|
->formatStateUsing(fn (string $state): string => match ($state) {
|
||||||
|
'present' => 'Present',
|
||||||
|
'absent' => 'Absent',
|
||||||
|
'permission' => 'Permission',
|
||||||
|
'sick' => 'Sick'
|
||||||
|
})
|
||||||
|
->color(fn (string $state): string => match ($state) {
|
||||||
|
'present' => 'success',
|
||||||
|
'absent' => 'danger',
|
||||||
|
'permission' => 'warning',
|
||||||
|
'sick' => 'warning'
|
||||||
|
}),
|
||||||
|
|
||||||
|
Tables\Columns\TextColumn::make('semester')
|
||||||
|
->label('Semester')
|
||||||
|
->formatStateUsing(fn (string $state): string => match ($state) {
|
||||||
|
'first' => 'First Semester',
|
||||||
|
'second' => 'Second Semester'
|
||||||
|
}),
|
||||||
|
|
||||||
|
Tables\Columns\TextColumn::make('teacherSubject.academic_year')
|
||||||
|
->label('Academic Year')
|
||||||
|
->searchable(),
|
||||||
|
|
||||||
|
Tables\Columns\TextColumn::make('recorder.name')
|
||||||
|
->label('Record by')
|
||||||
|
->toggleable(),
|
||||||
|
|
||||||
|
Tables\Columns\TextColumn::make('notes')
|
||||||
|
->label('Notes')
|
||||||
|
->toggleable()
|
||||||
|
])
|
||||||
|
->filters([
|
||||||
|
Tables\Filters\SelectFilter::make('status')
|
||||||
|
->label('Status')
|
||||||
|
->options([
|
||||||
|
'present' => 'Present',
|
||||||
|
'absent' => 'Absent',
|
||||||
|
'permission' => 'Permission',
|
||||||
|
'sick' => 'Sick'
|
||||||
|
]),
|
||||||
|
|
||||||
|
Tables\Filters\SelectFilter::make('semester')
|
||||||
|
->label('Semester')
|
||||||
|
->options([
|
||||||
|
'first' => 'First Semester',
|
||||||
|
'second' => 'Second Semester'
|
||||||
|
]),
|
||||||
|
|
||||||
|
Tables\Filters\Filter::make('date')
|
||||||
|
->form([
|
||||||
|
Forms\Components\DatePicker::make('date_from')
|
||||||
|
->label('From'),
|
||||||
|
Forms\Components\DatePicker::make('date_until')
|
||||||
|
->label('To'),
|
||||||
|
])
|
||||||
|
->query(function (Builder $query, array $data): Builder {
|
||||||
|
return $query
|
||||||
|
->when(
|
||||||
|
$data['date_from'],
|
||||||
|
fn (Builder $query, $date): Builder => $query->whereDate('date', '>=', $date),
|
||||||
|
)
|
||||||
|
->when(
|
||||||
|
$data['date_until'],
|
||||||
|
fn (Builder $query, $date): Builder => $query->whereDate('date', '<=', $date),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
|
||||||
|
Tables\Filters\SelectFilter::make('teacher_subject_id')
|
||||||
|
->label('Teacher Subject')
|
||||||
|
->relationship('teacherSubject', 'id')
|
||||||
|
->searchable()
|
||||||
|
->getOptionLabelFromRecordUsing(fn (TeacherSubject $record) =>
|
||||||
|
$record->teacher->name . ' - ' . $record->subject->name)
|
||||||
|
->preload(),
|
||||||
|
])
|
||||||
|
->actions([
|
||||||
|
Tables\Actions\EditAction::make()
|
||||||
|
->iconButton(),
|
||||||
|
Tables\Actions\DeleteAction::make()
|
||||||
|
->iconButton(),
|
||||||
|
])
|
||||||
|
->bulkActions([
|
||||||
|
Tables\Actions\BulkActionGroup::make([
|
||||||
|
Tables\Actions\DeleteBulkAction::make(),
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
->emptyStateActions([
|
||||||
|
Tables\Actions\CreateAction::make(),
|
||||||
|
])
|
||||||
|
->defaultSort('date', 'desc')
|
||||||
|
->groups([
|
||||||
|
Tables\Grouping\Group::make('date')
|
||||||
|
->label('Date')
|
||||||
|
->date()
|
||||||
|
->collapsible(),
|
||||||
|
Tables\Grouping\Group::make('teacherSubject.subject.name')
|
||||||
|
->label('Subjects')
|
||||||
|
->collapsible(),
|
||||||
|
Tables\Grouping\Group::make('semester')
|
||||||
|
->label('Semester')
|
||||||
|
->collapsible(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getRelations(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
//
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getPages(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'index' => Pages\ListAttendances::route('/'),
|
||||||
|
'create' => Pages\CreateAttendances::route('/create'),
|
||||||
|
'edit' => Pages\EditAttendances::route('/{record}/edit'),
|
||||||
|
'multiple' => Pages\MultipleAttendances::route('/multiple'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\AttendancesResource\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\AttendancesResource;
|
||||||
|
use Filament\Actions;
|
||||||
|
use Filament\Resources\Pages\CreateRecord;
|
||||||
|
|
||||||
|
class CreateAttendances extends CreateRecord
|
||||||
|
{
|
||||||
|
protected static string $resource = AttendancesResource::class;
|
||||||
|
}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\AttendancesResource\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\AttendancesResource;
|
||||||
|
use Filament\Actions;
|
||||||
|
use Filament\Resources\Pages\EditRecord;
|
||||||
|
|
||||||
|
class EditAttendances extends EditRecord
|
||||||
|
{
|
||||||
|
protected static string $resource = AttendancesResource::class;
|
||||||
|
|
||||||
|
protected function getHeaderActions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
Actions\DeleteAction::make(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\AttendancesResource\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\AttendancesResource;
|
||||||
|
use Filament\Actions;
|
||||||
|
use Filament\Resources\Pages\ListRecords;
|
||||||
|
|
||||||
|
class ListAttendances extends ListRecords
|
||||||
|
{
|
||||||
|
protected static string $resource = AttendancesResource::class;
|
||||||
|
|
||||||
|
protected function getHeaderActions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
Actions\CreateAction::make(),
|
||||||
|
Actions\Action::make('multiple')
|
||||||
|
->label('Multiple Attendance')
|
||||||
|
->url('attendances/multiple')
|
||||||
|
->icon('heroicon-o-user-group'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,167 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\AttendancesResource\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\AttendancesResource;
|
||||||
|
use App\Models\Attendances;
|
||||||
|
use App\Models\ClassRoom;
|
||||||
|
use App\Models\Student;
|
||||||
|
use App\Models\TeacherSubject;
|
||||||
|
use Filament\Actions;
|
||||||
|
use Filament\Forms\Components\DatePicker;
|
||||||
|
use Filament\Forms\Components\Select;
|
||||||
|
use Filament\Forms\Form;
|
||||||
|
use Filament\Notifications\Notification;
|
||||||
|
use Filament\Resources\Pages\Page;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
|
class MultipleAttendances extends Page
|
||||||
|
{
|
||||||
|
protected static string $resource = AttendancesResource::class;
|
||||||
|
protected static string $view = 'filament.resources.attendances-resource.pages.multiple-attendances';
|
||||||
|
|
||||||
|
public ?array $data = [];
|
||||||
|
public $classroomId;
|
||||||
|
public $teacherSubjectId;
|
||||||
|
|
||||||
|
public $teacherSubject;
|
||||||
|
public $attendanceDate;
|
||||||
|
public $semester = 'first'; // Default semester
|
||||||
|
public $students = [];
|
||||||
|
|
||||||
|
public function mount(): void
|
||||||
|
{
|
||||||
|
$this->form->fill([
|
||||||
|
'date' => now()->format('Y-m-d'),
|
||||||
|
'semester' => 'first',
|
||||||
|
]);
|
||||||
|
$this->attendanceDate = now()->format('Y-m-d');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function form(Form $form): Form
|
||||||
|
{
|
||||||
|
return $form
|
||||||
|
->schema([
|
||||||
|
Select::make('teacher_subject_id')
|
||||||
|
->label('Teacher Subject')
|
||||||
|
->required()
|
||||||
|
->options(TeacherSubject::with(['teacher', 'subject', 'class'])
|
||||||
|
->get()
|
||||||
|
->mapWithKeys(fn ($item) => [
|
||||||
|
$item->id => $item->teacher->name . ' - ' . $item->subject->name . ' - ' . $item->class->class_name
|
||||||
|
]))
|
||||||
|
->searchable()
|
||||||
|
->live()
|
||||||
|
->afterStateUpdated(function ($state) {
|
||||||
|
$this->teacherSubjectId = $state;
|
||||||
|
$this->loadStudents();
|
||||||
|
}),
|
||||||
|
|
||||||
|
DatePicker::make('date')
|
||||||
|
->label('Attendance Date')
|
||||||
|
->required()
|
||||||
|
->default(now())
|
||||||
|
->maxDate(now())
|
||||||
|
->live()
|
||||||
|
->afterStateUpdated(function ($state) {
|
||||||
|
$this->attendanceDate = $state;
|
||||||
|
$this->loadStudents();
|
||||||
|
}),
|
||||||
|
|
||||||
|
Select::make('semester')
|
||||||
|
->label('Semester')
|
||||||
|
->required()
|
||||||
|
->options([
|
||||||
|
'first' => 'First Semester',
|
||||||
|
'second' => 'Second Semester'
|
||||||
|
])
|
||||||
|
->live()
|
||||||
|
->afterStateUpdated(function ($state) {
|
||||||
|
$this->semester = $state;
|
||||||
|
$this->loadStudents();
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
->statePath('data');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function loadStudents(): void
|
||||||
|
{
|
||||||
|
if (!$this->attendanceDate || !$this->teacherSubjectId) {
|
||||||
|
$this->students = [];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$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();
|
||||||
|
|
||||||
|
return [
|
||||||
|
'id' => $student->id,
|
||||||
|
'name' => $student->full_name,
|
||||||
|
'nis' => $student->nis,
|
||||||
|
'status' => $existingAttendance ? $existingAttendance->status : null,
|
||||||
|
'attendance_id' => $existingAttendance ? $existingAttendance->id : null,
|
||||||
|
];
|
||||||
|
})
|
||||||
|
->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function markAll($status): void
|
||||||
|
{
|
||||||
|
foreach ($this->students as $key => $student) {
|
||||||
|
$this->students[$key]['status'] = $status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function submit(): void
|
||||||
|
{
|
||||||
|
DB::transaction(function () {
|
||||||
|
foreach ($this->students as $student) {
|
||||||
|
if ($student['status']) {
|
||||||
|
if ($student['attendance_id']) {
|
||||||
|
// Update existing attendance
|
||||||
|
Attendances::where('id', $student['attendance_id'])
|
||||||
|
->update([
|
||||||
|
'status' => $student['status'],
|
||||||
|
'recorded_by' => auth()->id(),
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
// Create new attendance
|
||||||
|
Attendances::create([
|
||||||
|
'student_id' => $student['id'],
|
||||||
|
'teacher_subject_id' => $this->teacherSubject->id,
|
||||||
|
'date' => $this->attendanceDate,
|
||||||
|
'status' => $student['status'],
|
||||||
|
'semester' => $this->semester,
|
||||||
|
'recorded_by' => auth()->id(),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Notification::make()
|
||||||
|
->title('Attendance saved successfully ' . $this->teacherSubject->id)
|
||||||
|
->success()
|
||||||
|
->send();
|
||||||
|
|
||||||
|
$this->loadStudents();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getHeaderActions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
Actions\Action::make('back')
|
||||||
|
->label('Back to Attendances')
|
||||||
|
->url(static::getResource()::getUrl('index')),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
143
app/Filament/Resources/TeacherSubjectResource.php
Normal file
143
app/Filament/Resources/TeacherSubjectResource.php
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources;
|
||||||
|
|
||||||
|
use App\Filament\Resources\TeacherSubjectResource\Pages;
|
||||||
|
use App\Models\ClassRoom;
|
||||||
|
use App\Models\Subject;
|
||||||
|
use App\Models\TeacherSubject;
|
||||||
|
use App\Models\User;
|
||||||
|
use Closure;
|
||||||
|
use Filament\Forms;
|
||||||
|
use Filament\Forms\Form;
|
||||||
|
use Filament\Resources\Resource;
|
||||||
|
use Filament\Tables;
|
||||||
|
use Filament\Tables\Table;
|
||||||
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
class TeacherSubjectResource extends Resource
|
||||||
|
{
|
||||||
|
protected static ?string $model = TeacherSubject::class;
|
||||||
|
|
||||||
|
protected static ?string $navigationIcon = 'heroicon-o-academic-cap';
|
||||||
|
protected static ?string $navigationGroup = 'Academic Management';
|
||||||
|
|
||||||
|
public static function form(Form $form): Form
|
||||||
|
{
|
||||||
|
return $form
|
||||||
|
->schema([
|
||||||
|
Forms\Components\Section::make('Teacher Subject Assignment')
|
||||||
|
->schema([
|
||||||
|
Forms\Components\Select::make('teacher_id')
|
||||||
|
->label('Teacher')
|
||||||
|
->required()
|
||||||
|
->options(User::role('teacher')->pluck('name', 'id')->toArray())
|
||||||
|
->searchable()
|
||||||
|
->native(false),
|
||||||
|
|
||||||
|
Forms\Components\Select::make('subject_id')
|
||||||
|
->label('Subject')
|
||||||
|
->required()
|
||||||
|
->options(Subject::pluck('name', 'id')->toArray())
|
||||||
|
->searchable()
|
||||||
|
->native(false),
|
||||||
|
|
||||||
|
Forms\Components\Select::make('class_id')
|
||||||
|
->label('Class')
|
||||||
|
->required()
|
||||||
|
->options(ClassRoom::pluck('class_name', 'id')->toArray())
|
||||||
|
->searchable()
|
||||||
|
->native(false),
|
||||||
|
|
||||||
|
Forms\Components\TextInput::make('academic_year')
|
||||||
|
->label('Academic Year')
|
||||||
|
->required()
|
||||||
|
->placeholder('e.g. 2023/2024')
|
||||||
|
->maxLength(9),
|
||||||
|
|
||||||
|
])
|
||||||
|
->columns(2),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function table(Table $table): Table
|
||||||
|
{
|
||||||
|
return $table
|
||||||
|
->columns([
|
||||||
|
Tables\Columns\TextColumn::make('teacher.name')
|
||||||
|
->label('Teacher')
|
||||||
|
->searchable()
|
||||||
|
->sortable(),
|
||||||
|
|
||||||
|
Tables\Columns\TextColumn::make('subject.name')
|
||||||
|
->label('Subject')
|
||||||
|
->searchable()
|
||||||
|
->sortable(),
|
||||||
|
|
||||||
|
Tables\Columns\TextColumn::make('class.class_name')
|
||||||
|
->label('Class')
|
||||||
|
->searchable()
|
||||||
|
->sortable(),
|
||||||
|
|
||||||
|
Tables\Columns\TextColumn::make('academic_year')
|
||||||
|
->label('Academic Year')
|
||||||
|
->searchable()
|
||||||
|
->sortable(),
|
||||||
|
|
||||||
|
Tables\Columns\TextColumn::make('created_at')
|
||||||
|
->label('Assigned At')
|
||||||
|
->dateTime()
|
||||||
|
->sortable()
|
||||||
|
->toggleable(isToggledHiddenByDefault: true),
|
||||||
|
])
|
||||||
|
->filters([
|
||||||
|
Tables\Filters\SelectFilter::make('teacher_id')
|
||||||
|
->label('Teacher')
|
||||||
|
->relationship('teacher', 'name')
|
||||||
|
->searchable()
|
||||||
|
->preload(),
|
||||||
|
|
||||||
|
Tables\Filters\SelectFilter::make('subject_id')
|
||||||
|
->label('Subject')
|
||||||
|
->relationship('subject', 'name')
|
||||||
|
->searchable()
|
||||||
|
->preload(),
|
||||||
|
|
||||||
|
Tables\Filters\SelectFilter::make('class_id')
|
||||||
|
->label('Class')
|
||||||
|
->relationship('class', 'class_name')
|
||||||
|
->searchable()
|
||||||
|
->preload(),
|
||||||
|
|
||||||
|
])
|
||||||
|
->actions([
|
||||||
|
Tables\Actions\EditAction::make(),
|
||||||
|
Tables\Actions\DeleteAction::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\ListTeacherSubjects::route('/'),
|
||||||
|
'create' => Pages\CreateTeacherSubject::route('/create'),
|
||||||
|
'edit' => Pages\EditTeacherSubject::route('/{record}/edit'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\TeacherSubjectResource\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\TeacherSubjectResource;
|
||||||
|
use App\Models\TeacherSubject;
|
||||||
|
use Filament\Actions;
|
||||||
|
use Filament\Notifications\Notification;
|
||||||
|
use Filament\Resources\Pages\CreateRecord;
|
||||||
|
|
||||||
|
class CreateTeacherSubject extends CreateRecord
|
||||||
|
{
|
||||||
|
protected static string $resource = TeacherSubjectResource::class;
|
||||||
|
|
||||||
|
protected function mutateFormDataBeforeCreate(array $data): array
|
||||||
|
{
|
||||||
|
$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'])
|
||||||
|
->exists();
|
||||||
|
|
||||||
|
if ($exists) {
|
||||||
|
Notification::make()
|
||||||
|
->title('Failed to save')
|
||||||
|
->body('The combination of teacher, subject, class, and academic year is already registered.')
|
||||||
|
->danger()
|
||||||
|
->send();
|
||||||
|
|
||||||
|
$this->halt();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\TeacherSubjectResource\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\TeacherSubjectResource;
|
||||||
|
use Filament\Actions;
|
||||||
|
use Filament\Resources\Pages\EditRecord;
|
||||||
|
|
||||||
|
class EditTeacherSubject extends EditRecord
|
||||||
|
{
|
||||||
|
protected static string $resource = TeacherSubjectResource::class;
|
||||||
|
|
||||||
|
protected function getHeaderActions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
Actions\DeleteAction::make(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Filament\Resources\TeacherSubjectResource\Pages;
|
||||||
|
|
||||||
|
use App\Filament\Resources\TeacherSubjectResource;
|
||||||
|
use Filament\Actions;
|
||||||
|
use Filament\Resources\Pages\ListRecords;
|
||||||
|
|
||||||
|
class ListTeacherSubjects extends ListRecords
|
||||||
|
{
|
||||||
|
protected static string $resource = TeacherSubjectResource::class;
|
||||||
|
|
||||||
|
protected function getHeaderActions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
Actions\CreateAction::make(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
26
app/Models/Attendances.php
Normal file
26
app/Models/Attendances.php
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class Attendances extends Model
|
||||||
|
{
|
||||||
|
protected $fillable = ['student_id', 'teacher_subject_id', 'date', 'status', 'recorded_by', 'semester', 'notes'];
|
||||||
|
|
||||||
|
public function student()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Student::class, 'student_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function teacherSubject()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(TeacherSubject::class, 'teacher_subject_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Relasi dengan guru yang merekam
|
||||||
|
public function recorder()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(User::class, 'recorded_by');
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,6 +3,7 @@
|
|||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
|
|
||||||
class ClassRoom extends Model
|
class ClassRoom extends Model
|
||||||
{
|
{
|
||||||
@ -22,4 +23,9 @@ class ClassRoom extends Model
|
|||||||
{
|
{
|
||||||
return $this->hasMany(Student::class, 'class_id');
|
return $this->hasMany(Student::class, 'class_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function teacherAssignments(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(TeacherSubject::class, 'class_id');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,5 +6,5 @@ use Illuminate\Database\Eloquent\Model;
|
|||||||
|
|
||||||
class Extracurricular extends Model
|
class Extracurricular extends Model
|
||||||
{
|
{
|
||||||
protected $fillable = ['name', 'description',];
|
protected $fillable = ['name', 'description'];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,17 +8,18 @@ class Student extends Model
|
|||||||
{
|
{
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'nis',
|
'nis',
|
||||||
|
'nisn',
|
||||||
'full_name',
|
'full_name',
|
||||||
'gender',
|
'gender',
|
||||||
'birth_date',
|
'birth_date',
|
||||||
'birth_place',
|
'birth_place',
|
||||||
'address',
|
'address',
|
||||||
|
'religion',
|
||||||
'phone',
|
'phone',
|
||||||
'email',
|
'email',
|
||||||
'class_id',
|
'class_id',
|
||||||
'parent_name',
|
'parent_name',
|
||||||
'parent_phone',
|
'parent_phone',
|
||||||
'profile_picture'
|
|
||||||
];
|
];
|
||||||
|
|
||||||
protected $casts = [
|
protected $casts = [
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
namespace App\Models;
|
namespace App\Models;
|
||||||
|
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
|
|
||||||
class Subject extends Model
|
class Subject extends Model
|
||||||
{
|
{
|
||||||
@ -13,6 +14,12 @@ class Subject extends Model
|
|||||||
return $this->hasMany(SubjectScope::class);
|
return $this->hasMany(SubjectScope::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function teacherAssignments(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(TeacherSubject::class, 'subject_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// public function grades()
|
// public function grades()
|
||||||
// {
|
// {
|
||||||
// return $this->hasMany(Grade::class);
|
// return $this->hasMany(Grade::class);
|
||||||
|
|||||||
56
app/Models/TeacherSubject.php
Normal file
56
app/Models/TeacherSubject.php
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
|
||||||
|
class TeacherSubject extends Model
|
||||||
|
{
|
||||||
|
protected $fillable = ['teacher_id', 'subject_id', 'class_id', 'academic_year'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -4,6 +4,7 @@ namespace App\Models;
|
|||||||
|
|
||||||
// use Illuminate\Contracts\Auth\MustVerifyEmail;
|
// use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||||
use Illuminate\Notifications\Notifiable;
|
use Illuminate\Notifications\Notifiable;
|
||||||
use Spatie\Permission\Traits\HasRoles;
|
use Spatie\Permission\Traits\HasRoles;
|
||||||
@ -51,4 +52,9 @@ class User extends Authenticatable
|
|||||||
'password' => 'hashed',
|
'password' => 'hashed',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function teacherSubjects(): HasMany
|
||||||
|
{
|
||||||
|
return $this->hasMany(TeacherSubject::class, 'teacher_id');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,33 @@
|
|||||||
|
<?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('teacher_subjects', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->foreignId('teacher_id')->constrained('users');
|
||||||
|
$table->foreignId('subject_id')->constrained('subjects');
|
||||||
|
$table->foreignId('class_id')->constrained('class_rooms');
|
||||||
|
$table->string('academic_year');
|
||||||
|
$table->timestamps();
|
||||||
|
|
||||||
|
$table->unique(['teacher_id', 'subject_id', 'class_id', 'academic_year']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('teacher_subjects');
|
||||||
|
}
|
||||||
|
};
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
<?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('attendances', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->foreignId('teacher_subject_id')->nullable()->constrained('teacher_subjects');
|
||||||
|
$table->foreignId('student_id')->constrained('students');
|
||||||
|
$table->date('date');
|
||||||
|
$table->enum('status', ['present', 'absent', 'permission', 'sick']);
|
||||||
|
$table->foreignId('recorded_by')->constrained('users');
|
||||||
|
$table->string('semester');
|
||||||
|
$table->text('notes')->nullable();
|
||||||
|
$table->timestamps();
|
||||||
|
|
||||||
|
$table->unique(['teacher_subject_id', 'student_id', 'date', 'semester']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('attendances');
|
||||||
|
}
|
||||||
|
};
|
||||||
28
database/seeders/AssessmentComponentSeeder.php
Normal file
28
database/seeders/AssessmentComponentSeeder.php
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use App\Models\AssessmentComponent;
|
||||||
|
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
|
||||||
|
class AssessmentComponentSeeder extends Seeder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the database seeds.
|
||||||
|
*/
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
$components = [
|
||||||
|
['name' => 'Ulangan Harian', 'code' => 'UH', 'weight' => 20, 'is_active' => true],
|
||||||
|
['name' => 'Penilaian Tengah Semester', 'code' => 'PTS', 'weight' => 20, 'is_active' => true],
|
||||||
|
['name' => 'Penilaian Akhir Semester', 'code' => 'PAS', 'weight' => 30, 'is_active' => true],
|
||||||
|
['name' => 'Absensi', 'code' => 'ABS', 'weight' => 20, 'is_active' => true],
|
||||||
|
['name' => 'Ekstrakurikuler', 'code' => 'EKS', 'weight' => 10, 'is_active' => true],
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($components as $component) {
|
||||||
|
AssessmentComponent::create($component);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
17
database/seeders/AttendanceSeeder.php
Normal file
17
database/seeders/AttendanceSeeder.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
|
||||||
|
class AttendanceSeeder extends Seeder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the database seeds.
|
||||||
|
*/
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
32
database/seeders/ClassRoomSeeder.php
Normal file
32
database/seeders/ClassRoomSeeder.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use App\Models\ClassRoom;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
use Spatie\Permission\Models\Role;
|
||||||
|
|
||||||
|
class ClassRoomSeeder extends Seeder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the database seeds.
|
||||||
|
*/
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
$teachers = User::role('teacher')->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'],
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($classes as $index => $class) {
|
||||||
|
$class['homeroom_teacher_id'] = $teachers[$index % count($teachers)]->id;
|
||||||
|
ClassRoom::create($class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -15,44 +15,14 @@ class DatabaseSeeder extends Seeder
|
|||||||
*/
|
*/
|
||||||
public function run(): void
|
public function run(): void
|
||||||
{
|
{
|
||||||
// 1. Buat user admin jika belum ada
|
$this->call([
|
||||||
$admin = User::firstOrCreate(
|
UserSeeder::class,
|
||||||
['email' => 'admin@example.com'],
|
SubjectSeeder::class,
|
||||||
[
|
ClassRoomSeeder::class,
|
||||||
'name' => 'admin',
|
StudentSeeder::class,
|
||||||
'password' => bcrypt('admin'),
|
ExtracurricularSeeder::class,
|
||||||
]
|
AssessmentComponentSeeder::class,
|
||||||
);
|
AttendanceSeeder::class,
|
||||||
|
|
||||||
// 2. Buat permission terkait role satu per satu
|
|
||||||
Permission::firstOrCreate(['name' => 'view_any_role', 'guard_name' => 'web']);
|
|
||||||
Permission::firstOrCreate(['name' => 'view_role', 'guard_name' => 'web']);
|
|
||||||
Permission::firstOrCreate(['name' => 'create_role', 'guard_name' => 'web']);
|
|
||||||
Permission::firstOrCreate(['name' => 'update_role', 'guard_name' => 'web']);
|
|
||||||
Permission::firstOrCreate(['name' => 'delete_role', 'guard_name' => 'web']);
|
|
||||||
Permission::firstOrCreate(['name' => 'delete_any_role', 'guard_name' => 'web']);
|
|
||||||
Permission::firstOrCreate(['name' => 'force_delete_role', 'guard_name' => 'web']);
|
|
||||||
Permission::firstOrCreate(['name' => 'force_delete_any_role', 'guard_name' => 'web']);
|
|
||||||
Permission::firstOrCreate(['name' => 'restore_role', 'guard_name' => 'web']);
|
|
||||||
Permission::firstOrCreate(['name' => 'restore_any_role', 'guard_name' => 'web']);
|
|
||||||
Permission::firstOrCreate(['name' => 'replicate_role', 'guard_name' => 'web']);
|
|
||||||
Permission::firstOrCreate(['name' => 'reorder_role', 'guard_name' => 'web']);
|
|
||||||
|
|
||||||
// 3. Ambil semua permission yang berhubungan dengan "_role"
|
|
||||||
$rolePermissions = Permission::where('name', 'like', '%_role')->pluck('name');
|
|
||||||
|
|
||||||
// 4. Buat role super_admin jika belum ada
|
|
||||||
$role = Role::firstOrCreate([
|
|
||||||
'name' => 'super_admin',
|
|
||||||
'guard_name' => 'web',
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// 5. Assign semua permission "_role" ke super_admin
|
|
||||||
$role->syncPermissions($rolePermissions);
|
|
||||||
|
|
||||||
// 6. Assign role super_admin ke user admin
|
|
||||||
if (!$admin->hasRole($role)) {
|
|
||||||
$admin->assignRole($role);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
26
database/seeders/ExtracurricularSeeder.php
Normal file
26
database/seeders/ExtracurricularSeeder.php
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use App\Models\Extracurricular;
|
||||||
|
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
|
||||||
|
class ExtracurricularSeeder extends Seeder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the database seeds.
|
||||||
|
*/
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
$extracurriculars = [
|
||||||
|
['name' => 'Pramuka', 'description' => 'Kegiatan kepramukaan'],
|
||||||
|
['name' => 'Basket', 'description' => 'Ekstrakurikuler basket'],
|
||||||
|
['name' => 'Futsal', 'description' => 'Ekstrakurikuler futsal'],
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($extracurriculars as $extracurricular) {
|
||||||
|
Extracurricular::create($extracurricular);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
44
database/seeders/StudentSeeder.php
Normal file
44
database/seeders/StudentSeeder.php
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use App\Models\ClassRoom;
|
||||||
|
use App\Models\Student;
|
||||||
|
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
|
||||||
|
class StudentSeeder extends Seeder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the database seeds.
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
|
||||||
|
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),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
17
database/seeders/SubjectScopeSeeder.php
Normal file
17
database/seeders/SubjectScopeSeeder.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
|
||||||
|
class SubjectScopeSeeder extends Seeder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the database seeds.
|
||||||
|
*/
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
55
database/seeders/SubjectSeeder.php
Normal file
55
database/seeders/SubjectSeeder.php
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use App\Models\Subject;
|
||||||
|
use App\Models\SubjectScope;
|
||||||
|
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
|
||||||
|
class SubjectSeeder extends Seeder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the database seeds.
|
||||||
|
*/
|
||||||
|
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],
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($subjects as $subject) {
|
||||||
|
$createdSubject = Subject::create($subject);
|
||||||
|
|
||||||
|
// Untuk mata pelajaran agama, buat scope
|
||||||
|
if (str_contains($createdSubject->name, 'Agama')) {
|
||||||
|
$religion = match(true) {
|
||||||
|
str_contains($createdSubject->name, 'Islam') => 'islam',
|
||||||
|
str_contains($createdSubject->name, 'Kristen') => 'kristen',
|
||||||
|
str_contains($createdSubject->name, 'Katolik') => 'katolik',
|
||||||
|
str_contains($createdSubject->name, 'Hindu') => 'hindu',
|
||||||
|
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'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
75
database/seeders/UserSeeder.php
Normal file
75
database/seeders/UserSeeder.php
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
use Spatie\Permission\Models\Permission;
|
||||||
|
use Spatie\Permission\Models\Role;
|
||||||
|
|
||||||
|
class UserSeeder extends Seeder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the database seeds.
|
||||||
|
*/
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
// 1. Buat user admin jika belum ada
|
||||||
|
$admin = User::firstOrCreate(
|
||||||
|
['email' => 'admin@example.com'],
|
||||||
|
[
|
||||||
|
'name' => 'admin',
|
||||||
|
'password' => bcrypt('admin'),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
$user = User::firstOrCreate(
|
||||||
|
['email' => 'teacher@example.com'],
|
||||||
|
[
|
||||||
|
'name' => 'teacher',
|
||||||
|
'password' => bcrypt('teacher'),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
// 2. Buat permission terkait role satu per satu
|
||||||
|
Permission::firstOrCreate(['name' => 'view_any_role', 'guard_name' => 'web']);
|
||||||
|
Permission::firstOrCreate(['name' => 'view_role', 'guard_name' => 'web']);
|
||||||
|
Permission::firstOrCreate(['name' => 'create_role', 'guard_name' => 'web']);
|
||||||
|
Permission::firstOrCreate(['name' => 'update_role', 'guard_name' => 'web']);
|
||||||
|
Permission::firstOrCreate(['name' => 'delete_role', 'guard_name' => 'web']);
|
||||||
|
Permission::firstOrCreate(['name' => 'delete_any_role', 'guard_name' => 'web']);
|
||||||
|
Permission::firstOrCreate(['name' => 'force_delete_role', 'guard_name' => 'web']);
|
||||||
|
Permission::firstOrCreate(['name' => 'force_delete_any_role', 'guard_name' => 'web']);
|
||||||
|
Permission::firstOrCreate(['name' => 'restore_role', 'guard_name' => 'web']);
|
||||||
|
Permission::firstOrCreate(['name' => 'restore_any_role', 'guard_name' => 'web']);
|
||||||
|
Permission::firstOrCreate(['name' => 'replicate_role', 'guard_name' => 'web']);
|
||||||
|
Permission::firstOrCreate(['name' => 'reorder_role', 'guard_name' => 'web']);
|
||||||
|
|
||||||
|
// 3. Ambil semua permission yang berhubungan dengan "_role"
|
||||||
|
$rolePermissions = Permission::where('name', 'like', '%_role')->pluck('name');
|
||||||
|
|
||||||
|
// 4. Buat role super_admin jika belum ada
|
||||||
|
$role = Role::firstOrCreate([
|
||||||
|
'name' => 'super_admin',
|
||||||
|
'guard_name' => 'web',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$teacher = Role::firstOrCreate([
|
||||||
|
'name' => 'teacher',
|
||||||
|
'guard_name' => 'web',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$teacher->syncPermissions($rolePermissions);
|
||||||
|
|
||||||
|
$role->syncPermissions($rolePermissions);
|
||||||
|
|
||||||
|
if (!$admin->hasRole($role)) {
|
||||||
|
$admin->assignRole($role);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$user->hasRole($teacher)) {
|
||||||
|
$user->assignRole($teacher);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,76 @@
|
|||||||
|
<x-filament-panels::page>
|
||||||
|
<x-filament-panels::form wire:submit="submit">
|
||||||
|
{{ $this->form }}
|
||||||
|
|
||||||
|
@if($this->attendanceDate && $this->teacherSubjectId)
|
||||||
|
<div class="mt-6 space-y-4">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<h3 class="text-lg font-medium">Student Attendance</h3>
|
||||||
|
<div class="flex gap-2 space-x-2">
|
||||||
|
<x-filament::button type="button" wire:click="markAll('present')" color="success">
|
||||||
|
Mark All Present
|
||||||
|
</x-filament::button>
|
||||||
|
<x-filament::button type="button" wire:click="markAll('absent')" color="danger">
|
||||||
|
Mark All Absent
|
||||||
|
</x-filament::button>
|
||||||
|
<x-filament::button type="button" wire:click="markAll('sick')" color="warning">
|
||||||
|
Mark All Sick
|
||||||
|
</x-filament::button>
|
||||||
|
<x-filament::button type="button" wire:click="markAll('permission')" color="warning">
|
||||||
|
Mark All Permission
|
||||||
|
</x-filament::button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="space-y-2">
|
||||||
|
@foreach($this->students as $index => $student)
|
||||||
|
<div class="flex items-center p-3 border rounded-lg">
|
||||||
|
<div class="flex-1 flex items-center gap-4">
|
||||||
|
<p class="font-medium">{{ $student['name'] }}</p>
|
||||||
|
<p class="text-sm text-gray-500">{{ $student['nis'] }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="flex gap-2 space-x-2">
|
||||||
|
<x-filament::button
|
||||||
|
type="button"
|
||||||
|
wire:click="$set('students.{{ $index }}.status', 'present')"
|
||||||
|
color="{{ $student['status'] === 'present' ? 'success' : 'gray' }}"
|
||||||
|
size="sm"
|
||||||
|
>
|
||||||
|
Present
|
||||||
|
</x-filament::button>
|
||||||
|
<x-filament::button
|
||||||
|
type="button"
|
||||||
|
wire:click="$set('students.{{ $index }}.status', 'absent')"
|
||||||
|
color="{{ $student['status'] === 'absent' ? 'danger' : 'gray' }}"
|
||||||
|
size="sm"
|
||||||
|
>
|
||||||
|
Absent
|
||||||
|
</x-filament::button>
|
||||||
|
<x-filament::button
|
||||||
|
type="button"
|
||||||
|
wire:click="$set('students.{{ $index }}.status', 'sick')"
|
||||||
|
color="{{ $student['status'] === 'sick' ? 'warning' : 'gray' }}"
|
||||||
|
size="sm"
|
||||||
|
>
|
||||||
|
Sick
|
||||||
|
</x-filament::button>
|
||||||
|
<x-filament::button
|
||||||
|
type="button"
|
||||||
|
wire:click="$set('students.{{ $index }}.status', 'permission')"
|
||||||
|
color="{{ $student['status'] === 'permission' ? 'warning' : 'gray' }}"
|
||||||
|
size="sm"
|
||||||
|
>
|
||||||
|
Permission
|
||||||
|
</x-filament::button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<x-filament::button type="submit" class="w-full">
|
||||||
|
Save Attendance
|
||||||
|
</x-filament::button>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
</x-filament-panels::form>
|
||||||
|
</x-filament-panels::page>
|
||||||
Loading…
x
Reference in New Issue
Block a user