feat: tambah sistem poin anggota (kehadiran +10, artikel +5)

- Model MemberPoint + migration
- PostObserver: +5 poin saat artikel dipublish
- ActivityObserver: +10 poin saat peserta hadir di kegiatan
- MemberPointResource: tampil di grup Organisasi
- MemberPointSeeder + update ActivitySeeder dengan pivot status kehadiran
- Update PermissionSeeder: anggota bisa lihat poin
This commit is contained in:
2026-04-04 06:44:54 +07:00
parent 9c72293476
commit ae0cddc270
16 changed files with 287 additions and 1 deletions
@@ -0,0 +1,26 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('member_points', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained('users');
$table->integer('points');
$table->string('reason');
$table->string('source_type')->nullable(); // activity, post
$table->unsignedBigInteger('source_id')->nullable();
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('member_points');
}
};
+6 -1
View File
@@ -46,7 +46,12 @@ class ActivitySeeder extends Seeder
foreach ($activities as $data) {
$activity = Activity::create(array_merge($data, ['created_by' => $pengurus?->id]));
$activity->participants()->sync($anggota->pluck('id'));
// Attach peserta dengan status kehadiran
$syncData = $anggota->mapWithKeys(fn ($user) => [
$user->id => ['status' => 'hadir', 'notes' => null]
])->toArray();
$activity->participants()->sync($syncData);
}
}
}
+1
View File
@@ -19,6 +19,7 @@ class DatabaseSeeder extends Seeder
VoteSeeder::class,
PostSeeder::class,
AuditSeeder::class,
MemberPointSeeder::class,
]);
}
}
+33
View File
@@ -0,0 +1,33 @@
<?php
namespace Database\Seeders;
use App\Models\Activity;
use App\Models\MemberPoint;
use App\Models\Post;
use App\Models\User;
use Illuminate\Database\Seeder;
class MemberPointSeeder extends Seeder
{
public function run(): void
{
// Poin kehadiran dari kegiatan yang sudah executed
Activity::whereNotNull('executed_at')->each(function ($activity) {
$activity->participants()->wherePivot('status', 'hadir')->each(function ($user) use ($activity) {
MemberPoint::firstOrCreate(
['user_id' => $user->id, 'source_type' => 'activity', 'source_id' => $activity->id],
['points' => 10, 'reason' => "Hadir di kegiatan: {$activity->title}"]
);
});
});
// Poin dari artikel yang sudah published
Post::where('status', 'published')->each(function ($post) {
MemberPoint::firstOrCreate(
['user_id' => $post->user_id, 'source_type' => 'post', 'source_id' => $post->id],
['points' => 5, 'reason' => "Artikel dipublikasi: {$post->title}"]
);
});
}
}
+1
View File
@@ -44,6 +44,7 @@ class PermissionSeeder extends Seeder
'ViewAny:Activity', 'View:Activity',
'ViewAny:Vote', 'View:Vote',
'ViewAny:Post', 'View:Post', 'Create:Post', 'Update:Post', 'Delete:Post',
'ViewAny:MemberPoint', 'View:MemberPoint',
])->get());
// Auditor: read-only semua + akses audit