feat: Implement Study Group management with CRUD actions, model, migrations, and Filament resource for creating, editing, and deleting study groups.
This commit is contained in:
parent
8c681ca5f2
commit
e2bb51561c
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Pages\Learning\StudyGroup\Actions;
|
||||
|
||||
use App\Filament\Actions\Cheerful\CreateAction;
|
||||
use App\Models\StudyGroup;
|
||||
use Filament\Support\Enums\Width;
|
||||
|
||||
class CreateStudyGroupAction extends CreateAction
|
||||
{
|
||||
public static function getDefaultName(): string
|
||||
{
|
||||
return 'createStudyGroup';
|
||||
}
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->model(StudyGroup::class)
|
||||
->label('Tambah')
|
||||
->modalHeading('Tambah Kelompok Belajar')
|
||||
->modalSubmitActionLabel('Simpan')
|
||||
->modalCancelActionLabel('Batal')
|
||||
->extraModalFooterActions(fn (CreateAction $action): array => [
|
||||
$action->makeModalSubmitAction('createAnother', arguments: ['another' => true])
|
||||
->label('Simpan dan Tambah Lagi'),
|
||||
])
|
||||
->modalWidth(Width::Large)
|
||||
->schema(fn ($livewire) => $livewire->studyGroupFormSchema())
|
||||
->after(function (StudyGroup $record, array $data) {
|
||||
if (isset($data['course_id'])) {
|
||||
$record->courses()->sync($data['course_id']);
|
||||
}
|
||||
if (isset($data['students'])) {
|
||||
$record->students()->sync($data['students']);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Pages\Learning\StudyGroup\Actions;
|
||||
|
||||
use App\Filament\Actions\Cheerful\DeleteAction;
|
||||
use App\Models\StudyGroup;
|
||||
|
||||
class DeleteStudyGroupAction extends DeleteAction
|
||||
{
|
||||
public static function getDefaultName(): string
|
||||
{
|
||||
return 'deleteStudyGroup';
|
||||
}
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->record(fn (array $arguments): StudyGroup => StudyGroup::findOrFail($arguments['record']))
|
||||
->modalHeading(fn (StudyGroup $record) => "Hapus {$record->name}")
|
||||
->modalDescription('Apakah Anda yakin ingin menghapus kelompok ini? Tindakan ini tidak dapat dibatalkan.')
|
||||
->modalSubmitActionLabel('Hapus')
|
||||
->modalCancelActionLabel('Batal');
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Pages\Learning\StudyGroup\Actions;
|
||||
|
||||
use App\Filament\Actions\Cheerful\EditAction;
|
||||
use App\Models\StudyGroup;
|
||||
use Filament\Support\Enums\Width;
|
||||
|
||||
class EditStudyGroupAction extends EditAction
|
||||
{
|
||||
public static function getDefaultName(): ?string
|
||||
{
|
||||
return 'editStudyGroup';
|
||||
}
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->record(fn (array $arguments): StudyGroup => StudyGroup::findOrFail($arguments['record']))
|
||||
->modalHeading(fn (StudyGroup $record) => "Ubah {$record->name}")
|
||||
->modalSubmitActionLabel('Simpan')
|
||||
->modalCancelActionLabel('Batal')
|
||||
->modalWidth(Width::Large)
|
||||
->schema(fn ($livewire) => $livewire->studyGroupFormSchema())
|
||||
->fillForm(function (StudyGroup $record): array {
|
||||
return [
|
||||
'name' => $record->name,
|
||||
'leader_id' => $record->leader_id,
|
||||
'course_id' => $record->courses->pluck('id')->toArray(),
|
||||
'students' => $record->students->pluck('id')->toArray(),
|
||||
];
|
||||
})
|
||||
->after(function (StudyGroup $record, array $data) {
|
||||
if (isset($data['course_id'])) {
|
||||
$record->courses()->sync($data['course_id']);
|
||||
}
|
||||
if (isset($data['students'])) {
|
||||
$record->students()->sync($data['students']);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
214
app/Filament/Pages/Learning/StudyGroup/Index.php
Normal file
214
app/Filament/Pages/Learning/StudyGroup/Index.php
Normal file
@ -0,0 +1,214 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Pages\Learning\StudyGroup;
|
||||
|
||||
use App\Filament\Pages\Learning\StudyGroup\Actions\CreateStudyGroupAction;
|
||||
use App\Filament\Pages\Learning\StudyGroup\Actions\DeleteStudyGroupAction;
|
||||
use App\Filament\Pages\Learning\StudyGroup\Actions\EditStudyGroupAction;
|
||||
use App\Models\Course;
|
||||
use App\Models\Student;
|
||||
use App\Models\StudyGroup;
|
||||
use BackedEnum;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\Concerns\InteractsWithActions;
|
||||
use Filament\Actions\Contracts\HasActions;
|
||||
use Filament\Forms\Components\Select;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Forms\Concerns\InteractsWithForms;
|
||||
use Filament\Forms\Contracts\HasForms;
|
||||
use Filament\Pages\Page;
|
||||
use Filament\Schemas\Components\Grid;
|
||||
use Filament\Schemas\Components\Utilities\Get;
|
||||
use Filament\Schemas\Schema;
|
||||
use Filament\Support\Icons\Heroicon;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Support\Collection;
|
||||
use UnitEnum;
|
||||
|
||||
class Index extends Page implements HasActions, HasForms
|
||||
{
|
||||
use InteractsWithActions, InteractsWithForms;
|
||||
|
||||
protected static ?string $model = StudyGroup::class;
|
||||
|
||||
protected static string|UnitEnum|null $navigationGroup = 'Pembelajaran';
|
||||
|
||||
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedUsers;
|
||||
|
||||
protected static ?string $title = 'Kelompok Belajar';
|
||||
|
||||
protected static ?int $navigationSort = 10;
|
||||
|
||||
protected static ?string $slug = 'learning/study-groups';
|
||||
|
||||
protected string $view = 'filament.pages.information.list-study-groups';
|
||||
|
||||
public ?int $course_id = null;
|
||||
|
||||
public function mount(): void
|
||||
{
|
||||
$this->form->fill([
|
||||
'course_id' => $this->course_id,
|
||||
]);
|
||||
}
|
||||
|
||||
public function form(Schema $schema): Schema
|
||||
{
|
||||
return $schema
|
||||
->schema([
|
||||
Select::make('course_id')
|
||||
->label('Mata Kuliah')
|
||||
->options(function () {
|
||||
return Course::all()
|
||||
->groupBy('semester')
|
||||
->mapWithKeys(function ($courses, $semester) {
|
||||
return [
|
||||
"Semester $semester" => $courses->pluck('name', 'id')->toArray(),
|
||||
];
|
||||
})
|
||||
->toArray();
|
||||
})
|
||||
->searchable()
|
||||
->required()
|
||||
->optionsLimit(100),
|
||||
|
||||
])
|
||||
->columns(1);
|
||||
}
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
CreateStudyGroupAction::make(),
|
||||
];
|
||||
}
|
||||
|
||||
protected function editStudyGroupAction(): Action
|
||||
{
|
||||
return EditStudyGroupAction::make();
|
||||
}
|
||||
|
||||
protected function deleteStudyGroupAction(): Action
|
||||
{
|
||||
return DeleteStudyGroupAction::make();
|
||||
}
|
||||
|
||||
public function getStudyGroups(): Collection
|
||||
{
|
||||
$query = StudyGroup::with(['leader', 'students', 'courses']);
|
||||
|
||||
if ($this->course_id) {
|
||||
$query->whereHas('courses', fn ($q) => $q->where('courses.id', $this->course_id));
|
||||
}
|
||||
|
||||
return $query->latest()->get();
|
||||
}
|
||||
|
||||
protected function getViewData(): array
|
||||
{
|
||||
return [
|
||||
'groups' => $this->getStudyGroups(),
|
||||
'studentId' => auth()->user()?->student?->id,
|
||||
];
|
||||
}
|
||||
|
||||
public function isMyGroup(StudyGroup $record): bool
|
||||
{
|
||||
$studentId = $this->studentId;
|
||||
|
||||
if (! $studentId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $record->leader_id === $studentId || $record->students->contains($studentId);
|
||||
}
|
||||
|
||||
public function studyGroupFormSchema(): array
|
||||
{
|
||||
return [
|
||||
Grid::make(2)
|
||||
->schema([
|
||||
Select::make('course_id')
|
||||
->label('Mata Kuliah')
|
||||
->options(function () {
|
||||
return Course::all()
|
||||
->groupBy('semester')
|
||||
->mapWithKeys(function ($courses, $semester) {
|
||||
return [
|
||||
"Semester $semester" => $courses->pluck('name', 'id')->toArray(),
|
||||
];
|
||||
})
|
||||
->toArray();
|
||||
})
|
||||
->multiple()
|
||||
->searchable()
|
||||
->required()
|
||||
->optionsLimit(100)
|
||||
->columnSpanFull(),
|
||||
|
||||
TextInput::make('name')
|
||||
->label('Nama Kelompok')
|
||||
->placeholder('Kelompok A / Kelompok 1')
|
||||
->required()
|
||||
->maxLength(100)
|
||||
->autocomplete(false),
|
||||
|
||||
Select::make('leader_id')
|
||||
->label('Ketua Kelompok')
|
||||
->options(function (Get $get, ?StudyGroup $record) {
|
||||
$query = Student::query();
|
||||
|
||||
$courseIds = array_values(array_filter((array) ($get('courses') ?? [])));
|
||||
if (! empty($courseIds)) {
|
||||
$allBusyIds = StudyGroup::getBusyStudentIdsForCourses(
|
||||
courseIds: $courseIds,
|
||||
excludeGroupId: $record?->id,
|
||||
);
|
||||
$selectedMembers = array_values(array_filter((array) ($get('students') ?? [])));
|
||||
$query->whereNotIn('id', array_unique(array_merge($allBusyIds, $selectedMembers)));
|
||||
}
|
||||
|
||||
return $query->get()->pluck('full_name', 'id')->toArray();
|
||||
})
|
||||
->searchable()
|
||||
->required()
|
||||
->native(false)
|
||||
->live(),
|
||||
|
||||
Select::make('students')
|
||||
->label('Anggota Mahasiswa')
|
||||
->relationship(
|
||||
name: 'students',
|
||||
titleAttribute: 'full_name',
|
||||
modifyQueryUsing: function (Builder $query, Get $get, ?StudyGroup $record) {
|
||||
|
||||
$courseIds = array_values(array_filter((array) ($get('courses') ?? [])));
|
||||
if (! empty($courseIds)) {
|
||||
$allBusyIds = StudyGroup::getBusyStudentIdsForCourses(
|
||||
courseIds: $courseIds,
|
||||
excludeGroupId: $record?->id,
|
||||
);
|
||||
$leaderId = $get('leader_id');
|
||||
if ($leaderId) {
|
||||
$allBusyIds[] = (int) $leaderId;
|
||||
}
|
||||
$query->whereNotIn(
|
||||
$query->getModel()->getTable().'.id',
|
||||
array_unique($allBusyIds)
|
||||
);
|
||||
}
|
||||
|
||||
return $query->orderBy('student_number', 'asc');
|
||||
}
|
||||
)
|
||||
->multiple()
|
||||
->searchable()
|
||||
->preload()
|
||||
->native(false)
|
||||
->live()
|
||||
->columnSpanFull()
|
||||
->required(),
|
||||
]),
|
||||
];
|
||||
}
|
||||
}
|
||||
70
app/Models/StudyGroup.php
Normal file
70
app/Models/StudyGroup.php
Normal file
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
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;
|
||||
|
||||
class StudyGroup extends Model
|
||||
{
|
||||
use HasFactory, SoftDeletes;
|
||||
|
||||
protected $fillable = [
|
||||
'leader_id',
|
||||
'name',
|
||||
];
|
||||
|
||||
public function isLeader(Student $student): bool
|
||||
{
|
||||
return $this->leader_id === $student->id;
|
||||
}
|
||||
|
||||
public function courses(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(Course::class, 'study_group_courses');
|
||||
}
|
||||
|
||||
public function leader(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Student::class, 'leader_id');
|
||||
}
|
||||
|
||||
public function members(): HasMany
|
||||
{
|
||||
return $this->hasMany(StudyGroupMember::class);
|
||||
}
|
||||
|
||||
public function students(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(Student::class, 'study_group_members');
|
||||
}
|
||||
|
||||
public static function getBusyStudentIdsForCourses(array $courseIds, ?int $excludeGroupId = null): array
|
||||
{
|
||||
$busyMemberIds = StudyGroupMember::whereHas(
|
||||
'studyGroup',
|
||||
function ($q) use ($courseIds, $excludeGroupId) {
|
||||
if ($excludeGroupId) {
|
||||
$q->where('study_groups.id', '!=', $excludeGroupId);
|
||||
}
|
||||
$q->whereHas('courses', function ($cq) use ($courseIds) {
|
||||
$cq->whereIn('courses.id', $courseIds);
|
||||
});
|
||||
}
|
||||
)->pluck('student_id')->toArray();
|
||||
|
||||
$busyLeaderIds = static::query()
|
||||
->when($excludeGroupId, fn ($q) => $q->where('id', '!=', $excludeGroupId))
|
||||
->whereHas('courses', function ($cq) use ($courseIds) {
|
||||
$cq->whereIn('courses.id', $courseIds);
|
||||
})
|
||||
->pluck('leader_id')
|
||||
->toArray();
|
||||
|
||||
return array_unique(array_merge($busyMemberIds, $busyLeaderIds));
|
||||
}
|
||||
}
|
||||
27
app/Models/StudyGroupMember.php
Normal file
27
app/Models/StudyGroupMember.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class StudyGroupMember extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
protected $fillable = [
|
||||
'student_id',
|
||||
'study_group_id',
|
||||
];
|
||||
|
||||
public function student(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Student::class);
|
||||
}
|
||||
|
||||
public function studyGroup(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(StudyGroup::class);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('study_groups', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('leader_id')->constrained('students')->cascadeOnDelete();
|
||||
$table->string('name', 100);
|
||||
$table->timestamp('created_at')->useCurrent();
|
||||
$table->timestamp('updated_at')->nullable()->useCurrentOnUpdate();
|
||||
$table->softDeletes();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('study_groups');
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('study_group_members', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('study_group_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('student_id')->constrained()->cascadeOnDelete();
|
||||
$table->timestamp('created_at')->useCurrent();
|
||||
$table->timestamp('updated_at')->nullable()->useCurrentOnUpdate();
|
||||
$table->unique(['study_group_id', 'student_id']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('study_group_members');
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('study_group_courses', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('study_group_id')->constrained()->cascadeOnDelete();
|
||||
$table->foreignId('course_id')->constrained()->cascadeOnDelete();
|
||||
$table->unique(['study_group_id', 'course_id']);
|
||||
$table->timestamp('created_at')->useCurrent();
|
||||
$table->timestamp('updated_at')->nullable()->useCurrentOnUpdate();
|
||||
$table->softDeletes();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('study_group_courses');
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,162 @@
|
||||
<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 pb-8">
|
||||
@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-3 -right-3 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>
|
||||
|
||||
<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' => $isMyGroup,
|
||||
])>
|
||||
<x-filament::button size="xs" color="warning" tooltip="Ubah" variant="primary"
|
||||
class="rounded-lg shadow-sm"
|
||||
wire:click="mountAction('editStudyGroup', { record: {{ $record->id }} })">
|
||||
<x-heroicon-m-pencil-square class="w-4 h-4" />
|
||||
</x-filament::button>
|
||||
|
||||
<x-filament::button size="xs" color="danger" tooltip="Hapus"
|
||||
class="rounded-lg shadow-sm"
|
||||
wire:click="mountAction('deleteStudyGroup', { record: {{ $record->id }} })">
|
||||
<x-heroicon-m-trash class="w-4 h-4" />
|
||||
</x-filament::button>
|
||||
</div>
|
||||
</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>
|
||||
Loading…
Reference in New Issue
Block a user