refactor: update course schedule and study group views for improved structure and functionality, including renaming views and enhancing data presentation
This commit is contained in:
parent
59af7bb595
commit
6d58190e28
@ -23,6 +23,7 @@
|
||||
use Filament\Schemas\Schema;
|
||||
use Filament\Support\Icons\Heroicon;
|
||||
use Illuminate\Support\Collection;
|
||||
use Livewire\Attributes\Computed;
|
||||
use UnitEnum;
|
||||
|
||||
class Index extends Page implements HasActions, HasForms
|
||||
@ -43,7 +44,7 @@ class Index extends Page implements HasActions, HasForms
|
||||
|
||||
protected static ?string $slug = 'information/course-schedules';
|
||||
|
||||
protected string $view = 'filament.pages.information.course-schedules';
|
||||
protected string $view = 'filament.pages.information.course-schedule.index';
|
||||
|
||||
public ?string $search = '';
|
||||
|
||||
@ -111,23 +112,39 @@ public function getSchedules(): Collection
|
||||
['day_of_week', 'asc'],
|
||||
['start_time', 'asc'],
|
||||
])
|
||||
->groupBy('day_of_week');
|
||||
->groupBy('day_of_week')
|
||||
->map(function ($daySchedules) {
|
||||
return $daySchedules->map(fn ($schedule) => (object) [
|
||||
'id' => $schedule->id,
|
||||
'course_code' => $schedule->course->code ?? 'MATKUL',
|
||||
'course_name' => $schedule->course->name ?? 'Mata Kuliah Tidak Diketahui',
|
||||
'lecturer' => $schedule->course->lecturer ?? 'Dosen Belum Ditentukan',
|
||||
'semester' => $schedule->course->semester,
|
||||
'credit' => $schedule->course->credit ?? '-',
|
||||
'room' => $schedule->room,
|
||||
'mode_label' => $schedule->mode?->getLabel(),
|
||||
'mode_color' => $schedule->mode?->getColor(),
|
||||
'time_range' => $schedule->start_time->format('H:i').' – '.$schedule->end_time->format('H:i'),
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
protected function getViewData(): array
|
||||
#[Computed]
|
||||
public function schedulesGrouped(): Collection
|
||||
{
|
||||
return [
|
||||
'schedules' => $this->getSchedules(),
|
||||
'days' => [
|
||||
1 => 'Senin',
|
||||
2 => 'Selasa',
|
||||
3 => 'Rabu',
|
||||
4 => 'Kamis',
|
||||
5 => 'Jumat',
|
||||
6 => 'Sabtu',
|
||||
7 => 'Minggu',
|
||||
],
|
||||
$days = [
|
||||
1 => 'Senin',
|
||||
2 => 'Selasa',
|
||||
3 => 'Rabu',
|
||||
4 => 'Kamis',
|
||||
5 => 'Jumat',
|
||||
6 => 'Sabtu',
|
||||
7 => 'Minggu',
|
||||
];
|
||||
|
||||
return $this->getSchedules()->mapWithKeys(function ($schedules, $day) use ($days) {
|
||||
return [$days[$day] ?? 'Lainnya' => $schedules];
|
||||
});
|
||||
}
|
||||
|
||||
public function scheduleFormSchema(): array
|
||||
|
||||
@ -24,7 +24,9 @@
|
||||
use Filament\Schemas\Schema;
|
||||
use Filament\Support\Icons\Heroicon;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
use Livewire\Attributes\Computed;
|
||||
use UnitEnum;
|
||||
|
||||
class Index extends Page implements HasActions, HasForms
|
||||
@ -48,7 +50,7 @@ public static function getPagePermission(): string
|
||||
return 'View:StudyGroup';
|
||||
}
|
||||
|
||||
protected string $view = 'filament.pages.learning.study-groups';
|
||||
protected string $view = 'filament.pages.learning.study-group.index';
|
||||
|
||||
public ?int $course_id = null;
|
||||
|
||||
@ -101,7 +103,8 @@ public function deleteStudyGroupAction(): Action
|
||||
->visible(fn () => auth()->user()->can('Delete:StudyGroup'));
|
||||
}
|
||||
|
||||
public function getStudyGroups(): Collection
|
||||
#[Computed]
|
||||
public function studyGroups(): Collection
|
||||
{
|
||||
$query = StudyGroup::with(['leader', 'students', 'courses'])
|
||||
->whereHas('courses', fn ($q) => $q->where('courses.semester', app(GeneralSettings::class)->current_semester));
|
||||
@ -110,15 +113,35 @@ public function getStudyGroups(): Collection
|
||||
$query->whereHas('courses', fn ($q) => $q->where('courses.id', $this->course_id));
|
||||
}
|
||||
|
||||
return $query->latest()->get();
|
||||
}
|
||||
$studentId = auth()->user()?->student?->id;
|
||||
|
||||
protected function getViewData(): array
|
||||
{
|
||||
return [
|
||||
'groups' => $this->getStudyGroups(),
|
||||
'studentId' => auth()->user()?->student?->id,
|
||||
];
|
||||
return $query->latest()->get()->map(function ($record) use ($studentId) {
|
||||
$isMyGroup = ($studentId && ($record->leader_id === $studentId || $record->students->contains($studentId)));
|
||||
|
||||
return (object) [
|
||||
'id' => $record->id,
|
||||
'name' => $record->name,
|
||||
'is_my_group' => $isMyGroup,
|
||||
'leader_avatar' => $record->leader?->user?->facehash_avatar_url,
|
||||
'leader_name' => $record->leader->full_name ?? 'Belum Ditentukan',
|
||||
'is_leader' => $studentId && $record->leader_id === $studentId,
|
||||
'courses' => $record->courses->map(fn ($c) => (object) [
|
||||
'name' => $c->name,
|
||||
]),
|
||||
'students_count' => $record->students->count(),
|
||||
'students' => $record->students->map(fn ($s) => (object) [
|
||||
'id' => $s->id,
|
||||
'full_name' => $s->full_name,
|
||||
'is_me' => $studentId && $s->id === $studentId,
|
||||
]),
|
||||
// Pre-calculated classes
|
||||
'card_classes' => Arr::toCssClasses([
|
||||
'fi-card flex flex-col justify-between rounded-xl border transition duration-200 group relative',
|
||||
'bg-white dark:bg-gray-900 border-gray-200 dark:border-gray-700 shadow-sm hover:shadow-md' => ! $isMyGroup,
|
||||
'bg-primary-50/30 dark:bg-primary-900/10 border-primary-500 shadow-md ring-1 ring-primary-500' => $isMyGroup,
|
||||
]),
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
public function isMyGroup(StudyGroup $record): bool
|
||||
|
||||
@ -14,13 +14,14 @@
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Resources\Pages\Page;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Support\Arr;
|
||||
use Livewire\Attributes\Computed;
|
||||
|
||||
class ListAssignments extends Page
|
||||
{
|
||||
protected static string $resource = AssignmentResource::class;
|
||||
|
||||
protected string $view = 'filament.resources.learning.assignments.pages.list-assignments';
|
||||
protected string $view = 'filament.resources.learning.assignments.index';
|
||||
|
||||
protected static ?string $title = 'Tugas';
|
||||
|
||||
@ -139,8 +140,33 @@ public function assignmentCards()
|
||||
'is_new' => $isNew,
|
||||
'is_pinned' => $isPinned,
|
||||
'status_label' => $statusLabel,
|
||||
'status_color' => $statusColor,
|
||||
'status_icon' => $statusIcon,
|
||||
// Pre-calculated classes
|
||||
'card_classes' => Arr::toCssClasses([
|
||||
'group flex w-full rounded-xl border transition-all overflow-hidden relative',
|
||||
'border-primary-300 dark:border-primary-700 bg-primary-50/30 dark:bg-primary-900/10 shadow-sm' => $isPinned,
|
||||
'border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800' => ! $isPinned && $canSubmitActual,
|
||||
'border-gray-100 dark:border-gray-800 bg-gray-50 dark:bg-gray-800/40 opacity-80' => ! $canSubmitActual,
|
||||
]),
|
||||
'icon_wrapper_classes' => Arr::toCssClasses([
|
||||
'flex h-11 w-11 shrink-0 items-center justify-center rounded-lg',
|
||||
'bg-success-50 dark:bg-success-900/20 text-success-600 dark:text-success-400' => $isSubmitted,
|
||||
'bg-danger-50 dark:bg-danger-900/20 text-danger-600 dark:text-danger-400' => ! $isSubmitted && $isOverdue,
|
||||
'bg-warning-50 dark:bg-warning-900/20 text-warning-600 dark:text-warning-400' => ! $isSubmitted && ! $isOverdue,
|
||||
]),
|
||||
'status_badge_classes' => Arr::toCssClasses([
|
||||
'inline-flex items-center gap-1 rounded-full px-2.5 py-0.5 text-xs font-medium',
|
||||
'bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400' => $statusColor === 'amber',
|
||||
'bg-success-100 text-success-700 dark:bg-success-900/30 dark:text-success-400' => $statusColor === 'success',
|
||||
'bg-danger-100 text-danger-700 dark:bg-danger-900/30 dark:text-danger-400' => $statusColor === 'danger',
|
||||
'bg-warning-100 text-warning-700 dark:bg-warning-900/30 dark:text-warning-400' => $statusColor === 'warning',
|
||||
'bg-gray-100 text-gray-500 dark:bg-gray-800 dark:text-gray-400' => $statusColor === 'gray',
|
||||
]),
|
||||
'indicator_icon_classes' => Arr::toCssClasses([
|
||||
'h-5 w-5 opacity-20',
|
||||
'text-primary-500' => $isLeader,
|
||||
'text-gray-400' => ! $isLeader,
|
||||
]),
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Resources\Pages\Page;
|
||||
use Filament\Support\Enums\Width;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
use Joaopaulolndev\FilamentPdfViewer\Infolists\Components\PdfViewerEntry;
|
||||
use Livewire\Attributes\Computed;
|
||||
@ -18,7 +19,7 @@ class SubmissionDetailPage extends Page
|
||||
{
|
||||
protected static string $resource = AssignmentResource::class;
|
||||
|
||||
protected string $view = 'filament.resources.learning.assignments.pages.submission-detail';
|
||||
protected string $view = 'filament.resources.learning.assignments.submission-detail';
|
||||
|
||||
protected static ?string $title = 'Detail Rekap Pengumpulan';
|
||||
|
||||
@ -32,29 +33,35 @@ public function statCards(): array
|
||||
'label' => $this->record->type === AssignmentType::Individual ? 'Total Mahasiswa' : 'Total Kelompok',
|
||||
'value' => $this->totalCount,
|
||||
'icon' => 'heroicon-o-user-group',
|
||||
'color' => 'bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-900',
|
||||
'color_classes' => 'bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-900',
|
||||
],
|
||||
[
|
||||
'label' => 'Sudah Kumpul',
|
||||
'value' => $this->doneCount,
|
||||
'icon' => 'heroicon-o-check-circle',
|
||||
'color' => 'bg-success-100 dark:bg-success-900/30 text-success-700 dark:text-success-400',
|
||||
'color_classes' => 'bg-success-100 dark:bg-success-900/30 text-success-700 dark:text-success-400',
|
||||
],
|
||||
[
|
||||
'label' => 'Belum Kumpul',
|
||||
'value' => $this->totalCount - $this->doneCount,
|
||||
'icon' => 'heroicon-o-x-circle',
|
||||
'color' => 'bg-danger-100 dark:bg-danger-900/30 text-danger-700 dark:text-danger-400',
|
||||
'color_classes' => 'bg-danger-100 dark:bg-danger-900/30 text-danger-700 dark:text-danger-400',
|
||||
],
|
||||
[
|
||||
'label' => 'Persentase',
|
||||
'value' => $this->percentage.'%',
|
||||
'icon' => 'heroicon-o-chart-bar',
|
||||
'color' => 'bg-primary-100 dark:bg-primary-900/30 text-primary-700 dark:text-primary-400',
|
||||
'color_classes' => 'bg-primary-100 dark:bg-primary-900/30 text-primary-700 dark:text-primary-400',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
#[Computed]
|
||||
public function progressColorClass(): string
|
||||
{
|
||||
return $this->percentage === 100 ? 'bg-success-500' : ($this->percentage > 0 ? 'bg-warning-500' : 'bg-gray-300');
|
||||
}
|
||||
|
||||
#[Computed]
|
||||
public function submissionSummary(): Collection
|
||||
{
|
||||
@ -78,10 +85,16 @@ public function submissionSummary(): Collection
|
||||
'is_individual' => true,
|
||||
'primary_name' => $student->full_name,
|
||||
'secondary_info' => $student->student_number,
|
||||
'submission' => $submission,
|
||||
'submission_id' => $submission?->id,
|
||||
'submitted' => $isSubmitted,
|
||||
'submitted_at_formatted' => $isSubmitted ? $submission->submitted_at?->translatedFormat('l, d F Y, H:i') : '-',
|
||||
'has_file' => $isSubmitted && $submission->hasMedia('submission'),
|
||||
'status_classes' => Arr::toCssClasses([
|
||||
'inline-flex items-center gap-1 rounded-full px-2.5 py-0.5 text-xs font-medium',
|
||||
'bg-success-100 text-success-700 dark:bg-success-900/30 dark:text-success-400' => $isSubmitted,
|
||||
'bg-danger-100 text-danger-700 dark:bg-danger-900/30 dark:text-danger-400' => ! $isSubmitted,
|
||||
]),
|
||||
'status_label' => $isSubmitted ? '✓ Terkumpul' : 'Belum',
|
||||
];
|
||||
})->sortBy('secondary_info')->values();
|
||||
} else {
|
||||
@ -101,10 +114,16 @@ public function submissionSummary(): Collection
|
||||
'is_individual' => false,
|
||||
'primary_name' => $group->name,
|
||||
'secondary_info' => $isSubmitted ? $submission->student->full_name : '-',
|
||||
'submission' => $submission,
|
||||
'submission_id' => $submission?->id,
|
||||
'submitted' => $isSubmitted,
|
||||
'submitted_at_formatted' => $isSubmitted ? $submission->submitted_at?->translatedFormat('l, d F Y, H:i') : '-',
|
||||
'has_file' => $isSubmitted && $submission->hasMedia('submission'),
|
||||
'status_classes' => Arr::toCssClasses([
|
||||
'inline-flex items-center gap-1 rounded-full px-2.5 py-0.5 text-xs font-medium',
|
||||
'bg-success-100 text-success-700 dark:bg-success-900/30 dark:text-success-400' => $isSubmitted,
|
||||
'bg-danger-100 text-danger-700 dark:bg-danger-900/30 dark:text-danger-400' => ! $isSubmitted,
|
||||
]),
|
||||
'status_label' => $isSubmitted ? '✓ Terkumpul' : 'Belum',
|
||||
];
|
||||
})->sortBy('primary_name')->values();
|
||||
}
|
||||
@ -136,7 +155,8 @@ public function isOverdue(): bool
|
||||
return now()->isAfter($this->record->due_date);
|
||||
}
|
||||
|
||||
public function getAssignmentInfo(): array
|
||||
#[Computed]
|
||||
public function assignmentSummary(): array
|
||||
{
|
||||
return [
|
||||
'title' => $this->record->title,
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
use App\Models\Student;
|
||||
use App\Models\StudyGroup;
|
||||
use Filament\Resources\Pages\Page;
|
||||
use Illuminate\Support\Arr;
|
||||
use Livewire\Attributes\Computed;
|
||||
use Livewire\WithFileUploads;
|
||||
|
||||
@ -20,7 +21,7 @@ class SubmitAssignmentPage extends Page
|
||||
|
||||
protected static string $resource = AssignmentResource::class;
|
||||
|
||||
protected string $view = 'filament.resources.learning.assignments.pages.submit-assignment';
|
||||
protected string $view = 'filament.resources.learning.assignments.submit-assignment';
|
||||
|
||||
protected static ?string $title = 'Kumpulkan Tugas';
|
||||
|
||||
@ -38,6 +39,11 @@ public function statusCards(): array
|
||||
'icon' => 'heroicon-o-clock',
|
||||
'is_danger' => $this->isOverdue,
|
||||
'badge' => $this->isOverdue ? '(Terlewat)' : null,
|
||||
'icon_classes' => Arr::toCssClasses([
|
||||
'flex h-9 w-9 shrink-0 items-center justify-center rounded-lg',
|
||||
'bg-danger-50 dark:bg-danger-900/20 text-danger-600' => $this->isOverdue,
|
||||
'bg-warning-50 dark:bg-warning-900/20 text-warning-600' => ! $this->isOverdue,
|
||||
]),
|
||||
],
|
||||
[
|
||||
'label' => 'Status Pengumpulan',
|
||||
@ -45,6 +51,11 @@ public function statusCards(): array
|
||||
'icon' => $this->isOverdue ? 'heroicon-o-lock-closed' : 'heroicon-o-check-circle',
|
||||
'is_danger' => $this->isOverdue,
|
||||
'is_success' => ! $this->isOverdue,
|
||||
'icon_classes' => Arr::toCssClasses([
|
||||
'flex h-9 w-9 shrink-0 items-center justify-center rounded-lg',
|
||||
'bg-danger-50 dark:bg-danger-900/20 text-danger-600' => $this->isOverdue,
|
||||
'bg-success-50 dark:bg-success-900/20 text-success-600' => ! $this->isOverdue,
|
||||
]),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
@ -20,6 +20,8 @@
|
||||
use Filament\Tables\Filters\SelectFilter;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Arr;
|
||||
use Livewire\Attributes\Computed;
|
||||
|
||||
class ManageAttendances extends Page implements HasForms, HasTable
|
||||
{
|
||||
@ -31,7 +33,7 @@ class ManageAttendances extends Page implements HasForms, HasTable
|
||||
|
||||
protected static ?string $navigationLabel = 'Presensi';
|
||||
|
||||
protected string $view = 'filament.resources.learning.attendances.pages.list-attendances';
|
||||
protected string $view = 'filament.resources.learning.attendances.index';
|
||||
|
||||
public function getSchedules()
|
||||
{
|
||||
@ -59,7 +61,8 @@ public function getSchedules()
|
||||
->get();
|
||||
}
|
||||
|
||||
public function getScheduleCards()
|
||||
#[Computed]
|
||||
public function scheduleCards()
|
||||
{
|
||||
return $this->getSchedules()
|
||||
->map(function ($schedule) {
|
||||
@ -68,7 +71,6 @@ public function getScheduleCards()
|
||||
|
||||
$now = now();
|
||||
$startTime = now()->setTimeFrom($schedule->start_time);
|
||||
|
||||
$canAttend = $now->greaterThanOrEqualTo($startTime);
|
||||
|
||||
$statusLabel = 'Belum Presensi';
|
||||
@ -96,17 +98,46 @@ public function getScheduleCards()
|
||||
'status_color' => $statusColor,
|
||||
'status_icon' => $statusIcon,
|
||||
'attended_at' => $isAttended ? $attendance->attended_at->format('H:i') : null,
|
||||
// Pre-calculated classes
|
||||
'card_classes' => Arr::toCssClasses([
|
||||
'fi-card flex flex-col justify-between rounded-xl border transition duration-200 group relative',
|
||||
'bg-white dark:bg-gray-900 border-gray-200 dark:border-gray-700 shadow-sm hover:shadow-md' => ! $isAttended,
|
||||
'bg-primary-50/30 dark:bg-primary-900/10 border-primary-500 shadow-md ring-1 ring-primary-500' => $isAttended,
|
||||
]),
|
||||
'title_classes' => Arr::toCssClasses([
|
||||
'text-lg font-bold leading-tight flex items-center gap-2 transition-colors',
|
||||
'text-gray-950 dark:text-white group-hover:text-primary-600 dark:group-hover:text-primary-400' => ! $isAttended,
|
||||
'text-primary-900 dark:text-primary-100' => $isAttended,
|
||||
]),
|
||||
'lecturer_wrapper_classes' => Arr::toCssClasses([
|
||||
'flex items-center gap-1.5',
|
||||
'text-gray-500 dark:text-gray-400' => ! $isAttended,
|
||||
'text-primary-600 dark:text-primary-400' => $isAttended,
|
||||
]),
|
||||
'icon_wrapper_classes' => Arr::toCssClasses([
|
||||
'w-8 h-8 rounded-full flex items-center justify-center mr-3 shrink-0 border',
|
||||
'bg-primary-100 dark:bg-primary-900/40 border-primary-200 dark:border-primary-800' => ! $isAttended,
|
||||
'bg-white dark:bg-primary-800 border-primary-300 dark:border-primary-600' => $isAttended,
|
||||
]),
|
||||
'time_label_classes' => Arr::toCssClasses([
|
||||
'truncate font-medium',
|
||||
'text-gray-100 dark:text-gray-200' => ! $isAttended,
|
||||
'text-primary-900 dark:text-primary-50' => $isAttended,
|
||||
]),
|
||||
'status_badge_classes' => Arr::toCssClasses([
|
||||
'text-[10px] font-bold px-1.5 py-0.5 rounded ring-1 ring-inset',
|
||||
'bg-success-50 dark:bg-success-500/10 text-success-600 dark:text-success-400 ring-success-600/20' => $isAttended,
|
||||
'bg-warning-50 dark:bg-warning-500/10 text-warning-600 dark:text-warning-400 ring-warning-600/20' => ! $isAttended && $canAttend && ! $isAttended,
|
||||
'bg-gray-50 dark:bg-gray-500/10 text-gray-600 dark:text-gray-400 ring-gray-600/20' => ! $isAttended && ! $canAttend,
|
||||
]),
|
||||
'footer_classes' => Arr::toCssClasses([
|
||||
'flex items-center justify-end gap-2 p-4 pt-0 rounded-b-xl',
|
||||
'bg-primary-100/30 dark:bg-primary-900/10 pt-4' => $isAttended || ($canAttend && ! $isAttended),
|
||||
]),
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
protected function getViewData(): array
|
||||
{
|
||||
return [
|
||||
'scheduleCards' => $this->getScheduleCards(),
|
||||
];
|
||||
}
|
||||
|
||||
public function attend(int $scheduleId): void
|
||||
{
|
||||
/** @var User $user */
|
||||
|
||||
@ -16,6 +16,7 @@
|
||||
use Filament\Resources\Pages\Page;
|
||||
use Filament\Support\Enums\Width;
|
||||
use Illuminate\Support\Collection;
|
||||
use Livewire\Attributes\Computed;
|
||||
|
||||
class ListCourseSessions extends Page implements HasActions, HasForms
|
||||
{
|
||||
@ -23,7 +24,7 @@ class ListCourseSessions extends Page implements HasActions, HasForms
|
||||
|
||||
protected static string $resource = ClassSessionResource::class;
|
||||
|
||||
protected string $view = 'filament.resources.learning.class-sessions.pages.list-course-sessions';
|
||||
protected string $view = 'filament.resources.learning.class-sessions.index';
|
||||
|
||||
public $courseId;
|
||||
|
||||
@ -32,19 +33,35 @@ public function mount($courseId): void
|
||||
$this->courseId = $courseId;
|
||||
}
|
||||
|
||||
public function getCourse(): Course
|
||||
#[Computed]
|
||||
public function course(): Course
|
||||
{
|
||||
return Course::findOrFail($this->courseId);
|
||||
}
|
||||
|
||||
public function getSessions(): Collection
|
||||
#[Computed]
|
||||
public function sessions(): Collection
|
||||
{
|
||||
return $this->getCourse()->classSessions()->orderBy('session_number', 'asc')->get();
|
||||
return $this->course->classSessions()
|
||||
->orderBy('session_number', 'asc')
|
||||
->get()
|
||||
->map(fn ($session) => (object) [
|
||||
'id' => $session->id,
|
||||
'session_number' => $session->session_number,
|
||||
'date_formatted' => $session->date->format('l, d F Y'),
|
||||
'time_range' => $session->start_time->format('H:i').' - '.$session->end_time->format('H:i'),
|
||||
]);
|
||||
}
|
||||
|
||||
#[Computed]
|
||||
public function description(): string
|
||||
{
|
||||
return 'Data sesi pembelajaran untuk mata kuliah '.$this->course->name;
|
||||
}
|
||||
|
||||
public function getTitle(): string
|
||||
{
|
||||
return 'Sesi Kelas - '.$this->getCourse()->name;
|
||||
return 'Sesi Kelas - '.$this->course->name;
|
||||
}
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
|
||||
@ -2,13 +2,11 @@
|
||||
|
||||
namespace App\Filament\Resources\Learning\ClassSessions\Pages;
|
||||
|
||||
use App\Filament\Actions\Cheerful\DeleteAction;
|
||||
use App\Filament\Resources\Learning\ClassSessions\ClassSessionResource;
|
||||
use App\Models\ClassSession;
|
||||
use App\Models\Course;
|
||||
use App\Models\CourseSchedule;
|
||||
use App\Settings\GeneralSettings;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\Concerns\InteractsWithActions;
|
||||
use Filament\Actions\Contracts\HasActions;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
@ -16,7 +14,9 @@
|
||||
use Filament\Forms\Contracts\HasForms;
|
||||
use Filament\Resources\Pages\Page;
|
||||
use Filament\Schemas\Schema;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Collection;
|
||||
use Livewire\Attributes\Computed;
|
||||
|
||||
class ManageClassSessions extends Page implements HasActions, HasForms
|
||||
{
|
||||
@ -35,6 +35,12 @@ public function mount(): void
|
||||
$this->form->fill();
|
||||
}
|
||||
|
||||
#[Computed]
|
||||
public function todayDate(): string
|
||||
{
|
||||
return now()->translatedFormat('l, d F Y');
|
||||
}
|
||||
|
||||
public function form(Schema $schema): Schema
|
||||
{
|
||||
return $schema
|
||||
@ -51,18 +57,8 @@ public function form(Schema $schema): Schema
|
||||
]);
|
||||
}
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function deleteSessionAction(): Action
|
||||
{
|
||||
return DeleteAction::make('deleteSession')
|
||||
->record(fn (array $arguments) => ClassSession::find($arguments['session']));
|
||||
}
|
||||
|
||||
public function getTodaySessions(): Collection
|
||||
#[Computed]
|
||||
public function todaySessions(): Collection
|
||||
{
|
||||
$dayOfWeek = now()->dayOfWeekIso; // 1 (Mon) - 7 (Sun)
|
||||
|
||||
@ -80,21 +76,52 @@ public function getTodaySessions(): Collection
|
||||
|
||||
return $schedules->map(function ($schedule) {
|
||||
$session = $schedule->course->classSessions->first();
|
||||
$isPending = ! $session;
|
||||
$sessionNumber = $session?->session_number ?? (ClassSession::where('course_id', $schedule->course_id)->max('session_number') ?? 0) + 1;
|
||||
|
||||
return (object) [
|
||||
'id' => $session?->id,
|
||||
'course' => $schedule->course,
|
||||
'session_number' => $session?->session_number ?? (ClassSession::where('course_id', $schedule->course_id)->max('session_number') ?? 0) + 1,
|
||||
'start_time' => $schedule->start_time,
|
||||
'end_time' => $schedule->end_time,
|
||||
'title' => $session?->title,
|
||||
'is_pending' => ! $session,
|
||||
'is_pending' => $isPending,
|
||||
'session_number' => $sessionNumber,
|
||||
'course_name' => $schedule->course->name,
|
||||
'course_code' => $schedule->course->code,
|
||||
'lecturer' => $schedule->course->lecturer ?? '-',
|
||||
'time_range' => $schedule->start_time->format('H:i').' - '.$schedule->end_time->format('H:i'),
|
||||
'attendances_count' => $session?->attendances_count ?? 0,
|
||||
'url' => $isPending ? null : ClassSessionResource::getUrl('course', ['courseId' => $schedule->course->id]),
|
||||
|
||||
// Pre-calculated classes
|
||||
'card_classes' => Arr::toCssClasses([
|
||||
'group flex w-full rounded-xl border overflow-hidden relative transition-all hover:shadow-md',
|
||||
'border-primary-300 dark:border-primary-700 bg-primary-50/30 dark:bg-primary-900/10 shadow-sm' => ! $isPending,
|
||||
'border-gray-300 dark:border-gray-700 bg-gray-50/30 dark:bg-gray-800/10 border-dashed' => $isPending,
|
||||
]),
|
||||
'session_badge_classes' => Arr::toCssClasses([
|
||||
'flex h-12 w-12 shrink-0 flex-col items-center justify-center rounded-lg font-bold border',
|
||||
'bg-primary-100 dark:bg-primary-800 text-primary-700 dark:text-primary-300 border-primary-200 dark:border-primary-700' => ! $isPending,
|
||||
'bg-gray-100 dark:bg-gray-800 text-gray-700 dark:text-gray-300 border-gray-200 dark:border-gray-700' => $isPending,
|
||||
]),
|
||||
'title_classes' => Arr::toCssClasses([
|
||||
'font-bold transition-colors',
|
||||
'text-gray-900 dark:text-white group-hover:text-primary-600' => ! $isPending,
|
||||
'text-gray-500 dark:text-gray-400' => $isPending,
|
||||
]),
|
||||
'status_badge_classes' => Arr::toCssClasses([
|
||||
'flex items-center gap-1.5 rounded-full px-3 py-1 text-[11px] font-bold shadow-sm',
|
||||
'bg-primary-500 text-white animate-pulse' => ! $isPending,
|
||||
'bg-gray-500 text-white opacity-60' => $isPending,
|
||||
]),
|
||||
'attendance_section_classes' => Arr::toCssClasses([
|
||||
'flex flex-col items-center justify-center border-l p-2 bg-white/30 dark:bg-black/10 transition-colors',
|
||||
'border-primary-200 dark:border-primary-800 group-hover:bg-primary-500/5' => ! $isPending,
|
||||
'border-gray-200 dark:border-gray-800' => $isPending,
|
||||
]),
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
public function getCourses(): Collection
|
||||
#[Computed]
|
||||
public function courses(): Collection
|
||||
{
|
||||
$query = Course::query()
|
||||
->where('semester', app(GeneralSettings::class)->current_semester)
|
||||
@ -109,6 +136,14 @@ public function getCourses(): Collection
|
||||
}
|
||||
|
||||
return $query->get()
|
||||
->sortBy('name');
|
||||
->sortBy('name')
|
||||
->map(fn ($course) => (object) [
|
||||
'id' => $course->id,
|
||||
'name' => $course->name,
|
||||
'code' => $course->code,
|
||||
'lecturer' => $course->lecturer ?? 'Dosen Belum Ditentukan',
|
||||
'sessions_count' => $course->classSessions->count(),
|
||||
'url' => ClassSessionResource::getUrl('course', ['courseId' => $course->id]),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,121 @@
|
||||
<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 class="text-sm text-info-800 dark:text-info-200">
|
||||
Geser ke samping untuk melihat hari lainnya.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between gap-4 mb-6">
|
||||
<div class="w-full max-w-xl">
|
||||
{{ $this->form }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if ($this->schedulesGrouped->isNotEmpty())
|
||||
<div class="overflow-x-auto">
|
||||
<div class="flex flex-nowrap gap-6 min-w-max pb-4">
|
||||
|
||||
@foreach ($this->schedulesGrouped as $dayName => $daySchedules)
|
||||
<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="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 }}
|
||||
</span>
|
||||
|
||||
@if ($schedule->mode_label)
|
||||
<span
|
||||
class="inline-flex items-center rounded-md bg-{{ $schedule->mode_color }}-50 dark:bg-{{ $schedule->mode_color }}-500/10 px-2 py-1 text-xs font-semibold text-{{ $schedule->mode_color }}-700 dark:text-{{ $schedule->mode_color }}-300 ring-1 ring-inset ring-{{ $schedule->mode_color }}-600/20">
|
||||
{{ $schedule->mode_label }}
|
||||
</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>
|
||||
|
||||
<h3
|
||||
class="text-base font-semibold leading-tight text-gray-950 dark:text-white">
|
||||
{{ $schedule->course_name }}
|
||||
</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->lecturer }}</span>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-2 mt-1">
|
||||
@if ($schedule->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->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->time_range }}
|
||||
</div>
|
||||
|
||||
<span
|
||||
class="text-[11px] font-semibold tracking-wide text-gray-400 uppercase">
|
||||
{{ $schedule->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>
|
||||
@endforeach
|
||||
</div>
|
||||
</section>
|
||||
@endforeach
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@else
|
||||
<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>
|
||||
</x-filament-panels::page>
|
||||
@ -1,136 +0,0 @@
|
||||
<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 class="text-sm text-info-800 dark:text-info-200">
|
||||
Geser ke samping untuk melihat hari lainnya.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between gap-4 mb-6">
|
||||
<div class="w-full max-w-xl">
|
||||
{{ $this->form }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@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
|
||||
|
||||
@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="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
|
||||
<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>
|
||||
|
||||
<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>
|
||||
</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>
|
||||
@endcanAny
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</section>
|
||||
@endif
|
||||
@endforeach
|
||||
|
||||
</div>
|
||||
</div>
|
||||
@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>
|
||||
</x-filament-panels::page>
|
||||
@ -9,38 +9,24 @@
|
||||
</x-filament::section>
|
||||
|
||||
<!-- Today's Sessions Section -->
|
||||
@if ($this->getTodaySessions()->isNotEmpty())
|
||||
@if ($this->todaySessions->isNotEmpty())
|
||||
<x-filament::section icon="heroicon-o-bolt" icon-color="primary">
|
||||
<x-slot name="heading">
|
||||
Sesi Hari Ini
|
||||
</x-slot>
|
||||
<x-slot name="description">
|
||||
Sesi yang dijadwalkan pada {{ now()->translatedFormat('l, d F Y') }}
|
||||
Sesi yang dijadwalkan pada {{ $this->today_date }}
|
||||
</x-slot>
|
||||
|
||||
<div class="space-y-4">
|
||||
@foreach ($this->getTodaySessions() as $session)
|
||||
@php
|
||||
$cardClass = \Illuminate\Support\Arr::toCssClasses([
|
||||
'group flex w-full rounded-xl border overflow-hidden relative transition-all hover:shadow-md',
|
||||
'border-primary-300 dark:border-primary-700 bg-primary-50/30 dark:bg-primary-900/10 shadow-sm' => !$session->is_pending,
|
||||
'border-gray-300 dark:border-gray-700 bg-gray-50/30 dark:bg-gray-800/10 border-dashed' => $session->is_pending,
|
||||
]);
|
||||
@endphp
|
||||
|
||||
@foreach ($this->todaySessions as $session)
|
||||
@if ($session->is_pending)
|
||||
<div class="{{ $cardClass }}">
|
||||
<div class="{{ $session->card_classes }}">
|
||||
@else
|
||||
<a href="{{ \App\Filament\Resources\Learning\ClassSessions\ClassSessionResource::getUrl('course', ['courseId' => $session->course->id]) }}"
|
||||
class="{{ $cardClass }}">
|
||||
<a href="{{ $session->url }}" class="{{ $session->card_classes }}">
|
||||
@endif
|
||||
<div class="flex flex-1 items-start gap-4 p-5">
|
||||
<div @class([
|
||||
'flex h-12 w-12 shrink-0 flex-col items-center justify-center rounded-lg font-bold border',
|
||||
'bg-primary-100 dark:bg-primary-800 text-primary-700 dark:text-primary-300 border-primary-200 dark:border-primary-700' => !$session->is_pending,
|
||||
'bg-gray-100 dark:bg-gray-800 text-gray-700 dark:text-gray-300 border-gray-200 dark:border-gray-700' =>
|
||||
$session->is_pending,
|
||||
])>
|
||||
<div class="{{ $session->session_badge_classes }}">
|
||||
<span class="text-[9px] uppercase leading-none opacity-60 mb-0.5 font-bold">Sesi</span>
|
||||
<span class="text-xl leading-none italic">#{{ $session->session_number }}</span>
|
||||
</div>
|
||||
@ -48,25 +34,16 @@ class="{{ $cardClass }}">
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="flex items-start justify-between gap-3 flex-wrap">
|
||||
<div>
|
||||
<h3 @class([
|
||||
'font-bold transition-colors',
|
||||
'text-gray-900 dark:text-white group-hover:text-primary-600' => !$session->is_pending,
|
||||
'text-gray-500 dark:text-gray-400' => $session->is_pending,
|
||||
])>
|
||||
{{ $session->course->name }}
|
||||
<h3 class="{{ $session->title_classes }}">
|
||||
{{ $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 ?? '-' }}
|
||||
{{ $session->course_code }} • {{ $session->lecturer }}
|
||||
</p>
|
||||
</div>
|
||||
<div @class([
|
||||
'flex items-center gap-1.5 rounded-full px-3 py-1 text-[11px] font-bold shadow-sm',
|
||||
'bg-primary-500 text-white animate-pulse' => !$session->is_pending,
|
||||
'bg-gray-500 text-white opacity-60' => $session->is_pending,
|
||||
])>
|
||||
<div class="{{ $session->status_badge_classes }}">
|
||||
<x-heroicon-s-clock class="w-3.5 h-3.5" />
|
||||
{{ $session->start_time->format('H:i') }} -
|
||||
{{ $session->end_time->format('H:i') }}
|
||||
{{ $session->time_range }}
|
||||
</div>
|
||||
@if ($session->is_pending)
|
||||
<div
|
||||
@ -78,11 +55,7 @@ class="mt-2 flex items-center gap-1.5 text-[10px] text-gray-400 dark:text-gray-5
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div @class([
|
||||
'flex flex-col items-center justify-center border-l p-2 bg-white/30 dark:bg-black/10 transition-colors',
|
||||
'border-primary-200 dark:border-primary-800 group-hover:bg-primary-500/5' => !$session->is_pending,
|
||||
'border-gray-200 dark:border-gray-800' => $session->is_pending,
|
||||
])>
|
||||
<div class="{{ $session->attendance_section_classes }}">
|
||||
@if ($session->is_pending)
|
||||
<div class="flex items-center gap-2 text-gray-400 dark:text-gray-500 p-2 opacity-50">
|
||||
<x-heroicon-m-clock class="w-5 h-5" />
|
||||
@ -119,10 +92,10 @@ class="mt-2 flex items-center gap-1.5 text-[10px] text-gray-400 dark:text-gray-5
|
||||
Pilih mata kuliah untuk melihat dan mengelola semua riwayat sesi.
|
||||
</x-slot>
|
||||
|
||||
@if ($this->getCourses()->isNotEmpty())
|
||||
@if ($this->courses->isNotEmpty())
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||
@foreach ($this->getCourses() as $course)
|
||||
<a href="{{ \App\Filament\Resources\Learning\ClassSessions\ClassSessionResource::getUrl('course', ['courseId' => $course->id]) }}"
|
||||
@foreach ($this->courses as $course)
|
||||
<a href="{{ $course->url }}"
|
||||
class="fi-card rounded-xl border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900 shadow-sm overflow-hidden transition hover:shadow-md h-fit block group">
|
||||
|
||||
<div class="p-5 space-y-4">
|
||||
@ -134,7 +107,7 @@ class="inline-flex items-center rounded-md bg-primary-50 dark:bg-primary-500/10
|
||||
|
||||
<div class="flex items-center gap-2 text-gray-400 dark:text-gray-500">
|
||||
<span class="text-xs font-medium">
|
||||
{{ $course->classSessions->count() }} Sesi
|
||||
{{ $course->sessions_count }} Sesi
|
||||
</span>
|
||||
<x-heroicon-m-chevron-right
|
||||
class="w-4 h-4 transition-transform duration-300 group-hover:translate-x-1" />
|
||||
@ -148,7 +121,7 @@ class="text-base font-bold text-gray-950 dark:text-white leading-tight group-hov
|
||||
</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 ?? 'Dosen Belum Ditentukan' }}</span>
|
||||
<span class="truncate">{{ $course->lecturer }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -0,0 +1,157 @@
|
||||
<x-filament-panels::page>
|
||||
<x-filament::section>
|
||||
<div class="flex items-center justify-between gap-4 mb-6">
|
||||
<div class="w-full max-w-xl">
|
||||
{{ $this->form }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if ($this->studyGroups->isNotEmpty())
|
||||
<div class="columns-1 md:columns-2 lg:columns-3 xl:columns-4 gap-6 space-y-6">
|
||||
@foreach ($this->studyGroups as $record)
|
||||
<div class="break-inside-avoid mb-6">
|
||||
<div class="{{ $record->card_classes }}">
|
||||
|
||||
@if ($record->is_my_group)
|
||||
<div class="absolute top-0 right-0 -translate-x-1 -translate-y-1/2 z-10">
|
||||
<span
|
||||
class="inline-flex items-center gap-1 rounded-full bg-primary-600 px-3 py-1 text-[10px] font-bold text-white dark:text-gray-900 shadow-lg ring-2 ring-white dark:ring-gray-900 uppercase tracking-widest">
|
||||
<x-heroicon-m-check-badge class="w-3.5 h-3.5" />
|
||||
Kelompok Saya
|
||||
</span>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="p-6 space-y-4 flex-1">
|
||||
<div class="flex flex-wrap items-center gap-1.5">
|
||||
@forelse ($record->courses as $course)
|
||||
<span @class([
|
||||
'inline-flex items-center rounded-md px-2.5 py-1 text-[10px] font-bold ring-1 ring-inset uppercase tracking-tight',
|
||||
'bg-primary-50 dark:bg-primary-500/10 text-primary-700 dark:text-primary-300 ring-primary-600/20' => !$record->is_my_group,
|
||||
'bg-white dark:bg-primary-800/40 text-primary-700 dark:text-primary-200 ring-primary-500/30' => $record->is_my_group,
|
||||
])>
|
||||
{{ $course->name }}
|
||||
</span>
|
||||
@empty
|
||||
<span
|
||||
class="inline-flex items-center rounded-md bg-gray-50 dark:bg-gray-500/10 px-2 py-0.5 text-[10px] font-bold text-gray-500 dark:text-gray-400 ring-1 ring-inset ring-gray-600/20 uppercase tracking-tight italic">
|
||||
Tanpa Mata Kuliah
|
||||
</span>
|
||||
@endforelse
|
||||
</div>
|
||||
|
||||
<div class="space-y-1">
|
||||
<h3 @class([
|
||||
'text-lg font-bold leading-tight flex items-center gap-2 transition-colors',
|
||||
'text-gray-950 dark:text-white group-hover:text-primary-600 dark:group-hover:text-primary-400' => !$record->is_my_group,
|
||||
'text-primary-900 dark:text-primary-100' => $record->is_my_group,
|
||||
])>
|
||||
<x-heroicon-o-users @class([
|
||||
'w-5 h-5 shrink-0',
|
||||
'opacity-40' => !$record->is_my_group,
|
||||
'text-primary-500' => $record->is_my_group,
|
||||
]) />
|
||||
{{ $record->name }}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="space-y-4">
|
||||
<div class="flex items-center text-sm text-gray-600 dark:text-gray-400">
|
||||
<div @class([
|
||||
'w-8 h-8 rounded-full flex items-center justify-center mr-3 shrink-0 border',
|
||||
'bg-primary-100 dark:bg-primary-900/40 border-primary-200 dark:border-primary-800' => !$record->is_my_group,
|
||||
'bg-white dark:bg-primary-800 border-primary-300 dark:border-primary-600' => $record->is_my_group,
|
||||
])>
|
||||
@if ($record->leader_avatar)
|
||||
<img src="{{ $record->leader_avatar }}"
|
||||
alt="User avatar" class="w-full h-full rounded-full object-cover" />
|
||||
@else
|
||||
<x-heroicon-m-user @class([
|
||||
'w-4 h-4',
|
||||
'text-primary-600 dark:text-primary-300' => !$record->is_my_group,
|
||||
'text-primary-700 dark:text-primary-200' => $record->is_my_group,
|
||||
]) />
|
||||
@endif
|
||||
</div>
|
||||
<div class="flex flex-col min-w-0">
|
||||
<span
|
||||
class="text-[10px] font-bold uppercase text-gray-400 tracking-widest leading-none mb-1">Ketua
|
||||
Kelompok</span>
|
||||
<span @class([
|
||||
'truncate font-medium',
|
||||
'text-gray-900 dark:text-gray-200' => !$record->is_my_group,
|
||||
'text-primary-900 dark:text-primary-50' => $record->is_my_group,
|
||||
])>
|
||||
{{ $record->leader_name }}
|
||||
@if ($record->is_leader)
|
||||
<span
|
||||
class="text-[10px] text-primary-600 font-bold ml-1">(Anda)</span>
|
||||
@endif
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div @class([
|
||||
'pt-4 border-t',
|
||||
'border-gray-100 dark:border-gray-800' => !$record->is_my_group,
|
||||
'border-primary-200 dark:border-primary-800' => $record->is_my_group,
|
||||
])>
|
||||
<div class="flex items-center justify-between mb-3">
|
||||
<span
|
||||
class="text-[10px] font-bold text-gray-400 uppercase tracking-widest leading-none">Anggota
|
||||
Kelompok</span>
|
||||
<span
|
||||
class="text-[10px] font-bold px-1.5 py-0.5 rounded bg-violet-50 dark:bg-violet-500/10 text-violet-600 dark:text-violet-400 ring-1 ring-inset ring-violet-600/20">
|
||||
{{ $record->students_count }} orang
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-1.5">
|
||||
@forelse ($record->students as $student)
|
||||
<span @class([
|
||||
'inline-flex items-center gap-1 rounded-md px-2 py-0.5 text-[10px] font-medium ring-1 ring-inset',
|
||||
'bg-gray-50 dark:bg-gray-800/50 text-gray-700 dark:text-gray-300 ring-gray-200 dark:ring-gray-700' =>
|
||||
!$student->is_me,
|
||||
'bg-primary-600 text-white dark:text-gray-900 ring-primary-700 font-bold shadow-sm' =>
|
||||
$student->is_me,
|
||||
])>
|
||||
{{ $student->full_name }}
|
||||
@if ($student->is_me)
|
||||
(Anda)
|
||||
@endif
|
||||
</span>
|
||||
@empty
|
||||
<span class="text-xs text-gray-400 italic">Belum ada anggota</span>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@canAny(['Update:StudyGroup', 'Delete:StudyGroup'])
|
||||
<div @class([
|
||||
'rounded-b-xl',
|
||||
'bg-primary-100/30 dark:bg-primary-900/10' => $record->is_my_group,
|
||||
])>
|
||||
<div @class([
|
||||
'mx-6 border-t flex items-center justify-end gap-2 py-4',
|
||||
'border-gray-100 dark:border-gray-800' => !$record->is_my_group,
|
||||
'border-primary-200 dark:border-primary-800' => $record->is_my_group,
|
||||
])>
|
||||
{{ $this->editStudyGroupAction(['studyGroup' => $record->id]) }}
|
||||
|
||||
{{ $this->deleteStudyGroupAction(['studyGroup' => $record->id]) }}
|
||||
</div>
|
||||
</div>
|
||||
@endcanAny
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($this->studyGroups->isEmpty())
|
||||
<x-filament::empty-state icon="heroicon-o-users" 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>
|
||||
</x-filament-panels::page>
|
||||
@ -1,162 +0,0 @@
|
||||
<x-filament-panels::page>
|
||||
<x-filament::section>
|
||||
<div class="flex items-center justify-between gap-4 mb-6">
|
||||
<div class="w-full max-w-xl">
|
||||
{{ $this->form }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if ($groups->isNotEmpty())
|
||||
<div class="columns-1 md:columns-2 lg:columns-3 xl:columns-4 gap-6 space-y-6">
|
||||
@foreach ($groups as $record)
|
||||
@php $isMyGroup = $this->isMyGroup($record); @endphp
|
||||
<div class="break-inside-avoid mb-6">
|
||||
<div @class([
|
||||
'fi-card flex flex-col justify-between rounded-xl border transition duration-200 group relative',
|
||||
'bg-white dark:bg-gray-900 border-gray-200 dark:border-gray-700 shadow-sm hover:shadow-md' => !$isMyGroup,
|
||||
'bg-primary-50/30 dark:bg-primary-900/10 border-primary-500 shadow-md ring-1 ring-primary-500' => $isMyGroup,
|
||||
])>
|
||||
|
||||
@if ($isMyGroup)
|
||||
<div class="absolute top-0 right-0 -translate-x-1 -translate-y-1/2 z-10">
|
||||
<span
|
||||
class="inline-flex items-center gap-1 rounded-full bg-primary-600 px-3 py-1 text-[10px] font-bold text-white dark:text-gray-900 shadow-lg ring-2 ring-white dark:ring-gray-900 uppercase tracking-widest">
|
||||
<x-heroicon-m-check-badge class="w-3.5 h-3.5" />
|
||||
Kelompok Saya
|
||||
</span>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="p-6 space-y-4 flex-1">
|
||||
<div class="flex flex-wrap items-center gap-1.5">
|
||||
@forelse ($record->courses as $course)
|
||||
<span @class([
|
||||
'inline-flex items-center rounded-md px-2.5 py-1 text-[10px] font-bold ring-1 ring-inset uppercase tracking-tight',
|
||||
'bg-primary-50 dark:bg-primary-500/10 text-primary-700 dark:text-primary-300 ring-primary-600/20' => !$isMyGroup,
|
||||
'bg-white dark:bg-primary-800/40 text-primary-700 dark:text-primary-200 ring-primary-500/30' => $isMyGroup,
|
||||
])>
|
||||
{{ $course->name }}
|
||||
</span>
|
||||
@empty
|
||||
<span
|
||||
class="inline-flex items-center rounded-md bg-gray-50 dark:bg-gray-500/10 px-2 py-0.5 text-[10px] font-bold text-gray-500 dark:text-gray-400 ring-1 ring-inset ring-gray-600/20 uppercase tracking-tight italic">
|
||||
Tanpa Mata Kuliah
|
||||
</span>
|
||||
@endforelse
|
||||
</div>
|
||||
|
||||
<div class="space-y-1">
|
||||
<h3 @class([
|
||||
'text-lg font-bold leading-tight flex items-center gap-2 transition-colors',
|
||||
'text-gray-950 dark:text-white group-hover:text-primary-600 dark:group-hover:text-primary-400' => !$isMyGroup,
|
||||
'text-primary-900 dark:text-primary-100' => $isMyGroup,
|
||||
])>
|
||||
<x-heroicon-o-users @class([
|
||||
'w-5 h-5 shrink-0',
|
||||
'opacity-40' => !$isMyGroup,
|
||||
'text-primary-500' => $isMyGroup,
|
||||
]) />
|
||||
{{ $record->name }}
|
||||
</h3>
|
||||
</div>
|
||||
<div class="space-y-4">
|
||||
<div class="flex items-center text-sm text-gray-600 dark:text-gray-400">
|
||||
<div @class([
|
||||
'w-8 h-8 rounded-full flex items-center justify-center mr-3 shrink-0 border',
|
||||
'bg-primary-100 dark:bg-primary-900/40 border-primary-200 dark:border-primary-800' => !$isMyGroup,
|
||||
'bg-white dark:bg-primary-800 border-primary-300 dark:border-primary-600' => $isMyGroup,
|
||||
])>
|
||||
@if ($record->leader)
|
||||
<img src="{{ $record->leader->user->facehash_avatar_url }}"
|
||||
alt="User avatar" class="w-full h-full rounded-full object-cover" />
|
||||
@else
|
||||
<x-heroicon-m-user @class([
|
||||
'w-4 h-4',
|
||||
'text-primary-600 dark:text-primary-300' => !$isMyGroup,
|
||||
'text-primary-700 dark:text-primary-200' => $isMyGroup,
|
||||
]) />
|
||||
@endif
|
||||
</div>
|
||||
<div class="flex flex-col min-w-0">
|
||||
<span
|
||||
class="text-[10px] font-bold uppercase text-gray-400 tracking-widest leading-none mb-1">Ketua
|
||||
Kelompok</span>
|
||||
<span @class([
|
||||
'truncate font-medium',
|
||||
'text-gray-900 dark:text-gray-200' => !$isMyGroup,
|
||||
'text-primary-900 dark:text-primary-50' => $isMyGroup,
|
||||
])>
|
||||
{{ $record->leader->full_name ?? 'Belum Ditentukan' }}
|
||||
@if ($studentId && $record->leader_id === $studentId)
|
||||
<span
|
||||
class="text-[10px] text-primary-600 font-bold ml-1">(Anda)</span>
|
||||
@endif
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div @class([
|
||||
'pt-4 border-t',
|
||||
'border-gray-100 dark:border-gray-800' => !$isMyGroup,
|
||||
'border-primary-200 dark:border-primary-800' => $isMyGroup,
|
||||
])>
|
||||
<div class="flex items-center justify-between mb-3">
|
||||
<span
|
||||
class="text-[10px] font-bold text-gray-400 uppercase tracking-widest leading-none">Anggota
|
||||
Kelompok</span>
|
||||
<span
|
||||
class="text-[10px] font-bold px-1.5 py-0.5 rounded bg-violet-50 dark:bg-violet-500/10 text-violet-600 dark:text-violet-400 ring-1 ring-inset ring-violet-600/20">
|
||||
{{ $record->students->count() }} orang
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-1.5">
|
||||
@forelse ($record->students as $student)
|
||||
<span @class([
|
||||
'inline-flex items-center gap-1 rounded-md px-2 py-0.5 text-[10px] font-medium ring-1 ring-inset',
|
||||
'bg-gray-50 dark:bg-gray-800/50 text-gray-700 dark:text-gray-300 ring-gray-200 dark:ring-gray-700' =>
|
||||
$student->id !== $studentId,
|
||||
'bg-primary-600 text-white dark:text-gray-900 ring-primary-700 font-bold shadow-sm' =>
|
||||
$student->id === $studentId,
|
||||
])>
|
||||
{{ $student->full_name }}
|
||||
@if ($student->id === $studentId)
|
||||
(Anda)
|
||||
@endif
|
||||
</span>
|
||||
@empty
|
||||
<span class="text-xs text-gray-400 italic">Belum ada anggota</span>
|
||||
@endforelse
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@canAny(['Update:StudyGroup', 'Delete:StudyGroup'])
|
||||
<div @class([
|
||||
'rounded-b-xl',
|
||||
'bg-primary-100/30 dark:bg-primary-900/10' => $isMyGroup,
|
||||
])>
|
||||
<div @class([
|
||||
'mx-6 border-t flex items-center justify-end gap-2 py-4',
|
||||
'border-gray-100 dark:border-gray-800' => !$isMyGroup,
|
||||
'border-primary-200 dark:border-primary-800' => $isMyGroup,
|
||||
])>
|
||||
{{ ($this->editStudyGroupAction)(['studyGroup' => $record->id]) }}
|
||||
|
||||
{{ ($this->deleteStudyGroupAction)(['studyGroup' => $record->id]) }}
|
||||
</div>
|
||||
</div>
|
||||
@endcanAny
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if ($groups->isEmpty())
|
||||
<x-filament::empty-state icon="heroicon-o-users" 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>
|
||||
</x-filament-panels::page>
|
||||
@ -10,26 +10,11 @@
|
||||
@else
|
||||
<div class="space-y-6">
|
||||
@foreach ($this->assignmentCards as $card)
|
||||
<div @class([
|
||||
'group flex w-full rounded-xl border transition-all overflow-hidden relative',
|
||||
'border-primary-300 dark:border-primary-700 bg-primary-50/30 dark:bg-primary-900/10 shadow-sm' =>
|
||||
$card->is_pinned,
|
||||
'border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800' =>
|
||||
!$card->is_pinned && $card->can_submit_actual,
|
||||
'border-gray-100 dark:border-gray-800 bg-gray-50 dark:bg-gray-800/40 opacity-80' => !$card->can_submit_actual,
|
||||
])>
|
||||
<div class="{{ $card->card_classes }}">
|
||||
<a href="{{ \App\Filament\Resources\Learning\Assignments\AssignmentResource::getUrl('submit', ['record' => $card->id]) }}"
|
||||
wire:navigate
|
||||
class="flex flex-1 items-start gap-4 p-5 text-left min-w-0 hover:bg-primary-500/5 transition-colors cursor-pointer">
|
||||
<div @class([
|
||||
'flex h-11 w-11 shrink-0 items-center justify-center rounded-lg',
|
||||
'bg-success-50 dark:bg-success-900/20 text-success-600 dark:text-success-400' =>
|
||||
$card->is_submitted,
|
||||
'bg-danger-50 dark:bg-danger-900/20 text-danger-600 dark:text-danger-400' =>
|
||||
!$card->is_submitted && $card->is_overdue,
|
||||
'bg-warning-50 dark:bg-warning-900/20 text-warning-600 dark:text-warning-400' =>
|
||||
!$card->is_submitted && !$card->is_overdue,
|
||||
])>
|
||||
<div class="{{ $card->icon_wrapper_classes }}">
|
||||
<x-filament::icon :icon="$card->status_icon" class="h-6 w-6" />
|
||||
</div>
|
||||
|
||||
@ -64,19 +49,7 @@ class="inline-flex items-center gap-1 rounded-full bg-blue-100 text-blue-700 dar
|
||||
</span>
|
||||
@endif
|
||||
|
||||
<span @class([
|
||||
'inline-flex items-center gap-1 rounded-full px-2.5 py-0.5 text-xs font-medium',
|
||||
'bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400' =>
|
||||
$card->status_color === 'amber',
|
||||
'bg-success-100 text-success-700 dark:bg-success-900/30 dark:text-success-400' =>
|
||||
$card->status_color === 'success',
|
||||
'bg-danger-100 text-danger-700 dark:bg-danger-900/30 dark:text-danger-400' =>
|
||||
$card->status_color === 'danger',
|
||||
'bg-warning-100 text-warning-700 dark:bg-warning-900/30 dark:text-warning-400' =>
|
||||
$card->status_color === 'warning',
|
||||
'bg-gray-100 text-gray-500 dark:bg-gray-800 dark:text-gray-400' =>
|
||||
$card->status_color === 'gray',
|
||||
])>
|
||||
<span class="{{ $card->status_badge_classes }}">
|
||||
@if ($card->status_icon)
|
||||
<x-filament::icon :icon="$card->status_icon" class="h-3 w-3" />
|
||||
@endif
|
||||
@ -132,11 +105,7 @@ class="flex flex-col items-center justify-center border-l border-gray-100 dark:b
|
||||
<div class="absolute right-12 top-2 pointer-events-none">
|
||||
<x-filament::icon
|
||||
icon="{{ $card->is_leader ? 'heroicon-m-sparkles' : 'heroicon-m-user-group' }}"
|
||||
@class([
|
||||
'h-5 w-5 opacity-20',
|
||||
'text-primary-500' => $card->is_leader,
|
||||
'text-gray-400' => !$card->is_leader,
|
||||
])
|
||||
class="{{ $card->indicator_icon_classes }}"
|
||||
title="{{ $card->is_leader ? 'Anda adalah Ketua Kelompok' : 'Anda adalah Anggota Kelompok' }}" />
|
||||
</div>
|
||||
@endif
|
||||
@ -5,7 +5,7 @@
|
||||
|
||||
<div class="flex items-center gap-2 mb-1">
|
||||
|
||||
<div class="flex h-7 w-7 items-center justify-center rounded-lg {{ $stat['color'] }}">
|
||||
<div class="flex h-7 w-7 items-center justify-center rounded-lg {{ $stat['color_classes'] }}">
|
||||
<x-filament::icon :icon="$stat['icon']" class="h-4 w-4 text-current" />
|
||||
</div>
|
||||
|
||||
@ -22,23 +22,19 @@
|
||||
</div>
|
||||
|
||||
<div class="w-full bg-gray-100 dark:bg-gray-700 rounded-full h-2">
|
||||
<div class="h-2 rounded-full transition-all {{ $this->percentage === 100 ? 'bg-success-500' : ($this->percentage > 0 ? 'bg-warning-500' : 'bg-gray-300') }}"
|
||||
<div class="h-2 rounded-full transition-all {{ $this->progress_color_class }}"
|
||||
style="width: {{ $this->percentage }}%"></div>
|
||||
</div>
|
||||
|
||||
@php
|
||||
$info = $this->getAssignmentInfo();
|
||||
@endphp
|
||||
|
||||
<x-filament::section>
|
||||
<x-slot name="heading">{{ $info['title'] }}</x-slot>
|
||||
<x-slot name="heading">{{ $this->assignmentSummary['title'] }}</x-slot>
|
||||
<x-slot name="description">
|
||||
{{ $info['course'] }}
|
||||
{{ $this->assignmentSummary['course'] }}
|
||||
·
|
||||
Batas: {{ $info['due_date'] }}
|
||||
Batas: {{ $this->assignmentSummary['due_date'] }}
|
||||
·
|
||||
Tipe: {{ $info['type'] }}
|
||||
@if ($info['is_overdue'])
|
||||
Tipe: {{ $this->assignmentSummary['type'] }}
|
||||
@if ($this->assignmentSummary['is_overdue'])
|
||||
<x-filament::badge color="danger" class="ml-2">Terlewat</x-filament::badge>
|
||||
@endif
|
||||
</x-slot>
|
||||
@ -84,17 +80,9 @@
|
||||
</td>
|
||||
|
||||
<td class="px-4 py-3">
|
||||
@if ($item->submitted)
|
||||
<span
|
||||
class="inline-flex items-center gap-1 rounded-full bg-success-100 text-success-700 dark:bg-success-900/30 dark:text-success-400 px-2.5 py-0.5 text-xs font-medium">
|
||||
✓ Terkumpul
|
||||
</span>
|
||||
@else
|
||||
<span
|
||||
class="inline-flex items-center rounded-full bg-danger-100 text-danger-700 dark:bg-danger-900/30 dark:text-danger-400 px-2.5 py-0.5 text-xs font-medium">
|
||||
Belum
|
||||
</span>
|
||||
@endif
|
||||
<span class="{{ $item->status_classes }}">
|
||||
{{ $item->status_label }}
|
||||
</span>
|
||||
</td>
|
||||
|
||||
<td class="px-4 py-3 text-gray-500 dark:text-gray-400 text-xs">
|
||||
@ -104,7 +92,7 @@ class="inline-flex items-center rounded-full bg-danger-100 text-danger-700 dark:
|
||||
<td class="px-4 py-3">
|
||||
@if ($item->has_file)
|
||||
<button type="button"
|
||||
wire:click="mountAction('previewSubmission', { submissionId: {{ $item->submission->id }} })"
|
||||
wire:click="mountAction('previewSubmission', { submissionId: {{ $item->submission_id }} })"
|
||||
class="inline-flex items-center gap-1 text-xs text-primary-600 dark:text-primary-400 hover:underline">
|
||||
<x-filament::icon icon="heroicon-o-eye" class="h-3.5 w-3.5" />
|
||||
Lihat Tugas
|
||||
@ -113,8 +101,6 @@ class="inline-flex items-center gap-1 text-xs text-primary-600 dark:text-primary
|
||||
<span class="text-xs text-gray-400">-</span>
|
||||
@endif
|
||||
</td>
|
||||
|
||||
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
@ -8,15 +8,7 @@
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3 mb-4">
|
||||
@foreach ($this->statusCards as $card)
|
||||
<div class="flex items-center gap-3 rounded-lg border border-gray-200 dark:border-gray-700 p-3">
|
||||
<div @class([
|
||||
'flex h-9 w-9 shrink-0 items-center justify-center rounded-lg',
|
||||
'bg-danger-50 dark:bg-danger-900/20 text-danger-600' =>
|
||||
$card['is_danger'] ?? false,
|
||||
'bg-success-50 dark:bg-success-900/20 text-success-600' =>
|
||||
$card['is_success'] ?? false,
|
||||
'bg-warning-50 dark:bg-warning-900/20 text-warning-600' =>
|
||||
!($card['is_danger'] ?? false) && !($card['is_success'] ?? false),
|
||||
])>
|
||||
<div class="{{ $card['icon_classes'] }}">
|
||||
<x-filament::icon :icon="$card['icon']" class="h-5 w-5" />
|
||||
</div>
|
||||
<div>
|
||||
@ -64,9 +56,8 @@ class="flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-dange
|
||||
<x-filament::icon icon="heroicon-o-lock-closed" class="h-5 w-5" />
|
||||
</div>
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
@php $status = $this->submissionStatus; @endphp
|
||||
<x-filament::badge :color="$status->badge_color" size="lg">
|
||||
{{ $status->badge_label }}
|
||||
<x-filament::badge :color="$this->submissionStatus->badge_color" size="lg">
|
||||
{{ $this->submissionStatus->badge_label }}
|
||||
</x-filament::badge>
|
||||
</div>
|
||||
</div>
|
||||
@ -3,24 +3,15 @@
|
||||
<x-slot name="heading">Sesi Kuliah</x-slot>
|
||||
<x-slot name="description">Silakan melakukan presensi sesuai dengan waktu kelas.</x-slot>
|
||||
|
||||
@if ($scheduleCards->isNotEmpty())
|
||||
@if ($this->scheduleCards->isNotEmpty())
|
||||
<div class="columns-1 md:columns-2 lg:columns-3 xl:columns-4 gap-6 space-y-6">
|
||||
@foreach ($scheduleCards as $card)
|
||||
@foreach ($this->scheduleCards as $card)
|
||||
<div class="break-inside-avoid mb-6">
|
||||
<div @class([
|
||||
'fi-card flex flex-col justify-between rounded-xl border transition duration-200 group relative',
|
||||
'bg-white dark:bg-gray-900 border-gray-200 dark:border-gray-700 shadow-sm hover:shadow-md' => !$card->is_attended,
|
||||
'bg-primary-50/30 dark:bg-primary-900/10 border-primary-500 shadow-md ring-1 ring-primary-500' =>
|
||||
$card->is_attended,
|
||||
])>
|
||||
<div class="{{ $card->card_classes }}">
|
||||
|
||||
<div class="p-6 space-y-4 flex-1">
|
||||
<div class="space-y-1">
|
||||
<h3 @class([
|
||||
'text-lg font-bold leading-tight flex items-center gap-2 transition-colors',
|
||||
'text-gray-950 dark:text-white group-hover:text-primary-600 dark:group-hover:text-primary-400' => !$card->is_attended,
|
||||
'text-primary-900 dark:text-primary-100' => $card->is_attended,
|
||||
])>
|
||||
<h3 class="{{ $card->title_classes }}">
|
||||
<x-heroicon-o-book-open @class([
|
||||
'w-5 h-5 shrink-0',
|
||||
'opacity-40' => !$card->is_attended,
|
||||
@ -28,11 +19,7 @@
|
||||
]) />
|
||||
{{ $card->course_name }}
|
||||
</h3>
|
||||
<div @class([
|
||||
'flex items-center gap-1.5',
|
||||
'text-gray-500 dark:text-gray-400' => !$card->is_attended,
|
||||
'text-primary-600 dark:text-primary-400' => $card->is_attended,
|
||||
])>
|
||||
<div class="{{ $card->lecturer_wrapper_classes }}">
|
||||
<x-heroicon-m-user @class(['w-3.5 h-3.5 shrink-0', 'opacity-50' => !$card->is_attended]) />
|
||||
<span class="text-[11px] font-medium leading-none">
|
||||
{{ $card->lecturer_name }}
|
||||
@ -42,12 +29,7 @@
|
||||
|
||||
<div class="space-y-4">
|
||||
<div class="flex items-center text-sm text-gray-600 dark:text-gray-400 pt-4">
|
||||
<div @class([
|
||||
'w-8 h-8 rounded-full flex items-center justify-center mr-3 shrink-0 border',
|
||||
'bg-primary-100 dark:bg-primary-900/40 border-primary-200 dark:border-primary-800' => !$card->is_attended,
|
||||
'bg-white dark:bg-primary-800 border-primary-300 dark:border-primary-600' =>
|
||||
$card->is_attended,
|
||||
])>
|
||||
<div class="{{ $card->icon_wrapper_classes }}">
|
||||
<x-heroicon-m-clock @class([
|
||||
'w-4 h-4',
|
||||
'text-primary-600 dark:text-primary-300' => !$card->is_attended,
|
||||
@ -58,11 +40,7 @@
|
||||
<span
|
||||
class="text-[10px] font-bold uppercase text-gray-400 tracking-widest leading-none mb-1">Waktu
|
||||
Kuliah</span>
|
||||
<span @class([
|
||||
'truncate font-medium',
|
||||
'text-gray-900 dark:text-gray-200' => !$card->is_attended,
|
||||
'text-primary-900 dark:text-primary-50' => $card->is_attended,
|
||||
])>
|
||||
<span class="{{ $card->time_label_classes }}">
|
||||
{{ $card->time_range }}
|
||||
</span>
|
||||
</div>
|
||||
@ -76,15 +54,7 @@ class="text-[10px] font-bold uppercase text-gray-400 tracking-widest leading-non
|
||||
<div class="flex items-center justify-between mb-3">
|
||||
<span
|
||||
class="text-[10px] font-bold text-gray-400 uppercase tracking-widest leading-none">Status</span>
|
||||
<span @class([
|
||||
'text-[10px] font-bold px-1.5 py-0.5 rounded ring-1 ring-inset',
|
||||
'bg-success-50 dark:bg-success-500/10 text-success-600 dark:text-success-400 ring-success-600/20' =>
|
||||
$card->is_attended,
|
||||
'bg-warning-50 dark:bg-warning-500/10 text-warning-600 dark:text-warning-400 ring-warning-600/20' =>
|
||||
!$card->is_attended && $card->can_attend,
|
||||
'bg-gray-50 dark:bg-gray-500/10 text-gray-600 dark:text-gray-400 ring-gray-600/20' =>
|
||||
!$card->is_attended && !$card->can_attend,
|
||||
])>
|
||||
<span class="{{ $card->status_badge_classes }}">
|
||||
{{ $card->status_label }}
|
||||
</span>
|
||||
</div>
|
||||
@ -99,11 +69,7 @@ class="text-primary-600 dark:text-primary-200">{{ $card->attended_at }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div @class([
|
||||
'flex items-center justify-end gap-2 p-4 pt-0 rounded-b-xl',
|
||||
'bg-primary-100/30 dark:bg-primary-900/10 pt-4' =>
|
||||
$card->is_attended || $card->can_attend,
|
||||
])>
|
||||
<div class="{{ $card->footer_classes }}">
|
||||
@if ($card->can_attend)
|
||||
<x-filament::button wire:click="attend({{ $card->id }})" color="primary"
|
||||
size="xs"
|
||||
@ -9,15 +9,15 @@ class="fi-btn fi-btn-size-md relative grid-flow-col items-center justify-center
|
||||
{{ $this->getTitle() }}
|
||||
</h1>
|
||||
<p class="text-sm font-medium text-gray-500 dark:text-gray-400">
|
||||
Data sesi pembelajaran untuk mata kuliah {{ $this->getCourse()->name }}
|
||||
{{ $this->description }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<x-filament::section>
|
||||
@if ($this->getSessions()->isNotEmpty())
|
||||
@if ($this->sessions->isNotEmpty())
|
||||
<div class="space-y-4">
|
||||
@foreach ($this->getSessions() as $session)
|
||||
@foreach ($this->sessions as $session)
|
||||
<div
|
||||
class="fi-card p-5 rounded-xl border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900 shadow-sm flex items-center justify-between hover:shadow-md transition">
|
||||
<div class="flex items-start gap-4">
|
||||
@ -31,25 +31,24 @@ class="flex flex-col items-center justify-center w-12 h-12 rounded-lg bg-primary
|
||||
class="flex flex-wrap items-center gap-x-4 gap-y-1 text-sm text-gray-500 dark:text-gray-400">
|
||||
<div class="flex items-center gap-1.5 font-bold text-gray-900 dark:text-white">
|
||||
<x-heroicon-o-calendar class="w-4 h-4 text-primary-500" />
|
||||
{{ $session->date->format('l, d F Y') }}
|
||||
{{ $session->date_formatted }}
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5 font-medium">
|
||||
<x-heroicon-o-clock class="w-4 h-4 text-primary-500" />
|
||||
{{ $session->start_time->format('H:i') }} -
|
||||
{{ $session->end_time->format('H:i') }}
|
||||
{{ $session->time_range }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-1">
|
||||
{{ ($this->editSessionAction)(['session' => $session->id]) }}
|
||||
{{ ($this->deleteSessionAction)(['session' => $session->id]) }}
|
||||
{{ $this->editSessionAction(['session' => $session->id]) }}
|
||||
{{ $this->deleteSessionAction(['session' => $session->id]) }}
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
@else
|
||||
@else
|
||||
<x-filament::empty-state icon="heroicon-o-presentation-chart-bar" heading="Tidak ada data yang ditemukan"
|
||||
description="Setelah Anda membuat data pertama, maka akan muncul disini." iconColor="gray">
|
||||
</x-filament::empty-state>
|
||||
@ -1,103 +0,0 @@
|
||||
<x-filament-panels::page>
|
||||
<x-filament::section>
|
||||
<div class="flex items-center justify-between gap-4 mb-6">
|
||||
<div class="w-full max-w-xl">
|
||||
{{ $this->form }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if($this->getCourses()->isNotEmpty())
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6">
|
||||
@foreach($this->getCourses() as $course)
|
||||
<div
|
||||
x-data="{ open: false }"
|
||||
class="fi-card rounded-xl border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900 shadow-sm overflow-hidden transition hover:shadow-md h-fit"
|
||||
>
|
||||
<!-- Course Header Card -->
|
||||
<div
|
||||
@click="open = !open"
|
||||
class="p-5 cursor-pointer space-y-4 group"
|
||||
>
|
||||
<div class="flex items-center justify-between">
|
||||
<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">
|
||||
{{ $course->code }}
|
||||
</span>
|
||||
|
||||
<div class="flex items-center gap-2 text-gray-400 dark:text-gray-500">
|
||||
<span class="text-xs font-medium">
|
||||
{{ $course->classSessions->count() }} Sesi
|
||||
</span>
|
||||
<x-heroicon-m-chevron-down
|
||||
class="w-4 h-4 transition-transform duration-300"
|
||||
x-bind:class="open ? 'rotate-180' : ''"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-1">
|
||||
<h3 class="text-base font-bold text-gray-950 dark:text-white leading-tight group-hover:text-primary-600 dark:group-hover:text-primary-400 transition">
|
||||
{{ $course->name }}
|
||||
</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 ?? 'Dosen Belum Ditentukan' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sessions List (Collapsible) -->
|
||||
<div
|
||||
x-show="open"
|
||||
x-collapse
|
||||
x-cloak
|
||||
class="border-t border-gray-100 dark:border-gray-800 bg-gray-50/50 dark:bg-gray-800/20"
|
||||
>
|
||||
@if($course->classSessions->isNotEmpty())
|
||||
<div class="divide-y divide-gray-100 dark:divide-gray-800 max-h-80 overflow-y-auto">
|
||||
@foreach($course->classSessions as $session)
|
||||
<div class="p-4 flex items-center justify-between hover:bg-gray-100/50 dark:hover:bg-gray-800/50 transition">
|
||||
<div class="space-y-1">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="inline-flex items-center rounded-full bg-gray-100 dark:bg-gray-700 px-2 py-0.5 text-[10px] font-bold text-gray-600 dark:text-gray-400 uppercase tracking-wider">
|
||||
#{{ $session->session_number }}
|
||||
</span>
|
||||
<span class="text-sm font-semibold text-gray-900 dark:text-white">
|
||||
{{ $session->date->format('l, d/m/Y') }}
|
||||
</span>
|
||||
</div>
|
||||
<p class="text-[12px] text-gray-600 dark:text-gray-400 line-clamp-1">
|
||||
{{ $session->title ?? 'Materi belum diisi' }}
|
||||
</p>
|
||||
<div class="flex items-center gap-1.5 text-[11px] text-gray-500 dark:text-gray-400">
|
||||
<x-heroicon-o-clock class="w-3.5 h-3.5 text-primary-500" />
|
||||
{{ $session->start_time->format('H:i') }} - {{ $session->end_time->format('H:i') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-1">
|
||||
{{ ($this->editSessionAction)(['session' => $session->id]) }}
|
||||
{{ ($this->deleteSessionAction)(['session' => $session->id]) }}
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
@else
|
||||
<div class="p-6 text-center">
|
||||
<x-heroicon-o-chat-bubble-bottom-center-text class="w-8 h-8 text-gray-300 dark:text-gray-600 mx-auto mb-2" />
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400 italic">
|
||||
Belum ada laporan sesi untuk mata kuliah ini.
|
||||
</p>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
@else
|
||||
<x-filament::empty-state
|
||||
icon="heroicon-o-presentation-chart-bar"
|
||||
heading="Tidak ada mata kuliah"
|
||||
description="Mata kuliah untuk semester ini belum tersedia atau tidak sesuai dengan pencarian."
|
||||
/>
|
||||
@endif
|
||||
</x-filament::section>
|
||||
</x-filament-panels::page>
|
||||
Loading…
Reference in New Issue
Block a user