refactor: remove Lecturer model and resources in favor of string-based lecturer names in Course model

This commit is contained in:
Yoga Pangestu 2026-03-29 21:26:32 +07:00
parent a30f4c5967
commit 0070c6b0b1
25 changed files with 149 additions and 791 deletions

View File

@ -26,7 +26,7 @@ protected function setUp(): void
$action->makeModalSubmitAction('createAnother', arguments: ['another' => true])
->label('Simpan dan Tambah Lagi'),
])
->modalWidth(Width::Large)
->modalWidth(Width::ThreeExtraLarge)
->schema(fn ($livewire) => $livewire->scheduleFormSchema());
}
}

View File

@ -30,7 +30,7 @@ protected function setUp(): void
->modalHeading('Ubah Jadwal Kuliah')
->modalSubmitActionLabel('Simpan')
->modalCancelActionLabel('Batal')
->modalWidth(Width::Large)
->modalWidth(Width::ThreeExtraLarge)
->schema(fn ($livewire) => $livewire->scheduleFormSchema());
}
}

View File

@ -95,21 +95,14 @@ public function deleteScheduleAction(): Action
public function getSchedules(): Collection
{
$user = auth()->user();
$lecturer = $user->lecturer;
$query = CourseSchedule::with(['course.lecturer']);
if ($lecturer) {
$query->whereHas('course', fn ($q) => $q->where('lecturer_id', $lecturer->id));
}
$query = CourseSchedule::with(['course']);
$query->whereHas('course', fn ($q) => $q->where('semester', app(GeneralSettings::class)->current_semester));
if ($this->search) {
$query->where(function ($q) {
$q->whereHas('course', fn ($sq) => $sq->where('name', 'like', "%{$this->search}%"))
->orWhereHas('course.lecturer', fn ($sq) => $sq->where('full_name', 'like', "%{$this->search}%"));
$q->whereHas('course', fn ($sq) => $sq->where('name', 'like', "%{$this->search}%")
->orWhere('lecturer', 'like', "%{$this->search}%"));
});
}

View File

@ -51,7 +51,7 @@ public function getSchedules()
->whereHas('course', function ($q) use ($semester) {
$q->where('semester', $semester);
})
->with(['course.lecturer', 'attendances' => function ($q) use ($student) {
->with(['course', 'attendances' => function ($q) use ($student) {
$q->where('student_id', $student->id)
->whereDate('date', now()->toDateString());
}])
@ -88,7 +88,7 @@ public function getScheduleCards()
return (object) [
'id' => $schedule->id,
'course_name' => $schedule->course->name,
'lecturer_name' => $schedule->course->lecturer?->full_name ?? 'Belum Ditentukan',
'lecturer_name' => $schedule->course->lecturer ?? 'Belum Ditentukan',
'time_range' => $schedule->start_time->format('H:i').' - '.$schedule->end_time->format('H:i'),
'is_attended' => $isAttended,
'can_attend' => $canAttend && ! $isAttended,
@ -187,7 +187,7 @@ public function table(Table $table): Table
->searchable()
->sortable()
->wrap()
->description(fn (Attendance $record): string => $record->courseSchedule->course->lecturer->full_name),
->description(fn (Attendance $record): string => $record->courseSchedule->course->lecturer ?? 'Belum Ditentukan'),
])
->defaultSort('date', 'desc')
->filters([

View File

@ -120,7 +120,7 @@ public function getTodaySessions(): Collection
->whereHas('course', function ($query) {
$query->where('semester', app(GeneralSettings::class)->current_semester);
})
->with(['course.lecturer', 'course.classSessions' => fn ($q) => $q->whereDate('date', now())])
->with(['course', 'course.classSessions' => fn ($q) => $q->whereDate('date', now())])
->orderBy('start_time', 'asc')
->get();
@ -143,13 +143,13 @@ public function getCourses(): Collection
{
$query = Course::query()
->where('semester', app(GeneralSettings::class)->current_semester)
->with(['classSessions' => fn ($q) => $q->orderBy('session_number', 'asc'), 'lecturer']);
->with(['classSessions' => fn ($q) => $q->orderBy('session_number', 'asc')]);
if ($this->search) {
$query->where(function ($q) {
$q->where('name', 'like', "%{$this->search}%")
->orWhere('code', 'like', "%{$this->search}%")
->orWhereHas('lecturer', fn ($sq) => $sq->where('full_name', 'like', "%{$this->search}%"));
->orWhere('lecturer', 'like', "%{$this->search}%");
});
}

View File

@ -13,7 +13,6 @@
use App\Models\Course;
use BackedEnum;
use Filament\Actions\BulkActionGroup;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
@ -35,6 +34,8 @@ class CourseResource extends Resource
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedRectangleStack;
protected static ?string $title = 'Mata Kuliah';
protected static ?string $navigationLabel = 'Mata Kuliah';
protected static ?int $navigationSort = 15;
@ -82,13 +83,11 @@ public static function form(Schema $schema): Schema
->maxValue(8)
->helperText('Semester ke berapa mata kuliah ini diajarkan (1-8).'),
Select::make('lecturer_id')
TextInput::make('lecturer')
->label('Dosen Pengampu')
->placeholder('Pilih Dosen')
->relationship('lecturer', 'full_name')
->preload()
->searchable()
->helperText('Pilih dosen yang mengampu mata kuliah ini.')
->placeholder('Ujang')
->maxLength(100)
->helperText('Masukkan nama dosen yang mengampu mata kuliah ini.')
->required(),
])
@ -112,8 +111,9 @@ public static function table(Table $table): Table
->label('SKS')
->sortable(),
TextColumn::make('lecturer.full_name')
TextColumn::make('lecturer')
->label('Dosen Pengampu')
->placeholder('Belum ditentukan')
->searchable(),
...TimestampColumns::make(),

View File

@ -1,52 +0,0 @@
<?php
namespace App\Filament\Resources\Master\Lecturers;
use App\Filament\Resources\Master\Lecturers\Pages\CreateLecturer;
use App\Filament\Resources\Master\Lecturers\Pages\EditLecturer;
use App\Filament\Resources\Master\Lecturers\Pages\ListLecturers;
use App\Filament\Resources\Master\Lecturers\Schemas\LecturerForm;
use App\Filament\Resources\Master\Lecturers\Tables\LecturersTable;
use App\Models\Lecturer;
use BackedEnum;
use Filament\Resources\Resource;
use Filament\Schemas\Schema;
use Filament\Support\Icons\Heroicon;
use Filament\Tables\Table;
use UnitEnum;
class LecturerResource extends Resource
{
protected static ?string $model = Lecturer::class;
protected static string|UnitEnum|null $navigationGroup = 'Master';
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedUserGroup;
protected static ?string $navigationLabel = 'Dosen';
protected static ?int $navigationSort = 10;
protected static ?string $recordTitleAttribute = 'full_name';
protected static ?string $slug = 'master/lecturers';
public static function form(Schema $schema): Schema
{
return LecturerForm::configure($schema);
}
public static function table(Table $table): Table
{
return LecturersTable::configure($table);
}
public static function getPages(): array
{
return [
'index' => ListLecturers::route('/'),
'create' => CreateLecturer::route('/create'),
'edit' => EditLecturer::route('/{record}/edit'),
];
}
}

View File

@ -1,36 +0,0 @@
<?php
namespace App\Filament\Resources\Master\Lecturers\Pages;
use App\Filament\Actions\BackAction;
use App\Filament\Resources\Master\Lecturers\LecturerResource;
use App\Filament\Support\SystemNotification;
use Filament\Notifications\Notification;
use Filament\Resources\Pages\CreateRecord;
class CreateLecturer extends CreateRecord
{
protected static string $resource = LecturerResource::class;
protected ?string $heading = 'Tambah Dosen';
protected static ?string $title = 'Tambah Dosen';
protected function getHeaderActions(): array
{
return [
BackAction::make()
->url(ListLecturers::getUrl()),
];
}
protected function getRedirectUrl(): string
{
return $this->getResource()::getUrl('index');
}
protected function getCreatedNotification(): ?Notification
{
return SystemNotification::create();
}
}

View File

@ -1,36 +0,0 @@
<?php
namespace App\Filament\Resources\Master\Lecturers\Pages;
use App\Filament\Actions\BackAction;
use App\Filament\Resources\Master\Lecturers\LecturerResource;
use App\Filament\Support\SystemNotification;
use Filament\Notifications\Notification;
use Filament\Resources\Pages\EditRecord;
class EditLecturer extends EditRecord
{
protected static string $resource = LecturerResource::class;
protected ?string $heading = 'Ubah Dosen';
protected static ?string $title = 'Ubah Dosen';
protected function getHeaderActions(): array
{
return [
BackAction::make()
->url(ListLecturers::getUrl()),
];
}
protected function getRedirectUrl(): string
{
return $this->getResource()::getUrl('index');
}
protected function getSavedNotification(): ?Notification
{
return SystemNotification::update();
}
}

View File

@ -1,22 +0,0 @@
<?php
namespace App\Filament\Resources\Master\Lecturers\Pages;
use App\Filament\Actions\Cheerful\CreateAction;
use App\Filament\Resources\Master\Lecturers\LecturerResource;
use Filament\Resources\Pages\ListRecords;
class ListLecturers extends ListRecords
{
protected static string $resource = LecturerResource::class;
protected static ?string $title = 'Dosen';
protected function getHeaderActions(): array
{
return [
CreateAction::make()
->label('Tambah'),
];
}
}

View File

@ -1,121 +0,0 @@
<?php
namespace App\Filament\Resources\Master\Lecturers\Schemas;
use App\Enums\Sex;
use App\Models\Course;
use Carbon\Carbon;
use Filament\Forms\Components\DatePicker;
use Filament\Forms\Components\Radio;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\TextInput;
use Filament\Schemas\Components\Grid;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Schema;
class LecturerForm
{
public static function configure(Schema $schema): Schema
{
return $schema
->components([
Section::make('Informasi Dosen')
->schema([
TextInput::make('full_name')
->label('Nama Lengkap')
->placeholder('John Doe')
->autocomplete(false)
->required()
->maxLength(100)
->minLength(3)
->autofocus()
->helperText('Masukkan nama lengkap dosen beserta gelar akademik.')
->columnSpanFull(),
Grid::make(3)
->schema([
TextInput::make('lecturer_identification_number')
->label('NIDN')
->placeholder('1234567890')
->autocomplete(false)
->required()
->numeric()
->rules(['digits:10'])
->unique(ignoreRecord: true),
TextInput::make('phone_number')
->label('Nomor Telepon')
->placeholder('0812 3456 78910')
->autocomplete(false)
->required()
->maxLength(20)
->minLength(10)
->rules(['regex:/^[0-9 ]+$/'])
->mask('9999 9999 99999'),
Radio::make('sex')
->label('Jenis Kelamin')
->options(Sex::class)
->required()
->inline(),
])
->columnSpanFull(),
Textarea::make('address')
->label('Alamat')
->placeholder('Jl. Contoh No. 123, Kota, Provinsi')
->autocomplete(false)
->rows(3)
->nullable()
->columnSpanFull(),
Grid::make(2)
->schema([
DatePicker::make('date_of_birth')
->label('Tanggal Lahir')
->placeholder('Pilih Tanggal')
->required()
->native(false)
->displayFormat('l, d F Y')
->maxDate(Carbon::now()->subYears(25)),
TextInput::make('place_of_birth')
->label('Tempat Lahir')
->placeholder('Purwakarta')
->autocomplete(false)
->required()
->maxLength(50)
->minLength(3),
])
->columnSpanFull(),
])
->columns(2),
Section::make('Mata Kuliah')
->description('Daftar mata kuliah yang diampu oleh dosen ini.')
->schema([
Select::make('courses')
->label('Mata Kuliah')
->multiple()
->relationship('courses', 'name')
->options(function () {
return Course::all()
->groupBy('semester')
->mapWithKeys(function ($courses, $semester) {
return [
"Semester $semester" => $courses->pluck('name', 'id')->toArray(),
];
})
->toArray();
})
->searchable()
->preload()
->live()
->required(),
]),
])
->columns(1);
}
}

View File

@ -1,116 +0,0 @@
<?php
namespace App\Filament\Resources\Master\Lecturers\Tables;
use App\Enums\RoleEnum;
use App\Enums\Sex;
use App\Filament\Actions\Cheerful\DeleteAction;
use App\Filament\Actions\Cheerful\EditAction;
use App\Filament\Actions\Cheerful\ForceDeleteAction;
use App\Filament\Actions\Cheerful\RestoreAction;
use App\Filament\Actions\DefaultBulkActions;
use App\Filament\Columns\TimestampColumns;
use App\Models\Lecturer;
use Filament\Actions\BulkActionGroup;
use Filament\Support\Enums\Width;
use Filament\Support\Icons\Heroicon;
use Filament\Tables\Columns\TextColumn;
use Filament\Tables\Filters\SelectFilter;
use Filament\Tables\Filters\TrashedFilter;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
class LecturersTable
{
public static function configure(Table $table): Table
{
return $table
->columns([
TextColumn::make('full_name')
->label('Nama Lengkap')
->searchable(query: function (Builder $query, string $search) {
$query->where('full_name', 'like', "%{$search}%")
->orWhere('lecturer_identification_number', 'like', "%{$search}%");
})
->sortable()
->description(fn (Lecturer $record) => $record->lecturer_identification_number),
TextColumn::make('phone_number')
->label('No. Telepon')
->searchable()
->sortable(),
TextColumn::make('sex')
->label('Jenis Kelamin')
->badge()
->sortable()
->searchable(query: function ($query, string $search) {
$search = strtolower($search);
if (str_contains('laki-laki', $search)) {
$query->orWhere('sex', 'male');
}
if (str_contains('perempuan', $search)) {
$query->orWhere('sex', 'female');
}
}),
TextColumn::make('date_of_birth')
->label('TTL')
->date('d F Y')
->sortable()
->prefix(fn (Lecturer $record) => $record->place_of_birth ? $record->place_of_birth.', ' : ''),
TextColumn::make('address')
->label('Alamat')
->limit(50),
TextColumn::make('courses.name')
->label('Mata Kuliah')
->badge()
->searchable()
->listWithLineBreaks(),
...TimestampColumns::make(),
])
->filters([
SelectFilter::make('sex')
->label('Jenis Kelamin')
->options(Sex::class)
->native(false),
SelectFilter::make('courses')
->label('Mata Kuliah')
->relationship('courses', 'name')
->multiple()
->preload(),
TrashedFilter::make()
->native(false)
->visible(fn () => auth()->user()->hasRole(RoleEnum::Developer)),
])
->recordActions([
EditAction::make()
->modalWidth(Width::Large),
DeleteAction::make(),
ForceDeleteAction::make(),
RestoreAction::make(),
])
->toolbarActions([
BulkActionGroup::make([
...DefaultBulkActions::make('Dosen'),
]),
])
->emptyStateIcon(Heroicon::OutlinedUserGroup)
->emptyStateDescription('Setelah Anda membuat data pertama, maka akan muncul disini.')
->defaultSort('created_at', 'desc')
->deferFilters(false)
->paginated([25, 50, 100, 'all'])
->defaultPaginationPageOption(25);
}
}

View File

@ -2,10 +2,8 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
@ -23,18 +21,6 @@ protected static function booted()
});
}
public function lecturer(): BelongsTo
{
return $this->belongsTo(Lecturer::class);
}
public static function getOptionsForLecturer(Lecturer $lecturer): Collection
{
return static::query()
->where('lecturer_id', $lecturer->id)
->get();
}
public function materials(): HasMany
{
return $this->hasMany(Material::class);

View File

@ -1,43 +0,0 @@
<?php
namespace App\Models;
use App\Enums\Sex;
use Illuminate\Database\Eloquent\Attributes\Scope;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
class Lecturer extends Model
{
use HasFactory, SoftDeletes;
protected $guarded = ['id'];
protected function casts(): array
{
return [
'date_of_birth' => 'date',
'sex' => Sex::class,
];
}
#[Scope]
protected function female(Builder $query): void
{
$query->where('sex', Sex::Female);
}
#[Scope]
protected function male(Builder $query): void
{
$query->where('sex', Sex::Male);
}
public function courses(): HasMany
{
return $this->hasMany(Course::class);
}
}

View File

@ -1,69 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Policies;
use App\Models\Lecturer;
use Illuminate\Auth\Access\HandlesAuthorization;
use Illuminate\Foundation\Auth\User as AuthUser;
class LecturerPolicy
{
use HandlesAuthorization;
public function viewAny(AuthUser $authUser): bool
{
return $authUser->can('ViewAny:Lecturer');
}
public function view(AuthUser $authUser, Lecturer $lecturer): bool
{
return $authUser->can('View:Lecturer');
}
public function create(AuthUser $authUser): bool
{
return $authUser->can('Create:Lecturer');
}
public function update(AuthUser $authUser, Lecturer $lecturer): bool
{
return $authUser->can('Update:Lecturer');
}
public function delete(AuthUser $authUser, Lecturer $lecturer): bool
{
return $authUser->can('Delete:Lecturer');
}
public function restore(AuthUser $authUser, Lecturer $lecturer): bool
{
return $authUser->can('Restore:Lecturer');
}
public function forceDelete(AuthUser $authUser, Lecturer $lecturer): bool
{
return $authUser->can('ForceDelete:Lecturer');
}
public function forceDeleteAny(AuthUser $authUser): bool
{
return $authUser->can('ForceDeleteAny:Lecturer');
}
public function restoreAny(AuthUser $authUser): bool
{
return $authUser->can('RestoreAny:Lecturer');
}
public function replicate(AuthUser $authUser, Lecturer $lecturer): bool
{
return $authUser->can('Replicate:Lecturer');
}
public function reorder(AuthUser $authUser): bool
{
return $authUser->can('Reorder:Lecturer');
}
}

View File

@ -1,37 +0,0 @@
<?php
use App\Enums\Sex;
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('lecturers', function (Blueprint $table) {
$table->id();
$table->string('lecturer_identification_number', 10)->unique();
$table->string('full_name', 100);
$table->string('phone_number', 20);
$table->enum('sex', Sex::cases());
$table->text('address')->nullable();
$table->date('date_of_birth');
$table->string('place_of_birth', 50);
$table->timestamp('created_at')->useCurrent();
$table->timestamp('updated_at')->nullable()->useCurrentOnUpdate();
$table->softDeletes();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('lecturers');
}
};

View File

@ -13,7 +13,7 @@ public function up(): void
{
Schema::create('courses', function (Blueprint $table) {
$table->id();
$table->foreignId('lecturer_id')->nullable()->constrained()->nullOnDelete();
$table->string('lecturer', 100)->nullable();
$table->string('code', 20)->unique();
$table->string('name', 100);
$table->integer('credit')->default(3);

View File

@ -3,7 +3,6 @@
namespace Database\Seeders;
use App\Models\Course;
use App\Models\Lecturer;
use Illuminate\Database\Seeder;
class CourseSeeder extends Seeder
@ -13,6 +12,17 @@ class CourseSeeder extends Seeder
*/
public function run(): void
{
$lecturers = [
'Dr. Ahmad Hidayat',
'Siti Aminah, M.Kom.',
'Budi Santoso, S.T., M.T.',
'Dr. Ir. Heru Prasetyo',
'Lusi Fitriani, M.Sc.',
'Andi Wijaya, M.T.',
'Rina Wahyuni, Ph.D.',
'Dian Pratama, M.Kom.',
];
$courses = [
// Semester 1
['code' => 'SIU101', 'name' => 'Ilmu Amaliah, Sosial, dan Budaya Dasar', 'credit' => 2, 'semester' => 1],
@ -84,31 +94,8 @@ public function run(): void
['code' => 'FSI839', 'name' => 'Startup Digital', 'credit' => 2, 'semester' => 8],
];
$lecturers = Lecturer::pluck('id')->toArray();
shuffle($lecturers);
$lecturerSemesterMap = [];
foreach ($courses as $i => &$course) {
$semester = $course['semester'];
if ($i < count($lecturers)) {
$lecturerId = $lecturers[$i];
} else {
do {
$lecturerId = $lecturers[array_rand($lecturers)];
} while (
isset($lecturerSemesterMap[$lecturerId]) &&
in_array($semester, $lecturerSemesterMap[$lecturerId])
);
}
$course['lecturer_id'] = $lecturerId;
$lecturerSemesterMap[$lecturerId][] = $semester;
foreach ($courses as &$course) {
$course['lecturer'] = $lecturers[array_rand($lecturers)];
}
Course::insert($courses);

View File

@ -17,7 +17,6 @@ public function run(): void
$this->call([
ShieldSeeder::class,
UserSeeder::class,
LecturerSeeder::class,
StudentSeeder::class,
CourseSeeder::class,
CourseScheduleSeeder::class,

View File

@ -1,62 +0,0 @@
<?php
namespace Database\Seeders;
use App\Enums\Sex;
use Carbon\Carbon;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
class LecturerSeeder extends Seeder
{
public function run(): void
{
$names = [
'Dr. Budi Santoso, S.Kom, M.Sc',
'Prof. Siti Rahmawati, M.T',
'Dr. Ahmad Fauzan, M.Kom',
'Ir. Dwi Prasetyo, M.Eng',
'Dr. Maya Kartika, M.Sc',
'Dr. Andi Saputra, M.Kom',
'Prof. Rizky Maulana, Ph.D',
'Dr. Nanda Putri, M.T',
'Ir. Fajar Hidayat, M.Eng',
'Dr. Taufik Ramadhan, M.Kom',
'Dr. Indah Permata Sari, M.Sc',
'Prof. Yoga Pratama, Ph.D',
'Dr. Dian Lestari, M.T',
'Ir. Arif Nugroho, M.Eng',
'Dr. Bagus Sekali Namanya, M.Kom',
];
$cities = [
'Bandung',
'Jakarta',
'Surabaya',
'Yogyakarta',
'Semarang',
'Bogor',
'Depok',
'Bekasi',
'Malang',
'Solo',
'Padang',
'Medan',
'Palembang',
'Makassar',
'Denpasar',
];
for ($i = 0; $i < 15; $i++) {
DB::table('lecturers')->insert([
'lecturer_identification_number' => str_pad($i + 1, 10, '0', STR_PAD_LEFT),
'full_name' => $names[$i],
'phone_number' => '08123456'.rand(1000, 9999),
'sex' => $i % 2 === 0 ? Sex::Male->value : Sex::Female->value,
'address' => 'Jl. Kampus No. '.rand(1, 200),
'date_of_birth' => Carbon::now()->subYears(rand(30, 55))->toDateString(),
'place_of_birth' => $cities[$i],
]);
}
}
}

View File

@ -34,17 +34,6 @@ public function run(): void
"Replicate:Student",
"Reorder:Student",
"ViewAny:Lecturer",
"View:Lecturer",
"Create:Lecturer",
"Update:Lecturer",
"Delete:Lecturer",
"Restore:Lecturer",
"ForceDelete:Lecturer",
"ForceDeleteAny:Lecturer",
"RestoreAny:Lecturer",
"Replicate:Lecturer",
"Reorder:Lecturer",
"ViewAny:Course",
"View:Course",
@ -153,8 +142,6 @@ public function run(): void
"ViewAny:Student",
"View:Student",
"ViewAny:Lecturer",
"View:Lecturer",
"ViewAny:Course",
"View:Course",
@ -183,9 +170,6 @@ public function run(): void
"Update:Student",
"Delete:Student",
"Create:Lecturer",
"Update:Lecturer",
"Delete:Lecturer",
"Create:Course",
"Update:Course",

View File

@ -1,135 +1,138 @@
<x-filament-panels::page>
<x-filament::section>
<div
class="fi-section-content rounded-xl border border-info-200 bg-info-50 dark:bg-info-500/10 dark:border-info-500/30 p-4 mb-6">
<div class="flex items-start gap-3">
<x-heroicon-o-information-circle class="w-5 h-5 text-info-600 dark:text-info-400 mt-0.5" />
<div>
<x-filament::section>
<div
class="fi-section-content rounded-xl border border-info-200 bg-info-50 dark:bg-info-500/10 dark:border-info-500/30 p-4 mb-6">
<div class="flex items-start gap-3">
<x-heroicon-o-information-circle class="w-5 h-5 text-info-600 dark:text-info-400 mt-0.5" />
<div class="text-sm text-info-800 dark:text-info-200">
Geser ke samping untuk melihat hari lainnya.
<div class="text-sm text-info-800 dark:text-info-200">
Geser ke samping untuk melihat hari lainnya.
</div>
</div>
</div>
</div>
<div class="flex items-center justify-between gap-4 mb-6">
<div class="w-full max-w-xl">
{{ $this->form }}
<div class="flex items-center justify-between gap-4 mb-6">
<div class="w-full max-w-xl">
{{ $this->form }}
</div>
</div>
</div>
@if ($schedules->isNotEmpty())
<div class="overflow-x-auto">
<div class="flex flex-nowrap gap-6 min-w-max pb-4">
@if ($schedules->isNotEmpty())
<div class="overflow-x-auto">
<div class="flex flex-nowrap gap-6 min-w-max pb-4">
@foreach ($days as $dayNumber => $dayName)
@php
$daySchedules = $schedules->get($dayNumber);
@endphp
@foreach ($days as $dayNumber => $dayName)
@php
$daySchedules = $schedules->get($dayNumber);
@endphp
@if ($daySchedules && $daySchedules->count() > 0)
<section class="w-80 shrink-0 space-y-4">
@if ($daySchedules && $daySchedules->count() > 0)
<section class="w-80 shrink-0 space-y-4">
<div class="flex items-center gap-2 border-b border-gray-200 dark:border-gray-700 pb-2">
<x-heroicon-o-calendar class="w-5 h-5 text-primary-500" />
<h2 class="text-lg font-semibold tracking-tight text-gray-950 dark:text-white">
{{ $dayName }}
</h2>
</div>
<div class="flex items-center gap-2 border-b border-gray-200 dark:border-gray-700 pb-2">
<x-heroicon-o-calendar class="w-5 h-5 text-primary-500" />
<h2 class="text-lg font-semibold tracking-tight text-gray-950 dark:text-white">
{{ $dayName }}
</h2>
</div>
<div class="space-y-4">
@foreach ($daySchedules as $schedule)
<div
class="fi-card rounded-xl border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900 shadow-sm hover:shadow-md transition">
<div class="space-y-4">
@foreach ($daySchedules as $schedule)
<div
class="fi-card rounded-xl border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900 shadow-sm hover:shadow-md transition">
<div class="p-5 space-y-4">
<div class="flex items-start justify-between gap-3">
<div class="flex items-center gap-2">
<span
class="inline-flex items-center rounded-md bg-primary-50 dark:bg-primary-500/10 px-2 py-1 text-xs font-semibold text-primary-700 dark:text-primary-300 ring-1 ring-inset ring-primary-600/20">
{{ $schedule->course->code ?? 'MATKUL' }}
</span>
@if ($schedule->mode)
@php
$color = $schedule->mode->getColor();
@endphp
<div class="p-5 space-y-4">
<div class="flex items-start justify-between gap-3">
<div class="flex items-center gap-2">
<span
class="inline-flex items-center rounded-md bg-{{ $color }}-50 dark:bg-{{ $color }}-500/10 px-2 py-1 text-xs font-semibold text-{{ $color }}-700 dark:text-{{ $color }}-300 ring-1 ring-inset ring-{{ $color }}-600/20">
{{ $schedule->mode->getLabel() }}
class="inline-flex items-center rounded-md bg-primary-50 dark:bg-primary-500/10 px-2 py-1 text-xs font-semibold text-primary-700 dark:text-primary-300 ring-1 ring-inset ring-primary-600/20">
{{ $schedule->course->code ?? 'MATKUL' }}
</span>
@if ($schedule->mode)
@php
$color = $schedule->mode->getColor();
@endphp
<span
class="inline-flex items-center rounded-md bg-{{ $color }}-50 dark:bg-{{ $color }}-500/10 px-2 py-1 text-xs font-semibold text-{{ $color }}-700 dark:text-{{ $color }}-300 ring-1 ring-inset ring-{{ $color }}-600/20">
{{ $schedule->mode->getLabel() }}
</span>
@endif
</div>
@if ($schedule->room)
<span
class="inline-flex items-center text-xs text-gray-500 dark:text-gray-400">
<x-heroicon-m-map-pin class="w-4 h-4 mr-1 opacity-70" />
{{ $schedule->room }}
</span>
@endif
</div>
@if ($schedule->room)
<span
class="inline-flex items-center text-xs text-gray-500 dark:text-gray-400">
<x-heroicon-m-map-pin class="w-4 h-4 mr-1 opacity-70" />
{{ $schedule->room }}
<h3
class="text-base font-semibold leading-tight text-gray-950 dark:text-white">
{{ $schedule->course->name ?? 'Mata Kuliah Tidak Diketahui' }}
</h3>
<div class="flex items-center text-sm text-gray-600 dark:text-gray-400">
<x-heroicon-m-user class="w-4 h-4 mr-1.5 opacity-70" />
<span class="truncate">
{{ $schedule->course->lecturer ?? 'Dosen Belum Ditentukan' }}
</span>
@endif
</div>
<h3
class="text-base font-semibold leading-tight text-gray-950 dark:text-white">
{{ $schedule->course->name ?? 'Mata Kuliah Tidak Diketahui' }}
</h3>
<div class="flex items-center text-sm text-gray-600 dark:text-gray-400">
<x-heroicon-m-user class="w-4 h-4 mr-1.5 opacity-70" />
<span class="truncate">
{{ $schedule->course->lecturer->full_name ?? 'Dosen Belum Ditentukan' }}
</span>
</div>
<div class="flex flex-wrap items-center gap-2 mt-1">
@if ($schedule->course?->semester)
<span
class="inline-flex items-center gap-1 rounded-md bg-amber-50 dark:bg-amber-500/10 px-2 py-1 text-xs font-medium text-amber-700 dark:text-amber-300 ring-1 ring-inset ring-amber-600/20">
<x-heroicon-m-academic-cap class="w-3 h-3" />
Semester {{ $schedule->course->semester }}
</span>
@endif
</div>
<div
class="flex items-center justify-between pt-3 border-t border-gray-100 dark:border-gray-800 text-sm">
<div
class="flex items-center font-medium text-gray-800 dark:text-gray-200">
<x-heroicon-o-clock class="w-4 h-4 mr-1.5 text-primary-500" />
{{ $schedule->start_time->format('H:i') }}
<span class="mx-1"></span>
{{ $schedule->end_time->format('H:i') }}
</div>
<span
class="text-[11px] font-semibold tracking-wide text-gray-400 uppercase">
{{ $schedule->course->credit ?? '-' }} SKS
</span>
</div>
@canAny(['Update:CourseSchedule', 'Delete:CourseSchedule'])
<div
class="flex items-center justify-end gap-1 pt-3 border-t border-gray-100 dark:border-gray-800">
{{ ($this->editScheduleAction)(['schedule' => $schedule->id]) }}
{{ ($this->deleteScheduleAction)(['schedule' => $schedule->id]) }}
<div class="flex flex-wrap items-center gap-2 mt-1">
@if ($schedule->course?->semester)
<span
class="inline-flex items-center gap-1 rounded-md bg-amber-50 dark:bg-amber-500/10 px-2 py-1 text-xs font-medium text-amber-700 dark:text-amber-300 ring-1 ring-inset ring-amber-600/20">
<x-heroicon-m-academic-cap class="w-3 h-3" />
Semester {{ $schedule->course->semester }}
</span>
@endif
</div>
@endcanAny
<div
class="flex items-center justify-between pt-3 border-t border-gray-100 dark:border-gray-800 text-sm">
<div
class="flex items-center font-medium text-gray-800 dark:text-gray-200">
<x-heroicon-o-clock class="w-4 h-4 mr-1.5 text-primary-500" />
{{ $schedule->start_time->format('H:i') }}
<span class="mx-1"></span>
{{ $schedule->end_time->format('H:i') }}
</div>
<span
class="text-[11px] font-semibold tracking-wide text-gray-400 uppercase">
{{ $schedule->course->credit ?? '-' }} SKS
</span>
</div>
@canAny(['Update:CourseSchedule', 'Delete:CourseSchedule'])
<div
class="flex items-center justify-end gap-1 pt-3 border-t border-gray-100 dark:border-gray-800">
{{ ($this->editScheduleAction)(['schedule' => $schedule->id]) }}
{{ ($this->deleteScheduleAction)(['schedule' => $schedule->id]) }}
</div>
@endcanAny
</div>
</div>
</div>
@endforeach
</div>
</section>
@endif
@endforeach
@endforeach
</div>
</section>
@endif
@endforeach
</div>
</div>
</div>
@endif
@endif
@if ($schedules->isEmpty())
<x-filament::empty-state icon="heroicon-o-calendar-days" heading="Tidak ada data yang ditemukan"
description="Setelah Anda membuat data pertama, maka akan muncul disini." iconColor="gray">
</x-filament::empty-state>
@endif
</x-filament::section>
@if ($schedules->isEmpty())
<x-filament::empty-state icon="heroicon-o-calendar-days" heading="Tidak ada data yang ditemukan"
description="Setelah Anda membuat data pertama, maka akan muncul disini." iconColor="gray">
</x-filament::empty-state>
@endif
</x-filament::section>
</div>
</x-filament-panels::page>

View File

@ -46,7 +46,7 @@
{{ $session->course->name }}
</h3>
<p class="text-sm text-gray-500 dark:text-gray-400 mt-0.5 font-medium">
{{ $session->course->code }} {{ $session->course->lecturer->full_name ?? '-' }}
{{ $session->course->code }} {{ $session->course->lecturer ?? '-' }}
</p>
</div>
<div @class([
@ -123,7 +123,7 @@ class="text-base font-bold text-gray-950 dark:text-white leading-tight group-hov
<div class="flex items-center text-sm text-gray-500 dark:text-gray-400">
<x-heroicon-m-user class="w-4 h-4 mr-1.5 opacity-70" />
<span
class="truncate">{{ $course->lecturer->full_name ?? 'Dosen Belum Ditentukan' }}</span>
class="truncate">{{ $course->lecturer ?? 'Dosen Belum Ditentukan' }}</span>
</div>
</div>
</div>

View File

@ -40,7 +40,7 @@ class="w-4 h-4 transition-transform duration-300"
</h3>
<div class="flex items-center text-sm text-gray-500 dark:text-gray-400">
<x-heroicon-m-user class="w-4 h-4 mr-1.5 opacity-70" />
<span class="truncate">{{ $course->lecturer->full_name ?? 'Dosen Belum Ditentukan' }}</span>
<span class="truncate">{{ $course->lecturer ?? 'Dosen Belum Ditentukan' }}</span>
</div>
</div>
</div>

View File

@ -108,7 +108,7 @@ class="badge bg-zinc-100 dark:bg-zinc-800 text-zinc-600 dark:text-zinc-400 capit
<h1 class="text-3xl font-bold tracking-tighter">{{ $course->name }}</h1>
<p class="text-zinc-500 flex items-center gap-2 tracking-tight text-sm font-medium">
<x-heroicon-o-user class="w-4 h-4 opacity-50" />
{{ $course->lecturer->full_name }}
{{ $course->lecturer }}
</p>
</div>